1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
8 * Base class for all our document implementations.
11 #include "mozilla/dom/Document.h"
12 #include "mozilla/dom/DocumentInlines.h"
20 #include <initializer_list>
23 #include <type_traits>
25 #include "ErrorList.h"
26 #include "ExpandedPrincipal.h"
27 #include "MainThreadUtils.h"
28 #include "MobileViewportManager.h"
29 #include "NodeUbiReporting.h"
30 #include "PLDHashTable.h"
31 #include "StorageAccessPermissionRequest.h"
32 #include "ThirdPartyUtil.h"
34 #include "gfxPlatform.h"
35 #include "imgIContainer.h"
36 #include "imgLoader.h"
37 #include "imgRequestProxy.h"
40 #include "mozAutoDocUpdate.h"
41 #include "mozIDOMWindow.h"
42 #include "mozIThirdPartyUtil.h"
43 #include "mozilla/AntiTrackingUtils.h"
44 #include "mozilla/ArrayIterator.h"
45 #include "mozilla/ArrayUtils.h"
46 #include "mozilla/AsyncEventDispatcher.h"
47 #include "mozilla/Base64.h"
48 #include "mozilla/BasePrincipal.h"
49 #include "mozilla/CSSEnabledState.h"
50 #include "mozilla/ContentBlockingAllowList.h"
51 #include "mozilla/ContentBlockingNotifier.h"
52 #include "mozilla/ContentBlockingUserInteraction.h"
53 #include "mozilla/ContentPrincipal.h"
54 #include "mozilla/CycleCollectedJSContext.h"
55 #include "mozilla/DebugOnly.h"
56 #include "mozilla/ProfilerMarkers.h"
57 #include "mozilla/AttributeStyles.h"
58 #include "mozilla/DocumentStyleRootIterator.h"
59 #include "mozilla/EditorBase.h"
60 #include "mozilla/EditorCommands.h"
61 #include "mozilla/Encoding.h"
62 #include "mozilla/ErrorResult.h"
63 #include "mozilla/EventDispatcher.h"
64 #include "mozilla/EventListenerManager.h"
65 #include "mozilla/EventQueue.h"
66 #include "mozilla/EventStateManager.h"
67 #include "mozilla/ExtensionPolicyService.h"
68 #include "mozilla/FullscreenChange.h"
69 #include "mozilla/GlobalStyleSheetCache.h"
70 #include "mozilla/MappedDeclarationsBuilder.h"
71 #include "mozilla/HTMLEditor.h"
72 #include "mozilla/HoldDropJSObjects.h"
73 #include "mozilla/IdentifierMapEntry.h"
74 #include "mozilla/InputTaskManager.h"
75 #include "mozilla/IntegerRange.h"
76 #include "mozilla/InternalMutationEvent.h"
77 #include "mozilla/Likely.h"
78 #include "mozilla/Logging.h"
79 #include "mozilla/LookAndFeel.h"
80 #include "mozilla/MacroForEach.h"
81 #include "mozilla/Maybe.h"
82 #include "mozilla/MediaFeatureChange.h"
83 #include "mozilla/MediaManager.h"
84 #include "mozilla/MemoryReporting.h"
85 #include "mozilla/NullPrincipal.h"
86 #include "mozilla/OriginAttributes.h"
87 #include "mozilla/OwningNonNull.h"
88 #include "mozilla/PendingFullscreenEvent.h"
89 #include "mozilla/PermissionDelegateHandler.h"
90 #include "mozilla/PermissionManager.h"
91 #include "mozilla/Preferences.h"
92 #include "mozilla/PreloadHashKey.h"
93 #include "mozilla/PresShell.h"
94 #include "mozilla/PresShellForwards.h"
95 #include "mozilla/PresShellInlines.h"
96 #include "mozilla/PseudoStyleType.h"
97 #include "mozilla/RefCountType.h"
98 #include "mozilla/RelativeTo.h"
99 #include "mozilla/RestyleManager.h"
100 #include "mozilla/ReverseIterator.h"
101 #include "mozilla/SchedulerGroup.h"
102 #include "mozilla/ScrollTimelineAnimationTracker.h"
103 #include "mozilla/SMILAnimationController.h"
104 #include "mozilla/SMILTimeContainer.h"
105 #include "mozilla/ScopeExit.h"
106 #include "mozilla/Components.h"
107 #include "mozilla/ServoStyleConsts.h"
108 #include "mozilla/ServoTypes.h"
109 #include "mozilla/SizeOfState.h"
110 #include "mozilla/Span.h"
111 #include "mozilla/Sprintf.h"
112 #include "mozilla/StaticAnalysisFunctions.h"
113 #include "mozilla/StaticPrefs_apz.h"
114 #include "mozilla/StaticPrefs_browser.h"
115 #include "mozilla/StaticPrefs_docshell.h"
116 #include "mozilla/StaticPrefs_dom.h"
117 #include "mozilla/StaticPrefs_fission.h"
118 #include "mozilla/StaticPrefs_full_screen_api.h"
119 #include "mozilla/StaticPrefs_layout.h"
120 #include "mozilla/StaticPrefs_network.h"
121 #include "mozilla/StaticPrefs_page_load.h"
122 #include "mozilla/StaticPrefs_privacy.h"
123 #include "mozilla/StaticPrefs_security.h"
124 #include "mozilla/StaticPrefs_widget.h"
125 #include "mozilla/StaticPresData.h"
126 #include "mozilla/StorageAccess.h"
127 #include "mozilla/StoragePrincipalHelper.h"
128 #include "mozilla/StyleSheet.h"
129 #include "mozilla/Telemetry.h"
130 #include "mozilla/TelemetryHistogramEnums.h"
131 #include "mozilla/TelemetryScalarEnums.h"
132 #include "mozilla/TextControlElement.h"
133 #include "mozilla/TextEditor.h"
134 #include "mozilla/TypedEnumBits.h"
135 #include "mozilla/URLDecorationStripper.h"
136 #include "mozilla/URLExtraData.h"
137 #include "mozilla/Unused.h"
138 #include "mozilla/css/ImageLoader.h"
139 #include "mozilla/css/Loader.h"
140 #include "mozilla/css/Rule.h"
141 #include "mozilla/css/SheetParsingMode.h"
142 #include "mozilla/dom/AnonymousContent.h"
143 #include "mozilla/dom/BlobURLProtocolHandler.h"
144 #include "mozilla/dom/BrowserChild.h"
145 #include "mozilla/dom/BrowsingContext.h"
146 #include "mozilla/dom/BrowsingContextGroup.h"
147 #include "mozilla/dom/CanonicalBrowsingContext.h"
148 #include "mozilla/dom/CanvasRenderingContextHelper.h"
149 #include "mozilla/dom/CDATASection.h"
150 #include "mozilla/dom/CSPDictionariesBinding.h"
151 #include "mozilla/dom/ChromeObserver.h"
152 #include "mozilla/dom/ClientInfo.h"
153 #include "mozilla/dom/ClientState.h"
154 #include "mozilla/dom/Comment.h"
155 #include "mozilla/dom/ContentChild.h"
156 #include "mozilla/dom/CSSBinding.h"
157 #include "mozilla/dom/CSSCustomPropertyRegisteredEvent.h"
158 #include "mozilla/dom/DOMImplementation.h"
159 #include "mozilla/dom/DOMIntersectionObserver.h"
160 #include "mozilla/dom/DOMStringList.h"
161 #include "mozilla/dom/DocGroup.h"
162 #include "mozilla/dom/DocumentBinding.h"
163 #include "mozilla/dom/DocumentFragment.h"
164 #include "mozilla/dom/DocumentL10n.h"
165 #include "mozilla/dom/DocumentTimeline.h"
166 #include "mozilla/dom/DocumentType.h"
167 #include "mozilla/dom/ElementBinding.h"
168 #include "mozilla/dom/ErrorEvent.h"
169 #include "mozilla/dom/Event.h"
170 #include "mozilla/dom/EventListenerBinding.h"
171 #include "mozilla/dom/FailedCertSecurityInfoBinding.h"
172 #include "mozilla/dom/FeaturePolicy.h"
173 #include "mozilla/dom/FeaturePolicyUtils.h"
174 #include "mozilla/dom/FontFaceSet.h"
175 #include "mozilla/dom/FromParser.h"
176 #include "mozilla/dom/HighlightRegistry.h"
177 #include "mozilla/dom/HTMLAllCollection.h"
178 #include "mozilla/dom/HTMLBodyElement.h"
179 #include "mozilla/dom/HTMLCollectionBinding.h"
180 #include "mozilla/dom/HTMLDialogElement.h"
181 #include "mozilla/dom/HTMLFormElement.h"
182 #include "mozilla/dom/HTMLIFrameElement.h"
183 #include "mozilla/dom/HTMLImageElement.h"
184 #include "mozilla/dom/HTMLInputElement.h"
185 #include "mozilla/dom/HTMLLinkElement.h"
186 #include "mozilla/dom/HTMLMediaElement.h"
187 #include "mozilla/dom/HTMLMetaElement.h"
188 #include "mozilla/dom/HTMLSharedElement.h"
189 #include "mozilla/dom/HTMLTextAreaElement.h"
190 #include "mozilla/dom/ImageTracker.h"
191 #include "mozilla/dom/InspectorUtils.h"
192 #include "mozilla/dom/Link.h"
193 #include "mozilla/dom/MediaQueryList.h"
194 #include "mozilla/dom/MediaSource.h"
195 #include "mozilla/dom/MutationObservers.h"
196 #include "mozilla/dom/NameSpaceConstants.h"
197 #include "mozilla/dom/Navigator.h"
198 #include "mozilla/dom/NetErrorInfoBinding.h"
199 #include "mozilla/dom/NodeInfo.h"
200 #include "mozilla/dom/NodeIterator.h"
201 #include "mozilla/dom/PContentChild.h"
202 #include "mozilla/dom/PWindowGlobalChild.h"
203 #include "mozilla/dom/PageTransitionEvent.h"
204 #include "mozilla/dom/PageTransitionEventBinding.h"
205 #include "mozilla/dom/Performance.h"
206 #include "mozilla/dom/PermissionMessageUtils.h"
207 #include "mozilla/dom/PostMessageEvent.h"
208 #include "mozilla/dom/ProcessingInstruction.h"
209 #include "mozilla/dom/Promise.h"
210 #include "mozilla/dom/PromiseNativeHandler.h"
211 #include "mozilla/dom/ResizeObserver.h"
212 #include "mozilla/dom/RustTypes.h"
213 #include "mozilla/dom/SVGElement.h"
214 #include "mozilla/dom/SVGDocument.h"
215 #include "mozilla/dom/SVGSVGElement.h"
216 #include "mozilla/dom/SVGUseElement.h"
217 #include "mozilla/dom/ScriptLoader.h"
218 #include "mozilla/dom/ScriptSettings.h"
219 #include "mozilla/dom/Selection.h"
220 #include "mozilla/dom/ServiceWorkerContainer.h"
221 #include "mozilla/dom/ServiceWorkerDescriptor.h"
222 #include "mozilla/dom/ServiceWorkerManager.h"
223 #include "mozilla/dom/ShadowIncludingTreeIterator.h"
224 #include "mozilla/dom/ShadowRoot.h"
225 #include "mozilla/dom/StyleSheetApplicableStateChangeEvent.h"
226 #include "mozilla/dom/StyleSheetApplicableStateChangeEventBinding.h"
227 #include "mozilla/dom/StyleSheetList.h"
228 #include "mozilla/dom/StyleSheetRemovedEvent.h"
229 #include "mozilla/dom/StyleSheetRemovedEventBinding.h"
230 #include "mozilla/dom/TimeoutManager.h"
231 #include "mozilla/dom/ToggleEvent.h"
232 #include "mozilla/dom/Touch.h"
233 #include "mozilla/dom/TouchEvent.h"
234 #include "mozilla/dom/TreeOrderedArrayInlines.h"
235 #include "mozilla/dom/TreeWalker.h"
236 #include "mozilla/dom/URL.h"
237 #include "mozilla/dom/UseCounterMetrics.h"
238 #include "mozilla/dom/UserActivation.h"
239 #include "mozilla/dom/WakeLockJS.h"
240 #include "mozilla/dom/WakeLockSentinel.h"
241 #include "mozilla/dom/WindowBinding.h"
242 #include "mozilla/dom/WindowContext.h"
243 #include "mozilla/dom/WindowGlobalChild.h"
244 #include "mozilla/dom/WindowProxyHolder.h"
245 #include "mozilla/dom/WorkerDocumentListener.h"
246 #include "mozilla/dom/XPathEvaluator.h"
247 #include "mozilla/dom/XPathExpression.h"
248 #include "mozilla/dom/nsCSPContext.h"
249 #include "mozilla/dom/nsCSPUtils.h"
250 #include "mozilla/extensions/WebExtensionPolicy.h"
251 #include "mozilla/fallible.h"
252 #include "mozilla/gfx/BaseCoord.h"
253 #include "mozilla/gfx/BaseSize.h"
254 #include "mozilla/gfx/Coord.h"
255 #include "mozilla/gfx/Point.h"
256 #include "mozilla/gfx/ScaleFactor.h"
257 #include "mozilla/glean/GleanMetrics.h"
258 #include "mozilla/intl/LocaleService.h"
259 #include "mozilla/ipc/IdleSchedulerChild.h"
260 #include "mozilla/ipc/MessageChannel.h"
261 #include "mozilla/net/ChannelEventQueue.h"
262 #include "mozilla/net/CookieJarSettings.h"
263 #include "mozilla/net/NeckoChannelParams.h"
264 #include "mozilla/net/RequestContextService.h"
265 #include "nsAboutProtocolUtils.h"
266 #include "nsAlgorithm.h"
267 #include "nsAttrValue.h"
268 #include "nsAttrValueInlines.h"
269 #include "nsBaseHashtable.h"
270 #include "nsBidiUtils.h"
272 #include "nsCSSPropertyID.h"
273 #include "nsCSSProps.h"
274 #include "nsCSSPseudoElements.h"
275 #include "nsCSSRendering.h"
276 #include "nsCanvasFrame.h"
277 #include "nsCaseTreatment.h"
278 #include "nsCharsetSource.h"
279 #include "nsCommandManager.h"
280 #include "nsCommandParams.h"
281 #include "nsComponentManagerUtils.h"
282 #include "nsContentCreatorFunctions.h"
283 #include "nsContentList.h"
284 #include "nsContentPermissionHelper.h"
285 #include "nsContentSecurityUtils.h"
286 #include "nsContentUtils.h"
288 #include "nsCycleCollectionNoteChild.h"
289 #include "nsCycleCollectionTraversalCallback.h"
290 #include "nsDOMAttributeMap.h"
291 #include "nsDOMCaretPosition.h"
292 #include "nsDOMNavigationTiming.h"
293 #include "nsDOMString.h"
294 #include "nsDeviceContext.h"
295 #include "nsDocShell.h"
296 #include "nsDocShellLoadTypes.h"
297 #include "nsEffectiveTLDService.h"
299 #include "nsEscape.h"
300 #include "nsFocusManager.h"
301 #include "nsFrameLoader.h"
302 #include "nsFrameLoaderOwner.h"
303 #include "nsGenericHTMLElement.h"
304 #include "nsGlobalWindowInner.h"
305 #include "nsGlobalWindowOuter.h"
306 #include "nsHTMLDocument.h"
307 #include "nsHtml5Module.h"
308 #include "nsHtml5Parser.h"
309 #include "nsHtml5TreeOpExecutor.h"
310 #include "nsIAsyncShutdown.h"
311 #include "nsIAuthPrompt.h"
312 #include "nsIAuthPrompt2.h"
313 #include "nsIBFCacheEntry.h"
314 #include "nsIBaseWindow.h"
315 #include "nsIBrowserChild.h"
316 #include "nsIBrowserUsage.h"
317 #include "nsICSSLoaderObserver.h"
318 #include "nsICategoryManager.h"
319 #include "nsICertOverrideService.h"
320 #include "nsIContent.h"
321 #include "nsIContentInlines.h"
322 #include "nsIContentPolicy.h"
323 #include "nsIContentSecurityPolicy.h"
324 #include "nsIContentSink.h"
325 #include "nsICookieJarSettings.h"
326 #include "nsICookieService.h"
327 #include "nsIDOMXULCommandDispatcher.h"
328 #include "nsIDocShell.h"
329 #include "nsIDocShellTreeItem.h"
330 #include "nsIDocumentActivity.h"
331 #include "nsIDocumentEncoder.h"
332 #include "nsIDocumentLoader.h"
333 #include "nsIDocumentLoaderFactory.h"
334 #include "nsIDocumentObserver.h"
335 #include "nsIDNSService.h"
336 #include "nsIEditingSession.h"
337 #include "nsIEditor.h"
338 #include "nsIEffectiveTLDService.h"
340 #include "nsIFileChannel.h"
341 #include "nsIFrame.h"
342 #include "nsIGlobalObject.h"
343 #include "nsIHTMLCollection.h"
344 #include "nsIHttpChannel.h"
345 #include "nsIHttpChannelInternal.h"
346 #include "nsIIOService.h"
347 #include "nsIImageLoadingContent.h"
348 #include "nsIInlineSpellChecker.h"
349 #include "nsIInputStreamChannel.h"
350 #include "nsIInterfaceRequestorUtils.h"
351 #include "nsILayoutHistoryState.h"
352 #include "nsIMultiPartChannel.h"
353 #include "nsIMutationObserver.h"
354 #include "nsINSSErrorsService.h"
355 #include "nsINamed.h"
356 #include "nsINodeList.h"
357 #include "nsIObjectLoadingContent.h"
358 #include "nsIObserverService.h"
359 #include "nsIPermission.h"
360 #include "nsIPrompt.h"
361 #include "nsIPropertyBag2.h"
362 #include "nsIPublicKeyPinningService.h"
363 #include "nsIReferrerInfo.h"
364 #include "nsIRefreshURI.h"
365 #include "nsIRequest.h"
366 #include "nsIRequestContext.h"
367 #include "nsIRunnable.h"
368 #include "nsISHEntry.h"
369 #include "nsIScriptElement.h"
370 #include "nsIScriptError.h"
371 #include "nsIScriptGlobalObject.h"
372 #include "nsIScriptSecurityManager.h"
373 #include "nsISecurityConsoleMessage.h"
374 #include "nsISelectionController.h"
375 #include "nsISerialEventTarget.h"
376 #include "nsISimpleEnumerator.h"
377 #include "nsISiteSecurityService.h"
378 #include "nsISocketProvider.h"
379 #include "nsISpeculativeConnect.h"
380 #include "nsIStructuredCloneContainer.h"
381 #include "nsIThread.h"
382 #include "nsITimedChannel.h"
383 #include "nsITimer.h"
384 #include "nsITransportSecurityInfo.h"
385 #include "nsIURIMutator.h"
386 #include "nsIVariant.h"
387 #include "nsIWeakReference.h"
388 #include "nsIWebNavigation.h"
389 #include "nsIWidget.h"
390 #include "nsIX509Cert.h"
391 #include "nsIX509CertValidity.h"
392 #include "nsIXMLContentSink.h"
393 #include "nsIHTMLContentSink.h"
394 #include "nsIXULRuntime.h"
395 #include "nsImageLoadingContent.h"
396 #include "nsImportModule.h"
397 #include "nsLanguageAtomService.h"
398 #include "nsLayoutUtils.h"
399 #include "nsMimeTypes.h"
400 #include "nsNetCID.h"
401 #include "nsNetUtil.h"
402 #include "nsNodeInfoManager.h"
403 #include "nsObjectLoadingContent.h"
404 #include "nsPIDOMWindowInlines.h"
405 #include "nsPIWindowRoot.h"
407 #include "nsPointerHashKeys.h"
408 #include "nsPresContext.h"
409 #include "nsQueryFrame.h"
410 #include "nsQueryObject.h"
413 #include "nsRefreshDriver.h"
414 #include "nsSandboxFlags.h"
415 #include "nsSerializationHelper.h"
416 #include "nsServiceManagerUtils.h"
417 #include "nsStringFlags.h"
418 #include "nsStyleUtil.h"
419 #include "nsStringIterator.h"
420 #include "nsStyleSheetService.h"
421 #include "nsStyleStruct.h"
422 #include "nsTextNode.h"
423 #include "nsUnicharUtils.h"
424 #include "nsWrapperCache.h"
425 #include "nsWrapperCacheInlines.h"
426 #include "nsXPCOMCID.h"
427 #include "nsXULAppAPI.h"
428 #include "prthread.h"
431 #include "xpcpublic.h"
433 // XXX Must be included after mozilla/Encoding.h
434 #include "encoding_rs.h"
436 #include "mozilla/dom/XULBroadcastManager.h"
437 #include "mozilla/dom/XULPersist.h"
438 #include "nsIAppWindow.h"
439 #include "nsXULPrototypeDocument.h"
440 #include "nsXULCommandDispatcher.h"
441 #include "nsXULPopupManager.h"
442 #include "nsIDocShellTreeOwner.h"
444 #define XML_DECLARATION_BITS_DECLARATION_EXISTS (1 << 0)
445 #define XML_DECLARATION_BITS_ENCODING_EXISTS (1 << 1)
446 #define XML_DECLARATION_BITS_STANDALONE_EXISTS (1 << 2)
447 #define XML_DECLARATION_BITS_STANDALONE_YES (1 << 3)
449 #define NS_MAX_DOCUMENT_WRITE_DEPTH 20
451 mozilla::LazyLogModule
gPageCacheLog("PageCache");
452 mozilla::LazyLogModule
gSHIPBFCacheLog("SHIPBFCache");
453 mozilla::LazyLogModule
gTimeoutDeferralLog("TimeoutDefer");
454 mozilla::LazyLogModule
gUseCountersLog("UseCounters");
459 class Document::HeaderData
{
461 HeaderData(nsAtom
* aField
, const nsAString
& aData
)
462 : mField(aField
), mData(aData
) {}
465 // Delete iteratively to avoid blowing up the stack, though it shouldn't
466 // happen in practice.
467 UniquePtr
<HeaderData
> next
= std::move(mNext
);
469 next
= std::move(next
->mNext
);
473 RefPtr
<nsAtom
> mField
;
475 UniquePtr
<HeaderData
> mNext
;
478 AutoTArray
<Document
*, 8>* Document::sLoadingForegroundTopLevelContentDocument
=
481 static LazyLogModule
gDocumentLeakPRLog("DocumentLeak");
482 static LazyLogModule
gCspPRLog("CSP");
483 LazyLogModule
gUserInteractionPRLog("UserInteraction");
485 static nsresult
GetHttpChannelHelper(nsIChannel
* aChannel
,
486 nsIHttpChannel
** aHttpChannel
) {
487 nsCOMPtr
<nsIHttpChannel
> httpChannel
= do_QueryInterface(aChannel
);
489 httpChannel
.forget(aHttpChannel
);
493 nsCOMPtr
<nsIMultiPartChannel
> multipart
= do_QueryInterface(aChannel
);
495 *aHttpChannel
= nullptr;
499 nsCOMPtr
<nsIChannel
> baseChannel
;
500 nsresult rv
= multipart
->GetBaseChannel(getter_AddRefs(baseChannel
));
501 if (NS_WARN_IF(NS_FAILED(rv
))) {
505 httpChannel
= do_QueryInterface(baseChannel
);
506 httpChannel
.forget(aHttpChannel
);
513 #define NAME_NOT_VALID ((nsSimpleContentList*)1)
515 IdentifierMapEntry::IdentifierMapEntry(
516 const IdentifierMapEntry::DependentAtomOrString
* aKey
)
517 : mKey(aKey
? *aKey
: nullptr) {}
519 void IdentifierMapEntry::Traverse(
520 nsCycleCollectionTraversalCallback
* aCallback
) {
521 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*aCallback
,
522 "mIdentifierMap mNameContentList");
523 aCallback
->NoteXPCOMChild(static_cast<nsINodeList
*>(mNameContentList
));
526 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*aCallback
,
527 "mIdentifierMap mImageElement element");
528 nsIContent
* imageElement
= mImageElement
;
529 aCallback
->NoteXPCOMChild(imageElement
);
533 bool IdentifierMapEntry::IsEmpty() {
534 return mIdContentList
->IsEmpty() && !mNameContentList
&& !mChangeCallbacks
&&
538 bool IdentifierMapEntry::HasNameElement() const {
539 return mNameContentList
&& mNameContentList
->Length() != 0;
542 void IdentifierMapEntry::AddContentChangeCallback(
543 Document::IDTargetObserver aCallback
, void* aData
, bool aForImage
) {
544 if (!mChangeCallbacks
) {
545 mChangeCallbacks
= MakeUnique
<nsTHashtable
<ChangeCallbackEntry
>>();
548 ChangeCallback cc
= {aCallback
, aData
, aForImage
};
549 mChangeCallbacks
->PutEntry(cc
);
552 void IdentifierMapEntry::RemoveContentChangeCallback(
553 Document::IDTargetObserver aCallback
, void* aData
, bool aForImage
) {
554 if (!mChangeCallbacks
) return;
555 ChangeCallback cc
= {aCallback
, aData
, aForImage
};
556 mChangeCallbacks
->RemoveEntry(cc
);
557 if (mChangeCallbacks
->Count() == 0) {
558 mChangeCallbacks
= nullptr;
562 void IdentifierMapEntry::FireChangeCallbacks(Element
* aOldElement
,
563 Element
* aNewElement
,
565 if (!mChangeCallbacks
) return;
567 for (auto iter
= mChangeCallbacks
->Iter(); !iter
.Done(); iter
.Next()) {
568 IdentifierMapEntry::ChangeCallbackEntry
* entry
= iter
.Get();
569 // Don't fire image changes for non-image observers, and don't fire element
570 // changes for image observers when an image override is active.
571 if (entry
->mKey
.mForImage
? (mImageElement
&& !aImageOnly
) : aImageOnly
) {
575 if (!entry
->mKey
.mCallback(aOldElement
, aNewElement
, entry
->mKey
.mData
)) {
581 void IdentifierMapEntry::AddIdElement(Element
* aElement
) {
582 MOZ_ASSERT(aElement
, "Must have element");
583 MOZ_ASSERT(!mIdContentList
->Contains(nullptr), "Why is null in our list?");
585 size_t index
= mIdContentList
.Insert(*aElement
);
587 Element
* oldElement
= mIdContentList
->SafeElementAt(1);
588 FireChangeCallbacks(oldElement
, aElement
);
592 void IdentifierMapEntry::RemoveIdElement(Element
* aElement
) {
593 MOZ_ASSERT(aElement
, "Missing element");
595 // This should only be called while the document is in an update.
596 // Assertions near the call to this method guarantee this.
598 // This could fire in OOM situations
599 // Only assert this in HTML documents for now as XUL does all sorts of weird
601 NS_ASSERTION(!aElement
->OwnerDoc()->IsHTMLDocument() ||
602 mIdContentList
->Contains(aElement
),
603 "Removing id entry that doesn't exist");
605 // XXXbz should this ever Compact() I guess when all the content is gone
606 // we'll just get cleaned up in the natural order of things...
607 Element
* currentElement
= mIdContentList
->SafeElementAt(0);
608 mIdContentList
.RemoveElement(*aElement
);
609 if (currentElement
== aElement
) {
610 FireChangeCallbacks(currentElement
, mIdContentList
->SafeElementAt(0));
614 void IdentifierMapEntry::SetImageElement(Element
* aElement
) {
615 Element
* oldElement
= GetImageIdElement();
616 mImageElement
= aElement
;
617 Element
* newElement
= GetImageIdElement();
618 if (oldElement
!= newElement
) {
619 FireChangeCallbacks(oldElement
, newElement
, true);
623 void IdentifierMapEntry::ClearAndNotify() {
624 Element
* currentElement
= mIdContentList
->SafeElementAt(0);
625 mIdContentList
.Clear();
626 if (currentElement
) {
627 FireChangeCallbacks(currentElement
, nullptr);
629 mNameContentList
= nullptr;
631 SetImageElement(nullptr);
633 mChangeCallbacks
= nullptr;
638 class SimpleHTMLCollection final
: public nsSimpleContentList
,
639 public nsIHTMLCollection
{
641 explicit SimpleHTMLCollection(nsINode
* aRoot
) : nsSimpleContentList(aRoot
) {}
643 NS_DECL_ISUPPORTS_INHERITED
645 virtual nsINode
* GetParentObject() override
{
646 return nsSimpleContentList::GetParentObject();
648 virtual uint32_t Length() override
{ return nsSimpleContentList::Length(); }
649 virtual Element
* GetElementAt(uint32_t aIndex
) override
{
650 return mElements
.SafeElementAt(aIndex
)->AsElement();
653 virtual Element
* GetFirstNamedElement(const nsAString
& aName
,
654 bool& aFound
) override
{
656 RefPtr
<nsAtom
> name
= NS_Atomize(aName
);
657 for (uint32_t i
= 0; i
< mElements
.Length(); i
++) {
658 MOZ_DIAGNOSTIC_ASSERT(mElements
[i
]);
659 Element
* element
= mElements
[i
]->AsElement();
660 if (element
->GetID() == name
||
661 (element
->HasName() &&
662 element
->GetParsedAttr(nsGkAtoms::name
)->GetAtomValue() == name
)) {
670 virtual void GetSupportedNames(nsTArray
<nsString
>& aNames
) override
{
671 AutoTArray
<nsAtom
*, 8> atoms
;
672 for (uint32_t i
= 0; i
< mElements
.Length(); i
++) {
673 MOZ_DIAGNOSTIC_ASSERT(mElements
[i
]);
674 Element
* element
= mElements
[i
]->AsElement();
676 nsAtom
* id
= element
->GetID();
677 MOZ_ASSERT(id
!= nsGkAtoms::_empty
);
678 if (id
&& !atoms
.Contains(id
)) {
679 atoms
.AppendElement(id
);
682 if (element
->HasName()) {
683 nsAtom
* name
= element
->GetParsedAttr(nsGkAtoms::name
)->GetAtomValue();
684 MOZ_ASSERT(name
&& name
!= nsGkAtoms::_empty
);
685 if (name
&& !atoms
.Contains(name
)) {
686 atoms
.AppendElement(name
);
691 nsString
* names
= aNames
.AppendElements(atoms
.Length());
692 for (uint32_t i
= 0; i
< atoms
.Length(); i
++) {
693 atoms
[i
]->ToString(names
[i
]);
697 virtual JSObject
* GetWrapperPreserveColorInternal() override
{
698 return nsWrapperCache::GetWrapperPreserveColor();
700 virtual void PreserveWrapperInternal(
701 nsISupports
* aScriptObjectHolder
) override
{
702 nsWrapperCache::PreserveWrapper(aScriptObjectHolder
);
704 virtual JSObject
* WrapObject(JSContext
* aCx
,
705 JS::Handle
<JSObject
*> aGivenProto
) override
{
706 return HTMLCollection_Binding::Wrap(aCx
, this, aGivenProto
);
709 using nsBaseContentList::Item
;
712 virtual ~SimpleHTMLCollection() = default;
715 NS_IMPL_ISUPPORTS_INHERITED(SimpleHTMLCollection
, nsSimpleContentList
,
720 void IdentifierMapEntry::AddNameElement(nsINode
* aNode
, Element
* aElement
) {
721 if (!mNameContentList
) {
722 mNameContentList
= new dom::SimpleHTMLCollection(aNode
);
725 mNameContentList
->AppendElement(aElement
);
728 void IdentifierMapEntry::RemoveNameElement(Element
* aElement
) {
729 if (mNameContentList
) {
730 mNameContentList
->RemoveElement(aElement
);
734 bool IdentifierMapEntry::HasIdElementExposedAsHTMLDocumentProperty() const {
735 Element
* idElement
= GetIdElement();
737 nsGenericHTMLElement::ShouldExposeIdAsHTMLDocumentProperty(idElement
);
740 size_t IdentifierMapEntry::SizeOfExcludingThis(
741 MallocSizeOf aMallocSizeOf
) const {
742 return mKey
.mString
.SizeOfExcludingThisIfUnshared(aMallocSizeOf
);
745 // Helper structs for the content->subdoc map
747 class SubDocMapEntry
: public PLDHashEntryHdr
{
749 // Both of these are strong references
750 dom::Element
* mKey
; // must be first, to look like PLDHashEntryStub
751 dom::Document
* mSubDocument
;
754 class OnloadBlocker final
: public nsIRequest
{
756 OnloadBlocker() = default;
762 ~OnloadBlocker() = default;
765 NS_IMPL_ISUPPORTS(OnloadBlocker
, nsIRequest
)
768 OnloadBlocker::GetName(nsACString
& aResult
) {
769 aResult
.AssignLiteral("about:document-onload-blocker");
774 OnloadBlocker::IsPending(bool* _retval
) {
780 OnloadBlocker::GetStatus(nsresult
* status
) {
785 NS_IMETHODIMP
OnloadBlocker::SetCanceledReason(const nsACString
& aReason
) {
786 return SetCanceledReasonImpl(aReason
);
789 NS_IMETHODIMP
OnloadBlocker::GetCanceledReason(nsACString
& aReason
) {
790 return GetCanceledReasonImpl(aReason
);
793 NS_IMETHODIMP
OnloadBlocker::CancelWithReason(nsresult aStatus
,
794 const nsACString
& aReason
) {
795 return CancelWithReasonImpl(aStatus
, aReason
);
798 OnloadBlocker::Cancel(nsresult status
) { return NS_OK
; }
800 OnloadBlocker::Suspend(void) { return NS_OK
; }
802 OnloadBlocker::Resume(void) { return NS_OK
; }
805 OnloadBlocker::GetLoadGroup(nsILoadGroup
** aLoadGroup
) {
806 *aLoadGroup
= nullptr;
811 OnloadBlocker::SetLoadGroup(nsILoadGroup
* aLoadGroup
) { return NS_OK
; }
814 OnloadBlocker::GetLoadFlags(nsLoadFlags
* aLoadFlags
) {
815 *aLoadFlags
= nsIRequest::LOAD_NORMAL
;
820 OnloadBlocker::GetTRRMode(nsIRequest::TRRMode
* aTRRMode
) {
821 return GetTRRModeImpl(aTRRMode
);
825 OnloadBlocker::SetTRRMode(nsIRequest::TRRMode aTRRMode
) {
826 return SetTRRModeImpl(aTRRMode
);
830 OnloadBlocker::SetLoadFlags(nsLoadFlags aLoadFlags
) { return NS_OK
; }
832 // ==================================================================
836 ExternalResourceMap::ExternalResourceMap() : mHaveShutDown(false) {}
838 Document
* ExternalResourceMap::RequestResource(
839 nsIURI
* aURI
, nsIReferrerInfo
* aReferrerInfo
, nsINode
* aRequestingNode
,
840 Document
* aDisplayDocument
, ExternalResourceLoad
** aPendingLoad
) {
841 // If we ever start allowing non-same-origin loads here, we might need to do
842 // something interesting with aRequestingPrincipal even for the hashtable
844 MOZ_ASSERT(aURI
, "Must have a URI");
845 MOZ_ASSERT(aRequestingNode
, "Must have a node");
846 MOZ_ASSERT(aReferrerInfo
, "Must have a referrerInfo");
847 *aPendingLoad
= nullptr;
852 // First, make sure we strip the ref from aURI.
853 nsCOMPtr
<nsIURI
> clone
;
854 nsresult rv
= NS_GetURIWithoutRef(aURI
, getter_AddRefs(clone
));
855 if (NS_FAILED(rv
) || !clone
) {
859 ExternalResource
* resource
;
860 mMap
.Get(clone
, &resource
);
862 return resource
->mDocument
;
865 bool loadStartSucceeded
=
866 mPendingLoads
.WithEntryHandle(clone
, [&](auto&& loadEntry
) {
868 loadEntry
.Insert(MakeRefPtr
<PendingLoad
>(aDisplayDocument
));
870 if (NS_FAILED(loadEntry
.Data()->StartLoad(clone
, aReferrerInfo
,
876 RefPtr
<PendingLoad
> load(loadEntry
.Data());
877 load
.forget(aPendingLoad
);
880 if (!loadStartSucceeded
) {
881 // Make sure we don't thrash things by trying this load again, since
882 // chances are it failed for good reasons (security check, etc).
883 // This must be done outside the WithEntryHandle functor, as it accesses
885 AddExternalResource(clone
, nullptr, nullptr, aDisplayDocument
);
891 void ExternalResourceMap::EnumerateResources(SubDocEnumFunc aCallback
) {
892 nsTArray
<RefPtr
<Document
>> docs(mMap
.Count());
893 for (const auto& entry
: mMap
.Values()) {
894 if (Document
* doc
= entry
->mDocument
) {
895 docs
.AppendElement(doc
);
899 for (auto& doc
: docs
) {
900 if (aCallback(*doc
) == CallState::Stop
) {
906 void ExternalResourceMap::Traverse(
907 nsCycleCollectionTraversalCallback
* aCallback
) const {
908 // mPendingLoads will get cleared out as the requests complete, so
909 // no need to worry about those here.
910 for (const auto& entry
: mMap
) {
911 ExternalResourceMap::ExternalResource
* resource
= entry
.GetWeak();
913 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*aCallback
,
914 "mExternalResourceMap.mMap entry"
916 aCallback
->NoteXPCOMChild(ToSupports(resource
->mDocument
));
918 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*aCallback
,
919 "mExternalResourceMap.mMap entry"
921 aCallback
->NoteXPCOMChild(resource
->mViewer
);
923 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*aCallback
,
924 "mExternalResourceMap.mMap entry"
926 aCallback
->NoteXPCOMChild(resource
->mLoadGroup
);
930 void ExternalResourceMap::HideViewers() {
931 for (const auto& entry
: mMap
) {
932 nsCOMPtr
<nsIDocumentViewer
> viewer
= entry
.GetData()->mViewer
;
939 void ExternalResourceMap::ShowViewers() {
940 for (const auto& entry
: mMap
) {
941 nsCOMPtr
<nsIDocumentViewer
> viewer
= entry
.GetData()->mViewer
;
948 void TransferShowingState(Document
* aFromDoc
, Document
* aToDoc
) {
949 MOZ_ASSERT(aFromDoc
&& aToDoc
, "transferring showing state from/to null doc");
951 if (aFromDoc
->IsShowing()) {
952 aToDoc
->OnPageShow(true, nullptr);
956 nsresult
ExternalResourceMap::AddExternalResource(nsIURI
* aURI
,
957 nsIDocumentViewer
* aViewer
,
958 nsILoadGroup
* aLoadGroup
,
959 Document
* aDisplayDocument
) {
960 MOZ_ASSERT(aURI
, "Unexpected call");
961 MOZ_ASSERT((aViewer
&& aLoadGroup
) || (!aViewer
&& !aLoadGroup
),
962 "Must have both or neither");
964 RefPtr
<PendingLoad
> load
;
965 mPendingLoads
.Remove(aURI
, getter_AddRefs(load
));
969 nsCOMPtr
<Document
> doc
;
971 doc
= aViewer
->GetDocument();
972 NS_ASSERTION(doc
, "Must have a document");
974 doc
->SetDisplayDocument(aDisplayDocument
);
976 // Make sure that hiding our viewer will tear down its presentation.
977 aViewer
->SetSticky(false);
979 rv
= aViewer
->Init(nullptr, nsIntRect(0, 0, 0, 0), nullptr);
980 if (NS_SUCCEEDED(rv
)) {
981 rv
= aViewer
->Open(nullptr, nullptr);
987 aLoadGroup
= nullptr;
991 ExternalResource
* newResource
=
992 mMap
.InsertOrUpdate(aURI
, MakeUnique
<ExternalResource
>()).get();
994 newResource
->mDocument
= doc
;
995 newResource
->mViewer
= aViewer
;
996 newResource
->mLoadGroup
= aLoadGroup
;
998 if (nsPresContext
* pc
= doc
->GetPresContext()) {
999 pc
->RecomputeBrowsingContextDependentData();
1001 TransferShowingState(aDisplayDocument
, doc
);
1004 const nsTArray
<nsCOMPtr
<nsIObserver
>>& obs
= load
->Observers();
1005 for (uint32_t i
= 0; i
< obs
.Length(); ++i
) {
1006 obs
[i
]->Observe(ToSupports(doc
), "external-resource-document-created",
1013 NS_IMPL_ISUPPORTS(ExternalResourceMap::PendingLoad
, nsIStreamListener
,
1017 ExternalResourceMap::PendingLoad::OnStartRequest(nsIRequest
* aRequest
) {
1018 ExternalResourceMap
& map
= mDisplayDocument
->ExternalResourceMap();
1019 if (map
.HaveShutDown()) {
1020 return NS_BINDING_ABORTED
;
1023 nsCOMPtr
<nsIDocumentViewer
> viewer
;
1024 nsCOMPtr
<nsILoadGroup
> loadGroup
;
1026 SetupViewer(aRequest
, getter_AddRefs(viewer
), getter_AddRefs(loadGroup
));
1028 // Make sure to do this no matter what
1030 map
.AddExternalResource(mURI
, viewer
, loadGroup
, mDisplayDocument
);
1031 if (NS_FAILED(rv
)) {
1034 if (NS_FAILED(rv2
)) {
1035 mTargetListener
= nullptr;
1039 return mTargetListener
->OnStartRequest(aRequest
);
1042 nsresult
ExternalResourceMap::PendingLoad::SetupViewer(
1043 nsIRequest
* aRequest
, nsIDocumentViewer
** aViewer
,
1044 nsILoadGroup
** aLoadGroup
) {
1045 MOZ_ASSERT(!mTargetListener
, "Unexpected call to OnStartRequest");
1047 *aLoadGroup
= nullptr;
1049 nsCOMPtr
<nsIChannel
> chan(do_QueryInterface(aRequest
));
1050 NS_ENSURE_TRUE(chan
, NS_ERROR_UNEXPECTED
);
1052 nsCOMPtr
<nsIHttpChannel
> httpChannel(do_QueryInterface(aRequest
));
1054 bool requestSucceeded
;
1055 if (NS_FAILED(httpChannel
->GetRequestSucceeded(&requestSucceeded
)) ||
1056 !requestSucceeded
) {
1057 // Bail out on this load, since it looks like we have an HTTP error page
1058 return NS_BINDING_ABORTED
;
1063 chan
->GetContentType(type
);
1065 nsCOMPtr
<nsILoadGroup
> loadGroup
;
1066 chan
->GetLoadGroup(getter_AddRefs(loadGroup
));
1068 // Give this document its own loadgroup
1069 nsCOMPtr
<nsILoadGroup
> newLoadGroup
=
1070 do_CreateInstance(NS_LOADGROUP_CONTRACTID
);
1071 NS_ENSURE_TRUE(newLoadGroup
, NS_ERROR_OUT_OF_MEMORY
);
1072 newLoadGroup
->SetLoadGroup(loadGroup
);
1074 nsCOMPtr
<nsIInterfaceRequestor
> callbacks
;
1075 loadGroup
->GetNotificationCallbacks(getter_AddRefs(callbacks
));
1077 nsCOMPtr
<nsIInterfaceRequestor
> newCallbacks
=
1078 new LoadgroupCallbacks(callbacks
);
1079 newLoadGroup
->SetNotificationCallbacks(newCallbacks
);
1081 // This is some serious hackery cribbed from docshell
1082 nsCOMPtr
<nsICategoryManager
> catMan
=
1083 do_GetService(NS_CATEGORYMANAGER_CONTRACTID
);
1084 NS_ENSURE_TRUE(catMan
, NS_ERROR_NOT_AVAILABLE
);
1085 nsCString contractId
;
1087 catMan
->GetCategoryEntry("Gecko-Content-Viewers", type
, contractId
);
1088 NS_ENSURE_SUCCESS(rv
, rv
);
1089 nsCOMPtr
<nsIDocumentLoaderFactory
> docLoaderFactory
=
1090 do_GetService(contractId
.get());
1091 NS_ENSURE_TRUE(docLoaderFactory
, NS_ERROR_NOT_AVAILABLE
);
1093 nsCOMPtr
<nsIDocumentViewer
> viewer
;
1094 nsCOMPtr
<nsIStreamListener
> listener
;
1095 rv
= docLoaderFactory
->CreateInstance(
1096 "external-resource", chan
, newLoadGroup
, type
, nullptr, nullptr,
1097 getter_AddRefs(listener
), getter_AddRefs(viewer
));
1098 NS_ENSURE_SUCCESS(rv
, rv
);
1099 NS_ENSURE_TRUE(viewer
, NS_ERROR_UNEXPECTED
);
1101 nsCOMPtr
<nsIParser
> parser
= do_QueryInterface(listener
);
1103 /// We don't want to deal with the various fake documents yet
1104 return NS_ERROR_NOT_IMPLEMENTED
;
1107 // We can't handle HTML and other weird things here yet.
1108 nsIContentSink
* sink
= parser
->GetContentSink();
1109 nsCOMPtr
<nsIXMLContentSink
> xmlSink
= do_QueryInterface(sink
);
1111 return NS_ERROR_NOT_IMPLEMENTED
;
1114 listener
.swap(mTargetListener
);
1115 viewer
.forget(aViewer
);
1116 newLoadGroup
.forget(aLoadGroup
);
1121 ExternalResourceMap::PendingLoad::OnDataAvailable(nsIRequest
* aRequest
,
1122 nsIInputStream
* aStream
,
1125 // mTargetListener might be null if SetupViewer or AddExternalResource failed.
1126 NS_ENSURE_TRUE(mTargetListener
, NS_ERROR_FAILURE
);
1127 if (mDisplayDocument
->ExternalResourceMap().HaveShutDown()) {
1128 return NS_BINDING_ABORTED
;
1130 return mTargetListener
->OnDataAvailable(aRequest
, aStream
, aOffset
, aCount
);
1134 ExternalResourceMap::PendingLoad::OnStopRequest(nsIRequest
* aRequest
,
1136 // mTargetListener might be null if SetupViewer or AddExternalResource failed
1137 if (mTargetListener
) {
1138 nsCOMPtr
<nsIStreamListener
> listener
;
1139 mTargetListener
.swap(listener
);
1140 return listener
->OnStopRequest(aRequest
, aStatus
);
1146 nsresult
ExternalResourceMap::PendingLoad::StartLoad(
1147 nsIURI
* aURI
, nsIReferrerInfo
* aReferrerInfo
, nsINode
* aRequestingNode
) {
1148 MOZ_ASSERT(aURI
, "Must have a URI");
1149 MOZ_ASSERT(aRequestingNode
, "Must have a node");
1150 MOZ_ASSERT(aReferrerInfo
, "Must have a referrerInfo");
1152 nsCOMPtr
<nsILoadGroup
> loadGroup
=
1153 aRequestingNode
->OwnerDoc()->GetDocumentLoadGroup();
1155 nsresult rv
= NS_OK
;
1156 nsCOMPtr
<nsIChannel
> channel
;
1157 rv
= NS_NewChannel(getter_AddRefs(channel
), aURI
, aRequestingNode
,
1158 nsILoadInfo::SEC_REQUIRE_SAME_ORIGIN_INHERITS_SEC_CONTEXT
,
1159 nsIContentPolicy::TYPE_OTHER
,
1160 nullptr, // aPerformanceStorage
1162 NS_ENSURE_SUCCESS(rv
, rv
);
1164 nsCOMPtr
<nsIHttpChannel
> httpChannel(do_QueryInterface(channel
));
1166 rv
= httpChannel
->SetReferrerInfo(aReferrerInfo
);
1167 Unused
<< NS_WARN_IF(NS_FAILED(rv
));
1172 return channel
->AsyncOpen(this);
1175 NS_IMPL_ISUPPORTS(ExternalResourceMap::LoadgroupCallbacks
,
1176 nsIInterfaceRequestor
)
1178 #define IMPL_SHIM(_i) \
1179 NS_IMPL_ISUPPORTS(ExternalResourceMap::LoadgroupCallbacks::_i##Shim, _i)
1181 IMPL_SHIM(nsILoadContext
)
1182 IMPL_SHIM(nsIProgressEventSink
)
1183 IMPL_SHIM(nsIChannelEventSink
)
1187 #define IID_IS(_i) aIID.Equals(NS_GET_IID(_i))
1189 #define TRY_SHIM(_i) \
1192 nsCOMPtr<_i> real = do_GetInterface(mCallbacks); \
1194 return NS_NOINTERFACE; \
1196 nsCOMPtr<_i> shim = new _i##Shim(this, real); \
1197 shim.forget(aSink); \
1203 ExternalResourceMap::LoadgroupCallbacks::GetInterface(const nsIID
& aIID
,
1205 if (mCallbacks
&& (IID_IS(nsIPrompt
) || IID_IS(nsIAuthPrompt
) ||
1206 IID_IS(nsIAuthPrompt2
) || IID_IS(nsIBrowserChild
))) {
1207 return mCallbacks
->GetInterface(aIID
, aSink
);
1212 TRY_SHIM(nsILoadContext
);
1213 TRY_SHIM(nsIProgressEventSink
);
1214 TRY_SHIM(nsIChannelEventSink
);
1216 return NS_NOINTERFACE
;
1222 ExternalResourceMap::ExternalResource::~ExternalResource() {
1224 mViewer
->Close(nullptr);
1229 // ==================================================================
1231 // ==================================================================
1233 // If we ever have an nsIDocumentObserver notification for stylesheet title
1234 // changes we should update the list from that instead of overriding
1236 class DOMStyleSheetSetList final
: public DOMStringList
{
1238 explicit DOMStyleSheetSetList(Document
* aDocument
);
1240 void Disconnect() { mDocument
= nullptr; }
1242 virtual void EnsureFresh() override
;
1245 Document
* mDocument
; // Our document; weak ref. It'll let us know if it
1249 DOMStyleSheetSetList::DOMStyleSheetSetList(Document
* aDocument
)
1250 : mDocument(aDocument
) {
1251 NS_ASSERTION(mDocument
, "Must have document!");
1254 void DOMStyleSheetSetList::EnsureFresh() {
1255 MOZ_ASSERT(NS_IsMainThread());
1260 return; // Spec says "no exceptions", and we have no style sets if we have
1261 // no document, for sure
1264 size_t count
= mDocument
->SheetCount();
1266 for (size_t index
= 0; index
< count
; index
++) {
1267 StyleSheet
* sheet
= mDocument
->SheetAt(index
);
1268 NS_ASSERTION(sheet
, "Null sheet in sheet list!");
1269 sheet
->GetTitle(title
);
1270 if (!title
.IsEmpty() && !mNames
.Contains(title
) && !Add(title
)) {
1276 Document::PendingFrameStaticClone::~PendingFrameStaticClone() = default;
1278 // ==================================================================
1280 // ==================================================================
1282 Document::InternalCommandDataHashtable
*
1283 Document::sInternalCommandDataHashtable
= nullptr;
1286 void Document::Shutdown() {
1287 if (sInternalCommandDataHashtable
) {
1288 sInternalCommandDataHashtable
->Clear();
1289 delete sInternalCommandDataHashtable
;
1290 sInternalCommandDataHashtable
= nullptr;
1294 Document::Document(const char* aContentType
)
1296 DocumentOrShadowRoot(this),
1297 mCharacterSet(WINDOWS_1252_ENCODING
),
1298 mCharacterSetSource(0),
1299 mParentDocument(nullptr),
1300 mCachedRootElement(nullptr),
1301 mNodeInfoManager(nullptr),
1303 mStyledLinksCleared(false),
1305 mCachedStateObjectValid(false),
1306 mBlockAllMixedContent(false),
1307 mBlockAllMixedContentPreloads(false),
1308 mUpgradeInsecureRequests(false),
1309 mUpgradeInsecurePreloads(false),
1310 mDevToolsWatchingDOMMutations(false),
1311 mBidiEnabled(false),
1312 mMayNeedFontPrefsUpdate(true),
1313 mMathMLEnabled(false),
1314 mIsInitialDocumentInWindow(false),
1315 mIsEverInitialDocumentInWindow(false),
1316 mIgnoreDocGroupMismatches(false),
1317 mLoadedAsData(false),
1318 mAddedToMemoryReportingAsDataDocument(false),
1319 mMayStartLayout(true),
1320 mHaveFiredTitleChange(false),
1323 mRemovedFromDocShell(false),
1324 // mAllowDNSPrefetch starts true, so that we can always reliably && it
1325 // with various values that might disable it. Since we never prefetch
1326 // unless we get a window, and in that case the docshell value will get
1327 // &&-ed in, this is safe.
1328 mAllowDNSPrefetch(true),
1329 mIsStaticDocument(false),
1330 mCreatingStaticClone(false),
1331 mHasPrintCallbacks(false),
1332 mInUnlinkOrDeletion(false),
1333 mHasHadScriptHandlingObject(false),
1334 mIsBeingUsedAsImage(false),
1335 mChromeRulesEnabled(false),
1336 mInChromeDocShell(false),
1337 mIsSyntheticDocument(false),
1338 mHasLinksToUpdateRunnable(false),
1339 mFlushingPendingLinkUpdates(false),
1340 mMayHaveDOMMutationObservers(false),
1341 mMayHaveAnimationObservers(false),
1342 mHasCSPDeliveredThroughHeader(false),
1343 mBFCacheDisallowed(false),
1344 mHasHadDefaultView(false),
1345 mStyleSheetChangeEventsEnabled(false),
1346 mDevToolsAnonymousAndShadowEventsEnabled(false),
1347 mIsSrcdocDocument(false),
1348 mHasDisplayDocument(false),
1349 mFontFaceSetDirty(true),
1350 mDidFireDOMContentLoaded(true),
1351 mFrameRequestCallbacksScheduled(false),
1352 mIsTopLevelContentDocument(false),
1353 mIsContentDocument(false),
1354 mDidCallBeginLoad(false),
1355 mEncodingMenuDisabled(false),
1356 mLinksEnabled(true),
1357 mIsSVGGlyphsDocument(false),
1358 mInDestructor(false),
1359 mIsGoingAway(false),
1360 mStyleSetFilled(false),
1361 mQuirkSheetAdded(false),
1362 mContentEditableSheetAdded(false),
1363 mDesignModeSheetAdded(false),
1364 mMayHaveTitleElement(false),
1365 mDOMLoadingSet(false),
1366 mDOMInteractiveSet(false),
1367 mDOMCompleteSet(false),
1368 mAutoFocusFired(false),
1369 mScrolledToRefAlready(false),
1370 mChangeScrollPosWhenScrollingToRef(false),
1371 mDelayFrameLoaderInitialization(false),
1372 mSynchronousDOMContentLoaded(false),
1373 mMaybeServiceWorkerControlled(false),
1375 mValidScaleFloat(false),
1376 mValidMinScale(false),
1377 mValidMaxScale(false),
1378 mWidthStrEmpty(false),
1379 mParserAborted(false),
1380 mReportedDocumentUseCounters(false),
1381 mHasReportedShadowDOMUsage(false),
1382 mHasDelayedRefreshEvent(false),
1383 mLoadEventFiring(false),
1384 mSkipLoadEventAfterClose(false),
1385 mDisableCookieAccess(false),
1386 mDisableDocWrite(false),
1387 mTooDeepWriteRecursion(false),
1388 mPendingMaybeEditingStateChanged(false),
1389 mHasBeenEditable(false),
1390 mHasWarnedAboutZoom(false),
1391 mIsRunningExecCommandByContent(false),
1392 mIsRunningExecCommandByChromeOrAddon(false),
1393 mSetCompleteAfterDOMContentLoaded(false),
1394 mDidHitCompleteSheetCache(false),
1395 mUseCountersInitialized(false),
1396 mShouldReportUseCounters(false),
1397 mShouldSendPageUseCounters(false),
1398 mUserHasInteracted(false),
1399 mHasUserInteractionTimerScheduled(false),
1400 mShouldResistFingerprinting(false),
1401 mCloningForSVGUse(false),
1402 mAllowDeclarativeShadowRoots(false),
1403 mXMLDeclarationBits(0),
1404 mOnloadBlockCount(0),
1406 mContentEditableCount(0),
1407 mEditingState(EditingState::eOff
),
1408 mCompatMode(eCompatibility_FullStandards
),
1409 mReadyState(ReadyState::READYSTATE_UNINITIALIZED
),
1410 mAncestorIsLoading(false),
1411 mVisibilityState(dom::VisibilityState::Hidden
),
1413 mDefaultElementType(0),
1414 mAllowXULXBL(eTriUnset
),
1415 mSkipDTDSecurityChecks(false),
1416 mBidiOptions(IBMBIDI_DEFAULT_BIDI_OPTIONS
),
1419 mMarkedCCGeneration(0),
1420 mPresShell(nullptr),
1421 mSubtreeModifiedDepth(0),
1422 mPreloadPictureDepth(0),
1423 mEventsSuppressed(0),
1424 mIgnoreDestructiveWritesCounter(0),
1425 mStaticCloneCount(0),
1427 mBFCacheEntry(nullptr),
1428 mInSyncOperationCount(0),
1429 mBlockDOMContentLoaded(0),
1430 mUpdateNestLevel(0),
1431 mHttpsOnlyStatus(nsILoadInfo::HTTPS_ONLY_UNINITIALIZED
),
1432 mViewportType(Unknown
),
1433 mViewportFit(ViewportFitType::Auto
),
1434 mSubDocuments(nullptr),
1435 mHeaderData(nullptr),
1436 mServoRestyleRootDirtyBits(0),
1437 mThrowOnDynamicMarkupInsertionCounter(0),
1438 mIgnoreOpensDuringUnloadCounter(0),
1439 mSavedResolution(1.0f
),
1440 mSavedResolutionBeforeMVM(1.0f
),
1442 mCachedTabSizeGeneration(0),
1444 mNextControlNumber(0),
1445 mPreloadService(this),
1446 mShouldNotifyFetchSuccess(false),
1447 mShouldNotifyFormOrPasswordRemoved(false) {
1448 MOZ_LOG(gDocumentLeakPRLog
, LogLevel::Debug
, ("DOCUMENT %p created", this));
1451 SetIsConnected(true);
1453 // Create these unconditionally, they will be used to warn about the `zoom`
1454 // property, even if use counters are disabled.
1455 mStyleUseCounters
.reset(Servo_UseCounters_Create());
1457 SetContentType(nsDependentCString(aContentType
));
1459 // Start out mLastStyleSheetSet as null, per spec
1460 SetDOMStringToNull(mLastStyleSheetSet
);
1462 // void state used to differentiate an empty source from an unselected source
1463 mPreloadPictureFoundSource
.SetIsVoid(true);
1465 RecomputeLanguageFromCharset();
1467 mPreloadReferrerInfo
= new dom::ReferrerInfo(nullptr);
1468 mReferrerInfo
= new dom::ReferrerInfo(nullptr);
1472 // unused by GeckoView
1473 static bool IsAboutErrorPage(nsGlobalWindowInner
* aWin
, const char* aSpec
) {
1474 if (NS_WARN_IF(!aWin
)) {
1478 nsIURI
* uri
= aWin
->GetDocumentURI();
1479 if (NS_WARN_IF(!uri
)) {
1482 // getSpec is an expensive operation, hence we first check the scheme
1483 // to see if the caller is actually an about: page.
1484 if (!uri
->SchemeIs("about")) {
1488 nsAutoCString aboutSpec
;
1489 nsresult rv
= NS_GetAboutModuleName(uri
, aboutSpec
);
1490 NS_ENSURE_SUCCESS(rv
, false);
1492 return aboutSpec
.EqualsASCII(aSpec
);
1496 bool Document::CallerIsTrustedAboutNetError(JSContext
* aCx
, JSObject
* aObject
) {
1497 nsGlobalWindowInner
* win
= xpc::WindowOrNull(aObject
);
1499 // GeckoView uses data URLs for error pages, so for now just check for any
1501 return win
&& win
->GetDocument() && win
->GetDocument()->IsErrorPage();
1503 return win
&& IsAboutErrorPage(win
, "neterror");
1507 bool Document::CallerIsTrustedAboutHttpsOnlyError(JSContext
* aCx
,
1508 JSObject
* aObject
) {
1509 nsGlobalWindowInner
* win
= xpc::WindowOrNull(aObject
);
1511 // GeckoView uses data URLs for error pages, so for now just check for any
1513 return win
&& win
->GetDocument() && win
->GetDocument()->IsErrorPage();
1515 return win
&& IsAboutErrorPage(win
, "httpsonlyerror");
1519 already_AddRefed
<mozilla::dom::Promise
> Document::AddCertException(
1520 bool aIsTemporary
, ErrorResult
& aError
) {
1521 RefPtr
<Promise
> promise
= Promise::Create(GetScopeObject(), aError
,
1522 Promise::ePropagateUserInteraction
);
1523 if (aError
.Failed()) {
1527 nsresult rv
= NS_OK
;
1528 if (NS_WARN_IF(!mFailedChannel
)) {
1529 promise
->MaybeReject(NS_ERROR_DOM_INVALID_STATE_ERR
);
1530 return promise
.forget();
1533 nsCOMPtr
<nsIURI
> failedChannelURI
;
1534 NS_GetFinalChannelURI(mFailedChannel
, getter_AddRefs(failedChannelURI
));
1535 if (!failedChannelURI
) {
1536 promise
->MaybeReject(NS_ERROR_DOM_INVALID_STATE_ERR
);
1537 return promise
.forget();
1540 nsCOMPtr
<nsIURI
> innerURI
= NS_GetInnermostURI(failedChannelURI
);
1542 promise
->MaybeReject(NS_ERROR_DOM_INVALID_STATE_ERR
);
1543 return promise
.forget();
1547 innerURI
->GetAsciiHost(host
);
1549 innerURI
->GetPort(&port
);
1551 nsCOMPtr
<nsITransportSecurityInfo
> tsi
;
1552 rv
= mFailedChannel
->GetSecurityInfo(getter_AddRefs(tsi
));
1553 if (NS_WARN_IF(NS_FAILED(rv
))) {
1554 promise
->MaybeReject(rv
);
1555 return promise
.forget();
1557 if (NS_WARN_IF(!tsi
)) {
1558 promise
->MaybeReject(NS_ERROR_DOM_INVALID_STATE_ERR
);
1559 return promise
.forget();
1562 nsCOMPtr
<nsIX509Cert
> cert
;
1563 rv
= tsi
->GetServerCert(getter_AddRefs(cert
));
1564 if (NS_WARN_IF(NS_FAILED(rv
))) {
1565 promise
->MaybeReject(rv
);
1566 return promise
.forget();
1568 if (NS_WARN_IF(!cert
)) {
1569 promise
->MaybeReject(NS_ERROR_DOM_INVALID_STATE_ERR
);
1570 return promise
.forget();
1573 if (XRE_IsContentProcess()) {
1574 ContentChild
* cc
= ContentChild::GetSingleton();
1576 OriginAttributes
const& attrs
= NodePrincipal()->OriginAttributesRef();
1577 cc
->SendAddCertException(cert
, host
, port
, attrs
, aIsTemporary
)
1578 ->Then(GetCurrentSerialEventTarget(), __func__
,
1579 [promise
](const mozilla::MozPromise
<
1580 nsresult
, mozilla::ipc::ResponseRejectReason
,
1581 true>::ResolveOrRejectValue
& aValue
) {
1582 if (aValue
.IsResolve()) {
1583 promise
->MaybeResolve(aValue
.ResolveValue());
1585 promise
->MaybeRejectWithUndefined();
1588 return promise
.forget();
1591 if (XRE_IsParentProcess()) {
1592 nsCOMPtr
<nsICertOverrideService
> overrideService
=
1593 do_GetService(NS_CERTOVERRIDE_CONTRACTID
);
1594 if (!overrideService
) {
1595 promise
->MaybeReject(NS_ERROR_FAILURE
);
1596 return promise
.forget();
1599 OriginAttributes
const& attrs
= NodePrincipal()->OriginAttributesRef();
1600 rv
= overrideService
->RememberValidityOverride(host
, port
, attrs
, cert
,
1602 if (NS_WARN_IF(NS_FAILED(rv
))) {
1603 promise
->MaybeReject(rv
);
1604 return promise
.forget();
1607 promise
->MaybeResolveWithUndefined();
1608 return promise
.forget();
1611 promise
->MaybeReject(NS_ERROR_FAILURE
);
1612 return promise
.forget();
1615 void Document::ReloadWithHttpsOnlyException() {
1616 if (WindowGlobalChild
* wgc
= GetWindowGlobalChild()) {
1617 wgc
->SendReloadWithHttpsOnlyException();
1621 void Document::GetNetErrorInfo(NetErrorInfo
& aInfo
, ErrorResult
& aRv
) {
1622 nsresult rv
= NS_OK
;
1623 if (NS_WARN_IF(!mFailedChannel
)) {
1624 aRv
.Throw(NS_ERROR_DOM_INVALID_STATE_ERR
);
1628 nsCOMPtr
<nsITransportSecurityInfo
> tsi
;
1629 rv
= mFailedChannel
->GetSecurityInfo(getter_AddRefs(tsi
));
1630 if (NS_WARN_IF(NS_FAILED(rv
))) {
1634 if (NS_WARN_IF(!tsi
)) {
1635 aRv
.Throw(NS_ERROR_DOM_INVALID_STATE_ERR
);
1639 nsAutoString errorCodeString
;
1640 rv
= tsi
->GetErrorCodeString(errorCodeString
);
1641 if (NS_WARN_IF(NS_FAILED(rv
))) {
1645 aInfo
.mErrorCodeString
.Assign(errorCodeString
);
1648 bool Document::CallerIsTrustedAboutCertError(JSContext
* aCx
,
1649 JSObject
* aObject
) {
1650 nsGlobalWindowInner
* win
= xpc::WindowOrNull(aObject
);
1652 // GeckoView uses data URLs for error pages, so for now just check for any
1654 return win
&& win
->GetDocument() && win
->GetDocument()->IsErrorPage();
1656 return win
&& IsAboutErrorPage(win
, "certerror");
1660 bool Document::CallerCanAccessPrivilegeSSA(JSContext
* aCx
, JSObject
* aObject
) {
1661 RefPtr
<BasePrincipal
> principal
=
1662 BasePrincipal::Cast(nsContentUtils::SubjectPrincipal(aCx
));
1668 // We allow the privilege SSA to be called from system principal.
1669 if (principal
->IsSystemPrincipal()) {
1673 // We only allow calling the privilege SSA from the content script of the
1674 // webcompat extension.
1675 if (auto* policy
= principal
->ContentScriptAddonPolicy()) {
1676 nsAutoString addonID
;
1677 policy
->GetId(addonID
);
1679 return addonID
.EqualsLiteral("webcompat@mozilla.org");
1685 bool Document::IsErrorPage() const {
1686 nsCOMPtr
<nsILoadInfo
> loadInfo
= mChannel
? mChannel
->LoadInfo() : nullptr;
1687 return loadInfo
&& loadInfo
->GetLoadErrorPage();
1690 void Document::GetFailedCertSecurityInfo(FailedCertSecurityInfo
& aInfo
,
1692 nsresult rv
= NS_OK
;
1693 if (NS_WARN_IF(!mFailedChannel
)) {
1694 aRv
.Throw(NS_ERROR_DOM_INVALID_STATE_ERR
);
1698 nsCOMPtr
<nsITransportSecurityInfo
> tsi
;
1699 rv
= mFailedChannel
->GetSecurityInfo(getter_AddRefs(tsi
));
1700 if (NS_WARN_IF(NS_FAILED(rv
))) {
1704 if (NS_WARN_IF(!tsi
)) {
1705 aRv
.Throw(NS_ERROR_DOM_INVALID_STATE_ERR
);
1709 nsAutoString errorCodeString
;
1710 rv
= tsi
->GetErrorCodeString(errorCodeString
);
1711 if (NS_WARN_IF(NS_FAILED(rv
))) {
1715 aInfo
.mErrorCodeString
.Assign(errorCodeString
);
1717 nsITransportSecurityInfo::OverridableErrorCategory errorCategory
;
1718 rv
= tsi
->GetOverridableErrorCategory(&errorCategory
);
1719 if (NS_WARN_IF(NS_FAILED(rv
))) {
1723 switch (errorCategory
) {
1724 case nsITransportSecurityInfo::OverridableErrorCategory::ERROR_TRUST
:
1725 aInfo
.mOverridableErrorCategory
=
1726 dom::OverridableErrorCategory::Trust_error
;
1728 case nsITransportSecurityInfo::OverridableErrorCategory::ERROR_DOMAIN
:
1729 aInfo
.mOverridableErrorCategory
=
1730 dom::OverridableErrorCategory::Domain_mismatch
;
1732 case nsITransportSecurityInfo::OverridableErrorCategory::ERROR_TIME
:
1733 aInfo
.mOverridableErrorCategory
=
1734 dom::OverridableErrorCategory::Expired_or_not_yet_valid
;
1737 aInfo
.mOverridableErrorCategory
= dom::OverridableErrorCategory::Unset
;
1741 nsCOMPtr
<nsIX509Cert
> cert
;
1742 nsCOMPtr
<nsIX509CertValidity
> validity
;
1743 rv
= tsi
->GetServerCert(getter_AddRefs(cert
));
1744 if (NS_WARN_IF(NS_FAILED(rv
))) {
1748 if (NS_WARN_IF(!cert
)) {
1749 aRv
.Throw(NS_ERROR_DOM_INVALID_STATE_ERR
);
1753 rv
= cert
->GetValidity(getter_AddRefs(validity
));
1754 if (NS_WARN_IF(NS_FAILED(rv
))) {
1758 if (NS_WARN_IF(!validity
)) {
1759 aRv
.Throw(NS_ERROR_DOM_INVALID_STATE_ERR
);
1763 PRTime validityResult
;
1764 rv
= validity
->GetNotBefore(&validityResult
);
1765 if (NS_WARN_IF(NS_FAILED(rv
))) {
1769 aInfo
.mValidNotBefore
= DOMTimeStamp(validityResult
/ PR_USEC_PER_MSEC
);
1771 rv
= validity
->GetNotAfter(&validityResult
);
1772 if (NS_WARN_IF(NS_FAILED(rv
))) {
1776 aInfo
.mValidNotAfter
= DOMTimeStamp(validityResult
/ PR_USEC_PER_MSEC
);
1778 nsAutoString issuerCommonName
;
1779 nsAutoString certChainPEMString
;
1780 Sequence
<nsString
>& certChainStrings
= aInfo
.mCertChainStrings
.Construct();
1781 int64_t maxValidity
= std::numeric_limits
<int64_t>::max();
1782 int64_t minValidity
= 0;
1783 PRTime notBefore
, notAfter
;
1784 nsTArray
<RefPtr
<nsIX509Cert
>> failedCertArray
;
1785 rv
= tsi
->GetFailedCertChain(failedCertArray
);
1786 if (NS_WARN_IF(NS_FAILED(rv
))) {
1791 if (NS_WARN_IF(failedCertArray
.IsEmpty())) {
1792 aRv
.Throw(NS_ERROR_DOM_INVALID_STATE_ERR
);
1796 for (const auto& certificate
: failedCertArray
) {
1797 rv
= certificate
->GetIssuerCommonName(issuerCommonName
);
1798 if (NS_WARN_IF(NS_FAILED(rv
))) {
1803 rv
= certificate
->GetValidity(getter_AddRefs(validity
));
1804 if (NS_WARN_IF(NS_FAILED(rv
))) {
1808 if (NS_WARN_IF(!validity
)) {
1809 aRv
.Throw(NS_ERROR_DOM_INVALID_STATE_ERR
);
1813 rv
= validity
->GetNotBefore(¬Before
);
1814 if (NS_WARN_IF(NS_FAILED(rv
))) {
1819 rv
= validity
->GetNotAfter(¬After
);
1820 if (NS_WARN_IF(NS_FAILED(rv
))) {
1825 notBefore
= std::max(minValidity
, notBefore
);
1826 notAfter
= std::min(maxValidity
, notAfter
);
1827 nsTArray
<uint8_t> certArray
;
1828 rv
= certificate
->GetRawDER(certArray
);
1829 if (NS_WARN_IF(NS_FAILED(rv
))) {
1835 rv
= Base64Encode(reinterpret_cast<const char*>(certArray
.Elements()),
1836 certArray
.Length(), der64
);
1837 if (NS_WARN_IF(NS_FAILED(rv
))) {
1841 if (!certChainStrings
.AppendElement(der64
, fallible
)) {
1842 aRv
.Throw(NS_ERROR_OUT_OF_MEMORY
);
1847 aInfo
.mIssuerCommonName
.Assign(issuerCommonName
);
1848 aInfo
.mCertValidityRangeNotAfter
= DOMTimeStamp(notAfter
/ PR_USEC_PER_MSEC
);
1849 aInfo
.mCertValidityRangeNotBefore
=
1850 DOMTimeStamp(notBefore
/ PR_USEC_PER_MSEC
);
1853 rv
= tsi
->GetErrorCode(&errorCode
);
1854 if (NS_WARN_IF(NS_FAILED(rv
))) {
1859 nsCOMPtr
<nsINSSErrorsService
> nsserr
=
1860 do_GetService("@mozilla.org/nss_errors_service;1");
1861 if (NS_WARN_IF(!nsserr
)) {
1862 aRv
.Throw(NS_ERROR_DOM_INVALID_STATE_ERR
);
1866 rv
= nsserr
->GetXPCOMFromNSSError(errorCode
, &res
);
1867 if (NS_WARN_IF(NS_FAILED(rv
))) {
1871 rv
= nsserr
->GetErrorMessage(res
, aInfo
.mErrorMessage
);
1872 if (NS_WARN_IF(NS_FAILED(rv
))) {
1877 OriginAttributes attrs
;
1878 StoragePrincipalHelper::GetRegularPrincipalOriginAttributes(this, attrs
);
1879 nsCOMPtr
<nsIURI
> aURI
;
1880 mFailedChannel
->GetURI(getter_AddRefs(aURI
));
1881 if (XRE_IsContentProcess()) {
1882 ContentChild
* cc
= ContentChild::GetSingleton();
1884 cc
->SendIsSecureURI(aURI
, attrs
, &aInfo
.mHasHSTS
);
1886 nsCOMPtr
<nsISiteSecurityService
> sss
=
1887 do_GetService(NS_SSSERVICE_CONTRACTID
);
1888 if (NS_WARN_IF(!sss
)) {
1891 Unused
<< NS_WARN_IF(
1892 NS_FAILED(sss
->IsSecureURI(aURI
, attrs
, &aInfo
.mHasHSTS
)));
1894 nsCOMPtr
<nsIPublicKeyPinningService
> pkps
=
1895 do_GetService(NS_PKPSERVICE_CONTRACTID
);
1896 if (NS_WARN_IF(!pkps
)) {
1899 Unused
<< NS_WARN_IF(NS_FAILED(pkps
->HostHasPins(aURI
, &aInfo
.mHasHPKP
)));
1902 bool Document::IsAboutPage() const {
1903 return NodePrincipal()->SchemeIs("about");
1906 void Document::ConstructUbiNode(void* storage
) {
1907 JS::ubi::Concrete
<Document
>::construct(storage
, this);
1910 void Document::LoadEventFired() {
1911 // Object used to collect some telemetry data so we don't need to query for it
1913 glean::perf::PageLoadExtra pageLoadEventData
;
1915 // Accumulate timing data located in each document's realm and report to
1917 AccumulateJSTelemetry(pageLoadEventData
);
1919 // Collect page load timings
1920 AccumulatePageLoadTelemetry(pageLoadEventData
);
1922 // Record page load event
1923 RecordPageLoadEventTelemetry(pageLoadEventData
);
1925 // Release the JS bytecode cache from its wait on the load event, and
1926 // potentially dispatch the encoding of the bytecode.
1927 if (ScriptLoader()) {
1928 ScriptLoader()->LoadEventFired();
1932 static uint32_t ConvertToUnsignedFromDouble(double aNumber
) {
1933 return aNumber
< 0 ? 0 : static_cast<uint32_t>(aNumber
);
1936 void Document::RecordPageLoadEventTelemetry(
1937 glean::perf::PageLoadExtra
& aEventTelemetryData
) {
1938 // If the page load time is empty, then the content wasn't something we want
1939 // to report (i.e. not a top level document).
1940 if (!aEventTelemetryData
.loadTime
) {
1943 MOZ_ASSERT(IsTopLevelContentDocument());
1945 nsPIDOMWindowOuter
* window
= GetWindow();
1950 nsIDocShell
* docshell
= window
->GetDocShell();
1955 nsAutoCString loadTypeStr
;
1956 switch (docshell
->GetLoadType()) {
1958 case LOAD_NORMAL_REPLACE
:
1959 case LOAD_NORMAL_BYPASS_CACHE
:
1960 case LOAD_NORMAL_BYPASS_PROXY
:
1961 case LOAD_NORMAL_BYPASS_PROXY_AND_CACHE
:
1962 loadTypeStr
.Append("NORMAL");
1965 loadTypeStr
.Append("HISTORY");
1967 case LOAD_RELOAD_NORMAL
:
1968 case LOAD_RELOAD_BYPASS_CACHE
:
1969 case LOAD_RELOAD_BYPASS_PROXY
:
1970 case LOAD_RELOAD_BYPASS_PROXY_AND_CACHE
:
1972 case LOAD_REFRESH_REPLACE
:
1973 case LOAD_RELOAD_CHARSET_CHANGE
:
1974 case LOAD_RELOAD_CHARSET_CHANGE_BYPASS_PROXY_AND_CACHE
:
1975 case LOAD_RELOAD_CHARSET_CHANGE_BYPASS_CACHE
:
1976 loadTypeStr
.Append("RELOAD");
1979 loadTypeStr
.Append("LINK");
1981 case LOAD_STOP_CONTENT
:
1982 case LOAD_STOP_CONTENT_AND_REPLACE
:
1983 loadTypeStr
.Append("STOP");
1985 case LOAD_ERROR_PAGE
:
1986 loadTypeStr
.Append("ERROR");
1989 loadTypeStr
.Append("OTHER");
1993 nsCOMPtr
<nsIEffectiveTLDService
> tldService
=
1994 do_GetService(NS_EFFECTIVETLDSERVICE_CONTRACTID
);
1995 if (tldService
&& mReferrerInfo
&&
1996 (docshell
->GetLoadType() & nsIDocShell::LOAD_CMD_NORMAL
)) {
1997 nsAutoCString currentBaseDomain
, referrerBaseDomain
;
1998 nsCOMPtr
<nsIURI
> referrerURI
= mReferrerInfo
->GetComputedReferrer();
2000 auto result
= NS_SUCCEEDED(
2001 tldService
->GetBaseDomain(referrerURI
, 0, referrerBaseDomain
));
2003 bool sameOrigin
= false;
2004 NodePrincipal()->IsSameOrigin(referrerURI
, &sameOrigin
);
2005 aEventTelemetryData
.sameOriginNav
= mozilla::Some(sameOrigin
);
2010 aEventTelemetryData
.loadType
= mozilla::Some(loadTypeStr
);
2012 // Sending a glean ping must be done on the parent process.
2013 if (ContentChild
* cc
= ContentChild::GetSingleton()) {
2014 cc
->SendRecordPageLoadEvent(aEventTelemetryData
);
2018 void Document::AccumulatePageLoadTelemetry(
2019 glean::perf::PageLoadExtra
& aEventTelemetryDataOut
) {
2020 // Interested only in top level documents for real websites that are in the
2022 if (!ShouldIncludeInTelemetry() || !IsTopLevelContentDocument() ||
2023 !GetNavigationTiming() ||
2024 !GetNavigationTiming()->DocShellHasBeenActiveSinceNavigationStart()) {
2028 if (!GetChannel()) {
2032 nsCOMPtr
<nsITimedChannel
> timedChannel(do_QueryInterface(GetChannel()));
2033 if (!timedChannel
) {
2037 // Default duration is 0, use this to check for bogus negative values.
2038 const TimeDuration zeroDuration
;
2040 TimeStamp responseStart
;
2041 timedChannel
->GetResponseStart(&responseStart
);
2043 TimeStamp redirectStart
, redirectEnd
;
2044 timedChannel
->GetRedirectStart(&redirectStart
);
2045 timedChannel
->GetRedirectEnd(&redirectEnd
);
2047 uint8_t redirectCount
;
2048 timedChannel
->GetRedirectCount(&redirectCount
);
2049 if (redirectCount
) {
2050 aEventTelemetryDataOut
.redirectCount
=
2051 mozilla::Some(static_cast<uint32_t>(redirectCount
));
2054 if (!redirectStart
.IsNull() && !redirectEnd
.IsNull()) {
2055 TimeDuration redirectTime
= redirectEnd
- redirectStart
;
2056 if (redirectTime
> zeroDuration
) {
2057 aEventTelemetryDataOut
.redirectTime
=
2058 mozilla::Some(static_cast<uint32_t>(redirectTime
.ToMilliseconds()));
2062 TimeStamp dnsLookupStart
, dnsLookupEnd
;
2063 timedChannel
->GetDomainLookupStart(&dnsLookupStart
);
2064 timedChannel
->GetDomainLookupEnd(&dnsLookupEnd
);
2066 if (!dnsLookupStart
.IsNull() && !dnsLookupEnd
.IsNull()) {
2067 TimeDuration dnsLookupTime
= dnsLookupEnd
- dnsLookupStart
;
2068 if (dnsLookupTime
> zeroDuration
) {
2069 aEventTelemetryDataOut
.dnsLookupTime
=
2070 mozilla::Some(static_cast<uint32_t>(dnsLookupTime
.ToMilliseconds()));
2074 TimeStamp navigationStart
=
2075 GetNavigationTiming()->GetNavigationStartTimeStamp();
2077 if (!responseStart
|| !navigationStart
) {
2081 nsAutoCString
dnsKey("Native");
2082 nsAutoCString http3Key
;
2083 nsAutoCString http3WithPriorityKey
;
2084 nsAutoCString earlyHintKey
;
2085 nsCOMPtr
<nsIHttpChannelInternal
> httpChannel
=
2086 do_QueryInterface(GetChannel());
2088 bool resolvedByTRR
= false;
2089 Unused
<< httpChannel
->GetIsResolvedByTRR(&resolvedByTRR
);
2090 if (resolvedByTRR
) {
2091 if (nsCOMPtr
<nsIDNSService
> dns
=
2092 do_GetService(NS_DNSSERVICE_CONTRACTID
)) {
2093 dns
->GetTRRDomainKey(dnsKey
);
2095 // Failed to get the DNS service.
2096 dnsKey
= "(fail)"_ns
;
2098 aEventTelemetryDataOut
.trrDomain
= mozilla::Some(dnsKey
);
2103 if (NS_SUCCEEDED(httpChannel
->GetResponseVersion(&major
, &minor
))) {
2105 http3Key
= "http3"_ns
;
2106 nsCOMPtr
<nsIHttpChannel
> httpChannel2
= do_QueryInterface(GetChannel());
2110 httpChannel2
->GetResponseHeader("priority"_ns
, header
)) &&
2111 !header
.IsEmpty()) {
2112 http3WithPriorityKey
= "with_priority"_ns
;
2114 http3WithPriorityKey
= "without_priority"_ns
;
2116 } else if (major
== 2) {
2117 bool supportHttp3
= false;
2118 if (NS_FAILED(httpChannel
->GetSupportsHTTP3(&supportHttp3
))) {
2119 supportHttp3
= false;
2122 http3Key
= "supports_http3"_ns
;
2126 aEventTelemetryDataOut
.httpVer
= mozilla::Some(major
);
2129 uint32_t earlyHintType
= 0;
2130 Unused
<< httpChannel
->GetEarlyHintLinkType(&earlyHintType
);
2131 if (earlyHintType
& LinkStyle::ePRECONNECT
) {
2132 earlyHintKey
.Append("preconnect_"_ns
);
2134 if (earlyHintType
& LinkStyle::ePRELOAD
) {
2135 earlyHintKey
.Append("preload_"_ns
);
2136 earlyHintKey
.Append(mPreloadService
.GetEarlyHintUsed() ? "1"_ns
: "0"_ns
);
2140 TimeStamp asyncOpen
;
2141 timedChannel
->GetAsyncOpen(&asyncOpen
);
2143 Telemetry::AccumulateTimeDelta(Telemetry::DNS_PERF_FIRST_BYTE_MS
, dnsKey
,
2144 asyncOpen
, responseStart
);
2147 // First Contentful Composite
2148 if (TimeStamp firstContentfulComposite
=
2149 GetNavigationTiming()->GetFirstContentfulCompositeTimeStamp()) {
2150 Telemetry::AccumulateTimeDelta(Telemetry::PERF_FIRST_CONTENTFUL_PAINT_MS
,
2151 navigationStart
, firstContentfulComposite
);
2153 if (!http3Key
.IsEmpty()) {
2154 Telemetry::AccumulateTimeDelta(
2155 Telemetry::HTTP3_PERF_FIRST_CONTENTFUL_PAINT_MS
, http3Key
,
2156 navigationStart
, firstContentfulComposite
);
2159 if (!http3WithPriorityKey
.IsEmpty()) {
2160 Telemetry::AccumulateTimeDelta(
2161 Telemetry::H3P_PERF_FIRST_CONTENTFUL_PAINT_MS
, http3WithPriorityKey
,
2162 navigationStart
, firstContentfulComposite
);
2165 if (!earlyHintKey
.IsEmpty()) {
2166 Telemetry::AccumulateTimeDelta(
2167 Telemetry::EH_PERF_FIRST_CONTENTFUL_PAINT_MS
, earlyHintKey
,
2168 navigationStart
, firstContentfulComposite
);
2171 Telemetry::AccumulateTimeDelta(
2172 Telemetry::DNS_PERF_FIRST_CONTENTFUL_PAINT_MS
, dnsKey
, navigationStart
,
2173 firstContentfulComposite
);
2175 Telemetry::AccumulateTimeDelta(
2176 Telemetry::PERF_FIRST_CONTENTFUL_PAINT_FROM_RESPONSESTART_MS
,
2177 responseStart
, firstContentfulComposite
);
2179 TimeDuration fcpTime
= firstContentfulComposite
- navigationStart
;
2180 if (fcpTime
> zeroDuration
) {
2181 aEventTelemetryDataOut
.fcpTime
=
2182 mozilla::Some(static_cast<uint32_t>(fcpTime
.ToMilliseconds()));
2186 // Report the most up to date LCP time. For our histogram we actually report
2187 // this on page unload.
2188 if (TimeStamp lcpTime
=
2189 GetNavigationTiming()->GetLargestContentfulRenderTimeStamp()) {
2190 aEventTelemetryDataOut
.lcpTime
= mozilla::Some(
2191 static_cast<uint32_t>((lcpTime
- navigationStart
).ToMilliseconds()));
2194 // DOM Content Loaded event
2195 if (TimeStamp dclEventStart
=
2196 GetNavigationTiming()->GetDOMContentLoadedEventStartTimeStamp()) {
2197 Telemetry::AccumulateTimeDelta(Telemetry::PERF_DOM_CONTENT_LOADED_TIME_MS
,
2198 navigationStart
, dclEventStart
);
2199 Telemetry::AccumulateTimeDelta(
2200 Telemetry::PERF_DOM_CONTENT_LOADED_TIME_FROM_RESPONSESTART_MS
,
2201 responseStart
, dclEventStart
);
2205 if (TimeStamp loadEventStart
=
2206 GetNavigationTiming()->GetLoadEventStartTimeStamp()) {
2207 Telemetry::AccumulateTimeDelta(Telemetry::PERF_PAGE_LOAD_TIME_MS
,
2208 navigationStart
, loadEventStart
);
2209 if (!http3Key
.IsEmpty()) {
2210 Telemetry::AccumulateTimeDelta(Telemetry::HTTP3_PERF_PAGE_LOAD_TIME_MS
,
2211 http3Key
, navigationStart
, loadEventStart
);
2214 if (!http3WithPriorityKey
.IsEmpty()) {
2215 Telemetry::AccumulateTimeDelta(Telemetry::H3P_PERF_PAGE_LOAD_TIME_MS
,
2216 http3WithPriorityKey
, navigationStart
,
2220 if (!earlyHintKey
.IsEmpty()) {
2221 Telemetry::AccumulateTimeDelta(Telemetry::EH_PERF_PAGE_LOAD_TIME_MS
,
2222 earlyHintKey
, navigationStart
,
2226 Telemetry::AccumulateTimeDelta(
2227 Telemetry::PERF_PAGE_LOAD_TIME_FROM_RESPONSESTART_MS
, responseStart
,
2230 TimeDuration responseTime
= responseStart
- navigationStart
;
2231 if (responseTime
> zeroDuration
) {
2232 aEventTelemetryDataOut
.responseTime
=
2233 mozilla::Some(static_cast<uint32_t>(responseTime
.ToMilliseconds()));
2236 TimeDuration loadTime
= loadEventStart
- navigationStart
;
2237 if (loadTime
> zeroDuration
) {
2238 aEventTelemetryDataOut
.loadTime
=
2239 mozilla::Some(static_cast<uint32_t>(loadTime
.ToMilliseconds()));
2244 void Document::AccumulateJSTelemetry(
2245 glean::perf::PageLoadExtra
& aEventTelemetryDataOut
) {
2246 if (!IsTopLevelContentDocument() || !ShouldIncludeInTelemetry()) {
2250 if (!GetScopeObject() || !GetScopeObject()->GetGlobalJSObject()) {
2255 JSObject
* globalObject
= GetScopeObject()->GetGlobalJSObject();
2256 JSAutoRealm
ar(cx
, globalObject
);
2257 JS::JSTimers timers
= JS::GetJSTimers(cx
);
2259 if (!timers
.executionTime
.IsZero()) {
2260 Telemetry::Accumulate(
2261 Telemetry::JS_PAGELOAD_EXECUTION_MS
,
2262 ConvertToUnsignedFromDouble(timers
.executionTime
.ToMilliseconds()));
2263 aEventTelemetryDataOut
.jsExecTime
= mozilla::Some(
2264 static_cast<uint32_t>(timers
.executionTime
.ToMilliseconds()));
2267 if (!timers
.delazificationTime
.IsZero()) {
2268 Telemetry::Accumulate(Telemetry::JS_PAGELOAD_DELAZIFICATION_MS
,
2269 ConvertToUnsignedFromDouble(
2270 timers
.delazificationTime
.ToMilliseconds()));
2273 if (!timers
.xdrEncodingTime
.IsZero()) {
2274 Telemetry::Accumulate(
2275 Telemetry::JS_PAGELOAD_XDR_ENCODING_MS
,
2276 ConvertToUnsignedFromDouble(timers
.xdrEncodingTime
.ToMilliseconds()));
2279 if (!timers
.baselineCompileTime
.IsZero()) {
2280 Telemetry::Accumulate(Telemetry::JS_PAGELOAD_BASELINE_COMPILE_MS
,
2281 ConvertToUnsignedFromDouble(
2282 timers
.baselineCompileTime
.ToMilliseconds()));
2285 if (!timers
.gcTime
.IsZero()) {
2286 Telemetry::Accumulate(
2287 Telemetry::JS_PAGELOAD_GC_MS
,
2288 ConvertToUnsignedFromDouble(timers
.gcTime
.ToMilliseconds()));
2291 if (!timers
.protectTime
.IsZero()) {
2292 Telemetry::Accumulate(
2293 Telemetry::JS_PAGELOAD_PROTECT_MS
,
2294 ConvertToUnsignedFromDouble(timers
.protectTime
.ToMilliseconds()));
2298 Document::~Document() {
2299 MOZ_LOG(gDocumentLeakPRLog
, LogLevel::Debug
, ("DOCUMENT %p destroyed", this));
2300 MOZ_ASSERT(!IsTopLevelContentDocument() || !IsResourceDoc(),
2301 "Can't be top-level and a resource doc at the same time");
2303 NS_ASSERTION(!mIsShowing
, "Destroying a currently-showing document");
2305 if (IsTopLevelContentDocument()) {
2306 RemoveToplevelLoadingDocument(this);
2308 // don't report for about: pages
2309 if (!IsAboutPage()) {
2310 if (MOZ_UNLIKELY(mMathMLEnabled
)) {
2311 ScalarAdd(Telemetry::ScalarID::MATHML_DOC_COUNT
, 1);
2314 if (IsHTMLDocument()) {
2315 switch (GetCompatibilityMode()) {
2316 case eCompatibility_FullStandards
:
2317 Telemetry::AccumulateCategorical(
2318 Telemetry::LABELS_QUIRKS_MODE::FullStandards
);
2320 case eCompatibility_AlmostStandards
:
2321 Telemetry::AccumulateCategorical(
2322 Telemetry::LABELS_QUIRKS_MODE::AlmostStandards
);
2324 case eCompatibility_NavQuirks
:
2325 Telemetry::AccumulateCategorical(
2326 Telemetry::LABELS_QUIRKS_MODE::NavQuirks
);
2329 MOZ_ASSERT_UNREACHABLE("Unknown quirks mode");
2336 mInDestructor
= true;
2337 mInUnlinkOrDeletion
= true;
2339 mozilla::DropJSObjects(this);
2341 // Clear mObservers to keep it in sync with the mutationobserver list
2344 mIntersectionObservers
.Clear();
2346 if (mStyleSheetSetList
) {
2347 mStyleSheetSetList
->Disconnect();
2350 if (mAnimationController
) {
2351 mAnimationController
->Disconnect();
2354 MOZ_ASSERT(mTimelines
.isEmpty());
2356 mParentDocument
= nullptr;
2358 // Kill the subdocument map, doing this will release its strong
2359 // references, if any.
2360 delete mSubDocuments
;
2361 mSubDocuments
= nullptr;
2363 nsAutoScriptBlocker scriptBlocker
;
2365 // Destroy link map now so we don't waste time removing
2367 DestroyElementMaps();
2369 // Invalidate cached array of child nodes
2370 InvalidateChildNodes();
2372 // We should not have child nodes when destructor is called,
2373 // since child nodes keep their owner document alive.
2374 MOZ_ASSERT(!HasChildren());
2376 mCachedRootElement
= nullptr;
2378 for (auto& sheets
: mAdditionalSheets
) {
2379 UnlinkStyleSheets(sheets
);
2382 if (mAttributeStyles
) {
2383 mAttributeStyles
->SetOwningDocument(nullptr);
2386 if (mListenerManager
) {
2387 mListenerManager
->Disconnect();
2388 UnsetFlags(NODE_HAS_LISTENERMANAGER
);
2391 if (mScriptLoader
) {
2392 mScriptLoader
->DropDocumentReference();
2396 // Could be null here if Init() failed or if we have been unlinked.
2397 mCSSLoader
->DropDocumentReference();
2400 if (mStyleImageLoader
) {
2401 mStyleImageLoader
->DropDocumentReference();
2404 if (mXULBroadcastManager
) {
2405 mXULBroadcastManager
->DropDocumentReference();
2409 mXULPersist
->DropDocumentReference();
2412 if (mPermissionDelegateHandler
) {
2413 mPermissionDelegateHandler
->DropDocumentReference();
2416 mHeaderData
= nullptr;
2418 mPendingTitleChangeEvent
.Revoke();
2420 MOZ_ASSERT(mDOMMediaQueryLists
.isEmpty(),
2421 "must not have media query lists left");
2423 if (mNodeInfoManager
) {
2424 mNodeInfoManager
->DropDocumentReference();
2428 MOZ_ASSERT(mDocGroup
->GetBrowsingContextGroup());
2429 mDocGroup
->GetBrowsingContextGroup()->RemoveDocument(this, mDocGroup
);
2432 UnlinkOriginalDocumentIfStatic();
2434 UnregisterFromMemoryReportingForDataDocument();
2437 void Document::DropStyleSet() { mStyleSet
= nullptr; }
2439 NS_INTERFACE_TABLE_HEAD(Document
)
2440 NS_WRAPPERCACHE_INTERFACE_TABLE_ENTRY
2441 NS_INTERFACE_TABLE_BEGIN
2442 NS_INTERFACE_TABLE_ENTRY_AMBIGUOUS(Document
, nsISupports
, nsINode
)
2443 NS_INTERFACE_TABLE_ENTRY(Document
, nsINode
)
2444 NS_INTERFACE_TABLE_ENTRY(Document
, Document
)
2445 NS_INTERFACE_TABLE_ENTRY(Document
, nsIScriptObjectPrincipal
)
2446 NS_INTERFACE_TABLE_ENTRY(Document
, EventTarget
)
2447 NS_INTERFACE_TABLE_ENTRY(Document
, nsISupportsWeakReference
)
2448 NS_INTERFACE_TABLE_END
2449 NS_INTERFACE_TABLE_TO_MAP_SEGUE_CYCLE_COLLECTION(Document
)
2450 NS_INTERFACE_MAP_END
2452 NS_IMPL_CYCLE_COLLECTING_ADDREF(Document
)
2453 NS_IMPL_CYCLE_COLLECTING_RELEASE_WITH_LAST_RELEASE(Document
, LastRelease())
2455 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_BEGIN(Document
)
2456 if (Element::CanSkip(tmp
, aRemovingAllowed
)) {
2457 EventListenerManager
* elm
= tmp
->GetExistingListenerManager();
2463 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_END
2465 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_BEGIN(Document
)
2466 return Element::CanSkipInCC(tmp
);
2467 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_END
2469 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_BEGIN(Document
)
2470 return Element::CanSkipThis(tmp
);
2471 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_END
2473 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INTERNAL(Document
)
2474 if (MOZ_UNLIKELY(cb
.WantDebugInfo())) {
2476 nsAutoCString loadedAsData
;
2477 if (tmp
->IsLoadedAsData()) {
2478 loadedAsData
.AssignLiteral("data");
2480 loadedAsData
.AssignLiteral("normal");
2482 uint32_t nsid
= tmp
->GetDefaultNamespaceID();
2484 if (tmp
->mDocumentURI
) uri
= tmp
->mDocumentURI
->GetSpecOrDefault();
2485 static const char* kNSURIs
[] = {"([none])", "(xmlns)", "(xml)",
2486 "(xhtml)", "(XLink)", "(XSLT)",
2487 "(MathML)", "(RDF)", "(XUL)"};
2488 if (nsid
< ArrayLength(kNSURIs
)) {
2489 SprintfLiteral(name
, "Document %s %s %s", loadedAsData
.get(),
2490 kNSURIs
[nsid
], uri
.get());
2492 SprintfLiteral(name
, "Document %s %s", loadedAsData
.get(), uri
.get());
2494 cb
.DescribeRefCountedNode(tmp
->mRefCnt
.get(), name
);
2496 NS_IMPL_CYCLE_COLLECTION_DESCRIBE(Document
, tmp
->mRefCnt
.get())
2499 if (!nsINode::Traverse(tmp
, cb
)) {
2500 return NS_SUCCESS_INTERRUPTED_TRAVERSE
;
2503 tmp
->mExternalResourceMap
.Traverse(&cb
);
2505 // Traverse all Document pointer members.
2506 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSecurityInfo
)
2507 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDisplayDocument
)
2508 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFontFaceSet
)
2509 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mReadyForIdle
)
2510 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocumentL10n
)
2511 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mHighlightRegistry
)
2513 // Traverse all Document nsCOMPtrs.
2514 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mParser
)
2515 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mScriptGlobalObject
)
2516 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mListenerManager
)
2517 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mStyleSheetSetList
)
2518 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mScriptLoader
)
2520 DocumentOrShadowRoot::Traverse(tmp
, cb
);
2522 if (tmp
->mRadioGroupContainer
) {
2523 RadioGroupContainer::Traverse(tmp
->mRadioGroupContainer
.get(), cb
);
2526 for (auto& sheets
: tmp
->mAdditionalSheets
) {
2527 tmp
->TraverseStyleSheets(sheets
, "mAdditionalSheets[<origin>][i]", cb
);
2530 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mOnloadBlocker
)
2531 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mLazyLoadObserver
)
2532 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mLastRememberedSizeObserver
)
2533 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDOMImplementation
)
2534 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mImageMaps
)
2535 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mOrientationPendingPromise
)
2536 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mOriginalDocument
)
2537 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCachedEncoder
)
2538 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocumentTimeline
)
2539 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mScrollTimelineAnimationTracker
)
2540 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mTemplateContentsOwner
)
2541 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mChildrenCollection
)
2542 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mImages
);
2543 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mEmbeds
);
2544 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mLinks
);
2545 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mForms
);
2546 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mScripts
);
2547 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mApplets
);
2548 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mAnchors
);
2549 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mAnonymousContents
)
2550 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCommandDispatcher
)
2551 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFeaturePolicy
)
2552 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSuppressedEventListener
)
2553 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPrototypeDocument
)
2554 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mMidasCommandManager
)
2555 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mAll
)
2556 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocGroup
)
2557 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFrameRequestManager
)
2559 // Traverse all our nsCOMArrays.
2560 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPreloadingImages
)
2562 // Traverse animation components
2563 if (tmp
->mAnimationController
) {
2564 tmp
->mAnimationController
->Traverse(&cb
);
2567 if (tmp
->mSubDocuments
) {
2568 for (auto iter
= tmp
->mSubDocuments
->Iter(); !iter
.Done(); iter
.Next()) {
2569 auto entry
= static_cast<SubDocMapEntry
*>(iter
.Get());
2571 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb
, "mSubDocuments entry->mKey");
2572 cb
.NoteXPCOMChild(entry
->mKey
);
2573 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb
,
2574 "mSubDocuments entry->mSubDocument");
2575 cb
.NoteXPCOMChild(ToSupports(entry
->mSubDocument
));
2579 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCSSLoader
)
2581 // We own only the items in mDOMMediaQueryLists that have listeners;
2582 // this reference is managed by their AddListener and RemoveListener
2584 for (MediaQueryList
* mql
= tmp
->mDOMMediaQueryLists
.getFirst(); mql
;
2585 mql
= static_cast<LinkedListElement
<MediaQueryList
>*>(mql
)->getNext()) {
2586 if (mql
->HasListeners() &&
2587 NS_SUCCEEDED(mql
->CheckCurrentGlobalCorrectness())) {
2588 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb
, "mDOMMediaQueryLists item");
2589 cb
.NoteXPCOMChild(static_cast<EventTarget
*>(mql
));
2593 // XXX: This should be not needed once bug 1569185 lands.
2594 for (const auto& entry
: tmp
->mL10nProtoElements
) {
2595 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb
, "mL10nProtoElements key");
2596 cb
.NoteXPCOMChild(entry
.GetKey());
2597 CycleCollectionNoteChild(cb
, entry
.GetWeak(), "mL10nProtoElements value");
2600 for (size_t i
= 0; i
< tmp
->mPendingFrameStaticClones
.Length(); ++i
) {
2601 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPendingFrameStaticClones
[i
].mElement
);
2602 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(
2603 mPendingFrameStaticClones
[i
].mStaticCloneOf
);
2606 for (auto& tableEntry
: tmp
->mActiveLocks
) {
2607 ImplCycleCollectionTraverse(cb
, *tableEntry
.GetModifiableData(),
2608 "mActiveLocks entry", 0);
2610 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
2612 NS_IMPL_CYCLE_COLLECTION_CLASS(Document
)
2614 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(Document
)
2615 NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER
2616 NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mCachedStateObject
)
2617 NS_IMPL_CYCLE_COLLECTION_TRACE_END
2619 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(Document
)
2620 tmp
->mInUnlinkOrDeletion
= true;
2622 tmp
->SetStateObject(nullptr);
2624 // Clear out our external resources
2625 tmp
->mExternalResourceMap
.Shutdown();
2627 nsAutoScriptBlocker scriptBlocker
;
2629 nsINode::Unlink(tmp
);
2631 while (tmp
->HasChildren()) {
2632 // Hold a strong ref to the node when we remove it, because we may be
2633 // the last reference to it.
2634 // If this code changes, change the corresponding code in Document's
2635 // unlink impl and ContentUnbinder::UnbindSubtree.
2636 nsCOMPtr
<nsIContent
> child
= tmp
->GetLastChild();
2637 tmp
->DisconnectChild(child
);
2638 child
->UnbindFromTree();
2641 tmp
->UnlinkOriginalDocumentIfStatic();
2643 tmp
->mCachedRootElement
= nullptr; // Avoid a dangling pointer
2645 tmp
->SetScriptGlobalObject(nullptr);
2647 for (auto& sheets
: tmp
->mAdditionalSheets
) {
2648 tmp
->UnlinkStyleSheets(sheets
);
2651 NS_IMPL_CYCLE_COLLECTION_UNLINK(mSecurityInfo
)
2652 NS_IMPL_CYCLE_COLLECTION_UNLINK(mDisplayDocument
)
2653 NS_IMPL_CYCLE_COLLECTION_UNLINK(mLazyLoadObserver
)
2654 NS_IMPL_CYCLE_COLLECTION_UNLINK(mLastRememberedSizeObserver
)
2655 NS_IMPL_CYCLE_COLLECTION_UNLINK(mFontFaceSet
)
2656 NS_IMPL_CYCLE_COLLECTION_UNLINK(mReadyForIdle
)
2657 NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocumentL10n
)
2658 NS_IMPL_CYCLE_COLLECTION_UNLINK(mHighlightRegistry
)
2659 NS_IMPL_CYCLE_COLLECTION_UNLINK(mParser
)
2660 NS_IMPL_CYCLE_COLLECTION_UNLINK(mOnloadBlocker
)
2661 NS_IMPL_CYCLE_COLLECTION_UNLINK(mDOMImplementation
)
2662 NS_IMPL_CYCLE_COLLECTION_UNLINK(mImageMaps
)
2663 NS_IMPL_CYCLE_COLLECTION_UNLINK(mOrientationPendingPromise
)
2664 NS_IMPL_CYCLE_COLLECTION_UNLINK(mOriginalDocument
)
2665 NS_IMPL_CYCLE_COLLECTION_UNLINK(mCachedEncoder
)
2666 NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocumentTimeline
)
2667 NS_IMPL_CYCLE_COLLECTION_UNLINK(mScrollTimelineAnimationTracker
)
2668 NS_IMPL_CYCLE_COLLECTION_UNLINK(mTemplateContentsOwner
)
2669 NS_IMPL_CYCLE_COLLECTION_UNLINK(mChildrenCollection
)
2670 NS_IMPL_CYCLE_COLLECTION_UNLINK(mImages
);
2671 NS_IMPL_CYCLE_COLLECTION_UNLINK(mEmbeds
);
2672 NS_IMPL_CYCLE_COLLECTION_UNLINK(mLinks
);
2673 NS_IMPL_CYCLE_COLLECTION_UNLINK(mForms
);
2674 NS_IMPL_CYCLE_COLLECTION_UNLINK(mScripts
);
2675 NS_IMPL_CYCLE_COLLECTION_UNLINK(mApplets
);
2676 NS_IMPL_CYCLE_COLLECTION_UNLINK(mAnchors
);
2677 NS_IMPL_CYCLE_COLLECTION_UNLINK(mAnonymousContents
)
2678 NS_IMPL_CYCLE_COLLECTION_UNLINK(mCommandDispatcher
)
2679 NS_IMPL_CYCLE_COLLECTION_UNLINK(mFeaturePolicy
)
2680 NS_IMPL_CYCLE_COLLECTION_UNLINK(mSuppressedEventListener
)
2681 NS_IMPL_CYCLE_COLLECTION_UNLINK(mPrototypeDocument
)
2682 NS_IMPL_CYCLE_COLLECTION_UNLINK(mMidasCommandManager
)
2683 NS_IMPL_CYCLE_COLLECTION_UNLINK(mAll
)
2684 NS_IMPL_CYCLE_COLLECTION_UNLINK(mReferrerInfo
)
2685 NS_IMPL_CYCLE_COLLECTION_UNLINK(mPreloadReferrerInfo
)
2687 if (tmp
->mDocGroup
&& tmp
->mDocGroup
->GetBrowsingContextGroup()) {
2688 tmp
->mDocGroup
->GetBrowsingContextGroup()->RemoveDocument(tmp
,
2691 tmp
->mDocGroup
= nullptr;
2693 if (tmp
->IsTopLevelContentDocument()) {
2694 RemoveToplevelLoadingDocument(tmp
);
2697 tmp
->mParentDocument
= nullptr;
2699 NS_IMPL_CYCLE_COLLECTION_UNLINK(mPreloadingImages
)
2701 NS_IMPL_CYCLE_COLLECTION_UNLINK(mIntersectionObservers
)
2703 if (tmp
->mListenerManager
) {
2704 tmp
->mListenerManager
->Disconnect();
2705 tmp
->UnsetFlags(NODE_HAS_LISTENERMANAGER
);
2706 tmp
->mListenerManager
= nullptr;
2709 if (tmp
->mStyleSheetSetList
) {
2710 tmp
->mStyleSheetSetList
->Disconnect();
2711 tmp
->mStyleSheetSetList
= nullptr;
2714 delete tmp
->mSubDocuments
;
2715 tmp
->mSubDocuments
= nullptr;
2717 NS_IMPL_CYCLE_COLLECTION_UNLINK(mFrameRequestManager
)
2718 MOZ_RELEASE_ASSERT(!tmp
->mFrameRequestCallbacksScheduled
,
2719 "How did we get here without our presshell going away "
2722 DocumentOrShadowRoot::Unlink(tmp
);
2724 tmp
->mRadioGroupContainer
= nullptr;
2726 // Document has a pretty complex destructor, so we're going to
2727 // assume that *most* cycles you actually want to break somewhere
2728 // else, and not unlink an awful lot here.
2730 tmp
->mExpandoAndGeneration
.OwnerUnlinked();
2732 if (tmp
->mAnimationController
) {
2733 tmp
->mAnimationController
->Unlink();
2736 tmp
->mPendingTitleChangeEvent
.Revoke();
2738 if (tmp
->mCSSLoader
) {
2739 tmp
->mCSSLoader
->DropDocumentReference();
2740 NS_IMPL_CYCLE_COLLECTION_UNLINK(mCSSLoader
)
2743 // We own only the items in mDOMMediaQueryLists that have listeners;
2744 // this reference is managed by their AddListener and RemoveListener
2746 for (MediaQueryList
* mql
= tmp
->mDOMMediaQueryLists
.getFirst(); mql
;) {
2747 MediaQueryList
* next
=
2748 static_cast<LinkedListElement
<MediaQueryList
>*>(mql
)->getNext();
2753 tmp
->mPendingFrameStaticClones
.Clear();
2755 tmp
->mActiveLocks
.Clear();
2757 tmp
->mInUnlinkOrDeletion
= false;
2759 tmp
->UnregisterFromMemoryReportingForDataDocument();
2761 NS_IMPL_CYCLE_COLLECTION_UNLINK(mL10nProtoElements
)
2762 NS_IMPL_CYCLE_COLLECTION_UNLINK_WEAK_PTR
2763 NS_IMPL_CYCLE_COLLECTION_UNLINK_WEAK_REFERENCE
2764 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
2766 nsresult
Document::Init(nsIPrincipal
* aPrincipal
,
2767 nsIPrincipal
* aPartitionedPrincipal
) {
2768 if (mCSSLoader
|| mStyleImageLoader
|| mNodeInfoManager
|| mScriptLoader
) {
2769 return NS_ERROR_ALREADY_INITIALIZED
;
2772 // Force initialization.
2773 mOnloadBlocker
= new OnloadBlocker();
2774 mStyleImageLoader
= new css::ImageLoader(this);
2776 mNodeInfoManager
= new nsNodeInfoManager(this, aPrincipal
);
2778 // mNodeInfo keeps NodeInfoManager alive!
2779 mNodeInfo
= mNodeInfoManager
->GetDocumentNodeInfo();
2780 NS_ENSURE_TRUE(mNodeInfo
, NS_ERROR_OUT_OF_MEMORY
);
2781 MOZ_ASSERT(mNodeInfo
->NodeType() == DOCUMENT_NODE
,
2782 "Bad NodeType in aNodeInfo");
2784 NS_ASSERTION(OwnerDoc() == this, "Our nodeinfo is busted!");
2786 mCSSLoader
= new css::Loader(this);
2787 // Assume we're not quirky, until we know otherwise
2788 mCSSLoader
->SetCompatibilityMode(eCompatibility_FullStandards
);
2790 // If after creation the owner js global is not set for a document
2791 // we use the default compartment for this document, instead of creating
2792 // wrapper in some random compartment when the document is exposed to js
2794 nsCOMPtr
<nsIGlobalObject
> global
=
2795 xpc::NativeGlobal(xpc::PrivilegedJunkScope());
2796 NS_ENSURE_TRUE(global
, NS_ERROR_FAILURE
);
2797 mScopeObject
= do_GetWeakReference(global
);
2798 MOZ_ASSERT(mScopeObject
);
2800 mScriptLoader
= new dom::ScriptLoader(this);
2802 // we need to create a policy here so getting the policy within
2803 // ::Policy() can *always* return a non null policy
2804 mFeaturePolicy
= new dom::FeaturePolicy(this);
2805 mFeaturePolicy
->SetDefaultOrigin(NodePrincipal());
2808 SetPrincipals(aPrincipal
, aPartitionedPrincipal
);
2810 RecomputeResistFingerprinting();
2816 void Document::RemoveAllProperties() { PropertyTable().RemoveAllProperties(); }
2818 void Document::RemoveAllPropertiesFor(nsINode
* aNode
) {
2819 PropertyTable().RemoveAllPropertiesFor(aNode
);
2822 void Document::Reset(nsIChannel
* aChannel
, nsILoadGroup
* aLoadGroup
) {
2823 nsCOMPtr
<nsIURI
> uri
;
2824 nsCOMPtr
<nsIPrincipal
> principal
;
2825 nsCOMPtr
<nsIPrincipal
> partitionedPrincipal
;
2827 mIsInPrivateBrowsing
= NS_UsePrivateBrowsing(aChannel
);
2829 // Note: this code is duplicated in PrototypeDocumentContentSink::Init and
2830 // nsScriptSecurityManager::GetChannelResultPrincipals.
2831 // Note: this should match the uri used for the OnNewURI call in
2832 // nsDocShell::CreateDocumentViewer.
2833 NS_GetFinalChannelURI(aChannel
, getter_AddRefs(uri
));
2835 nsIScriptSecurityManager
* securityManager
=
2836 nsContentUtils::GetSecurityManager();
2837 if (securityManager
) {
2838 securityManager
->GetChannelResultPrincipals(
2839 aChannel
, getter_AddRefs(principal
),
2840 getter_AddRefs(partitionedPrincipal
));
2844 bool equal
= principal
->Equals(partitionedPrincipal
);
2846 principal
= MaybeDowngradePrincipal(principal
);
2848 partitionedPrincipal
= principal
;
2850 partitionedPrincipal
= MaybeDowngradePrincipal(partitionedPrincipal
);
2853 ResetToURI(uri
, aLoadGroup
, principal
, partitionedPrincipal
);
2855 // Note that, since mTiming does not change during a reset, the
2856 // navigationStart time remains unchanged and therefore any future new
2857 // timeline will have the same global clock time as the old one.
2858 mDocumentTimeline
= nullptr;
2860 if (nsCOMPtr
<nsIPropertyBag2
> bag
= do_QueryInterface(aChannel
)) {
2861 if (nsCOMPtr
<nsIURI
> baseURI
= do_GetProperty(bag
, u
"baseURI"_ns
)) {
2862 mDocumentBaseURI
= baseURI
.forget();
2863 mChromeXHRDocBaseURI
= nullptr;
2867 mChannel
= aChannel
;
2868 RecomputeResistFingerprinting();
2871 void Document::DisconnectNodeTree() {
2872 // Delete references to sub-documents and kill the subdocument map,
2873 // if any. This is not strictly needed, but makes the node tree
2874 // teardown a bit faster.
2875 delete mSubDocuments
;
2876 mSubDocuments
= nullptr;
2878 bool oldVal
= mInUnlinkOrDeletion
;
2879 mInUnlinkOrDeletion
= true;
2880 { // Scope for update
2881 MOZ_AUTO_DOC_UPDATE(this, true);
2883 // Destroy link map now so we don't waste time removing
2885 DestroyElementMaps();
2887 // Invalidate cached array of child nodes
2888 InvalidateChildNodes();
2890 while (HasChildren()) {
2891 nsMutationGuard::DidMutate();
2892 nsCOMPtr
<nsIContent
> content
= GetLastChild();
2893 nsIContent
* previousSibling
= content
->GetPreviousSibling();
2894 DisconnectChild(content
);
2895 if (content
== mCachedRootElement
) {
2896 // Immediately clear mCachedRootElement, now that it's been removed
2897 // from mChildren, so that GetRootElement() will stop returning this
2899 mCachedRootElement
= nullptr;
2901 MutationObservers::NotifyContentRemoved(this, content
, previousSibling
);
2902 content
->UnbindFromTree();
2904 MOZ_ASSERT(!mCachedRootElement
,
2905 "After removing all children, there should be no root elem");
2907 mInUnlinkOrDeletion
= oldVal
;
2910 void Document::ResetToURI(nsIURI
* aURI
, nsILoadGroup
* aLoadGroup
,
2911 nsIPrincipal
* aPrincipal
,
2912 nsIPrincipal
* aPartitionedPrincipal
) {
2913 MOZ_ASSERT(aURI
, "Null URI passed to ResetToURI");
2914 MOZ_ASSERT(!!aPrincipal
== !!aPartitionedPrincipal
);
2916 MOZ_LOG(gDocumentLeakPRLog
, LogLevel::Debug
,
2917 ("DOCUMENT %p ResetToURI %s", this, aURI
->GetSpecOrDefault().get()));
2919 mSecurityInfo
= nullptr;
2921 nsCOMPtr
<nsILoadGroup
> group
= do_QueryReferent(mDocumentLoadGroup
);
2922 if (!aLoadGroup
|| group
!= aLoadGroup
) {
2923 mDocumentLoadGroup
= nullptr;
2926 DisconnectNodeTree();
2928 // Reset our stylesheets
2929 ResetStylesheetsToURI(aURI
);
2931 // Release the listener manager
2932 if (mListenerManager
) {
2933 mListenerManager
->Disconnect();
2934 mListenerManager
= nullptr;
2937 // Release the stylesheets list.
2938 mDOMStyleSheets
= nullptr;
2940 // Release our principal after tearing down the document, rather than before.
2941 // This ensures that, during teardown, the document and the dying window
2942 // (which already nulled out its document pointer and cached the principal)
2943 // have matching principals.
2944 SetPrincipals(nullptr, nullptr);
2946 // Clear the original URI so SetDocumentURI sets it.
2947 mOriginalURI
= nullptr;
2949 SetDocumentURI(aURI
);
2950 mChromeXHRDocURI
= nullptr;
2951 // If mDocumentBaseURI is null, Document::GetBaseURI() returns
2953 mDocumentBaseURI
= nullptr;
2954 mChromeXHRDocBaseURI
= nullptr;
2957 nsCOMPtr
<nsIInterfaceRequestor
> callbacks
;
2958 aLoadGroup
->GetNotificationCallbacks(getter_AddRefs(callbacks
));
2960 nsCOMPtr
<nsILoadContext
> loadContext
= do_GetInterface(callbacks
);
2962 // This is asserting that if we previously set mIsInPrivateBrowsing
2963 // to true from the channel in Document::Reset, that the loadContext
2964 // also believes it to be true.
2965 // MOZ_ASSERT(!mIsInPrivateBrowsing ||
2966 // mIsInPrivateBrowsing == loadContext->UsePrivateBrowsing());
2967 mIsInPrivateBrowsing
= loadContext
->UsePrivateBrowsing();
2971 mDocumentLoadGroup
= do_GetWeakReference(aLoadGroup
);
2972 // there was an assertion here that aLoadGroup was not null. This
2973 // is no longer valid: nsDocShell::SetDocument does not create a
2974 // load group, and it works just fine
2976 // XXXbz what does "just fine" mean exactly? And given that there
2977 // is no nsDocShell::SetDocument, what is this talking about?
2979 if (IsContentDocument()) {
2980 // Inform the associated request context about this load start so
2981 // any of its internal load progress flags gets reset.
2982 nsCOMPtr
<nsIRequestContextService
> rcsvc
=
2983 net::RequestContextService::GetOrCreate();
2985 nsCOMPtr
<nsIRequestContext
> rc
;
2986 rcsvc
->GetRequestContextFromLoadGroup(aLoadGroup
, getter_AddRefs(rc
));
2994 mLastModified
.Truncate();
2995 // XXXbz I guess we're assuming that the caller will either pass in
2996 // a channel with a useful type or call SetContentType?
2997 SetContentType(""_ns
);
2998 mContentLanguage
= nullptr;
2999 mBaseTarget
.Truncate();
3001 mXMLDeclarationBits
= 0;
3003 // Now get our new principal
3005 SetPrincipals(aPrincipal
, aPartitionedPrincipal
);
3007 nsIScriptSecurityManager
* securityManager
=
3008 nsContentUtils::GetSecurityManager();
3009 if (securityManager
) {
3010 nsCOMPtr
<nsILoadContext
> loadContext(mDocumentContainer
);
3012 if (!loadContext
&& aLoadGroup
) {
3013 nsCOMPtr
<nsIInterfaceRequestor
> cbs
;
3014 aLoadGroup
->GetNotificationCallbacks(getter_AddRefs(cbs
));
3015 loadContext
= do_GetInterface(cbs
);
3018 MOZ_ASSERT(loadContext
,
3019 "must have a load context or pass in an explicit principal");
3021 nsCOMPtr
<nsIPrincipal
> principal
;
3022 nsresult rv
= securityManager
->GetLoadContextContentPrincipal(
3023 mDocumentURI
, loadContext
, getter_AddRefs(principal
));
3024 if (NS_SUCCEEDED(rv
)) {
3025 SetPrincipals(principal
, principal
);
3031 mFontFaceSet
->RefreshStandardFontLoadPrincipal();
3034 // Refresh the principal on the realm.
3035 if (nsPIDOMWindowInner
* win
= GetInnerWindow()) {
3036 nsGlobalWindowInner::Cast(win
)->RefreshRealmPrincipal();
3040 already_AddRefed
<nsIPrincipal
> Document::MaybeDowngradePrincipal(
3041 nsIPrincipal
* aPrincipal
) {
3046 // We can't load a document with an expanded principal. If we're given one,
3047 // automatically downgrade it to the last principal it subsumes (which is the
3048 // extension principal, in the case of extension content scripts).
3049 auto* basePrin
= BasePrincipal::Cast(aPrincipal
);
3050 if (basePrin
->Is
<ExpandedPrincipal
>()) {
3051 MOZ_DIAGNOSTIC_ASSERT(false,
3052 "Should never try to create a document with "
3053 "an expanded principal");
3055 auto* expanded
= basePrin
->As
<ExpandedPrincipal
>();
3056 return do_AddRef(expanded
->AllowList().LastElement());
3059 if (aPrincipal
->IsSystemPrincipal() && mDocumentContainer
) {
3060 // We basically want the parent document here, but because this is very
3061 // early in the load, GetInProcessParentDocument() returns null, so we use
3062 // the docshell hierarchy to get this information instead.
3063 if (RefPtr
<BrowsingContext
> parent
=
3064 mDocumentContainer
->GetBrowsingContext()->GetParent()) {
3065 auto* parentWin
= nsGlobalWindowOuter::Cast(parent
->GetDOMWindow());
3066 if (!parentWin
|| !parentWin
->GetPrincipal()->IsSystemPrincipal()) {
3067 nsCOMPtr
<nsIPrincipal
> nullPrincipal
=
3068 NullPrincipal::CreateWithoutOriginAttributes();
3069 return nullPrincipal
.forget();
3073 nsCOMPtr
<nsIPrincipal
> principal(aPrincipal
);
3074 return principal
.forget();
3077 size_t Document::FindDocStyleSheetInsertionPoint(const StyleSheet
& aSheet
) {
3078 nsStyleSheetService
* sheetService
= nsStyleSheetService::GetInstance();
3079 ServoStyleSet
& styleSet
= EnsureStyleSet();
3081 // lowest index first
3082 int32_t newDocIndex
= StyleOrderIndexOfSheet(aSheet
);
3084 size_t count
= styleSet
.SheetCount(StyleOrigin::Author
);
3086 for (; index
< count
; index
++) {
3087 auto* sheet
= styleSet
.SheetAt(StyleOrigin::Author
, index
);
3089 int32_t sheetDocIndex
= StyleOrderIndexOfSheet(*sheet
);
3090 if (sheetDocIndex
> newDocIndex
) {
3094 // If the sheet is not owned by the document it can be an author
3095 // sheet registered at nsStyleSheetService or an additional author
3096 // sheet on the document, which means the new
3097 // doc sheet should end up before it.
3098 if (sheetDocIndex
< 0) {
3100 auto& authorSheets
= *sheetService
->AuthorStyleSheets();
3101 if (authorSheets
.IndexOf(sheet
) != authorSheets
.NoIndex
) {
3105 if (sheet
== GetFirstAdditionalAuthorSheet()) {
3114 void Document::ResetStylesheetsToURI(nsIURI
* aURI
) {
3117 ClearAdoptedStyleSheets();
3118 ServoStyleSet
& styleSet
= EnsureStyleSet();
3120 auto ClearSheetList
= [&](nsTArray
<RefPtr
<StyleSheet
>>& aSheetList
) {
3121 for (auto& sheet
: Reversed(aSheetList
)) {
3122 sheet
->ClearAssociatedDocumentOrShadowRoot();
3123 if (mStyleSetFilled
) {
3124 styleSet
.RemoveStyleSheet(*sheet
);
3129 ClearSheetList(mStyleSheets
);
3130 for (auto& sheets
: mAdditionalSheets
) {
3131 ClearSheetList(sheets
);
3133 if (mStyleSetFilled
) {
3134 if (auto* ss
= nsStyleSheetService::GetInstance()) {
3135 for (auto& sheet
: Reversed(*ss
->AuthorStyleSheets())) {
3136 MOZ_ASSERT(!sheet
->GetAssociatedDocumentOrShadowRoot());
3137 if (sheet
->IsApplicable()) {
3138 styleSet
.RemoveStyleSheet(*sheet
);
3144 // Now reset our inline style and attribute sheets.
3145 if (mAttributeStyles
) {
3146 mAttributeStyles
->Reset();
3147 mAttributeStyles
->SetOwningDocument(this);
3149 mAttributeStyles
= new AttributeStyles(this);
3152 if (mStyleSetFilled
) {
3153 FillStyleSetDocumentSheets();
3155 if (styleSet
.StyleSheetsHaveChanged()) {
3156 ApplicableStylesChanged();
3161 static void AppendSheetsToStyleSet(
3162 ServoStyleSet
* aStyleSet
, const nsTArray
<RefPtr
<StyleSheet
>>& aSheets
) {
3163 for (StyleSheet
* sheet
: Reversed(aSheets
)) {
3164 aStyleSet
->AppendStyleSheet(*sheet
);
3168 void Document::FillStyleSetUserAndUASheets() {
3169 // Make sure this does the same thing as PresShell::Add{User,Agent}Sheet wrt
3172 // The document will fill in the document sheets when we create the presshell
3173 auto* cache
= GlobalStyleSheetCache::Singleton();
3175 nsStyleSheetService
* sheetService
= nsStyleSheetService::GetInstance();
3176 MOZ_ASSERT(sheetService
,
3177 "should never be creating a StyleSet after the style sheet "
3178 "service has gone");
3180 ServoStyleSet
& styleSet
= EnsureStyleSet();
3181 for (StyleSheet
* sheet
: *sheetService
->UserStyleSheets()) {
3182 styleSet
.AppendStyleSheet(*sheet
);
3185 StyleSheet
* sheet
= IsInChromeDocShell() ? cache
->GetUserChromeSheet()
3186 : cache
->GetUserContentSheet();
3188 styleSet
.AppendStyleSheet(*sheet
);
3191 styleSet
.AppendStyleSheet(*cache
->UASheet());
3193 if (MOZ_LIKELY(NodeInfoManager()->MathMLEnabled())) {
3194 styleSet
.AppendStyleSheet(*cache
->MathMLSheet());
3197 if (MOZ_LIKELY(NodeInfoManager()->SVGEnabled())) {
3198 styleSet
.AppendStyleSheet(*cache
->SVGSheet());
3201 styleSet
.AppendStyleSheet(*cache
->HTMLSheet());
3203 if (nsLayoutUtils::ShouldUseNoFramesSheet(this)) {
3204 styleSet
.AppendStyleSheet(*cache
->NoFramesSheet());
3207 styleSet
.AppendStyleSheet(*cache
->CounterStylesSheet());
3209 // Only load the full XUL sheet if we'll need it.
3210 if (LoadsFullXULStyleSheetUpFront()) {
3211 styleSet
.AppendStyleSheet(*cache
->XULSheet());
3214 styleSet
.AppendStyleSheet(*cache
->FormsSheet());
3215 styleSet
.AppendStyleSheet(*cache
->ScrollbarsSheet());
3217 for (StyleSheet
* sheet
: *sheetService
->AgentStyleSheets()) {
3218 styleSet
.AppendStyleSheet(*sheet
);
3221 MOZ_ASSERT(!mQuirkSheetAdded
);
3222 if (NeedsQuirksSheet()) {
3223 styleSet
.AppendStyleSheet(*cache
->QuirkSheet());
3224 mQuirkSheetAdded
= true;
3228 void Document::FillStyleSet() {
3229 MOZ_ASSERT(!mStyleSetFilled
);
3230 FillStyleSetUserAndUASheets();
3231 FillStyleSetDocumentSheets();
3232 mStyleSetFilled
= true;
3235 void Document::RemoveContentEditableStyleSheets() {
3236 MOZ_ASSERT(IsHTMLOrXHTML());
3238 ServoStyleSet
& styleSet
= EnsureStyleSet();
3239 auto* cache
= GlobalStyleSheetCache::Singleton();
3240 bool changed
= false;
3241 if (mDesignModeSheetAdded
) {
3242 styleSet
.RemoveStyleSheet(*cache
->DesignModeSheet());
3243 mDesignModeSheetAdded
= false;
3246 if (mContentEditableSheetAdded
) {
3247 styleSet
.RemoveStyleSheet(*cache
->ContentEditableSheet());
3248 mContentEditableSheetAdded
= false;
3252 MOZ_ASSERT(mStyleSetFilled
);
3253 ApplicableStylesChanged();
3257 void Document::AddContentEditableStyleSheetsToStyleSet(bool aDesignMode
) {
3258 MOZ_ASSERT(IsHTMLOrXHTML());
3259 MOZ_DIAGNOSTIC_ASSERT(mStyleSetFilled
,
3260 "Caller should ensure we're being rendered");
3262 ServoStyleSet
& styleSet
= EnsureStyleSet();
3263 auto* cache
= GlobalStyleSheetCache::Singleton();
3264 bool changed
= false;
3265 if (!mContentEditableSheetAdded
) {
3266 styleSet
.AppendStyleSheet(*cache
->ContentEditableSheet());
3267 mContentEditableSheetAdded
= true;
3270 if (mDesignModeSheetAdded
!= aDesignMode
) {
3271 if (mDesignModeSheetAdded
) {
3272 styleSet
.RemoveStyleSheet(*cache
->DesignModeSheet());
3274 styleSet
.AppendStyleSheet(*cache
->DesignModeSheet());
3276 mDesignModeSheetAdded
= !mDesignModeSheetAdded
;
3280 ApplicableStylesChanged();
3284 void Document::FillStyleSetDocumentSheets() {
3285 ServoStyleSet
& styleSet
= EnsureStyleSet();
3286 MOZ_ASSERT(styleSet
.SheetCount(StyleOrigin::Author
) == 0,
3287 "Style set already has document sheets?");
3289 // Sheets are added in reverse order to avoid worst-case time complexity when
3290 // looking up the index of a sheet.
3292 // Note that usually appending is faster (rebuilds less stuff in the
3293 // styleset), but in this case it doesn't matter since we're filling the
3294 // styleset from scratch anyway.
3295 for (StyleSheet
* sheet
: Reversed(mStyleSheets
)) {
3296 if (sheet
->IsApplicable()) {
3297 styleSet
.AddDocStyleSheet(*sheet
);
3301 EnumerateUniqueAdoptedStyleSheetsBackToFront([&](StyleSheet
& aSheet
) {
3302 if (aSheet
.IsApplicable()) {
3303 styleSet
.AddDocStyleSheet(aSheet
);
3307 nsStyleSheetService
* sheetService
= nsStyleSheetService::GetInstance();
3308 for (StyleSheet
* sheet
: *sheetService
->AuthorStyleSheets()) {
3309 styleSet
.AppendStyleSheet(*sheet
);
3312 AppendSheetsToStyleSet(&styleSet
, mAdditionalSheets
[eAgentSheet
]);
3313 AppendSheetsToStyleSet(&styleSet
, mAdditionalSheets
[eUserSheet
]);
3314 AppendSheetsToStyleSet(&styleSet
, mAdditionalSheets
[eAuthorSheet
]);
3317 void Document::CompatibilityModeChanged() {
3318 MOZ_ASSERT(IsHTMLOrXHTML());
3319 CSSLoader()->SetCompatibilityMode(mCompatMode
);
3322 mStyleSet
->CompatibilityModeChanged();
3324 if (!mStyleSetFilled
) {
3325 MOZ_ASSERT(!mQuirkSheetAdded
);
3329 MOZ_ASSERT(mStyleSet
);
3330 if (PresShell
* presShell
= GetPresShell()) {
3331 // Selectors may have become case-sensitive / case-insensitive, the stylist
3332 // has already performed the relevant invalidation.
3333 presShell
->EnsureStyleFlush();
3335 if (mQuirkSheetAdded
== NeedsQuirksSheet()) {
3338 auto* cache
= GlobalStyleSheetCache::Singleton();
3339 StyleSheet
* sheet
= cache
->QuirkSheet();
3340 if (mQuirkSheetAdded
) {
3341 mStyleSet
->RemoveStyleSheet(*sheet
);
3343 mStyleSet
->AppendStyleSheet(*sheet
);
3345 mQuirkSheetAdded
= !mQuirkSheetAdded
;
3346 ApplicableStylesChanged();
3349 void Document::SetCompatibilityMode(nsCompatibility aMode
) {
3350 NS_ASSERTION(IsHTMLDocument() || aMode
== eCompatibility_FullStandards
,
3351 "Bad compat mode for XHTML document!");
3353 if (mCompatMode
== aMode
) {
3356 mCompatMode
= aMode
;
3357 CompatibilityModeChanged();
3358 // Trigger recomputation of the nsViewportInfo the next time it's queried.
3359 mViewportType
= Unknown
;
3362 static void WarnIfSandboxIneffective(nsIDocShell
* aDocShell
,
3363 uint32_t aSandboxFlags
,
3364 nsIChannel
* aChannel
) {
3365 // If the document permits allow-top-navigation and
3366 // allow-top-navigation-by-user-activation this will permit all top
3368 if (aSandboxFlags
!= SANDBOXED_NONE
&&
3369 !(aSandboxFlags
& SANDBOXED_TOPLEVEL_NAVIGATION
) &&
3370 !(aSandboxFlags
& SANDBOXED_TOPLEVEL_NAVIGATION_USER_ACTIVATION
)) {
3371 nsContentUtils::ReportToConsole(
3372 nsIScriptError::warningFlag
, "Iframe Sandbox"_ns
,
3373 aDocShell
->GetDocument(), nsContentUtils::eSECURITY_PROPERTIES
,
3374 "BothAllowTopNavigationAndUserActivationPresent");
3376 // If the document is sandboxed (via the HTML5 iframe sandbox
3377 // attribute) and both the allow-scripts and allow-same-origin
3378 // keywords are supplied, the sandboxed document can call into its
3379 // parent document and remove its sandboxing entirely - we print a
3380 // warning to the web console in this case.
3381 if (aSandboxFlags
& SANDBOXED_NAVIGATION
&&
3382 !(aSandboxFlags
& SANDBOXED_SCRIPTS
) &&
3383 !(aSandboxFlags
& SANDBOXED_ORIGIN
)) {
3384 RefPtr
<BrowsingContext
> bc
= aDocShell
->GetBrowsingContext();
3385 MOZ_ASSERT(bc
->IsInProcess());
3387 RefPtr
<BrowsingContext
> parentBC
= bc
->GetParent();
3388 if (!parentBC
|| !parentBC
->IsInProcess()) {
3389 // If parent document is not in process, then by construction it
3390 // cannot be same origin.
3394 // Don't warn if our parent is not the top-level document.
3395 if (!parentBC
->IsTopContent()) {
3399 nsCOMPtr
<nsIDocShell
> parentDocShell
= parentBC
->GetDocShell();
3400 MOZ_ASSERT(parentDocShell
);
3402 nsCOMPtr
<nsIChannel
> parentChannel
;
3403 parentDocShell
->GetCurrentDocumentChannel(getter_AddRefs(parentChannel
));
3404 if (!parentChannel
) {
3407 nsresult rv
= nsContentUtils::CheckSameOrigin(aChannel
, parentChannel
);
3408 if (NS_FAILED(rv
)) {
3412 nsCOMPtr
<Document
> parentDocument
= parentDocShell
->GetDocument();
3413 nsCOMPtr
<nsIURI
> iframeUri
;
3414 parentChannel
->GetURI(getter_AddRefs(iframeUri
));
3415 nsContentUtils::ReportToConsole(nsIScriptError::warningFlag
,
3416 "Iframe Sandbox"_ns
, parentDocument
,
3417 nsContentUtils::eSECURITY_PROPERTIES
,
3418 "BothAllowScriptsAndSameOriginPresent",
3419 nsTArray
<nsString
>(), iframeUri
);
3423 bool Document::IsSynthesized() {
3424 nsCOMPtr
<nsILoadInfo
> loadInfo
= mChannel
? mChannel
->LoadInfo() : nullptr;
3425 return loadInfo
&& loadInfo
->GetServiceWorkerTaintingSynthesized();
3429 bool Document::IsCallerChromeOrAddon(JSContext
* aCx
, JSObject
* aObject
) {
3430 nsIPrincipal
* principal
= nsContentUtils::SubjectPrincipal(aCx
);
3431 return principal
&& (principal
->IsSystemPrincipal() ||
3432 principal
->GetIsAddonOrExpandedAddonPrincipal());
3435 static void CheckIsBadPolicy(nsILoadInfo::CrossOriginOpenerPolicy aPolicy
,
3436 BrowsingContext
* aContext
, nsIChannel
* aChannel
) {
3437 #if defined(EARLY_BETA_OR_EARLIER)
3439 nsILoadInfo::OPENER_POLICY_SAME_ORIGIN_EMBEDDER_POLICY_REQUIRE_CORP
;
3441 if (aContext
->GetOpenerPolicy() == aPolicy
||
3442 (aContext
->GetOpenerPolicy() != requireCORP
&& aPolicy
!= requireCORP
)) {
3446 nsCOMPtr
<nsIURI
> uri
;
3447 bool hasURI
= NS_SUCCEEDED(aChannel
->GetOriginalURI(getter_AddRefs(uri
)));
3449 bool isViewSource
= hasURI
&& uri
->SchemeIs("view-source");
3451 nsCString contentType
;
3452 nsCOMPtr
<nsIPropertyBag2
> bag
= do_QueryInterface(aChannel
);
3453 bool isPDFJS
= bag
&&
3454 NS_SUCCEEDED(bag
->GetPropertyAsACString(u
"contentType"_ns
,
3456 contentType
.EqualsLiteral(APPLICATION_PDF
);
3458 MOZ_DIAGNOSTIC_ASSERT(!isViewSource
,
3459 "Bug 1834864: Assert due to view-source.");
3460 MOZ_DIAGNOSTIC_ASSERT(!isPDFJS
, "Bug 1834864: Assert due to pdfjs.");
3461 MOZ_DIAGNOSTIC_ASSERT(aPolicy
== requireCORP
,
3462 "Assert due to clearing REQUIRE_CORP.");
3463 MOZ_DIAGNOSTIC_ASSERT(aContext
->GetOpenerPolicy() == requireCORP
,
3464 "Assert due to setting REQUIRE_CORP.");
3465 #endif // defined(EARLY_BETA_OR_EARLIER)
3468 nsresult
Document::StartDocumentLoad(const char* aCommand
, nsIChannel
* aChannel
,
3469 nsILoadGroup
* aLoadGroup
,
3470 nsISupports
* aContainer
,
3471 nsIStreamListener
** aDocListener
,
3473 if (MOZ_LOG_TEST(gDocumentLeakPRLog
, LogLevel::Debug
)) {
3474 nsCOMPtr
<nsIURI
> uri
;
3475 aChannel
->GetURI(getter_AddRefs(uri
));
3476 MOZ_LOG(gDocumentLeakPRLog
, LogLevel::Debug
,
3477 ("DOCUMENT %p StartDocumentLoad %s", this,
3478 uri
? uri
->GetSpecOrDefault().get() : ""));
3481 MOZ_ASSERT(GetReadyStateEnum() == Document::READYSTATE_UNINITIALIZED
,
3483 SetReadyStateInternal(READYSTATE_LOADING
);
3485 if (nsCRT::strcmp(kLoadAsData
, aCommand
) == 0) {
3486 mLoadedAsData
= true;
3487 SetLoadedAsData(true, /* aConsiderForMemoryReporting */ true);
3488 // We need to disable script & style loading in this case.
3489 // We leave them disabled even in EndLoad(), and let anyone
3490 // who puts the document on display to worry about enabling.
3492 // Do not load/process scripts when loading as data
3493 ScriptLoader()->SetEnabled(false);
3496 CSSLoader()->SetEnabled(
3497 false); // Do not load/process styles when loading as data
3498 } else if (nsCRT::strcmp("external-resource", aCommand
) == 0) {
3499 // Allow CSS, but not scripts
3500 ScriptLoader()->SetEnabled(false);
3503 mMayStartLayout
= false;
3504 MOZ_ASSERT(!mReadyForIdle
,
3505 "We should never hit DOMContentLoaded before this point");
3508 Reset(aChannel
, aLoadGroup
);
3511 nsAutoCString contentType
;
3512 nsCOMPtr
<nsIPropertyBag2
> bag
= do_QueryInterface(aChannel
);
3513 if ((bag
&& NS_SUCCEEDED(bag
->GetPropertyAsACString(u
"contentType"_ns
,
3515 NS_SUCCEEDED(aChannel
->GetContentType(contentType
))) {
3516 // XXX this is only necessary for viewsource:
3517 nsACString::const_iterator start
, end
, semicolon
;
3518 contentType
.BeginReading(start
);
3519 contentType
.EndReading(end
);
3521 FindCharInReadable(';', semicolon
, end
);
3522 SetContentType(Substring(start
, semicolon
));
3525 RetrieveRelevantHeaders(aChannel
);
3527 mChannel
= aChannel
;
3528 RecomputeResistFingerprinting();
3529 nsCOMPtr
<nsIInputStreamChannel
> inStrmChan
= do_QueryInterface(mChannel
);
3531 bool isSrcdocChannel
;
3532 inStrmChan
->GetIsSrcdocChannel(&isSrcdocChannel
);
3533 if (isSrcdocChannel
) {
3534 mIsSrcdocDocument
= true;
3539 nsLoadFlags loadFlags
;
3540 mChannel
->GetLoadFlags(&loadFlags
);
3541 bool isDocument
= false;
3542 mChannel
->GetIsDocument(&isDocument
);
3543 if (loadFlags
& nsIRequest::LOAD_DOCUMENT_NEEDS_COOKIE
&& isDocument
&&
3544 IsSynthesized() && XRE_IsContentProcess()) {
3545 ContentChild::UpdateCookieStatus(mChannel
);
3548 // Store the security info for future use.
3549 mChannel
->GetSecurityInfo(getter_AddRefs(mSecurityInfo
));
3552 // If this document is being loaded by a docshell, copy its sandbox flags
3553 // to the document, and store the fullscreen enabled flag. These are
3554 // immutable after being set here.
3555 nsCOMPtr
<nsIDocShell
> docShell
= do_QueryInterface(aContainer
);
3557 // If this is an error page, don't inherit sandbox flags
3558 nsCOMPtr
<nsILoadInfo
> loadInfo
= aChannel
->LoadInfo();
3559 if (docShell
&& !loadInfo
->GetLoadErrorPage()) {
3560 mSandboxFlags
= loadInfo
->GetSandboxFlags();
3561 WarnIfSandboxIneffective(docShell
, mSandboxFlags
, GetChannel());
3564 // Set the opener policy for the top level content document.
3565 nsCOMPtr
<nsIHttpChannelInternal
> httpChan
= do_QueryInterface(mChannel
);
3566 nsILoadInfo::CrossOriginOpenerPolicy policy
=
3567 nsILoadInfo::OPENER_POLICY_UNSAFE_NONE
;
3568 if (IsTopLevelContentDocument() && httpChan
&&
3569 NS_SUCCEEDED(httpChan
->GetCrossOriginOpenerPolicy(&policy
)) && docShell
&&
3570 docShell
->GetBrowsingContext()) {
3571 CheckIsBadPolicy(policy
, docShell
->GetBrowsingContext(), aChannel
);
3573 // Setting the opener policy on a discarded context has no effect.
3574 Unused
<< docShell
->GetBrowsingContext()->SetOpenerPolicy(policy
);
3577 // The CSP directives upgrade-insecure-requests as well as
3578 // block-all-mixed-content not only apply to the toplevel document,
3579 // but also to nested documents. The loadInfo of a subdocument
3580 // load already holds the correct flag, so let's just set it here
3581 // on the document. Please note that we set the appropriate preload
3582 // bits just for the sake of completeness here, because the preloader
3583 // does not reach into subdocuments.
3584 mUpgradeInsecureRequests
= loadInfo
->GetUpgradeInsecureRequests();
3585 mUpgradeInsecurePreloads
= mUpgradeInsecureRequests
;
3586 mBlockAllMixedContent
= loadInfo
->GetBlockAllMixedContent();
3587 mBlockAllMixedContentPreloads
= mBlockAllMixedContent
;
3589 // HTTPS-Only Mode flags
3590 // The HTTPS_ONLY_EXEMPT flag of the HTTPS-Only state gets propagated to all
3591 // sub-resources and sub-documents.
3592 mHttpsOnlyStatus
= loadInfo
->GetHttpsOnlyStatus();
3594 nsresult rv
= InitReferrerInfo(aChannel
);
3595 NS_ENSURE_SUCCESS(rv
, rv
);
3597 rv
= InitCOEP(aChannel
);
3598 NS_ENSURE_SUCCESS(rv
, rv
);
3600 // HACK: Calling EnsureIPCPoliciesRead() here will parse the CSP using the
3601 // context's current mSelfURI (which is still the previous mSelfURI),
3602 // bypassing some internal bugs with 'self' and iframe inheritance.
3603 // Not calling it here results in the mSelfURI being the current mSelfURI and
3604 // not the previous which breaks said inheritance.
3605 // https://bugzilla.mozilla.org/show_bug.cgi?id=1793560#ch-8
3606 nsCOMPtr
<nsIContentSecurityPolicy
> cspToInherit
= loadInfo
->GetCspToInherit();
3608 cspToInherit
->EnsureIPCPoliciesRead();
3611 rv
= InitCSP(aChannel
);
3612 NS_ENSURE_SUCCESS(rv
, rv
);
3614 // Initialize FeaturePolicy
3615 rv
= InitFeaturePolicy(aChannel
);
3616 NS_ENSURE_SUCCESS(rv
, rv
);
3618 rv
= loadInfo
->GetCookieJarSettings(getter_AddRefs(mCookieJarSettings
));
3619 NS_ENSURE_SUCCESS(rv
, rv
);
3621 // Generally XFO and CSP frame-ancestors is handled within
3622 // DocumentLoadListener. However, the DocumentLoadListener can not handle
3623 // object and embed. Until then we have to enforce it here (See Bug 1646899).
3624 nsContentPolicyType internalContentType
=
3625 loadInfo
->InternalContentPolicyType();
3626 if (internalContentType
== nsIContentPolicy::TYPE_INTERNAL_OBJECT
||
3627 internalContentType
== nsIContentPolicy::TYPE_INTERNAL_EMBED
) {
3628 nsContentSecurityUtils::PerformCSPFrameAncestorAndXFOCheck(aChannel
);
3631 aChannel
->GetStatus(&status
);
3632 if (status
== NS_ERROR_XFO_VIOLATION
) {
3633 // stop! ERROR page!
3634 // But before we have to reset the principal of the document
3635 // because the onload() event fires before the error page
3636 // is displayed and we do not want the enclosing document
3637 // to access the contentDocument.
3638 RefPtr
<NullPrincipal
> nullPrincipal
=
3639 NullPrincipal::CreateWithInheritedAttributes(NodePrincipal());
3640 // Before calling SetPrincipals() we should ensure that mFontFaceSet
3641 // and also GetInnerWindow() is still null at this point, before
3642 // we can fix Bug 1614735: Evaluate calls to SetPrincipal
3643 // within Document.cpp
3644 MOZ_ASSERT(!mFontFaceSet
&& !GetInnerWindow());
3645 SetPrincipals(nullPrincipal
, nullPrincipal
);
3652 void Document::SetLoadedAsData(bool aLoadedAsData
,
3653 bool aConsiderForMemoryReporting
) {
3654 mLoadedAsData
= aLoadedAsData
;
3655 if (aConsiderForMemoryReporting
) {
3656 nsIGlobalObject
* global
= GetScopeObject();
3658 if (nsPIDOMWindowInner
* window
= global
->GetAsInnerWindow()) {
3659 nsGlobalWindowInner::Cast(window
)
3660 ->RegisterDataDocumentForMemoryReporting(this);
3666 nsIContentSecurityPolicy
* Document::GetCsp() const { return mCSP
; }
3668 void Document::SetCsp(nsIContentSecurityPolicy
* aCSP
) { mCSP
= aCSP
; }
3670 nsIContentSecurityPolicy
* Document::GetPreloadCsp() const {
3674 void Document::SetPreloadCsp(nsIContentSecurityPolicy
* aPreloadCSP
) {
3675 mPreloadCSP
= aPreloadCSP
;
3678 void Document::GetCspJSON(nsString
& aJSON
) {
3682 dom::CSPPolicies jsonPolicies
;
3683 jsonPolicies
.ToJSON(aJSON
);
3686 mCSP
->ToJSON(aJSON
);
3689 void Document::SendToConsole(nsCOMArray
<nsISecurityConsoleMessage
>& aMessages
) {
3690 for (uint32_t i
= 0; i
< aMessages
.Length(); ++i
) {
3691 nsAutoString messageTag
;
3692 aMessages
[i
]->GetTag(messageTag
);
3694 nsAutoString category
;
3695 aMessages
[i
]->GetCategory(category
);
3697 nsContentUtils::ReportToConsole(nsIScriptError::warningFlag
,
3698 NS_ConvertUTF16toUTF8(category
), this,
3699 nsContentUtils::eSECURITY_PROPERTIES
,
3700 NS_ConvertUTF16toUTF8(messageTag
).get());
3704 void Document::ApplySettingsFromCSP(bool aSpeculative
) {
3705 nsresult rv
= NS_OK
;
3706 if (!aSpeculative
) {
3707 // 1) apply settings from regular CSP
3709 // Set up 'block-all-mixed-content' if not already inherited
3710 // from the parent context or set by any other CSP.
3711 if (!mBlockAllMixedContent
) {
3713 rv
= mCSP
->GetBlockAllMixedContent(&block
);
3714 NS_ENSURE_SUCCESS_VOID(rv
);
3715 mBlockAllMixedContent
= block
;
3717 if (!mBlockAllMixedContentPreloads
) {
3718 mBlockAllMixedContentPreloads
= mBlockAllMixedContent
;
3721 // Set up 'upgrade-insecure-requests' if not already inherited
3722 // from the parent context or set by any other CSP.
3723 if (!mUpgradeInsecureRequests
) {
3724 bool upgrade
= false;
3725 rv
= mCSP
->GetUpgradeInsecureRequests(&upgrade
);
3726 NS_ENSURE_SUCCESS_VOID(rv
);
3727 mUpgradeInsecureRequests
= upgrade
;
3729 if (!mUpgradeInsecurePreloads
) {
3730 mUpgradeInsecurePreloads
= mUpgradeInsecureRequests
;
3732 // Update csp settings in the parent process
3733 if (auto* wgc
= GetWindowGlobalChild()) {
3734 wgc
->SendUpdateDocumentCspSettings(mBlockAllMixedContent
,
3735 mUpgradeInsecureRequests
);
3741 // 2) apply settings from speculative csp
3743 if (!mBlockAllMixedContentPreloads
) {
3745 rv
= mPreloadCSP
->GetBlockAllMixedContent(&block
);
3746 NS_ENSURE_SUCCESS_VOID(rv
);
3747 mBlockAllMixedContent
= block
;
3749 if (!mUpgradeInsecurePreloads
) {
3750 bool upgrade
= false;
3751 rv
= mPreloadCSP
->GetUpgradeInsecureRequests(&upgrade
);
3752 NS_ENSURE_SUCCESS_VOID(rv
);
3753 mUpgradeInsecurePreloads
= upgrade
;
3758 nsresult
Document::InitCSP(nsIChannel
* aChannel
) {
3759 MOZ_ASSERT(!mScriptGlobalObject
,
3760 "CSP must be initialized before mScriptGlobalObject is set!");
3762 // If this is a data document - no need to set CSP.
3763 if (mLoadedAsData
) {
3767 // If this is an image, no need to set a CSP. Otherwise SVG images
3768 // served with a CSP might block internally applied inline styles.
3769 nsCOMPtr
<nsILoadInfo
> loadInfo
= aChannel
->LoadInfo();
3770 if (loadInfo
->GetExternalContentPolicyType() ==
3771 ExtContentPolicy::TYPE_IMAGE
||
3772 loadInfo
->GetExternalContentPolicyType() ==
3773 ExtContentPolicy::TYPE_IMAGESET
) {
3777 MOZ_ASSERT(!mCSP
, "where did mCSP get set if not here?");
3779 // If there is a CSP that needs to be inherited from whatever
3780 // global is considered the client of the document fetch then
3781 // we query it here from the loadinfo in case the newly created
3782 // document needs to inherit the CSP. See:
3783 // https://w3c.github.io/webappsec-csp/#initialize-document-csp
3784 bool inheritedCSP
= CSP_ShouldResponseInheritCSP(aChannel
);
3786 mCSP
= loadInfo
->GetCspToInherit();
3789 // If there is no CSP to inherit, then we create a new CSP here so
3790 // that history entries always have the right reference in case a
3791 // Meta CSP gets dynamically added after the history entry has
3792 // already been created.
3794 mCSP
= new nsCSPContext();
3797 // Always overwrite the requesting context of the CSP so that any new
3798 // 'self' keyword added to an inherited CSP translates correctly.
3799 nsresult rv
= mCSP
->SetRequestContextWithDocument(this);
3800 if (NS_WARN_IF(NS_FAILED(rv
))) {
3804 nsAutoCString tCspHeaderValue
, tCspROHeaderValue
;
3806 nsCOMPtr
<nsIHttpChannel
> httpChannel
;
3807 rv
= GetHttpChannelHelper(aChannel
, getter_AddRefs(httpChannel
));
3808 if (NS_WARN_IF(NS_FAILED(rv
))) {
3813 Unused
<< httpChannel
->GetResponseHeader("content-security-policy"_ns
,
3816 Unused
<< httpChannel
->GetResponseHeader(
3817 "content-security-policy-report-only"_ns
, tCspROHeaderValue
);
3819 NS_ConvertASCIItoUTF16
cspHeaderValue(tCspHeaderValue
);
3820 NS_ConvertASCIItoUTF16
cspROHeaderValue(tCspROHeaderValue
);
3822 // Check if this is a document from a WebExtension.
3823 nsCOMPtr
<nsIPrincipal
> principal
= NodePrincipal();
3824 auto addonPolicy
= BasePrincipal::Cast(principal
)->AddonPolicy();
3826 // If there's no CSP to apply, go ahead and return early
3827 if (!inheritedCSP
&& !addonPolicy
&& cspHeaderValue
.IsEmpty() &&
3828 cspROHeaderValue
.IsEmpty()) {
3829 if (MOZ_LOG_TEST(gCspPRLog
, LogLevel::Debug
)) {
3830 nsCOMPtr
<nsIURI
> chanURI
;
3831 aChannel
->GetURI(getter_AddRefs(chanURI
));
3832 nsAutoCString aspec
;
3833 chanURI
->GetAsciiSpec(aspec
);
3834 MOZ_LOG(gCspPRLog
, LogLevel::Debug
,
3835 ("no CSP for document, %s", aspec
.get()));
3841 MOZ_LOG(gCspPRLog
, LogLevel::Debug
,
3842 ("Document is an add-on or CSP header specified %p", this));
3844 // ----- if the doc is an addon, apply its CSP.
3846 mCSP
->AppendPolicy(addonPolicy
->BaseCSP(), false, false);
3848 mCSP
->AppendPolicy(addonPolicy
->ExtensionPageCSP(), false, false);
3849 // Bug 1548468: Move CSP off ExpandedPrincipal
3850 // Currently the LoadInfo holds the source of truth for every resource load
3851 // because LoadInfo::GetCsp() queries the CSP from an ExpandedPrincipal
3852 // (and not from the Client) if the load was triggered by an extension.
3853 auto* basePrin
= BasePrincipal::Cast(principal
);
3854 if (basePrin
->Is
<ExpandedPrincipal
>()) {
3855 basePrin
->As
<ExpandedPrincipal
>()->SetCsp(mCSP
);
3859 // ----- if there's a full-strength CSP header, apply it.
3860 if (!cspHeaderValue
.IsEmpty()) {
3861 mHasCSPDeliveredThroughHeader
= true;
3862 rv
= CSP_AppendCSPFromHeader(mCSP
, cspHeaderValue
, false);
3863 NS_ENSURE_SUCCESS(rv
, rv
);
3866 // ----- if there's a report-only CSP header, apply it.
3867 if (!cspROHeaderValue
.IsEmpty()) {
3868 rv
= CSP_AppendCSPFromHeader(mCSP
, cspROHeaderValue
, true);
3869 NS_ENSURE_SUCCESS(rv
, rv
);
3872 // ----- Enforce sandbox policy if supplied in CSP header
3873 // The document may already have some sandbox flags set (e.g. if the document
3874 // is an iframe with the sandbox attribute set). If we have a CSP sandbox
3875 // directive, intersect the CSP sandbox flags with the existing flags. This
3876 // corresponds to the _least_ permissive policy.
3877 uint32_t cspSandboxFlags
= SANDBOXED_NONE
;
3878 rv
= mCSP
->GetCSPSandboxFlags(&cspSandboxFlags
);
3879 NS_ENSURE_SUCCESS(rv
, rv
);
3881 // Probably the iframe sandbox attribute already caused the creation of a
3882 // new NullPrincipal. Only create a new NullPrincipal if CSP requires so
3883 // and no one has been created yet.
3884 bool needNewNullPrincipal
= (cspSandboxFlags
& SANDBOXED_ORIGIN
) &&
3885 !(mSandboxFlags
& SANDBOXED_ORIGIN
);
3887 mSandboxFlags
|= cspSandboxFlags
;
3889 if (needNewNullPrincipal
) {
3890 principal
= NullPrincipal::CreateWithInheritedAttributes(principal
);
3891 // Skip setting the content blocking allowlist principal to NullPrincipal.
3892 // The principal is only used to enable/disable trackingprotection via
3893 // permission and can be shared with the top level sandboxed site.
3895 SetPrincipals(principal
, principal
);
3898 ApplySettingsFromCSP(false);
3902 static Document
* GetInProcessParentDocumentFrom(BrowsingContext
* aContext
) {
3903 BrowsingContext
* parentContext
= aContext
->GetParent();
3904 if (!parentContext
) {
3908 WindowContext
* windowContext
= parentContext
->GetCurrentWindowContext();
3909 if (!windowContext
) {
3913 return windowContext
->GetDocument();
3916 already_AddRefed
<dom::FeaturePolicy
> Document::GetParentFeaturePolicy() {
3917 BrowsingContext
* browsingContext
= GetBrowsingContext();
3918 if (!browsingContext
) {
3921 if (!browsingContext
->IsContentSubframe()) {
3925 HTMLIFrameElement
* iframe
=
3926 HTMLIFrameElement::FromNodeOrNull(browsingContext
->GetEmbedderElement());
3928 return do_AddRef(iframe
->FeaturePolicy());
3931 if (XRE_IsParentProcess()) {
3932 return do_AddRef(browsingContext
->Canonical()->GetContainerFeaturePolicy());
3935 if (Document
* parentDocument
=
3936 GetInProcessParentDocumentFrom(browsingContext
)) {
3937 return do_AddRef(parentDocument
->FeaturePolicy());
3940 WindowContext
* windowContext
= browsingContext
->GetCurrentWindowContext();
3941 if (!windowContext
) {
3945 WindowGlobalChild
* child
= windowContext
->GetWindowGlobalChild();
3950 return do_AddRef(child
->GetContainerFeaturePolicy());
3953 void Document::InitFeaturePolicy() {
3954 MOZ_ASSERT(mFeaturePolicy
, "we should have FeaturePolicy created");
3956 mFeaturePolicy
->ResetDeclaredPolicy();
3958 mFeaturePolicy
->SetDefaultOrigin(NodePrincipal());
3960 RefPtr
<mozilla::dom::FeaturePolicy
> parentPolicy
= GetParentFeaturePolicy();
3962 // Let's inherit the policy from the parent HTMLIFrameElement if it exists.
3963 mFeaturePolicy
->InheritPolicy(parentPolicy
);
3964 mFeaturePolicy
->SetSrcOrigin(parentPolicy
->GetSrcOrigin());
3968 nsresult
Document::InitFeaturePolicy(nsIChannel
* aChannel
) {
3969 InitFeaturePolicy();
3971 // We don't want to parse the http Feature-Policy header if this pref is off.
3972 if (!StaticPrefs::dom_security_featurePolicy_header_enabled()) {
3976 nsCOMPtr
<nsIHttpChannel
> httpChannel
;
3977 nsresult rv
= GetHttpChannelHelper(aChannel
, getter_AddRefs(httpChannel
));
3978 if (NS_WARN_IF(NS_FAILED(rv
))) {
3986 // query the policy from the header
3987 nsAutoCString value
;
3988 rv
= httpChannel
->GetResponseHeader("Feature-Policy"_ns
, value
);
3989 if (NS_SUCCEEDED(rv
)) {
3990 mFeaturePolicy
->SetDeclaredPolicy(this, NS_ConvertUTF8toUTF16(value
),
3991 NodePrincipal(), nullptr);
3997 void Document::EnsureNotEnteringAndExitFullscreen() {
3998 Document::ClearPendingFullscreenRequests(this);
3999 if (GetFullscreenElement()) {
4000 Document::AsyncExitFullscreen(this);
4004 void Document::SetReferrerInfo(nsIReferrerInfo
* aReferrerInfo
) {
4005 mReferrerInfo
= aReferrerInfo
;
4006 mCachedReferrerInfoForInternalCSSAndSVGResources
= nullptr;
4007 mCachedURLData
= nullptr;
4010 nsresult
Document::InitReferrerInfo(nsIChannel
* aChannel
) {
4011 MOZ_ASSERT(mReferrerInfo
);
4012 MOZ_ASSERT(mPreloadReferrerInfo
);
4014 if (ReferrerInfo::ShouldResponseInheritReferrerInfo(aChannel
)) {
4015 // The channel is loading `about:srcdoc`. Srcdoc loads should respond with
4016 // their parent's ReferrerInfo when asked for their ReferrerInfo, unless
4017 // they have an opaque origin.
4018 // https://w3c.github.io/webappsec-referrer-policy/#determine-requests-referrer
4019 if (BrowsingContext
* bc
= GetBrowsingContext()) {
4020 // At this point the document is not fully created and mParentDocument has
4021 // not been set yet,
4022 Document
* parentDoc
= bc
->GetEmbedderElement()
4023 ? bc
->GetEmbedderElement()->OwnerDoc()
4026 SetReferrerInfo(parentDoc
->GetReferrerInfo());
4027 mPreloadReferrerInfo
= mReferrerInfo
;
4031 MOZ_ASSERT(bc
->IsInProcess() || NodePrincipal()->GetIsNullPrincipal(),
4032 "srcdoc without null principal as toplevel!");
4036 nsCOMPtr
<nsIHttpChannel
> httpChannel
;
4037 nsresult rv
= GetHttpChannelHelper(aChannel
, getter_AddRefs(httpChannel
));
4038 if (NS_WARN_IF(NS_FAILED(rv
))) {
4046 if (nsCOMPtr
<nsIReferrerInfo
> referrerInfo
= httpChannel
->GetReferrerInfo()) {
4047 SetReferrerInfo(referrerInfo
);
4050 // Override policy if we get one from Referrerr-Policy header
4051 mozilla::dom::ReferrerPolicy policy
=
4052 nsContentUtils::GetReferrerPolicyFromChannel(aChannel
);
4053 nsCOMPtr
<nsIReferrerInfo
> clone
=
4054 static_cast<dom::ReferrerInfo
*>(mReferrerInfo
.get())
4055 ->CloneWithNewPolicy(policy
);
4056 SetReferrerInfo(clone
);
4057 mPreloadReferrerInfo
= mReferrerInfo
;
4061 nsresult
Document::InitCOEP(nsIChannel
* aChannel
) {
4062 nsCOMPtr
<nsIHttpChannel
> httpChannel
;
4063 nsresult rv
= GetHttpChannelHelper(aChannel
, getter_AddRefs(httpChannel
));
4064 if (NS_FAILED(rv
)) {
4068 nsCOMPtr
<nsIHttpChannelInternal
> intChannel
= do_QueryInterface(httpChannel
);
4074 nsILoadInfo::CrossOriginEmbedderPolicy policy
=
4075 nsILoadInfo::EMBEDDER_POLICY_NULL
;
4076 if (NS_SUCCEEDED(intChannel
->GetResponseEmbedderPolicy(
4077 mTrials
.IsEnabled(OriginTrial::CoepCredentialless
), &policy
))) {
4078 mEmbedderPolicy
= Some(policy
);
4084 void Document::StopDocumentLoad() {
4086 mParserAborted
= true;
4087 mParser
->Terminate();
4091 void Document::SetDocumentURI(nsIURI
* aURI
) {
4092 nsCOMPtr
<nsIURI
> oldBase
= GetDocBaseURI();
4093 mDocumentURI
= aURI
;
4094 nsIURI
* newBase
= GetDocBaseURI();
4096 mChromeRulesEnabled
= URLExtraData::ChromeRulesEnabled(aURI
);
4098 bool equalBases
= false;
4099 // Changing just the ref of a URI does not change how relative URIs would
4100 // resolve wrt to it, so we can treat the bases as equal as long as they're
4101 // equal ignoring the ref.
4102 if (oldBase
&& newBase
) {
4103 oldBase
->EqualsExceptRef(newBase
, &equalBases
);
4105 equalBases
= !oldBase
&& !newBase
;
4108 // If this is the first time we're setting the document's URI, set the
4109 // document's original URI.
4110 if (!mOriginalURI
) mOriginalURI
= mDocumentURI
;
4112 // If changing the document's URI changed the base URI of the document, we
4113 // need to refresh the hrefs of all the links on the page.
4115 mCachedURLData
= nullptr;
4119 // Recalculate our base domain
4120 mBaseDomain
.Truncate();
4121 ThirdPartyUtil
* thirdPartyUtil
= ThirdPartyUtil::GetInstance();
4122 if (thirdPartyUtil
) {
4123 Unused
<< thirdPartyUtil
->GetBaseDomain(mDocumentURI
, mBaseDomain
);
4126 // Tell our WindowGlobalParent that the document's URI has been changed.
4127 if (WindowGlobalChild
* wgc
= GetWindowGlobalChild()) {
4128 wgc
->SetDocumentURI(mDocumentURI
);
4132 static void GetFormattedTimeString(PRTime aTime
,
4133 nsAString
& aFormattedTimeString
) {
4134 PRExplodedTime prtime
;
4135 PR_ExplodeTime(aTime
, PR_LocalTimeParameters
, &prtime
);
4136 // "MM/DD/YYYY hh:mm:ss"
4137 char formatedTime
[24];
4138 if (SprintfLiteral(formatedTime
, "%02d/%02d/%04d %02d:%02d:%02d",
4139 prtime
.tm_month
+ 1, prtime
.tm_mday
, int(prtime
.tm_year
),
4140 prtime
.tm_hour
, prtime
.tm_min
, prtime
.tm_sec
)) {
4141 CopyASCIItoUTF16(nsDependentCString(formatedTime
), aFormattedTimeString
);
4143 // If we for whatever reason failed to find the last modified time
4144 // (or even the current time), fall back to what NS4.x returned.
4145 aFormattedTimeString
.AssignLiteral(u
"01/01/1970 00:00:00");
4149 void Document::GetLastModified(nsAString
& aLastModified
) const {
4150 if (!mLastModified
.IsEmpty()) {
4151 aLastModified
.Assign(mLastModified
);
4153 GetFormattedTimeString(PR_Now(), aLastModified
);
4157 static void IncrementExpandoGeneration(Document
& aDoc
) {
4158 ++aDoc
.mExpandoAndGeneration
.generation
;
4161 void Document::AddToNameTable(Element
* aElement
, nsAtom
* aName
) {
4163 nsGenericHTMLElement::ShouldExposeNameAsHTMLDocumentProperty(aElement
),
4164 "Only put elements that need to be exposed as document['name'] in "
4165 "the named table.");
4167 IdentifierMapEntry
* entry
= mIdentifierMap
.PutEntry(aName
);
4169 // Null for out-of-memory
4171 if (!entry
->HasNameElement() &&
4172 !entry
->HasIdElementExposedAsHTMLDocumentProperty()) {
4173 IncrementExpandoGeneration(*this);
4175 entry
->AddNameElement(this, aElement
);
4179 void Document::RemoveFromNameTable(Element
* aElement
, nsAtom
* aName
) {
4180 // Speed up document teardown
4181 if (mIdentifierMap
.Count() == 0) return;
4183 IdentifierMapEntry
* entry
= mIdentifierMap
.GetEntry(aName
);
4184 if (!entry
) // Could be false if the element was anonymous, hence never added
4187 entry
->RemoveNameElement(aElement
);
4188 if (!entry
->HasNameElement() &&
4189 !entry
->HasIdElementExposedAsHTMLDocumentProperty()) {
4190 IncrementExpandoGeneration(*this);
4194 void Document::AddToIdTable(Element
* aElement
, nsAtom
* aId
) {
4195 IdentifierMapEntry
* entry
= mIdentifierMap
.PutEntry(aId
);
4197 if (entry
) { /* True except on OOM */
4198 if (nsGenericHTMLElement::ShouldExposeIdAsHTMLDocumentProperty(aElement
) &&
4199 !entry
->HasNameElement() &&
4200 !entry
->HasIdElementExposedAsHTMLDocumentProperty()) {
4201 IncrementExpandoGeneration(*this);
4203 entry
->AddIdElement(aElement
);
4207 void Document::RemoveFromIdTable(Element
* aElement
, nsAtom
* aId
) {
4208 NS_ASSERTION(aId
, "huhwhatnow?");
4210 // Speed up document teardown
4211 if (mIdentifierMap
.Count() == 0) {
4215 IdentifierMapEntry
* entry
= mIdentifierMap
.GetEntry(aId
);
4216 if (!entry
) // Can be null for XML elements with changing ids.
4219 entry
->RemoveIdElement(aElement
);
4220 if (nsGenericHTMLElement::ShouldExposeIdAsHTMLDocumentProperty(aElement
) &&
4221 !entry
->HasNameElement() &&
4222 !entry
->HasIdElementExposedAsHTMLDocumentProperty()) {
4223 IncrementExpandoGeneration(*this);
4225 if (entry
->IsEmpty()) {
4226 mIdentifierMap
.RemoveEntry(entry
);
4230 void Document::UpdateReferrerInfoFromMeta(const nsAString
& aMetaReferrer
,
4232 ReferrerPolicyEnum policy
=
4233 ReferrerInfo::ReferrerPolicyFromMetaString(aMetaReferrer
);
4234 // The empty string "" corresponds to no referrer policy, causing a fallback
4235 // to a referrer policy defined elsewhere.
4236 if (policy
== ReferrerPolicy::_empty
) {
4240 MOZ_ASSERT(mReferrerInfo
);
4241 MOZ_ASSERT(mPreloadReferrerInfo
);
4244 mPreloadReferrerInfo
=
4245 static_cast<mozilla::dom::ReferrerInfo
*>((mPreloadReferrerInfo
).get())
4246 ->CloneWithNewPolicy(policy
);
4248 nsCOMPtr
<nsIReferrerInfo
> clone
=
4249 static_cast<mozilla::dom::ReferrerInfo
*>((mReferrerInfo
).get())
4250 ->CloneWithNewPolicy(policy
);
4251 SetReferrerInfo(clone
);
4255 void Document::SetPrincipals(nsIPrincipal
* aNewPrincipal
,
4256 nsIPrincipal
* aNewPartitionedPrincipal
) {
4257 MOZ_ASSERT(!!aNewPrincipal
== !!aNewPartitionedPrincipal
);
4258 if (aNewPrincipal
&& mAllowDNSPrefetch
&&
4259 StaticPrefs::network_dns_disablePrefetchFromHTTPS()) {
4260 if (aNewPrincipal
->SchemeIs("https")) {
4261 mAllowDNSPrefetch
= false;
4265 mCSSLoader
->DeregisterFromSheetCache();
4267 mNodeInfoManager
->SetDocumentPrincipal(aNewPrincipal
);
4268 mPartitionedPrincipal
= aNewPartitionedPrincipal
;
4270 mCachedURLData
= nullptr;
4272 mCSSLoader
->RegisterInSheetCache();
4274 RecomputeResistFingerprinting();
4277 // Validate that the docgroup is set correctly by calling its getter and
4278 // triggering its sanity check.
4280 // If we're setting the principal to null, we don't want to perform the check,
4281 // as the document is entering an intermediate state where it does not have a
4282 // principal. It will be given another real principal shortly which we will
4283 // check. It's not unsafe to have a document which has a null principal in the
4284 // same docgroup as another document, so this should not be a problem.
4285 if (aNewPrincipal
) {
4292 void Document::AssertDocGroupMatchesKey() const {
4293 // Sanity check that we have an up-to-date and accurate docgroup
4294 // We only check if the principal when we can get the browsing context.
4296 // Note that we can be invoked during cycle collection, so we need to handle
4297 // the browsingcontext being partially unlinked - normally you shouldn't
4298 // null-check `Group()` as it shouldn't return nullptr.
4299 if (!GetBrowsingContext() || !GetBrowsingContext()->Group()) {
4303 if (mDocGroup
&& mDocGroup
->GetBrowsingContextGroup()) {
4304 MOZ_ASSERT(mDocGroup
->GetBrowsingContextGroup() ==
4305 GetBrowsingContext()->Group());
4307 // GetKey() can fail, e.g. after the TLD service has shut down.
4308 nsAutoCString docGroupKey
;
4309 nsresult rv
= mozilla::dom::DocGroup::GetKey(
4311 GetBrowsingContext()->Group()->IsPotentiallyCrossOriginIsolated(),
4313 if (NS_SUCCEEDED(rv
)) {
4314 MOZ_ASSERT(mDocGroup
->MatchesKey(docGroupKey
));
4320 nsresult
Document::Dispatch(already_AddRefed
<nsIRunnable
>&& aRunnable
) const {
4321 return SchedulerGroup::Dispatch(std::move(aRunnable
));
4324 void Document::NoteScriptTrackingStatus(const nsACString
& aURL
,
4327 mTrackingScripts
.Insert(aURL
);
4329 MOZ_ASSERT(!mTrackingScripts
.Contains(aURL
));
4333 bool Document::IsScriptTracking(JSContext
* aCx
) const {
4334 JS::AutoFilename filename
;
4335 if (!JS::DescribeScriptedCaller(aCx
, &filename
)) {
4338 return mTrackingScripts
.Contains(nsDependentCString(filename
.get()));
4341 void Document::GetContentType(nsAString
& aContentType
) {
4342 CopyUTF8toUTF16(GetContentTypeInternal(), aContentType
);
4345 void Document::SetContentType(const nsACString
& aContentType
) {
4346 if (!IsHTMLOrXHTML() && mDefaultElementType
== kNameSpaceID_None
&&
4347 aContentType
.EqualsLiteral("application/xhtml+xml")) {
4348 mDefaultElementType
= kNameSpaceID_XHTML
;
4351 mCachedEncoder
= nullptr;
4352 mContentType
= aContentType
;
4355 bool Document::GetAllowPlugins() {
4356 // First, we ask our docshell if it allows plugins.
4357 auto* browsingContext
= GetBrowsingContext();
4359 if (browsingContext
) {
4360 if (!browsingContext
->GetAllowPlugins()) {
4364 // If the docshell allows plugins, we check whether
4365 // we are sandboxed and plugins should not be allowed.
4366 if (mSandboxFlags
& SANDBOXED_PLUGINS
) {
4374 bool Document::HasPendingInitialTranslation() {
4375 return mDocumentL10n
&& mDocumentL10n
->GetState() != DocumentL10nState::Ready
;
4378 bool Document::HasPendingL10nMutations() const {
4379 return mDocumentL10n
&& mDocumentL10n
->HasPendingMutations();
4382 bool Document::DocumentSupportsL10n(JSContext
* aCx
, JSObject
* aObject
) {
4383 JS::Rooted
<JSObject
*> object(aCx
, aObject
);
4384 nsCOMPtr
<nsIPrincipal
> callerPrincipal
=
4385 nsContentUtils::SubjectPrincipal(aCx
);
4386 nsGlobalWindowInner
* win
= xpc::WindowOrNull(object
);
4387 bool allowed
= false;
4388 callerPrincipal
->IsL10nAllowed(win
? win
->GetDocumentURI() : nullptr,
4393 void Document::LocalizationLinkAdded(Element
* aLinkElement
) {
4394 if (!AllowsL10n()) {
4399 aLinkElement
->GetAttr(nsGkAtoms::href
, href
);
4401 if (!mDocumentL10n
) {
4402 Element
* elem
= GetDocumentElement();
4403 MOZ_DIAGNOSTIC_ASSERT(elem
);
4405 bool isSync
= elem
->HasAttr(nsGkAtoms::datal10nsync
);
4406 mDocumentL10n
= DocumentL10n::Create(this, isSync
);
4407 if (NS_WARN_IF(!mDocumentL10n
)) {
4412 mDocumentL10n
->AddResourceId(NS_ConvertUTF16toUTF8(href
));
4414 if (mReadyState
>= READYSTATE_INTERACTIVE
) {
4415 nsContentUtils::AddScriptRunner(NewRunnableMethod(
4416 "DocumentL10n::TriggerInitialTranslation()", mDocumentL10n
,
4417 &DocumentL10n::TriggerInitialTranslation
));
4419 if (!mDocumentL10n
->mBlockingLayout
) {
4420 // Our initial translation is going to block layout start. Make sure
4421 // we don't fire the load event until after that stops happening and
4422 // layout has a chance to start.
4424 mDocumentL10n
->mBlockingLayout
= true;
4429 void Document::LocalizationLinkRemoved(Element
* aLinkElement
) {
4430 if (!AllowsL10n()) {
4434 if (mDocumentL10n
) {
4436 aLinkElement
->GetAttr(nsGkAtoms::href
, href
);
4437 uint32_t remaining
=
4438 mDocumentL10n
->RemoveResourceId(NS_ConvertUTF16toUTF8(href
));
4439 if (remaining
== 0) {
4440 if (mDocumentL10n
->mBlockingLayout
) {
4441 mDocumentL10n
->mBlockingLayout
= false;
4442 UnblockOnload(/* aFireSync = */ false);
4444 mDocumentL10n
= nullptr;
4450 * This method should be called once the end of the l10n
4451 * resource container has been parsed.
4453 * In XUL this is the end of the first </linkset>,
4454 * In XHTML/HTML this is the end of </head>.
4456 * This milestone is used to allow for batch
4457 * localization context I/O and building done
4458 * once when all resources in the document have been
4461 void Document::OnL10nResourceContainerParsed() {
4462 // XXX: This is a scaffolding for where we might inject prefetch
4466 void Document::OnParsingCompleted() {
4467 // Let's call it again, in case the resource
4468 // container has not been closed, and only
4469 // now we're closing the document.
4470 OnL10nResourceContainerParsed();
4472 if (mDocumentL10n
) {
4473 RefPtr
<DocumentL10n
> l10n
= mDocumentL10n
;
4474 l10n
->TriggerInitialTranslation();
4478 void Document::InitialTranslationCompleted(bool aL10nCached
) {
4479 if (mDocumentL10n
&& mDocumentL10n
->mBlockingLayout
) {
4480 // This means we blocked the load event in LocalizationLinkAdded. It's
4481 // important that the load blocker removal here be async, because our caller
4482 // will notify the content sink after us, and we want the content sync's
4483 // work to happen before the load event fires.
4484 mDocumentL10n
->mBlockingLayout
= false;
4485 UnblockOnload(/* aFireSync = */ false);
4488 mL10nProtoElements
.Clear();
4490 nsXULPrototypeDocument
* proto
= GetPrototype();
4492 proto
->SetIsL10nCached(aL10nCached
);
4496 bool Document::AllowsL10n() const {
4497 if (IsStaticDocument()) {
4498 // We don't allow l10n on static documents, because the nodes are already
4499 // cloned translated, and static docs don't get parsed so we never
4500 // TriggerInitialTranslation, etc, so a load blocker would keep hanging
4504 bool allowed
= false;
4505 NodePrincipal()->IsL10nAllowed(GetDocumentURI(), &allowed
);
4509 bool Document::IsWebAnimationsGetAnimationsEnabled(JSContext
* aCx
,
4510 JSObject
* /*unused*/
4512 MOZ_ASSERT(NS_IsMainThread());
4514 return nsContentUtils::IsSystemCaller(aCx
) ||
4515 StaticPrefs::dom_animations_api_getAnimations_enabled();
4518 bool Document::AreWebAnimationsTimelinesEnabled(JSContext
* aCx
,
4519 JSObject
* /*unused*/
4521 MOZ_ASSERT(NS_IsMainThread());
4523 return nsContentUtils::IsSystemCaller(aCx
) ||
4524 StaticPrefs::dom_animations_api_timelines_enabled();
4527 DocumentTimeline
* Document::Timeline() {
4528 if (!mDocumentTimeline
) {
4529 mDocumentTimeline
= new DocumentTimeline(this, TimeDuration(0));
4532 return mDocumentTimeline
;
4535 SVGSVGElement
* Document::GetSVGRootElement() const {
4536 Element
* root
= GetRootElement();
4537 if (!root
|| !root
->IsSVGElement(nsGkAtoms::svg
)) {
4540 return static_cast<SVGSVGElement
*>(root
);
4543 /* Return true if the document is in the focused top-level window, and is an
4544 * ancestor of the focused DOMWindow. */
4545 bool Document::HasFocus(ErrorResult
& rv
) const {
4546 nsFocusManager
* fm
= nsFocusManager::GetFocusManager();
4548 rv
.Throw(NS_ERROR_NOT_AVAILABLE
);
4552 BrowsingContext
* bc
= GetBrowsingContext();
4557 if (!fm
->IsInActiveWindow(bc
)) {
4561 return fm
->IsSameOrAncestor(bc
, fm
->GetFocusedBrowsingContext());
4564 bool Document::ThisDocumentHasFocus() const {
4565 nsFocusManager
* fm
= nsFocusManager::GetFocusManager();
4566 return fm
&& fm
->GetFocusedWindow() &&
4567 fm
->GetFocusedWindow()->GetExtantDoc() == this;
4570 void Document::GetDesignMode(nsAString
& aDesignMode
) {
4571 if (IsInDesignMode()) {
4572 aDesignMode
.AssignLiteral("on");
4574 aDesignMode
.AssignLiteral("off");
4578 void Document::SetDesignMode(const nsAString
& aDesignMode
,
4579 nsIPrincipal
& aSubjectPrincipal
, ErrorResult
& rv
) {
4580 SetDesignMode(aDesignMode
, Some(&aSubjectPrincipal
), rv
);
4583 static void NotifyEditableStateChange(Document
& aDoc
) {
4584 #ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
4587 for (nsIContent
* node
= aDoc
.GetNextNode(&aDoc
); node
;
4588 node
= node
->GetNextNode(&aDoc
)) {
4589 if (auto* element
= Element::FromNode(node
)) {
4590 element
->UpdateEditableState(true);
4593 MOZ_DIAGNOSTIC_ASSERT(!g
.Mutated(0));
4596 void Document::SetDesignMode(const nsAString
& aDesignMode
,
4597 const Maybe
<nsIPrincipal
*>& aSubjectPrincipal
,
4599 if (aSubjectPrincipal
.isSome() &&
4600 !aSubjectPrincipal
.value()->Subsumes(NodePrincipal())) {
4601 rv
.Throw(NS_ERROR_DOM_PROP_ACCESS_DENIED
);
4604 const bool editableMode
= IsInDesignMode();
4605 if (aDesignMode
.LowerCaseEqualsASCII(editableMode
? "off" : "on")) {
4606 SetEditableFlag(!editableMode
);
4607 // Changing the NODE_IS_EDITABLE flags on document changes the intrinsic
4608 // state of all descendant elements of it. Update that now.
4609 NotifyEditableStateChange(*this);
4610 rv
= EditingStateChanged();
4614 nsCommandManager
* Document::GetMidasCommandManager() {
4615 // check if we have it cached
4616 if (mMidasCommandManager
) {
4617 return mMidasCommandManager
;
4620 nsPIDOMWindowOuter
* window
= GetWindow();
4625 nsIDocShell
* docshell
= window
->GetDocShell();
4630 mMidasCommandManager
= docshell
->GetCommandManager();
4631 return mMidasCommandManager
;
4635 void Document::EnsureInitializeInternalCommandDataHashtable() {
4636 if (sInternalCommandDataHashtable
) {
4639 using CommandOnTextEditor
= InternalCommandData::CommandOnTextEditor
;
4640 sInternalCommandDataHashtable
= new InternalCommandDataHashtable();
4642 sInternalCommandDataHashtable
->InsertOrUpdate(
4644 InternalCommandData(
4646 Command::FormatBold
,
4647 ExecCommandParam::Ignore
,
4648 StyleUpdatingCommand::GetInstance
,
4649 CommandOnTextEditor::Disabled
));
4650 sInternalCommandDataHashtable
->InsertOrUpdate(
4652 InternalCommandData(
4654 Command::FormatItalic
,
4655 ExecCommandParam::Ignore
,
4656 StyleUpdatingCommand::GetInstance
,
4657 CommandOnTextEditor::Disabled
));
4658 sInternalCommandDataHashtable
->InsertOrUpdate(
4660 InternalCommandData(
4662 Command::FormatUnderline
,
4663 ExecCommandParam::Ignore
,
4664 StyleUpdatingCommand::GetInstance
,
4665 CommandOnTextEditor::Disabled
));
4666 sInternalCommandDataHashtable
->InsertOrUpdate(
4667 u
"strikethrough"_ns
,
4668 InternalCommandData(
4669 "cmd_strikethrough",
4670 Command::FormatStrikeThrough
,
4671 ExecCommandParam::Ignore
,
4672 StyleUpdatingCommand::GetInstance
,
4673 CommandOnTextEditor::Disabled
));
4674 sInternalCommandDataHashtable
->InsertOrUpdate(
4676 InternalCommandData(
4678 Command::FormatSubscript
,
4679 ExecCommandParam::Ignore
,
4680 StyleUpdatingCommand::GetInstance
,
4681 CommandOnTextEditor::Disabled
));
4682 sInternalCommandDataHashtable
->InsertOrUpdate(
4684 InternalCommandData(
4686 Command::FormatSuperscript
,
4687 ExecCommandParam::Ignore
,
4688 StyleUpdatingCommand::GetInstance
,
4689 CommandOnTextEditor::Disabled
));
4690 sInternalCommandDataHashtable
->InsertOrUpdate(
4692 InternalCommandData(
4695 ExecCommandParam::Ignore
,
4696 CutCommand::GetInstance
,
4697 CommandOnTextEditor::Enabled
));
4698 sInternalCommandDataHashtable
->InsertOrUpdate(
4700 InternalCommandData(
4703 ExecCommandParam::Ignore
,
4704 CopyCommand::GetInstance
,
4705 CommandOnTextEditor::Enabled
));
4706 sInternalCommandDataHashtable
->InsertOrUpdate(
4708 InternalCommandData(
4711 ExecCommandParam::Ignore
,
4712 PasteCommand::GetInstance
,
4713 CommandOnTextEditor::Enabled
));
4714 sInternalCommandDataHashtable
->InsertOrUpdate(
4716 InternalCommandData(
4717 "cmd_deleteCharBackward",
4718 Command::DeleteCharBackward
,
4719 ExecCommandParam::Ignore
,
4720 DeleteCommand::GetInstance
,
4721 CommandOnTextEditor::Enabled
));
4722 sInternalCommandDataHashtable
->InsertOrUpdate(
4723 u
"forwarddelete"_ns
,
4724 InternalCommandData(
4725 "cmd_deleteCharForward",
4726 Command::DeleteCharForward
,
4727 ExecCommandParam::Ignore
,
4728 DeleteCommand::GetInstance
,
4729 CommandOnTextEditor::Enabled
));
4730 sInternalCommandDataHashtable
->InsertOrUpdate(
4732 InternalCommandData(
4735 ExecCommandParam::Ignore
,
4736 SelectAllCommand::GetInstance
,
4737 CommandOnTextEditor::Enabled
));
4738 sInternalCommandDataHashtable
->InsertOrUpdate(
4740 InternalCommandData(
4742 Command::HistoryUndo
,
4743 ExecCommandParam::Ignore
,
4744 UndoCommand::GetInstance
,
4745 CommandOnTextEditor::Enabled
));
4746 sInternalCommandDataHashtable
->InsertOrUpdate(
4748 InternalCommandData(
4750 Command::HistoryRedo
,
4751 ExecCommandParam::Ignore
,
4752 RedoCommand::GetInstance
,
4753 CommandOnTextEditor::Enabled
));
4754 sInternalCommandDataHashtable
->InsertOrUpdate(
4756 InternalCommandData("cmd_indent",
4757 Command::FormatIndent
,
4758 ExecCommandParam::Ignore
,
4759 IndentCommand::GetInstance
,
4760 CommandOnTextEditor::Disabled
));
4761 sInternalCommandDataHashtable
->InsertOrUpdate(
4763 InternalCommandData(
4765 Command::FormatOutdent
,
4766 ExecCommandParam::Ignore
,
4767 OutdentCommand::GetInstance
,
4768 CommandOnTextEditor::Disabled
));
4769 sInternalCommandDataHashtable
->InsertOrUpdate(
4771 InternalCommandData(
4773 Command::FormatBackColor
,
4774 ExecCommandParam::String
,
4775 HighlightColorStateCommand::GetInstance
,
4776 CommandOnTextEditor::Disabled
));
4777 sInternalCommandDataHashtable
->InsertOrUpdate(
4779 InternalCommandData(
4781 Command::FormatBackColor
,
4782 ExecCommandParam::String
,
4783 HighlightColorStateCommand::GetInstance
,
4784 CommandOnTextEditor::Disabled
));
4785 sInternalCommandDataHashtable
->InsertOrUpdate(
4787 InternalCommandData(
4789 Command::FormatFontColor
,
4790 ExecCommandParam::String
,
4791 FontColorStateCommand::GetInstance
,
4792 CommandOnTextEditor::Disabled
));
4793 sInternalCommandDataHashtable
->InsertOrUpdate(
4795 InternalCommandData(
4797 Command::FormatFontName
,
4798 ExecCommandParam::String
,
4799 FontFaceStateCommand::GetInstance
,
4800 CommandOnTextEditor::Disabled
));
4801 sInternalCommandDataHashtable
->InsertOrUpdate(
4803 InternalCommandData(
4805 Command::FormatFontSize
,
4806 ExecCommandParam::String
,
4807 FontSizeStateCommand::GetInstance
,
4808 CommandOnTextEditor::Disabled
));
4809 sInternalCommandDataHashtable
->InsertOrUpdate(
4810 u
"inserthorizontalrule"_ns
,
4811 InternalCommandData(
4813 Command::InsertHorizontalRule
,
4814 ExecCommandParam::Ignore
,
4815 InsertTagCommand::GetInstance
,
4816 CommandOnTextEditor::Disabled
));
4817 sInternalCommandDataHashtable
->InsertOrUpdate(
4819 InternalCommandData(
4820 "cmd_insertLinkNoUI",
4821 Command::InsertLink
,
4822 ExecCommandParam::String
,
4823 InsertTagCommand::GetInstance
,
4824 CommandOnTextEditor::Disabled
));
4825 sInternalCommandDataHashtable
->InsertOrUpdate(
4827 InternalCommandData(
4828 "cmd_insertImageNoUI",
4829 Command::InsertImage
,
4830 ExecCommandParam::String
,
4831 InsertTagCommand::GetInstance
,
4832 CommandOnTextEditor::Disabled
));
4833 sInternalCommandDataHashtable
->InsertOrUpdate(
4835 InternalCommandData(
4837 Command::InsertHTML
,
4838 ExecCommandParam::String
,
4839 InsertHTMLCommand::GetInstance
,
4840 // TODO: Chromium inserts text content of the document fragment
4841 // created from the param.
4842 // https://source.chromium.org/chromium/chromium/src/+/master:third_party/blink/renderer/core/editing/commands/insert_commands.cc;l=105;drc=a4708b724062f17824815b896c3aaa43825128f8
4843 CommandOnTextEditor::Disabled
));
4844 sInternalCommandDataHashtable
->InsertOrUpdate(
4846 InternalCommandData(
4848 Command::InsertText
,
4849 ExecCommandParam::String
,
4850 InsertPlaintextCommand::GetInstance
,
4851 CommandOnTextEditor::Enabled
));
4852 sInternalCommandDataHashtable
->InsertOrUpdate(
4854 InternalCommandData(
4856 Command::FormatJustifyLeft
,
4857 ExecCommandParam::Ignore
, // Will be set to "left"
4858 AlignCommand::GetInstance
,
4859 CommandOnTextEditor::Disabled
));
4860 sInternalCommandDataHashtable
->InsertOrUpdate(
4862 InternalCommandData(
4864 Command::FormatJustifyRight
,
4865 ExecCommandParam::Ignore
, // Will be set to "right"
4866 AlignCommand::GetInstance
,
4867 CommandOnTextEditor::Disabled
));
4868 sInternalCommandDataHashtable
->InsertOrUpdate(
4869 u
"justifycenter"_ns
,
4870 InternalCommandData(
4872 Command::FormatJustifyCenter
,
4873 ExecCommandParam::Ignore
, // Will be set to "center"
4874 AlignCommand::GetInstance
,
4875 CommandOnTextEditor::Disabled
));
4876 sInternalCommandDataHashtable
->InsertOrUpdate(
4878 InternalCommandData(
4880 Command::FormatJustifyFull
,
4881 ExecCommandParam::Ignore
, // Will be set to "justify"
4882 AlignCommand::GetInstance
,
4883 CommandOnTextEditor::Disabled
));
4884 sInternalCommandDataHashtable
->InsertOrUpdate(
4886 InternalCommandData(
4888 Command::FormatRemove
,
4889 ExecCommandParam::Ignore
,
4890 RemoveStylesCommand::GetInstance
,
4891 CommandOnTextEditor::Disabled
));
4892 sInternalCommandDataHashtable
->InsertOrUpdate(
4894 InternalCommandData(
4896 Command::FormatRemoveLink
,
4897 ExecCommandParam::Ignore
,
4898 StyleUpdatingCommand::GetInstance
,
4899 CommandOnTextEditor::Disabled
));
4900 sInternalCommandDataHashtable
->InsertOrUpdate(
4901 u
"insertorderedlist"_ns
,
4902 InternalCommandData(
4904 Command::InsertOrderedList
,
4905 ExecCommandParam::Ignore
,
4906 ListCommand::GetInstance
,
4907 CommandOnTextEditor::Disabled
));
4908 sInternalCommandDataHashtable
->InsertOrUpdate(
4909 u
"insertunorderedlist"_ns
,
4910 InternalCommandData(
4912 Command::InsertUnorderedList
,
4913 ExecCommandParam::Ignore
,
4914 ListCommand::GetInstance
,
4915 CommandOnTextEditor::Disabled
));
4916 sInternalCommandDataHashtable
->InsertOrUpdate(
4917 u
"insertparagraph"_ns
,
4918 InternalCommandData(
4919 "cmd_insertParagraph",
4920 Command::InsertParagraph
,
4921 ExecCommandParam::Ignore
,
4922 InsertParagraphCommand::GetInstance
,
4923 CommandOnTextEditor::Enabled
));
4924 sInternalCommandDataHashtable
->InsertOrUpdate(
4925 u
"insertlinebreak"_ns
,
4926 InternalCommandData(
4927 "cmd_insertLineBreak",
4928 Command::InsertLineBreak
,
4929 ExecCommandParam::Ignore
,
4930 InsertLineBreakCommand::GetInstance
,
4931 CommandOnTextEditor::Enabled
));
4932 sInternalCommandDataHashtable
->InsertOrUpdate(
4934 InternalCommandData(
4936 Command::FormatBlock
,
4937 ExecCommandParam::String
,
4938 FormatBlockStateCommand::GetInstance
,
4939 CommandOnTextEditor::Disabled
));
4940 sInternalCommandDataHashtable
->InsertOrUpdate(
4942 InternalCommandData(
4943 "cmd_setDocumentUseCSS",
4944 Command::SetDocumentUseCSS
,
4945 ExecCommandParam::Boolean
,
4946 SetDocumentStateCommand::GetInstance
,
4947 CommandOnTextEditor::FallThrough
));
4948 sInternalCommandDataHashtable
->InsertOrUpdate(
4949 u
"usecss"_ns
, // Legacy command
4950 InternalCommandData(
4951 "cmd_setDocumentUseCSS",
4952 Command::SetDocumentUseCSS
,
4953 ExecCommandParam::InvertedBoolean
,
4954 SetDocumentStateCommand::GetInstance
,
4955 CommandOnTextEditor::FallThrough
));
4956 sInternalCommandDataHashtable
->InsertOrUpdate(
4957 u
"contentReadOnly"_ns
,
4958 InternalCommandData(
4959 "cmd_setDocumentReadOnly",
4960 Command::SetDocumentReadOnly
,
4961 ExecCommandParam::Boolean
,
4962 SetDocumentStateCommand::GetInstance
,
4963 CommandOnTextEditor::Enabled
));
4964 sInternalCommandDataHashtable
->InsertOrUpdate(
4965 u
"insertBrOnReturn"_ns
,
4966 InternalCommandData(
4967 "cmd_insertBrOnReturn",
4968 Command::SetDocumentInsertBROnEnterKeyPress
,
4969 ExecCommandParam::Boolean
,
4970 SetDocumentStateCommand::GetInstance
,
4971 CommandOnTextEditor::FallThrough
));
4972 sInternalCommandDataHashtable
->InsertOrUpdate(
4973 u
"defaultParagraphSeparator"_ns
,
4974 InternalCommandData(
4975 "cmd_defaultParagraphSeparator",
4976 Command::SetDocumentDefaultParagraphSeparator
,
4977 ExecCommandParam::String
,
4978 SetDocumentStateCommand::GetInstance
,
4979 CommandOnTextEditor::FallThrough
));
4980 sInternalCommandDataHashtable
->InsertOrUpdate(
4981 u
"enableObjectResizing"_ns
,
4982 InternalCommandData(
4983 "cmd_enableObjectResizing",
4984 Command::ToggleObjectResizers
,
4985 ExecCommandParam::Boolean
,
4986 SetDocumentStateCommand::GetInstance
,
4987 CommandOnTextEditor::FallThrough
));
4988 sInternalCommandDataHashtable
->InsertOrUpdate(
4989 u
"enableInlineTableEditing"_ns
,
4990 InternalCommandData(
4991 "cmd_enableInlineTableEditing",
4992 Command::ToggleInlineTableEditor
,
4993 ExecCommandParam::Boolean
,
4994 SetDocumentStateCommand::GetInstance
,
4995 CommandOnTextEditor::FallThrough
));
4996 sInternalCommandDataHashtable
->InsertOrUpdate(
4997 u
"enableAbsolutePositionEditing"_ns
,
4998 InternalCommandData(
4999 "cmd_enableAbsolutePositionEditing",
5000 Command::ToggleAbsolutePositionEditor
,
5001 ExecCommandParam::Boolean
,
5002 SetDocumentStateCommand::GetInstance
,
5003 CommandOnTextEditor::FallThrough
));
5004 sInternalCommandDataHashtable
->InsertOrUpdate(
5005 u
"enableCompatibleJoinSplitDirection"_ns
,
5006 InternalCommandData("cmd_enableCompatibleJoinSplitNodeDirection",
5007 Command::EnableCompatibleJoinSplitNodeDirection
,
5008 ExecCommandParam::Boolean
,
5009 SetDocumentStateCommand::GetInstance
,
5010 CommandOnTextEditor::FallThrough
));
5012 // with empty string
5013 sInternalCommandDataHashtable
->InsertOrUpdate(
5015 InternalCommandData(
5018 ExecCommandParam::Ignore
,
5020 CommandOnTextEditor::Disabled
)); // Not implemented yet.
5021 // REQUIRED SPECIAL REVIEW special review
5022 sInternalCommandDataHashtable
->InsertOrUpdate(
5024 InternalCommandData(
5027 ExecCommandParam::Boolean
,
5029 CommandOnTextEditor::FallThrough
)); // Not implemented yet.
5030 // REQUIRED SPECIAL REVIEW special review
5031 sInternalCommandDataHashtable
->InsertOrUpdate(
5033 InternalCommandData(
5036 ExecCommandParam::Boolean
,
5038 CommandOnTextEditor::FallThrough
)); // Not implemented yet.
5043 Document::InternalCommandData
Document::ConvertToInternalCommand(
5044 const nsAString
& aHTMLCommandName
, const nsAString
& aValue
/* = u""_ns */,
5045 nsAString
* aAdjustedValue
/* = nullptr */) {
5046 MOZ_ASSERT(!aAdjustedValue
|| aAdjustedValue
->IsEmpty());
5047 EnsureInitializeInternalCommandDataHashtable();
5048 InternalCommandData commandData
;
5049 if (!sInternalCommandDataHashtable
->Get(aHTMLCommandName
, &commandData
)) {
5050 return InternalCommandData();
5052 // Ignore if the command is disabled by a corresponding pref due to Gecko
5054 switch (commandData
.mCommand
) {
5055 case Command::SetDocumentReadOnly
:
5056 if (!StaticPrefs::dom_document_edit_command_contentReadOnly_enabled() &&
5057 aHTMLCommandName
.LowerCaseEqualsLiteral("contentreadonly")) {
5058 return InternalCommandData();
5061 case Command::SetDocumentInsertBROnEnterKeyPress
:
5062 MOZ_DIAGNOSTIC_ASSERT(
5063 aHTMLCommandName
.LowerCaseEqualsLiteral("insertbronreturn"));
5064 if (!StaticPrefs::dom_document_edit_command_insertBrOnReturn_enabled()) {
5065 return InternalCommandData();
5071 if (!aAdjustedValue
) {
5072 // No further work to do
5075 switch (commandData
.mExecCommandParam
) {
5076 case ExecCommandParam::Ignore
:
5077 // Just have to copy it, no checking
5078 switch (commandData
.mCommand
) {
5079 case Command::FormatJustifyLeft
:
5080 aAdjustedValue
->AssignLiteral("left");
5082 case Command::FormatJustifyRight
:
5083 aAdjustedValue
->AssignLiteral("right");
5085 case Command::FormatJustifyCenter
:
5086 aAdjustedValue
->AssignLiteral("center");
5088 case Command::FormatJustifyFull
:
5089 aAdjustedValue
->AssignLiteral("justify");
5092 MOZ_ASSERT(EditorCommand::GetParamType(commandData
.mCommand
) ==
5093 EditorCommandParamType::None
);
5098 case ExecCommandParam::Boolean
:
5099 MOZ_ASSERT(!!(EditorCommand::GetParamType(commandData
.mCommand
) &
5100 EditorCommandParamType::Bool
));
5101 // If this is a boolean value and it's not explicitly false (e.g. no
5102 // value). We default to "true" (see bug 301490).
5103 if (!aValue
.LowerCaseEqualsLiteral("false")) {
5104 aAdjustedValue
->AssignLiteral("true");
5106 aAdjustedValue
->AssignLiteral("false");
5110 case ExecCommandParam::InvertedBoolean
:
5111 MOZ_ASSERT(!!(EditorCommand::GetParamType(commandData
.mCommand
) &
5112 EditorCommandParamType::Bool
));
5113 // For old backwards commands we invert the check.
5114 if (aValue
.LowerCaseEqualsLiteral("false")) {
5115 aAdjustedValue
->AssignLiteral("true");
5117 aAdjustedValue
->AssignLiteral("false");
5121 case ExecCommandParam::String
:
5123 EditorCommand::GetParamType(commandData
.mCommand
) &
5124 (EditorCommandParamType::String
| EditorCommandParamType::CString
)));
5125 switch (commandData
.mCommand
) {
5126 case Command::FormatBlock
: {
5127 const char16_t
* start
= aValue
.BeginReading();
5128 const char16_t
* end
= aValue
.EndReading();
5129 if (start
!= end
&& *start
== '<' && *(end
- 1) == '>') {
5133 // XXX Should we reorder this array with actual usage?
5134 static const nsStaticAtom
* kFormattableBlockTags
[] = {
5139 nsGkAtoms::blockquote
,
5160 nsAutoString
value(nsDependentSubstring(start
, end
));
5162 const nsStaticAtom
* valueAtom
= NS_GetStaticAtom(value
);
5163 for (const nsStaticAtom
* kTag
: kFormattableBlockTags
) {
5164 if (valueAtom
== kTag
) {
5165 kTag
->ToString(*aAdjustedValue
);
5169 return InternalCommandData();
5171 case Command::FormatFontSize
: {
5172 // Per editing spec as of April 23, 2012, we need to reject the value
5173 // if it's not a valid floating-point number surrounded by optional
5174 // whitespace. Otherwise, we parse it as a legacy font size. For
5175 // now, we just parse as a legacy font size regardless (matching
5176 // WebKit) -- bug 747879.
5177 int32_t size
= nsContentUtils::ParseLegacyFontSize(aValue
);
5179 return InternalCommandData();
5181 MOZ_ASSERT(aAdjustedValue
->IsEmpty());
5182 aAdjustedValue
->AppendInt(size
);
5185 case Command::InsertImage
:
5186 case Command::InsertLink
:
5187 if (aValue
.IsEmpty()) {
5188 // Invalid value, return false
5189 return InternalCommandData();
5191 aAdjustedValue
->Assign(aValue
);
5193 case Command::SetDocumentDefaultParagraphSeparator
:
5194 if (!aValue
.LowerCaseEqualsLiteral("div") &&
5195 !aValue
.LowerCaseEqualsLiteral("p") &&
5196 !aValue
.LowerCaseEqualsLiteral("br")) {
5198 return InternalCommandData();
5200 aAdjustedValue
->Assign(aValue
);
5203 aAdjustedValue
->Assign(aValue
);
5208 MOZ_ASSERT_UNREACHABLE("New ExecCommandParam value hasn't been handled");
5209 return InternalCommandData();
5213 Document::AutoEditorCommandTarget::AutoEditorCommandTarget(
5214 Document
& aDocument
, const InternalCommandData
& aCommandData
)
5215 : mCommandData(aCommandData
) {
5216 // We'll retrieve an editor with current DOM tree and layout information.
5217 // However, JS may have already hidden or remove exposed root content of
5218 // the editor. Therefore, we need the latest layout information here.
5219 aDocument
.FlushPendingNotifications(FlushType::Layout
);
5220 if (!aDocument
.GetPresShell() || aDocument
.GetPresShell()->IsDestroying()) {
5225 if (nsPresContext
* presContext
= aDocument
.GetPresContext()) {
5226 // Consider context of command handling which is automatically resolved
5227 // by order of controllers in `nsCommandManager::GetControllerForCommand()`.
5229 // 1. TextEditor if there is an active element and it has TextEditor like
5230 // <input type="text"> or <textarea>.
5231 // 2. HTMLEditor for the document, if there is.
5232 // 3. Retarget to the DocShell or nsCommandManager as what we've done.
5233 if (aCommandData
.IsCutOrCopyCommand()) {
5234 // Note that we used to use DocShell to handle `cut` and `copy` command
5235 // for dispatching corresponding events for making possible web apps to
5236 // implement their own editor without editable elements but supports
5237 // standard shortcut keys, etc. In this case, we prefer to use active
5238 // element's editor to keep same behavior.
5239 mActiveEditor
= nsContentUtils::GetActiveEditor(presContext
);
5241 mActiveEditor
= nsContentUtils::GetActiveEditor(presContext
);
5242 mHTMLEditor
= nsContentUtils::GetHTMLEditor(presContext
);
5243 if (!mActiveEditor
) {
5244 mActiveEditor
= mHTMLEditor
;
5249 // Then, retrieve editor command class instance which should handle it
5250 // and can handle it now.
5251 if (!mActiveEditor
) {
5252 // If the command is available without editor, we should redirect the
5253 // command to focused descendant with DocShell.
5254 if (aCommandData
.IsAvailableOnlyWhenEditable()) {
5261 // Otherwise, we should use EditorCommand instance (which is singleton
5262 // instance) when it's enabled.
5263 mEditorCommand
= aCommandData
.mGetEditorCommandFunc
5264 ? aCommandData
.mGetEditorCommandFunc()
5266 if (!mEditorCommand
) {
5268 mActiveEditor
= nullptr;
5269 mHTMLEditor
= nullptr;
5273 if (IsCommandEnabled()) {
5277 // If the EditorCommand instance is disabled, we should do nothing if
5278 // the command requires an editor.
5279 if (aCommandData
.IsAvailableOnlyWhenEditable()) {
5280 // Do nothing if editor specific commands is disabled (bug 760052).
5285 // Otherwise, we should redirect it to focused descendant with DocShell.
5286 mEditorCommand
= nullptr;
5287 mActiveEditor
= nullptr;
5288 mHTMLEditor
= nullptr;
5291 EditorBase
* Document::AutoEditorCommandTarget::GetTargetEditor() const {
5292 using CommandOnTextEditor
= InternalCommandData::CommandOnTextEditor
;
5293 switch (mCommandData
.mCommandOnTextEditor
) {
5294 case CommandOnTextEditor::Enabled
:
5295 return mActiveEditor
;
5296 case CommandOnTextEditor::Disabled
:
5297 return mActiveEditor
&& mActiveEditor
->IsTextEditor()
5299 : mActiveEditor
.get();
5300 case CommandOnTextEditor::FallThrough
:
5306 bool Document::AutoEditorCommandTarget::IsEditable(Document
* aDocument
) const {
5307 if (RefPtr
<Document
> doc
= aDocument
->GetInProcessParentDocument()) {
5308 // Make sure frames are up to date, since that can affect whether
5310 doc
->FlushPendingNotifications(FlushType::Frames
);
5312 EditorBase
* targetEditor
= GetTargetEditor();
5313 if (targetEditor
&& targetEditor
->IsTextEditor()) {
5314 // FYI: When `disabled` attribute is set, `TextEditor` treats it as
5316 return !targetEditor
->IsReadonly();
5318 return aDocument
->IsEditingOn();
5321 bool Document::AutoEditorCommandTarget::IsCommandEnabled() const {
5322 EditorBase
* targetEditor
= GetTargetEditor();
5323 if (!targetEditor
) {
5326 MOZ_ASSERT(targetEditor
== mActiveEditor
|| targetEditor
== mHTMLEditor
);
5327 return MOZ_KnownLive(mEditorCommand
)
5328 ->IsCommandEnabled(mCommandData
.mCommand
, MOZ_KnownLive(targetEditor
));
5331 nsresult
Document::AutoEditorCommandTarget::DoCommand(
5332 nsIPrincipal
* aPrincipal
) const {
5333 MOZ_ASSERT(!DoNothing());
5334 MOZ_ASSERT(mEditorCommand
);
5335 EditorBase
* targetEditor
= GetTargetEditor();
5336 if (!targetEditor
) {
5337 return NS_SUCCESS_DOM_NO_OPERATION
;
5339 MOZ_ASSERT(targetEditor
== mActiveEditor
|| targetEditor
== mHTMLEditor
);
5340 return MOZ_KnownLive(mEditorCommand
)
5341 ->DoCommand(mCommandData
.mCommand
, MOZ_KnownLive(*targetEditor
),
5345 template <typename ParamType
>
5346 nsresult
Document::AutoEditorCommandTarget::DoCommandParam(
5347 const ParamType
& aParam
, nsIPrincipal
* aPrincipal
) const {
5348 MOZ_ASSERT(!DoNothing());
5349 MOZ_ASSERT(mEditorCommand
);
5350 EditorBase
* targetEditor
= GetTargetEditor();
5351 if (!targetEditor
) {
5352 return NS_SUCCESS_DOM_NO_OPERATION
;
5354 MOZ_ASSERT(targetEditor
== mActiveEditor
|| targetEditor
== mHTMLEditor
);
5355 return MOZ_KnownLive(mEditorCommand
)
5356 ->DoCommandParam(mCommandData
.mCommand
, aParam
,
5357 MOZ_KnownLive(*targetEditor
), aPrincipal
);
5360 nsresult
Document::AutoEditorCommandTarget::GetCommandStateParams(
5361 nsCommandParams
& aParams
) const {
5362 MOZ_ASSERT(mEditorCommand
);
5363 EditorBase
* targetEditor
= GetTargetEditor();
5364 if (!targetEditor
) {
5367 MOZ_ASSERT(targetEditor
== mActiveEditor
|| targetEditor
== mHTMLEditor
);
5368 return MOZ_KnownLive(mEditorCommand
)
5369 ->GetCommandStateParams(mCommandData
.mCommand
, MOZ_KnownLive(aParams
),
5370 MOZ_KnownLive(targetEditor
), nullptr);
5373 Document::AutoRunningExecCommandMarker::AutoRunningExecCommandMarker(
5374 Document
& aDocument
, nsIPrincipal
* aPrincipal
)
5375 : mDocument(aDocument
),
5376 mTreatAsUserInput(EditorBase::TreatAsUserInput(aPrincipal
)),
5377 mHasBeenRunningByContent(aDocument
.mIsRunningExecCommandByContent
),
5378 mHasBeenRunningByChromeOrAddon(
5379 aDocument
.mIsRunningExecCommandByChromeOrAddon
) {
5380 if (mTreatAsUserInput
) {
5381 aDocument
.mIsRunningExecCommandByChromeOrAddon
= true;
5383 aDocument
.mIsRunningExecCommandByContent
= true;
5387 bool Document::ExecCommand(const nsAString
& aHTMLCommandName
, bool aShowUI
,
5388 const nsAString
& aValue
,
5389 nsIPrincipal
& aSubjectPrincipal
, ErrorResult
& aRv
) {
5390 // Only allow on HTML documents.
5391 if (!IsHTMLOrXHTML()) {
5392 aRv
.ThrowInvalidStateError(
5393 "execCommand is only supported on HTML documents");
5396 // Otherwise, don't throw exception for compatibility with Chrome.
5398 // if they are requesting UI from us, let's fail since we have no UI
5403 // for optional parameters see dom/src/base/nsHistory.cpp: HistoryImpl::Go()
5404 // this might add some ugly JS dependencies?
5406 nsAutoString adjustedValue
;
5407 InternalCommandData commandData
=
5408 ConvertToInternalCommand(aHTMLCommandName
, aValue
, &adjustedValue
);
5409 switch (commandData
.mCommand
) {
5410 case Command::DoNothing
:
5412 case Command::SetDocumentReadOnly
:
5413 SetUseCounter(eUseCounter_custom_DocumentExecCommandContentReadOnly
);
5415 case Command::EnableCompatibleJoinSplitNodeDirection
:
5416 // We didn't allow to enable the legacy behavior once we've enabled the
5417 // new behavior by default. For keeping the behavior at supporting both
5418 // mode, we should keep returning `false` if the web app to enable the
5419 // legacy mode. Additionally, we don't support the legacy direction
5420 // anymore. Therefore, we can return `false` here even if the caller is
5421 // an addon or chrome script.
5422 if (!adjustedValue
.EqualsLiteral("true")) {
5430 AutoRunningExecCommandMarker
markRunningExecCommand(*this,
5431 &aSubjectPrincipal
);
5433 // If we're running an execCommand, we should just return false.
5434 // https://github.com/w3c/editing/issues/200#issuecomment-575241816
5435 if (!StaticPrefs::dom_document_exec_command_nested_calls_allowed() &&
5436 !markRunningExecCommand
.IsSafeToRun()) {
5440 // Do security check first.
5441 if (commandData
.IsCutOrCopyCommand()) {
5442 if (!nsContentUtils::IsCutCopyAllowed(this, aSubjectPrincipal
)) {
5443 // We have rejected the event due to it not being performed in an
5444 // input-driven context therefore, we report the error to the console.
5445 nsContentUtils::ReportToConsole(nsIScriptError::warningFlag
, "DOM"_ns
,
5446 this, nsContentUtils::eDOM_PROPERTIES
,
5447 "ExecCommandCutCopyDeniedNotInputDriven");
5450 } else if (commandData
.IsPasteCommand()) {
5451 if (!nsContentUtils::PrincipalHasPermission(aSubjectPrincipal
,
5452 nsGkAtoms::clipboardRead
)) {
5457 // Next, consider context of command handling which is automatically resolved
5458 // by order of controllers in `nsCommandManager::GetControllerForCommand()`.
5459 AutoEditorCommandTarget
editCommandTarget(*this, commandData
);
5460 if (commandData
.IsAvailableOnlyWhenEditable() &&
5461 !editCommandTarget
.IsEditable(this)) {
5465 if (editCommandTarget
.DoNothing()) {
5469 // If we cannot use EditorCommand instance directly, we need to handle the
5470 // command with traditional path (i.e., with DocShell or nsCommandManager).
5471 if (!editCommandTarget
.IsEditor()) {
5472 MOZ_ASSERT(!commandData
.IsAvailableOnlyWhenEditable());
5474 // Special case clipboard write commands like Command::Cut and
5475 // Command::Copy. For such commands, we need the behaviour from
5476 // nsWindowRoot::GetControllers() which is to look at the focused element,
5477 // and defer to a focused textbox's controller. The code past taken by
5478 // other commands in ExecCommand() always uses the window directly, rather
5479 // than deferring to the textbox, which is desireable for most editor
5480 // commands, but not these commands (as those should allow copying out of
5481 // embedded editors). This behaviour is invoked if we call DoCommand()
5482 // directly on the docShell.
5483 // XXX This means that we allow web app to pick up selected content in
5484 // descendant document and write it into the clipboard when a
5485 // descendant document has focus. However, Chromium does not allow
5486 // this and this seems that it's not good behavior from point of view
5487 // of security. We should treat this issue in another bug.
5488 if (commandData
.IsCutOrCopyCommand()) {
5489 nsCOMPtr
<nsIDocShell
> docShell(mDocumentContainer
);
5493 nsresult rv
= docShell
->DoCommand(commandData
.mXULCommandName
);
5494 if (rv
== NS_SUCCESS_DOM_NO_OPERATION
) {
5497 return NS_SUCCEEDED(rv
);
5500 // Otherwise (currently, only clipboard read commands like Command::Paste),
5501 // we don't need to redirect the command to focused subdocument.
5502 // Therefore, we should handle it with nsCommandManager as used to be.
5503 // It may dispatch only preceding event of editing on non-editable element
5504 // to make web apps possible to handle standard shortcut key, etc in
5505 // their own editor.
5506 RefPtr
<nsCommandManager
> commandManager
= GetMidasCommandManager();
5507 if (!commandManager
) {
5511 nsCOMPtr
<nsPIDOMWindowOuter
> window
= GetWindow();
5516 // Return false for disabled commands (bug 760052)
5517 if (!commandManager
->IsCommandEnabled(
5518 nsDependentCString(commandData
.mXULCommandName
), window
)) {
5522 MOZ_ASSERT(commandData
.IsPasteCommand() ||
5523 commandData
.mCommand
== Command::SelectAll
);
5525 commandManager
->DoCommand(commandData
.mXULCommandName
, nullptr, window
);
5526 return NS_SUCCEEDED(rv
) && rv
!= NS_SUCCESS_DOM_NO_OPERATION
;
5529 // Now, our target is fixed to the editor. So, we can use EditorCommand
5530 // in EditorCommandTarget directly.
5532 EditorCommandParamType paramType
=
5533 EditorCommand::GetParamType(commandData
.mCommand
);
5535 // If we don't have meaningful parameter or the EditorCommand does not
5536 // require additional parameter, we can use `DoCommand()`.
5537 if (adjustedValue
.IsEmpty() || paramType
== EditorCommandParamType::None
) {
5538 MOZ_ASSERT(!(paramType
& EditorCommandParamType::Bool
));
5539 nsresult rv
= editCommandTarget
.DoCommand(&aSubjectPrincipal
);
5540 return NS_SUCCEEDED(rv
) && rv
!= NS_SUCCESS_DOM_NO_OPERATION
;
5543 // If the EditorCommand requires `bool` parameter, `adjustedValue` must be
5544 // "true" or "false" here. So, we can use `DoCommandParam()` which takes
5546 if (!!(paramType
& EditorCommandParamType::Bool
)) {
5547 MOZ_ASSERT(adjustedValue
.EqualsLiteral("true") ||
5548 adjustedValue
.EqualsLiteral("false"));
5549 nsresult rv
= editCommandTarget
.DoCommandParam(
5550 Some(adjustedValue
.EqualsLiteral("true")), &aSubjectPrincipal
);
5551 return NS_SUCCEEDED(rv
) && rv
!= NS_SUCCESS_DOM_NO_OPERATION
;
5554 // Now, the EditorCommand requires `nsAString` or `nsACString` parameter
5555 // in this case. However, `paramType` may contain both `String` and
5556 // `CString` but in such case, we should use `DoCommandParam()` which
5557 // takes `nsAString`. So, we should check whether `paramType` contains
5558 // `String` or not first.
5559 if (!!(paramType
& EditorCommandParamType::String
)) {
5560 MOZ_ASSERT(!adjustedValue
.IsVoid());
5562 editCommandTarget
.DoCommandParam(adjustedValue
, &aSubjectPrincipal
);
5563 return NS_SUCCEEDED(rv
) && rv
!= NS_SUCCESS_DOM_NO_OPERATION
;
5566 // Finally, `paramType` should have `CString`. We should use
5567 // `DoCommandParam()` which takes `nsACString`.
5568 if (!!(paramType
& EditorCommandParamType::CString
)) {
5569 NS_ConvertUTF16toUTF8
utf8Value(adjustedValue
);
5570 MOZ_ASSERT(!utf8Value
.IsVoid());
5572 editCommandTarget
.DoCommandParam(utf8Value
, &aSubjectPrincipal
);
5573 return NS_SUCCEEDED(rv
) && rv
!= NS_SUCCESS_DOM_NO_OPERATION
;
5576 MOZ_ASSERT_UNREACHABLE(
5577 "Not yet implemented to handle new EditorCommandParamType");
5581 bool Document::QueryCommandEnabled(const nsAString
& aHTMLCommandName
,
5582 nsIPrincipal
& aSubjectPrincipal
,
5584 // Only allow on HTML documents.
5585 if (!IsHTMLOrXHTML()) {
5586 aRv
.ThrowInvalidStateError(
5587 "queryCommandEnabled is only supported on HTML documents");
5590 // Otherwise, don't throw exception for compatibility with Chrome.
5592 InternalCommandData commandData
= ConvertToInternalCommand(aHTMLCommandName
);
5593 switch (commandData
.mCommand
) {
5594 case Command::DoNothing
:
5596 case Command::SetDocumentReadOnly
:
5598 eUseCounter_custom_DocumentQueryCommandSupportedOrEnabledContentReadOnly
);
5600 case Command::SetDocumentInsertBROnEnterKeyPress
:
5602 eUseCounter_custom_DocumentQueryCommandSupportedOrEnabledInsertBrOnReturn
);
5608 // cut & copy are always allowed
5609 if (commandData
.IsCutOrCopyCommand()) {
5610 return nsContentUtils::IsCutCopyAllowed(this, aSubjectPrincipal
);
5613 // Report false for restricted commands
5614 if (commandData
.IsPasteCommand() && !aSubjectPrincipal
.IsSystemPrincipal()) {
5618 AutoEditorCommandTarget
editCommandTarget(*this, commandData
);
5619 if (commandData
.IsAvailableOnlyWhenEditable() &&
5620 !editCommandTarget
.IsEditable(this)) {
5624 if (editCommandTarget
.IsEditor()) {
5625 return editCommandTarget
.IsCommandEnabled();
5628 // get command manager and dispatch command to our window if it's acceptable
5629 RefPtr
<nsCommandManager
> commandManager
= GetMidasCommandManager();
5630 if (!commandManager
) {
5634 nsPIDOMWindowOuter
* window
= GetWindow();
5639 return commandManager
->IsCommandEnabled(
5640 nsDependentCString(commandData
.mXULCommandName
), window
);
5643 bool Document::QueryCommandIndeterm(const nsAString
& aHTMLCommandName
,
5645 // Only allow on HTML documents.
5646 if (!IsHTMLOrXHTML()) {
5647 aRv
.ThrowInvalidStateError(
5648 "queryCommandIndeterm is only supported on HTML documents");
5651 // Otherwise, don't throw exception for compatibility with Chrome.
5653 InternalCommandData commandData
= ConvertToInternalCommand(aHTMLCommandName
);
5654 if (commandData
.mCommand
== Command::DoNothing
) {
5658 AutoEditorCommandTarget
editCommandTarget(*this, commandData
);
5659 if (commandData
.IsAvailableOnlyWhenEditable() &&
5660 !editCommandTarget
.IsEditable(this)) {
5663 RefPtr
<nsCommandParams
> params
= new nsCommandParams();
5664 if (editCommandTarget
.IsEditor()) {
5665 if (NS_FAILED(editCommandTarget
.GetCommandStateParams(*params
))) {
5669 // get command manager and dispatch command to our window if it's acceptable
5670 RefPtr
<nsCommandManager
> commandManager
= GetMidasCommandManager();
5671 if (!commandManager
) {
5675 nsPIDOMWindowOuter
* window
= GetWindow();
5680 if (NS_FAILED(commandManager
->GetCommandState(commandData
.mXULCommandName
,
5686 // If command does not have a state_mixed value, this call fails and sets
5687 // retval to false. This is fine -- we want to return false in that case
5688 // anyway (bug 738385), so we just don't throw regardless.
5689 return params
->GetBool("state_mixed");
5692 bool Document::QueryCommandState(const nsAString
& aHTMLCommandName
,
5694 // Only allow on HTML documents.
5695 if (!IsHTMLOrXHTML()) {
5696 aRv
.ThrowInvalidStateError(
5697 "queryCommandState is only supported on HTML documents");
5700 // Otherwise, don't throw exception for compatibility with Chrome.
5702 InternalCommandData commandData
= ConvertToInternalCommand(aHTMLCommandName
);
5703 switch (commandData
.mCommand
) {
5704 case Command::DoNothing
:
5706 case Command::SetDocumentReadOnly
:
5708 eUseCounter_custom_DocumentQueryCommandStateOrValueContentReadOnly
);
5710 case Command::SetDocumentInsertBROnEnterKeyPress
:
5712 eUseCounter_custom_DocumentQueryCommandStateOrValueInsertBrOnReturn
);
5718 if (aHTMLCommandName
.LowerCaseEqualsLiteral("usecss")) {
5719 // Per spec, state is supported for styleWithCSS but not useCSS, so we just
5720 // return false always.
5724 AutoEditorCommandTarget
editCommandTarget(*this, commandData
);
5725 if (commandData
.IsAvailableOnlyWhenEditable() &&
5726 !editCommandTarget
.IsEditable(this)) {
5729 RefPtr
<nsCommandParams
> params
= new nsCommandParams();
5730 if (editCommandTarget
.IsEditor()) {
5731 if (NS_FAILED(editCommandTarget
.GetCommandStateParams(*params
))) {
5735 // get command manager and dispatch command to our window if it's acceptable
5736 RefPtr
<nsCommandManager
> commandManager
= GetMidasCommandManager();
5737 if (!commandManager
) {
5741 nsPIDOMWindowOuter
* window
= GetWindow();
5746 if (NS_FAILED(commandManager
->GetCommandState(commandData
.mXULCommandName
,
5752 // handle alignment as a special case (possibly other commands too?)
5753 // Alignment is special because the external api is individual
5754 // commands but internally we use cmd_align with different
5755 // parameters. When getting the state of this command, we need to
5756 // return the boolean for this particular alignment rather than the
5757 // string of 'which alignment is this?'
5758 switch (commandData
.mCommand
) {
5759 case Command::FormatJustifyLeft
: {
5760 nsAutoCString currentValue
;
5761 nsresult rv
= params
->GetCString("state_attribute", currentValue
);
5762 if (NS_FAILED(rv
)) {
5765 return currentValue
.EqualsLiteral("left");
5767 case Command::FormatJustifyRight
: {
5768 nsAutoCString currentValue
;
5769 nsresult rv
= params
->GetCString("state_attribute", currentValue
);
5770 if (NS_FAILED(rv
)) {
5773 return currentValue
.EqualsLiteral("right");
5775 case Command::FormatJustifyCenter
: {
5776 nsAutoCString currentValue
;
5777 nsresult rv
= params
->GetCString("state_attribute", currentValue
);
5778 if (NS_FAILED(rv
)) {
5781 return currentValue
.EqualsLiteral("center");
5783 case Command::FormatJustifyFull
: {
5784 nsAutoCString currentValue
;
5785 nsresult rv
= params
->GetCString("state_attribute", currentValue
);
5786 if (NS_FAILED(rv
)) {
5789 return currentValue
.EqualsLiteral("justify");
5795 // If command does not have a state_all value, this call fails and sets
5796 // retval to false. This is fine -- we want to return false in that case
5797 // anyway (bug 738385), so we just succeed and return false regardless.
5798 return params
->GetBool("state_all");
5801 bool Document::QueryCommandSupported(const nsAString
& aHTMLCommandName
,
5802 CallerType aCallerType
, ErrorResult
& aRv
) {
5803 // Only allow on HTML documents.
5804 if (!IsHTMLOrXHTML()) {
5805 aRv
.ThrowInvalidStateError(
5806 "queryCommandSupported is only supported on HTML documents");
5809 // Otherwise, don't throw exception for compatibility with Chrome.
5811 InternalCommandData commandData
= ConvertToInternalCommand(aHTMLCommandName
);
5812 switch (commandData
.mCommand
) {
5813 case Command::DoNothing
:
5815 case Command::SetDocumentReadOnly
:
5817 eUseCounter_custom_DocumentQueryCommandSupportedOrEnabledContentReadOnly
);
5819 case Command::SetDocumentInsertBROnEnterKeyPress
:
5821 eUseCounter_custom_DocumentQueryCommandSupportedOrEnabledInsertBrOnReturn
);
5827 // Gecko technically supports all the clipboard commands including
5828 // cut/copy/paste, but non-privileged content will be unable to call
5829 // paste, and depending on the pref "dom.allow_cut_copy", cut and copy
5830 // may also be disallowed to be called from non-privileged content.
5831 // For that reason, we report the support status of corresponding
5832 // command accordingly.
5833 if (aCallerType
!= CallerType::System
) {
5834 if (commandData
.IsPasteCommand()) {
5837 if (commandData
.IsCutOrCopyCommand() &&
5838 !StaticPrefs::dom_allow_cut_copy()) {
5839 // XXXbz should we worry about correctly reporting "true" in the
5840 // "restricted, but we're an addon with clipboardWrite permissions" case?
5841 // See also nsContentUtils::IsCutCopyAllowed.
5846 // aHTMLCommandName is supported if it can be converted to a Midas command
5850 void Document::QueryCommandValue(const nsAString
& aHTMLCommandName
,
5851 nsAString
& aValue
, ErrorResult
& aRv
) {
5854 // Only allow on HTML documents.
5855 if (!IsHTMLOrXHTML()) {
5856 aRv
.ThrowInvalidStateError(
5857 "queryCommandValue is only supported on HTML documents");
5860 // Otherwise, don't throw exception for compatibility with Chrome.
5862 InternalCommandData commandData
= ConvertToInternalCommand(aHTMLCommandName
);
5863 switch (commandData
.mCommand
) {
5864 case Command::DoNothing
:
5865 // Return empty string
5867 case Command::SetDocumentReadOnly
:
5869 eUseCounter_custom_DocumentQueryCommandStateOrValueContentReadOnly
);
5871 case Command::SetDocumentInsertBROnEnterKeyPress
:
5873 eUseCounter_custom_DocumentQueryCommandStateOrValueInsertBrOnReturn
);
5879 AutoEditorCommandTarget
editCommandTarget(*this, commandData
);
5880 if (commandData
.IsAvailableOnlyWhenEditable() &&
5881 !editCommandTarget
.IsEditable(this)) {
5884 RefPtr
<nsCommandParams
> params
= new nsCommandParams();
5885 if (editCommandTarget
.IsEditor()) {
5886 if (NS_FAILED(params
->SetCString("state_attribute", ""_ns
))) {
5890 if (NS_FAILED(editCommandTarget
.GetCommandStateParams(*params
))) {
5894 // get command manager and dispatch command to our window if it's acceptable
5895 RefPtr
<nsCommandManager
> commandManager
= GetMidasCommandManager();
5896 if (!commandManager
) {
5900 nsCOMPtr
<nsPIDOMWindowOuter
> window
= GetWindow();
5905 if (NS_FAILED(params
->SetCString("state_attribute", ""_ns
))) {
5909 if (NS_FAILED(commandManager
->GetCommandState(commandData
.mXULCommandName
,
5915 // If command does not have a state_attribute value, this call fails, and
5916 // aValue will wind up being the empty string. This is fine -- we want to
5917 // return "" in that case anyway (bug 738385), so we just return NS_OK
5919 nsAutoCString result
;
5920 params
->GetCString("state_attribute", result
);
5921 CopyUTF8toUTF16(result
, aValue
);
5924 void Document::MaybeEditingStateChanged() {
5925 if (!mPendingMaybeEditingStateChanged
&& mMayStartLayout
&&
5926 mUpdateNestLevel
== 0 && (mContentEditableCount
> 0) != IsEditingOn()) {
5927 if (nsContentUtils::IsSafeToRunScript()) {
5928 EditingStateChanged();
5929 } else if (!mInDestructor
) {
5930 nsContentUtils::AddScriptRunner(
5931 NewRunnableMethod("Document::MaybeEditingStateChanged", this,
5932 &Document::MaybeEditingStateChanged
));
5937 void Document::NotifyFetchOrXHRSuccess() {
5938 if (mShouldNotifyFetchSuccess
) {
5939 nsContentUtils::DispatchEventOnlyToChrome(
5940 this, this, u
"DOMDocFetchSuccess"_ns
, CanBubble::eNo
, Cancelable::eNo
,
5941 /* DefaultAction */ nullptr);
5945 void Document::SetNotifyFetchSuccess(bool aShouldNotify
) {
5946 mShouldNotifyFetchSuccess
= aShouldNotify
;
5949 void Document::SetNotifyFormOrPasswordRemoved(bool aShouldNotify
) {
5950 mShouldNotifyFormOrPasswordRemoved
= aShouldNotify
;
5953 void Document::TearingDownEditor() {
5954 if (IsEditingOn()) {
5955 mEditingState
= EditingState::eTearingDown
;
5956 if (IsHTMLOrXHTML()) {
5957 RemoveContentEditableStyleSheets();
5962 nsresult
Document::TurnEditingOff() {
5963 NS_ASSERTION(mEditingState
!= EditingState::eOff
, "Editing is already off.");
5965 nsPIDOMWindowOuter
* window
= GetWindow();
5967 return NS_ERROR_FAILURE
;
5970 nsIDocShell
* docshell
= window
->GetDocShell();
5972 return NS_ERROR_FAILURE
;
5975 bool isBeingDestroyed
= false;
5976 docshell
->IsBeingDestroyed(&isBeingDestroyed
);
5977 if (isBeingDestroyed
) {
5978 return NS_ERROR_FAILURE
;
5981 nsCOMPtr
<nsIEditingSession
> editSession
;
5982 nsresult rv
= docshell
->GetEditingSession(getter_AddRefs(editSession
));
5983 NS_ENSURE_SUCCESS(rv
, rv
);
5986 rv
= editSession
->TearDownEditorOnWindow(window
);
5987 NS_ENSURE_SUCCESS(rv
, rv
);
5989 mEditingState
= EditingState::eOff
;
5991 // Editor resets selection since it is being destroyed. But if focus is
5992 // still into editable control, we have to initialize selection again.
5993 if (nsFocusManager
* fm
= nsFocusManager::GetFocusManager()) {
5994 if (RefPtr
<TextControlElement
> textControlElement
=
5995 TextControlElement::FromNodeOrNull(fm
->GetFocusedElement())) {
5996 if (RefPtr
<TextEditor
> textEditor
= textControlElement
->GetTextEditor()) {
5997 textEditor
->ReinitializeSelection(*textControlElement
);
6005 static bool HasPresShell(nsPIDOMWindowOuter
* aWindow
) {
6006 nsIDocShell
* docShell
= aWindow
->GetDocShell();
6010 return docShell
->GetPresShell() != nullptr;
6013 HTMLEditor
* Document::GetHTMLEditor() const {
6014 nsPIDOMWindowOuter
* window
= GetWindow();
6019 nsIDocShell
* docshell
= window
->GetDocShell();
6024 return docshell
->GetHTMLEditor();
6027 nsresult
Document::EditingStateChanged() {
6028 if (mRemovedFromDocShell
) {
6032 if (mEditingState
== EditingState::eSettingUp
||
6033 mEditingState
== EditingState::eTearingDown
) {
6034 // XXX We shouldn't recurse
6038 const bool designMode
= IsInDesignMode();
6039 EditingState newState
=
6040 designMode
? EditingState::eDesignMode
6041 : (mContentEditableCount
> 0 ? EditingState::eContentEditable
6042 : EditingState::eOff
);
6043 if (mEditingState
== newState
) {
6044 // No changes in editing mode.
6048 const bool thisDocumentHasFocus
= ThisDocumentHasFocus();
6049 if (newState
== EditingState::eOff
) {
6050 // Editing is being turned off.
6051 nsAutoScriptBlocker scriptBlocker
;
6052 RefPtr
<HTMLEditor
> htmlEditor
= GetHTMLEditor();
6053 NotifyEditableStateChange(*this);
6054 nsresult rv
= TurnEditingOff();
6055 // If this document has focus and the editing state of this document
6056 // becomes "off", it means that HTMLEditor won't handle any inputs nor
6057 // modify the DOM tree. However, HTMLEditor may not receive `blur`
6058 // event for this state change since this may occur without focus change.
6059 // Therefore, let's notify HTMLEditor of this editing state change.
6060 // Note that even if focusedElement is an editable text control element,
6061 // it becomes not editable from HTMLEditor point of view since text
6062 // control elements are manged by TextEditor.
6063 RefPtr
<Element
> focusedElement
=
6064 nsFocusManager::GetFocusManager()
6065 ? nsFocusManager::GetFocusManager()->GetFocusedElement()
6067 DebugOnly
<nsresult
> rvIgnored
=
6068 HTMLEditor::FocusedElementOrDocumentBecomesNotEditable(
6069 htmlEditor
, *this, focusedElement
);
6070 NS_WARNING_ASSERTION(
6071 NS_SUCCEEDED(rvIgnored
),
6072 "HTMLEditor::FocusedElementOrDocumentBecomesNotEditable() failed, but "
6077 // Flush out style changes on our _parent_ document, if any, so that
6078 // our check for a presshell won't get stale information.
6079 if (mParentDocument
) {
6080 mParentDocument
->FlushPendingNotifications(FlushType::Style
);
6083 // get editing session, make sure this is a strong reference so the
6084 // window can't get deleted during the rest of this call.
6085 const nsCOMPtr
<nsPIDOMWindowOuter
> window
= GetWindow();
6087 return NS_ERROR_FAILURE
;
6090 nsIDocShell
* docshell
= window
->GetDocShell();
6092 return NS_ERROR_FAILURE
;
6095 // FlushPendingNotifications might destroy our docshell.
6096 bool isBeingDestroyed
= false;
6097 docshell
->IsBeingDestroyed(&isBeingDestroyed
);
6098 if (isBeingDestroyed
) {
6099 return NS_ERROR_FAILURE
;
6102 nsCOMPtr
<nsIEditingSession
> editSession
;
6103 nsresult rv
= docshell
->GetEditingSession(getter_AddRefs(editSession
));
6104 NS_ENSURE_SUCCESS(rv
, rv
);
6106 RefPtr
<HTMLEditor
> htmlEditor
= editSession
->GetHTMLEditorForWindow(window
);
6108 // We might already have an editor if it was set up for mail, let's see
6109 // if this is actually the case.
6111 htmlEditor
->GetFlags(&flags
);
6112 if (flags
& nsIEditor::eEditorMailMask
) {
6113 // We already have a mail editor, then we should not attempt to create
6119 if (!HasPresShell(window
)) {
6120 // We should not make the window editable or setup its editor.
6121 // It's probably style=display:none.
6125 bool makeWindowEditable
= mEditingState
== EditingState::eOff
;
6126 bool spellRecheckAll
= false;
6127 bool putOffToRemoveScriptBlockerUntilModifyingEditingState
= false;
6128 htmlEditor
= nullptr;
6131 EditingState oldState
= mEditingState
;
6132 nsAutoEditingState
push(this, EditingState::eSettingUp
);
6134 RefPtr
<PresShell
> presShell
= GetPresShell();
6135 NS_ENSURE_TRUE(presShell
, NS_ERROR_FAILURE
);
6137 // If we're entering the design mode from non-editable state, put the
6138 // selection at the beginning of the document for compatibility reasons.
6139 bool collapseSelectionAtBeginningOfDocument
=
6140 designMode
&& oldState
== EditingState::eOff
;
6141 // However, mEditingState may be eOff even if there is some
6142 // `contenteditable` area and selection has been initialized for it because
6143 // mEditingState for `contenteditable` may have been scheduled to modify
6144 // when safe. In such case, we should not reinitialize selection.
6145 if (collapseSelectionAtBeginningOfDocument
&& mContentEditableCount
) {
6146 Selection
* selection
=
6147 presShell
->GetSelection(nsISelectionController::SELECTION_NORMAL
);
6148 NS_WARNING_ASSERTION(selection
, "Why don't we have Selection?");
6149 if (selection
&& selection
->RangeCount()) {
6150 // Perhaps, we don't need to check whether the selection is in
6151 // an editing host or not because all contents will be editable
6152 // in designMode. (And we don't want to make this code so complicated
6153 // because of legacy API.)
6154 collapseSelectionAtBeginningOfDocument
= false;
6158 MOZ_ASSERT(mStyleSetFilled
);
6160 // Before making this window editable, we need to modify UA style sheet
6161 // because new style may change whether focused element will be focusable
6163 if (IsHTMLOrXHTML()) {
6164 AddContentEditableStyleSheetsToStyleSet(designMode
);
6168 // designMode is being turned on (overrides contentEditable).
6169 spellRecheckAll
= oldState
== EditingState::eContentEditable
;
6172 // Adjust focused element with new style but blur event shouldn't be fired
6173 // until mEditingState is modified with newState.
6174 nsAutoScriptBlocker scriptBlocker
;
6176 nsCOMPtr
<nsPIDOMWindowOuter
> focusedWindow
;
6177 nsIContent
* focusedContent
= nsFocusManager::GetFocusedDescendant(
6178 window
, nsFocusManager::eOnlyCurrentWindow
,
6179 getter_AddRefs(focusedWindow
));
6180 if (focusedContent
) {
6181 nsIFrame
* focusedFrame
= focusedContent
->GetPrimaryFrame();
6182 bool clearFocus
= focusedFrame
6183 ? !focusedFrame
->IsFocusable()
6184 : !focusedContent
->IsFocusableWithoutStyle();
6186 if (RefPtr
<nsFocusManager
> fm
= nsFocusManager::GetFocusManager()) {
6187 fm
->ClearFocus(window
);
6188 // If we need to dispatch blur event, we should put off after
6189 // modifying mEditingState since blur event handler may change
6190 // designMode state again.
6191 putOffToRemoveScriptBlockerUntilModifyingEditingState
= true;
6197 if (makeWindowEditable
) {
6198 // Editing is being turned on (through designMode or contentEditable)
6200 // XXX This can cause flushing which can change the editing state, so make
6201 // sure to avoid recursing.
6202 rv
= editSession
->MakeWindowEditable(window
, "html", false, false, true);
6203 NS_ENSURE_SUCCESS(rv
, rv
);
6206 // XXX Need to call TearDownEditorOnWindow for all failures.
6207 htmlEditor
= docshell
->GetHTMLEditor();
6209 // Return NS_OK even though we've failed to create an editor here. This
6210 // is so that the setter of designMode on non-HTML documents does not
6212 // This is OK to do because in nsEditingSession::SetupEditorOnWindow() we
6213 // would detect that we can't support the mimetype if appropriate and
6214 // would fall onto the eEditorErrorCantEditMimeType path.
6218 if (collapseSelectionAtBeginningOfDocument
) {
6219 htmlEditor
->BeginningOfDocument();
6222 if (putOffToRemoveScriptBlockerUntilModifyingEditingState
) {
6223 nsContentUtils::AddScriptBlocker();
6227 mEditingState
= newState
;
6228 if (putOffToRemoveScriptBlockerUntilModifyingEditingState
) {
6229 nsContentUtils::RemoveScriptBlocker();
6230 // If mEditingState is overwritten by another call and already disabled
6231 // the editing, we shouldn't keep making window editable.
6232 if (mEditingState
== EditingState::eOff
) {
6237 if (makeWindowEditable
) {
6238 // TODO: We should do this earlier in this method.
6239 // Previously, we called `ExecCommand` with `insertBrOnReturn` command
6240 // whose argument is false here. Then, if it returns error, we
6241 // stopped making it editable. However, after bug 1697078 fixed,
6242 // `ExecCommand` returns error only when the document is not XHTML's
6243 // nor HTML's. Therefore, we use same error handling for now.
6244 if (MOZ_UNLIKELY(NS_WARN_IF(!IsHTMLOrXHTML()))) {
6245 // Editor setup failed. Editing is not on after all.
6246 // XXX Should we reset the editable flag on nodes?
6247 editSession
->TearDownEditorOnWindow(window
);
6248 mEditingState
= EditingState::eOff
;
6249 return NS_ERROR_DOM_INVALID_STATE_ERR
;
6251 // Set the editor to not insert <br> elements on return when in <p> elements
6253 htmlEditor
->SetReturnInParagraphCreatesNewParagraph(true);
6256 // Resync the editor's spellcheck state.
6257 if (spellRecheckAll
) {
6258 nsCOMPtr
<nsISelectionController
> selectionController
=
6259 htmlEditor
->GetSelectionController();
6260 if (NS_WARN_IF(!selectionController
)) {
6261 return NS_ERROR_FAILURE
;
6264 RefPtr
<Selection
> spellCheckSelection
= selectionController
->GetSelection(
6265 nsISelectionController::SELECTION_SPELLCHECK
);
6266 if (spellCheckSelection
) {
6267 spellCheckSelection
->RemoveAllRanges(IgnoreErrors());
6270 htmlEditor
->SyncRealTimeSpell();
6272 MaybeDispatchCheckKeyPressEventModelEvent();
6274 // If this document keeps having focus and the HTMLEditor is in the design
6275 // mode, it may not receive `focus` event for this editing state change since
6276 // this may occur without a focus change. Therefore, let's notify HTMLEditor
6277 // of this editing state change.
6278 if (thisDocumentHasFocus
&& htmlEditor
->IsInDesignMode() &&
6279 ThisDocumentHasFocus()) {
6280 DebugOnly
<nsresult
> rvIgnored
=
6281 htmlEditor
->FocusedElementOrDocumentBecomesEditable(*this, nullptr);
6282 NS_WARNING_ASSERTION(NS_SUCCEEDED(rvIgnored
),
6283 "HTMLEditor::FocusedElementOrDocumentBecomesEditable()"
6284 " failed, but ignored");
6290 // Helper class, used below in ChangeContentEditableCount().
6291 class DeferredContentEditableCountChangeEvent
: public Runnable
{
6293 DeferredContentEditableCountChangeEvent(Document
* aDoc
, Element
* aElement
)
6294 : mozilla::Runnable("DeferredContentEditableCountChangeEvent"),
6296 mElement(aElement
) {}
6298 MOZ_CAN_RUN_SCRIPT_BOUNDARY NS_IMETHOD
Run() override
{
6299 if (mElement
&& mElement
->OwnerDoc() == mDoc
) {
6300 RefPtr
<Document
> doc
= std::move(mDoc
);
6301 RefPtr
<Element
> element
= std::move(mElement
);
6302 doc
->DeferredContentEditableCountChange(element
);
6308 RefPtr
<Document
> mDoc
;
6309 RefPtr
<Element
> mElement
;
6312 void Document::ChangeContentEditableCount(Element
* aElement
, int32_t aChange
) {
6313 NS_ASSERTION(int32_t(mContentEditableCount
) + aChange
>= 0,
6314 "Trying to decrement too much.");
6316 mContentEditableCount
+= aChange
;
6319 nsContentUtils::AddScriptRunner(
6320 new DeferredContentEditableCountChangeEvent(this, aElement
));
6324 void Document::DeferredContentEditableCountChange(Element
* aElement
) {
6325 const RefPtr
<nsFocusManager
> fm
= nsFocusManager::GetFocusManager();
6326 const bool elementHasFocus
=
6327 aElement
&& fm
&& fm
->GetFocusedElement() == aElement
;
6328 if (elementHasFocus
) {
6329 MOZ_ASSERT(nsContentUtils::IsSafeToRunScript());
6330 // When contenteditable of aElement is changed and HTMLEditor works with it
6331 // or needs to start working with it, HTMLEditor may not receive `focus`
6332 // event nor `blur` event because this may occur without a focus change.
6333 // Therefore, we need to notify HTMLEditor of this contenteditable attribute
6335 RefPtr
<HTMLEditor
> htmlEditor
= GetHTMLEditor();
6336 if (aElement
->HasFlag(NODE_IS_EDITABLE
)) {
6338 DebugOnly
<nsresult
> rvIgnored
=
6339 htmlEditor
->FocusedElementOrDocumentBecomesEditable(*this,
6341 NS_WARNING_ASSERTION(
6342 NS_SUCCEEDED(rvIgnored
),
6343 "HTMLEditor::FocusedElementOrDocumentBecomesEditable() failed, but "
6347 DebugOnly
<nsresult
> rvIgnored
=
6348 HTMLEditor::FocusedElementOrDocumentBecomesNotEditable(
6349 htmlEditor
, *this, aElement
);
6350 NS_WARNING_ASSERTION(
6351 NS_SUCCEEDED(rvIgnored
),
6352 "HTMLEditor::FocusedElementOrDocumentBecomesNotEditable() failed, "
6358 (mUpdateNestLevel
> 0 && (mContentEditableCount
> 0) != IsEditingOn())) {
6362 EditingState oldState
= mEditingState
;
6364 nsresult rv
= EditingStateChanged();
6365 NS_ENSURE_SUCCESS_VOID(rv
);
6367 if (oldState
== mEditingState
&&
6368 mEditingState
== EditingState::eContentEditable
) {
6369 // We just changed the contentEditable state of a node, we need to reset
6370 // the spellchecking state of that node.
6372 if (RefPtr
<HTMLEditor
> htmlEditor
= GetHTMLEditor()) {
6373 nsCOMPtr
<nsIInlineSpellChecker
> spellChecker
;
6374 rv
= htmlEditor
->GetInlineSpellChecker(false,
6375 getter_AddRefs(spellChecker
));
6376 NS_ENSURE_SUCCESS_VOID(rv
);
6379 aElement
->InclusiveDescendantMayNeedSpellchecking(htmlEditor
)) {
6380 RefPtr
<nsRange
> range
= nsRange::Create(aElement
);
6381 IgnoredErrorResult res
;
6382 range
->SelectNode(*aElement
, res
);
6384 // The node might be detached from the document at this point,
6385 // which would cause this call to fail. In this case, we can
6386 // safely ignore the contenteditable count change.
6390 rv
= spellChecker
->SpellCheckRange(range
);
6391 NS_ENSURE_SUCCESS_VOID(rv
);
6397 // aElement causes creating new HTMLEditor and the element had and keep
6398 // having focus, the HTMLEditor won't receive `focus` event. Therefore, we
6399 // need to notify HTMLEditor of it becomes editable.
6400 if (elementHasFocus
&& aElement
->HasFlag(NODE_IS_EDITABLE
) &&
6401 fm
->GetFocusedElement() == aElement
) {
6402 if (RefPtr
<HTMLEditor
> htmlEditor
= GetHTMLEditor()) {
6403 DebugOnly
<nsresult
> rvIgnored
=
6404 htmlEditor
->FocusedElementOrDocumentBecomesEditable(*this, aElement
);
6405 NS_WARNING_ASSERTION(
6406 NS_SUCCEEDED(rvIgnored
),
6407 "HTMLEditor::FocusedElementOrDocumentBecomesEditable() failed, but "
6413 void Document::MaybeDispatchCheckKeyPressEventModelEvent() {
6414 // Currently, we need to check only when we're becoming editable for
6416 if (mEditingState
!= EditingState::eContentEditable
) {
6420 if (mHasBeenEditable
) {
6423 mHasBeenEditable
= true;
6425 // Dispatch "CheckKeyPressEventModel" event. That is handled only by
6426 // KeyPressEventModelCheckerChild. Then, it calls SetKeyPressEventModel()
6427 // with proper keypress event for the active web app.
6428 WidgetEvent
checkEvent(true, eUnidentifiedEvent
);
6429 checkEvent
.mSpecifiedEventType
= nsGkAtoms::onCheckKeyPressEventModel
;
6430 checkEvent
.mFlags
.mCancelable
= false;
6431 checkEvent
.mFlags
.mBubbles
= false;
6432 checkEvent
.mFlags
.mOnlySystemGroupDispatch
= true;
6433 // Post the event rather than dispatching it synchronously because we need
6434 // a call of SetKeyPressEventModel() before first key input. Therefore, we
6435 // can avoid paying unnecessary runtime cost for most web apps.
6436 (new AsyncEventDispatcher(this, checkEvent
))->PostDOMEvent();
6439 void Document::SetKeyPressEventModel(uint16_t aKeyPressEventModel
) {
6440 PresShell
* presShell
= GetPresShell();
6444 presShell
->SetKeyPressEventModel(aKeyPressEventModel
);
6447 TimeStamp
Document::LastFocusTime() const { return mLastFocusTime
; }
6449 void Document::SetLastFocusTime(const TimeStamp
& aFocusTime
) {
6450 MOZ_DIAGNOSTIC_ASSERT(!aFocusTime
.IsNull());
6451 MOZ_DIAGNOSTIC_ASSERT(mLastFocusTime
.IsNull() ||
6452 aFocusTime
>= mLastFocusTime
);
6453 mLastFocusTime
= aFocusTime
;
6456 void Document::GetReferrer(nsAString
& aReferrer
) const {
6457 aReferrer
.Truncate();
6458 if (!mReferrerInfo
) {
6462 nsCOMPtr
<nsIURI
> referrer
= mReferrerInfo
->GetComputedReferrer();
6468 nsresult rv
= URLDecorationStripper::StripTrackingIdentifiers(referrer
, uri
);
6469 if (NS_WARN_IF(NS_FAILED(rv
))) {
6473 CopyUTF8toUTF16(uri
, aReferrer
);
6476 void Document::GetCookie(nsAString
& aCookie
, ErrorResult
& aRv
) {
6477 aCookie
.Truncate(); // clear current cookie in case service fails;
6478 // no cookie isn't an error condition.
6480 if (mDisableCookieAccess
) {
6484 // If the document's sandboxed origin flag is set, then reading cookies
6486 if (mSandboxFlags
& SANDBOXED_ORIGIN
) {
6487 aRv
.ThrowSecurityError(
6488 "Forbidden in a sandboxed document without the 'allow-same-origin' "
6493 StorageAccess storageAccess
= CookieAllowedForDocument(this);
6494 if (storageAccess
== StorageAccess::eDeny
) {
6498 if (ShouldPartitionStorage(storageAccess
) &&
6499 !StoragePartitioningEnabled(storageAccess
, CookieJarSettings())) {
6503 // If the document is a cookie-averse Document... return the empty string.
6504 if (IsCookieAverse()) {
6508 // not having a cookie service isn't an error
6509 nsCOMPtr
<nsICookieService
> service
=
6510 do_GetService(NS_COOKIESERVICE_CONTRACTID
);
6512 nsAutoCString cookie
;
6513 service
->GetCookieStringFromDocument(this, cookie
);
6514 // CopyUTF8toUTF16 doesn't handle error
6515 // because it assumes that the input is valid.
6516 UTF_8_ENCODING
->DecodeWithoutBOMHandling(cookie
, aCookie
);
6520 void Document::SetCookie(const nsAString
& aCookie
, ErrorResult
& aRv
) {
6521 if (mDisableCookieAccess
) {
6525 // If the document's sandboxed origin flag is set, then setting cookies
6527 if (mSandboxFlags
& SANDBOXED_ORIGIN
) {
6528 aRv
.ThrowSecurityError(
6529 "Forbidden in a sandboxed document without the 'allow-same-origin' "
6534 StorageAccess storageAccess
= CookieAllowedForDocument(this);
6535 if (storageAccess
== StorageAccess::eDeny
) {
6539 if (ShouldPartitionStorage(storageAccess
) &&
6540 !StoragePartitioningEnabled(storageAccess
, CookieJarSettings())) {
6544 // If the document is a cookie-averse Document... do nothing.
6545 if (IsCookieAverse()) {
6549 if (!mDocumentURI
) {
6553 // not having a cookie service isn't an error
6554 nsCOMPtr
<nsICookieService
> service
=
6555 do_GetService(NS_COOKIESERVICE_CONTRACTID
);
6560 NS_ConvertUTF16toUTF8
cookie(aCookie
);
6561 nsresult rv
= service
->SetCookieStringFromDocument(this, cookie
);
6563 // No warning messages here.
6564 if (NS_FAILED(rv
)) {
6568 nsCOMPtr
<nsIObserverService
> observerService
=
6569 mozilla::services::GetObserverService();
6570 if (observerService
) {
6571 observerService
->NotifyObservers(ToSupports(this), "document-set-cookie",
6572 nsString(aCookie
).get());
6576 ReferrerPolicy
Document::GetReferrerPolicy() const {
6577 return mReferrerInfo
? mReferrerInfo
->ReferrerPolicy()
6578 : ReferrerPolicy::_empty
;
6581 void Document::GetAlinkColor(nsAString
& aAlinkColor
) {
6582 aAlinkColor
.Truncate();
6584 HTMLBodyElement
* body
= GetBodyElement();
6586 body
->GetALink(aAlinkColor
);
6590 void Document::SetAlinkColor(const nsAString
& aAlinkColor
) {
6591 HTMLBodyElement
* body
= GetBodyElement();
6593 body
->SetALink(aAlinkColor
);
6597 void Document::GetLinkColor(nsAString
& aLinkColor
) {
6598 aLinkColor
.Truncate();
6600 HTMLBodyElement
* body
= GetBodyElement();
6602 body
->GetLink(aLinkColor
);
6606 void Document::SetLinkColor(const nsAString
& aLinkColor
) {
6607 HTMLBodyElement
* body
= GetBodyElement();
6609 body
->SetLink(aLinkColor
);
6613 void Document::GetVlinkColor(nsAString
& aVlinkColor
) {
6614 aVlinkColor
.Truncate();
6616 HTMLBodyElement
* body
= GetBodyElement();
6618 body
->GetVLink(aVlinkColor
);
6622 void Document::SetVlinkColor(const nsAString
& aVlinkColor
) {
6623 HTMLBodyElement
* body
= GetBodyElement();
6625 body
->SetVLink(aVlinkColor
);
6629 void Document::GetBgColor(nsAString
& aBgColor
) {
6630 aBgColor
.Truncate();
6632 HTMLBodyElement
* body
= GetBodyElement();
6634 body
->GetBgColor(aBgColor
);
6638 void Document::SetBgColor(const nsAString
& aBgColor
) {
6639 HTMLBodyElement
* body
= GetBodyElement();
6641 body
->SetBgColor(aBgColor
);
6645 void Document::GetFgColor(nsAString
& aFgColor
) {
6646 aFgColor
.Truncate();
6648 HTMLBodyElement
* body
= GetBodyElement();
6650 body
->GetText(aFgColor
);
6654 void Document::SetFgColor(const nsAString
& aFgColor
) {
6655 HTMLBodyElement
* body
= GetBodyElement();
6657 body
->SetText(aFgColor
);
6661 void Document::CaptureEvents() {
6662 WarnOnceAbout(DeprecatedOperations::eUseOfCaptureEvents
);
6665 void Document::ReleaseEvents() {
6666 WarnOnceAbout(DeprecatedOperations::eUseOfReleaseEvents
);
6669 HTMLAllCollection
* Document::All() {
6671 mAll
= new HTMLAllCollection(this);
6676 nsresult
Document::GetSrcdocData(nsAString
& aSrcdocData
) {
6677 if (mIsSrcdocDocument
) {
6678 nsCOMPtr
<nsIInputStreamChannel
> inStrmChan
= do_QueryInterface(mChannel
);
6680 return inStrmChan
->GetSrcdocData(aSrcdocData
);
6683 aSrcdocData
= VoidString();
6687 Nullable
<WindowProxyHolder
> Document::GetDefaultView() const {
6688 nsPIDOMWindowOuter
* win
= GetWindow();
6692 return WindowProxyHolder(win
->GetBrowsingContext());
6695 nsIContent
* Document::GetUnretargetedFocusedContent(
6696 IncludeChromeOnly aIncludeChromeOnly
) const {
6697 nsCOMPtr
<nsPIDOMWindowOuter
> window
= GetWindow();
6701 nsCOMPtr
<nsPIDOMWindowOuter
> focusedWindow
;
6702 nsIContent
* focusedContent
= nsFocusManager::GetFocusedDescendant(
6703 window
, nsFocusManager::eOnlyCurrentWindow
,
6704 getter_AddRefs(focusedWindow
));
6705 if (!focusedContent
) {
6708 // be safe and make sure the element is from this document
6709 if (focusedContent
->OwnerDoc() != this) {
6712 if (focusedContent
->ChromeOnlyAccess() &&
6713 aIncludeChromeOnly
== IncludeChromeOnly::No
) {
6714 return focusedContent
->FindFirstNonChromeOnlyAccessContent();
6716 return focusedContent
;
6719 Element
* Document::GetActiveElement() {
6720 // Get the focused element.
6721 Element
* focusedElement
= GetRetargetedFocusedElement();
6722 if (focusedElement
) {
6723 return focusedElement
;
6726 // No focused element anywhere in this document. Try to get the BODY.
6727 if (IsHTMLOrXHTML()) {
6728 Element
* bodyElement
= AsHTMLDocument()->GetBody();
6732 // Special case to handle the transition to XHTML from XUL documents
6733 // where there currently isn't a body element, but we need to match the
6734 // XUL behavior. This should be removed when bug 1540278 is resolved.
6735 if (nsContentUtils::IsChromeDoc(this)) {
6736 Element
* docElement
= GetDocumentElement();
6737 if (docElement
&& docElement
->IsXULElement()) {
6741 // Because of IE compatibility, return null when html document doesn't have
6746 // If we couldn't get a BODY, return the root element.
6747 return GetDocumentElement();
6750 Element
* Document::GetCurrentScript() {
6751 nsCOMPtr
<Element
> el(do_QueryInterface(ScriptLoader()->GetCurrentScript()));
6755 void Document::ReleaseCapture() const {
6756 // only release the capture if the caller can access it. This prevents a
6757 // page from stopping a scrollbar grab for example.
6758 nsCOMPtr
<nsINode
> node
= PresShell::GetCapturingContent();
6759 if (node
&& nsContentUtils::CanCallerAccess(node
)) {
6760 PresShell::ReleaseCapturingContent();
6764 nsIURI
* Document::GetBaseURI(bool aTryUseXHRDocBaseURI
) const {
6765 if (aTryUseXHRDocBaseURI
&& mChromeXHRDocBaseURI
) {
6766 return mChromeXHRDocBaseURI
;
6769 return GetDocBaseURI();
6772 void Document::SetBaseURI(nsIURI
* aURI
) {
6773 if (!aURI
&& !mDocumentBaseURI
) {
6777 // Don't do anything if the URI wasn't actually changed.
6778 if (aURI
&& mDocumentBaseURI
) {
6779 bool equalBases
= false;
6780 mDocumentBaseURI
->Equals(aURI
, &equalBases
);
6786 mDocumentBaseURI
= aURI
;
6787 mCachedURLData
= nullptr;
6791 Result
<OwningNonNull
<nsIURI
>, nsresult
> Document::ResolveWithBaseURI(
6792 const nsAString
& aURI
) {
6793 RefPtr
<nsIURI
> resolvedURI
;
6795 NS_NewURI(getter_AddRefs(resolvedURI
), aURI
, nullptr, GetDocBaseURI()));
6796 return OwningNonNull
<nsIURI
>(std::move(resolvedURI
));
6799 nsIReferrerInfo
* Document::ReferrerInfoForInternalCSSAndSVGResources() {
6800 if (!mCachedReferrerInfoForInternalCSSAndSVGResources
) {
6801 mCachedReferrerInfoForInternalCSSAndSVGResources
=
6802 ReferrerInfo::CreateForInternalCSSAndSVGResources(this);
6804 return mCachedReferrerInfoForInternalCSSAndSVGResources
;
6807 URLExtraData
* Document::DefaultStyleAttrURLData() {
6808 MOZ_ASSERT(NS_IsMainThread());
6809 if (!mCachedURLData
) {
6810 mCachedURLData
= new URLExtraData(
6811 GetDocBaseURI(), ReferrerInfoForInternalCSSAndSVGResources(),
6814 return mCachedURLData
;
6817 void Document::SetDocumentCharacterSet(NotNull
<const Encoding
*> aEncoding
) {
6818 if (mCharacterSet
!= aEncoding
) {
6819 mCharacterSet
= aEncoding
;
6820 mEncodingMenuDisabled
= aEncoding
== UTF_8_ENCODING
;
6821 RecomputeLanguageFromCharset();
6823 if (nsPresContext
* context
= GetPresContext()) {
6824 context
->DocumentCharSetChanged(aEncoding
);
6829 void Document::GetSandboxFlagsAsString(nsAString
& aFlags
) {
6830 nsContentUtils::SandboxFlagsToString(mSandboxFlags
, aFlags
);
6833 void Document::GetHeaderData(nsAtom
* aHeaderField
, nsAString
& aData
) const {
6835 const HeaderData
* data
= mHeaderData
.get();
6837 if (data
->mField
== aHeaderField
) {
6838 aData
= data
->mData
;
6841 data
= data
->mNext
.get();
6845 void Document::SetHeaderData(nsAtom
* aHeaderField
, const nsAString
& aData
) {
6846 if (!aHeaderField
) {
6847 NS_ERROR("null headerField");
6852 if (!aData
.IsEmpty()) { // don't bother storing empty string
6853 mHeaderData
= MakeUnique
<HeaderData
>(aHeaderField
, aData
);
6856 HeaderData
* data
= mHeaderData
.get();
6857 UniquePtr
<HeaderData
>* lastPtr
= &mHeaderData
;
6859 do { // look for existing and replace
6860 if (data
->mField
== aHeaderField
) {
6861 if (!aData
.IsEmpty()) {
6862 data
->mData
.Assign(aData
);
6863 } else { // don't store empty string
6864 // Note that data->mNext is moved to a temporary before the old value
6865 // of *lastPtr is deleted.
6866 *lastPtr
= std::move(data
->mNext
);
6872 lastPtr
= &data
->mNext
;
6873 data
= lastPtr
->get();
6876 if (!aData
.IsEmpty() && !found
) {
6877 // didn't find, append
6878 *lastPtr
= MakeUnique
<HeaderData
>(aHeaderField
, aData
);
6882 if (aHeaderField
== nsGkAtoms::headerContentLanguage
) {
6883 if (aData
.IsEmpty()) {
6884 mContentLanguage
= nullptr;
6886 mContentLanguage
= NS_AtomizeMainThread(aData
);
6888 mMayNeedFontPrefsUpdate
= true;
6889 if (auto* presContext
= GetPresContext()) {
6890 presContext
->ContentLanguageChanged();
6894 if (aHeaderField
== nsGkAtoms::origin_trial
) {
6895 mTrials
.UpdateFromToken(aData
, NodePrincipal());
6896 if (mTrials
.IsEnabled(OriginTrial::CoepCredentialless
)) {
6899 // If we still don't have a WindowContext, WindowContext::OnNewDocument
6900 // will take care of this.
6901 if (WindowContext
* ctx
= GetWindowContext()) {
6902 if (mEmbedderPolicy
) {
6903 Unused
<< ctx
->SetEmbedderPolicy(mEmbedderPolicy
.value());
6909 if (aHeaderField
== nsGkAtoms::headerDefaultStyle
) {
6910 SetPreferredStyleSheetSet(aData
);
6913 if (aHeaderField
== nsGkAtoms::refresh
&& !IsStaticDocument()) {
6914 // We get into this code before we have a script global yet, so get to our
6915 // container via mDocumentContainer.
6916 if (mDocumentContainer
) {
6917 // Note: using mDocumentURI instead of mBaseURI here, for consistency
6918 // (used to just use the current URI of our webnavigation, but that
6919 // should really be the same thing). Note that this code can run
6920 // before the current URI of the webnavigation has been updated, so we
6921 // can't assert equality here.
6922 mDocumentContainer
->SetupRefreshURIFromHeader(this, aData
);
6926 if (aHeaderField
== nsGkAtoms::headerDNSPrefetchControl
&&
6927 mAllowDNSPrefetch
) {
6928 // Chromium treats any value other than 'on' (case insensitive) as 'off'.
6929 mAllowDNSPrefetch
= aData
.IsEmpty() || aData
.LowerCaseEqualsLiteral("on");
6932 if (aHeaderField
== nsGkAtoms::handheldFriendly
) {
6933 mViewportType
= Unknown
;
6937 void Document::SetEarlyHints(
6938 nsTArray
<net::EarlyHintConnectArgs
>&& aEarlyHints
) {
6939 mEarlyHints
= std::move(aEarlyHints
);
6942 void Document::TryChannelCharset(nsIChannel
* aChannel
, int32_t& aCharsetSource
,
6943 NotNull
<const Encoding
*>& aEncoding
,
6944 nsHtml5TreeOpExecutor
* aExecutor
) {
6946 nsAutoCString charsetVal
;
6947 nsresult rv
= aChannel
->GetContentCharset(charsetVal
);
6948 if (NS_SUCCEEDED(rv
)) {
6949 const Encoding
* preferred
= Encoding::ForLabel(charsetVal
);
6951 if (aExecutor
&& preferred
== REPLACEMENT_ENCODING
) {
6952 aExecutor
->ComplainAboutBogusProtocolCharset(this, false);
6954 aEncoding
= WrapNotNull(preferred
);
6955 aCharsetSource
= kCharsetFromChannel
;
6957 } else if (aExecutor
&& !charsetVal
.IsEmpty()) {
6958 aExecutor
->ComplainAboutBogusProtocolCharset(this, true);
6964 static inline void AssertNoStaleServoDataIn(nsINode
& aSubtreeRoot
) {
6966 for (nsINode
* node
: ShadowIncludingTreeIterator(aSubtreeRoot
)) {
6967 const Element
* element
= Element::FromNode(node
);
6971 MOZ_ASSERT(!element
->HasServoData());
6976 already_AddRefed
<PresShell
> Document::CreatePresShell(
6977 nsPresContext
* aContext
, nsViewManager
* aViewManager
) {
6978 MOZ_DIAGNOSTIC_ASSERT(!mPresShell
, "We have a presshell already!");
6980 NS_ENSURE_FALSE(GetBFCacheEntry(), nullptr);
6982 AssertNoStaleServoDataIn(*this);
6984 RefPtr
<PresShell
> presShell
= new PresShell(this);
6985 // Note: we don't hold a ref to the shell (it holds a ref to us)
6986 mPresShell
= presShell
;
6988 if (!mStyleSetFilled
) {
6992 presShell
->Init(aContext
, aViewManager
);
6993 if (RefPtr
<class HighlightRegistry
> highlightRegistry
= mHighlightRegistry
) {
6994 highlightRegistry
->AddHighlightSelectionsToFrameSelection();
6996 // Gaining a shell causes changes in how media queries are evaluated, so
6998 aContext
->MediaFeatureValuesChanged(
6999 {MediaFeatureChange::kAllChanges
},
7000 MediaFeatureChangePropagation::JustThisDocument
);
7002 // Make sure to never paint if we belong to an invisible DocShell.
7003 nsCOMPtr
<nsIDocShell
> docShell(mDocumentContainer
);
7004 if (docShell
&& docShell
->IsInvisible()) {
7005 presShell
->SetNeverPainting(true);
7008 MOZ_LOG(gDocumentLeakPRLog
, LogLevel::Debug
,
7009 ("DOCUMENT %p with PressShell %p and DocShell %p", this,
7010 presShell
.get(), docShell
.get()));
7012 mExternalResourceMap
.ShowViewers();
7014 UpdateFrameRequestCallbackSchedulingState();
7016 if (mDocumentL10n
) {
7017 // In case we already accumulated mutations,
7018 // we'll trigger the refresh driver now.
7019 mDocumentL10n
->OnCreatePresShell();
7022 if (HasAutoFocusCandidates()) {
7023 ScheduleFlushAutoFocusCandidates();
7025 // Now that we have a shell, we might have @font-face rules (the presence of a
7026 // shell may change which rules apply to us). We don't need to do anything
7027 // like EnsureStyleFlush or such, there's nothing to update yet and when stuff
7028 // is ready to update we'll flush the font set.
7029 MarkUserFontSetDirty();
7031 // Take the author style disabled state from the top browsing cvontext.
7032 // (PageStyleChild.sys.mjs ensures this is up to date.)
7033 if (BrowsingContext
* bc
= GetBrowsingContext()) {
7034 presShell
->SetAuthorStyleDisabled(bc
->Top()->AuthorStyleDisabledDefault());
7037 return presShell
.forget();
7040 void Document::UpdateFrameRequestCallbackSchedulingState(
7041 PresShell
* aOldPresShell
) {
7042 // If this condition changes to depend on some other variable, make sure to
7043 // call UpdateFrameRequestCallbackSchedulingState() calls to the places where
7044 // that variable can change. Also consider if you should change
7045 // WouldScheduleFrameRequestCallbacks() instead of adding more stuff to this
7047 bool shouldBeScheduled
=
7048 WouldScheduleFrameRequestCallbacks() && !mFrameRequestManager
.IsEmpty();
7049 if (shouldBeScheduled
== mFrameRequestCallbacksScheduled
) {
7054 PresShell
* presShell
= aOldPresShell
? aOldPresShell
: mPresShell
;
7055 MOZ_RELEASE_ASSERT(presShell
);
7057 nsRefreshDriver
* rd
= presShell
->GetPresContext()->RefreshDriver();
7058 if (shouldBeScheduled
) {
7059 rd
->ScheduleFrameRequestCallbacks(this);
7061 rd
->RevokeFrameRequestCallbacks(this);
7064 mFrameRequestCallbacksScheduled
= shouldBeScheduled
;
7067 void Document::TakeFrameRequestCallbacks(nsTArray
<FrameRequest
>& aCallbacks
) {
7068 MOZ_ASSERT(aCallbacks
.IsEmpty());
7069 mFrameRequestManager
.Take(aCallbacks
);
7070 // No need to manually remove ourselves from the refresh driver; it will
7071 // handle that part. But we do have to update our state.
7072 mFrameRequestCallbacksScheduled
= false;
7075 bool Document::ShouldThrottleFrameRequests() const {
7076 if (mStaticCloneCount
> 0) {
7077 // Even if we're not visible, a static clone may be, so run at full speed.
7082 // We're not visible (probably in a background tab or the bf cache).
7087 // Can't do anything smarter. We don't run frame requests in documents
7088 // without a pres shell anyways.
7092 if (!mPresShell
->IsActive()) {
7093 // The pres shell is not active (we're an invisible OOP iframe or such), so
7098 if (mPresShell
->IsPaintingSuppressed()) {
7099 // Historically we have throttled frame requests until we've painted at
7100 // least once, so keep doing that.
7104 if (mPresShell
->IsUnderHiddenEmbedderElement()) {
7105 // For display: none and visibility: hidden we always throttle, for
7106 // consistency with OOP iframes.
7110 Element
* el
= GetEmbedderElement();
7112 // If we're not in-process, our refresh driver is throttled separately (via
7113 // PresShell::SetIsActive, so not much more we can do here.
7117 if (!StaticPrefs::layout_throttle_in_process_iframes()) {
7121 // Note that because we have to scroll this document into view at least once
7122 // to unthrottle it, we will drop one requestAnimationFrame frame when a
7123 // document that previously wasn't visible scrolls into view. This is
7124 // acceptable / unlikely to be human-perceivable, though we could improve on
7125 // it if needed by adding an intersection margin or something of that sort.
7126 const IntersectionInput input
= DOMIntersectionObserver::ComputeInput(
7127 *el
->OwnerDoc(), /* aRoot = */ nullptr, /* aRootMargin = */ nullptr);
7128 const IntersectionOutput output
=
7129 DOMIntersectionObserver::Intersect(input
, *el
);
7130 return !output
.Intersects();
7133 void Document::DeletePresShell() {
7134 mExternalResourceMap
.HideViewers();
7135 if (nsPresContext
* presContext
= mPresShell
->GetPresContext()) {
7136 presContext
->RefreshDriver()->CancelPendingFullscreenEvents(this);
7137 presContext
->RefreshDriver()->CancelFlushAutoFocus(this);
7140 // When our shell goes away, request that all our images be immediately
7141 // discarded, so we don't carry around decoded image data for a document we
7142 // no longer intend to paint.
7143 ImageTracker()->RequestDiscardAll();
7145 // Now that we no longer have a shell, we need to forget about any FontFace
7146 // objects for @font-face rules that came from the style set. There's no need
7147 // to call EnsureStyleFlush either, the shell is going away anyway, so there's
7149 MarkUserFontSetDirty();
7151 if (IsEditingOn()) {
7155 PresShell
* oldPresShell
= mPresShell
;
7156 mPresShell
= nullptr;
7157 UpdateFrameRequestCallbackSchedulingState(oldPresShell
);
7159 ClearStaleServoData();
7160 AssertNoStaleServoDataIn(*this);
7162 mStyleSet
->ShellDetachedFromDocument();
7163 mStyleSetFilled
= false;
7164 mQuirkSheetAdded
= false;
7165 mContentEditableSheetAdded
= false;
7166 mDesignModeSheetAdded
= false;
7169 void Document::DisallowBFCaching(uint32_t aStatus
) {
7170 NS_ASSERTION(!mBFCacheEntry
, "We're already in the bfcache!");
7171 if (!mBFCacheDisallowed
) {
7172 if (WindowGlobalChild
* wgc
= GetWindowGlobalChild()) {
7173 wgc
->SendUpdateBFCacheStatus(aStatus
, 0);
7176 mBFCacheDisallowed
= true;
7179 void Document::SetBFCacheEntry(nsIBFCacheEntry
* aEntry
) {
7180 MOZ_ASSERT(IsBFCachingAllowed() || !aEntry
, "You should have checked!");
7184 mPresShell
->StopObservingRefreshDriver();
7185 } else if (mBFCacheEntry
) {
7186 mPresShell
->StartObservingRefreshDriver();
7189 mBFCacheEntry
= aEntry
;
7192 bool Document::RemoveFromBFCacheSync() {
7193 bool removed
= false;
7194 if (nsCOMPtr
<nsIBFCacheEntry
> entry
= GetBFCacheEntry()) {
7195 entry
->RemoveFromBFCacheSync();
7197 } else if (!IsCurrentActiveDocument()) {
7198 // In the old bfcache implementation while the new page is loading, but
7199 // before nsIDocumentViewer.show() has been called, the previous page
7200 // doesn't yet have nsIBFCacheEntry. However, the previous page isn't the
7201 // current active document anymore.
7202 DisallowBFCaching();
7206 if (mozilla::SessionHistoryInParent() && XRE_IsContentProcess()) {
7207 if (BrowsingContext
* bc
= GetBrowsingContext()) {
7208 if (bc
->IsInBFCache()) {
7209 ContentChild
* cc
= ContentChild::GetSingleton();
7210 // IPC is asynchronous but the caller is supposed to check the return
7211 // value. The reason for 'Sync' in the method name is that the old
7212 // implementation may run scripts. There is Async variant in
7213 // the old session history implementation for the cases where
7214 // synchronous operation isn't safe.
7215 cc
->SendRemoveFromBFCache(bc
->Top());
7223 static void SubDocClearEntry(PLDHashTable
* table
, PLDHashEntryHdr
* entry
) {
7224 SubDocMapEntry
* e
= static_cast<SubDocMapEntry
*>(entry
);
7226 NS_RELEASE(e
->mKey
);
7227 if (e
->mSubDocument
) {
7228 e
->mSubDocument
->SetParentDocument(nullptr);
7229 NS_RELEASE(e
->mSubDocument
);
7233 static void SubDocInitEntry(PLDHashEntryHdr
* entry
, const void* key
) {
7235 const_cast<SubDocMapEntry
*>(static_cast<const SubDocMapEntry
*>(entry
));
7237 e
->mKey
= const_cast<Element
*>(static_cast<const Element
*>(key
));
7240 e
->mSubDocument
= nullptr;
7243 nsresult
Document::SetSubDocumentFor(Element
* aElement
, Document
* aSubDoc
) {
7244 NS_ENSURE_TRUE(aElement
, NS_ERROR_UNEXPECTED
);
7247 // aSubDoc is nullptr, remove the mapping
7249 if (mSubDocuments
) {
7250 mSubDocuments
->Remove(aElement
);
7253 if (!mSubDocuments
) {
7254 // Create a new hashtable
7256 static const PLDHashTableOps hash_table_ops
= {
7257 PLDHashTable::HashVoidPtrKeyStub
, PLDHashTable::MatchEntryStub
,
7258 PLDHashTable::MoveEntryStub
, SubDocClearEntry
, SubDocInitEntry
};
7260 mSubDocuments
= new PLDHashTable(&hash_table_ops
, sizeof(SubDocMapEntry
));
7263 // Add a mapping to the hash table
7265 static_cast<SubDocMapEntry
*>(mSubDocuments
->Add(aElement
, fallible
));
7268 return NS_ERROR_OUT_OF_MEMORY
;
7271 if (entry
->mSubDocument
) {
7272 entry
->mSubDocument
->SetParentDocument(nullptr);
7274 // Release the old sub document
7275 NS_RELEASE(entry
->mSubDocument
);
7278 entry
->mSubDocument
= aSubDoc
;
7279 NS_ADDREF(entry
->mSubDocument
);
7281 aSubDoc
->SetParentDocument(this);
7287 Document
* Document::GetSubDocumentFor(nsIContent
* aContent
) const {
7288 if (mSubDocuments
&& aContent
->IsElement()) {
7289 auto entry
= static_cast<SubDocMapEntry
*>(
7290 mSubDocuments
->Search(aContent
->AsElement()));
7293 return entry
->mSubDocument
;
7300 Element
* Document::GetEmbedderElement() const {
7301 // We check if we're the active document in our BrowsingContext
7302 // by comparing against its document, rather than checking if the
7303 // WindowContext is cached, since mWindow may be null when we're
7304 // called (such as in nsPresContext::Init).
7305 if (BrowsingContext
* bc
= GetBrowsingContext()) {
7306 return bc
->GetExtantDocument() == this ? bc
->GetEmbedderElement() : nullptr;
7312 Element
* Document::GetRootElement() const {
7313 return (mCachedRootElement
&& mCachedRootElement
->GetParentNode() == this)
7314 ? mCachedRootElement
7315 : GetRootElementInternal();
7318 Element
* Document::GetUnfocusedKeyEventTarget() { return GetRootElement(); }
7320 Element
* Document::GetRootElementInternal() const {
7321 // We invoke GetRootElement() immediately before the servo traversal, so we
7322 // should always have a cache hit from Servo.
7323 MOZ_ASSERT(NS_IsMainThread());
7325 // Loop backwards because any non-elements, such as doctypes and PIs
7326 // are likely to appear before the root element.
7327 for (nsIContent
* child
= GetLastChild(); child
;
7328 child
= child
->GetPreviousSibling()) {
7329 if (Element
* element
= Element::FromNode(child
)) {
7330 const_cast<Document
*>(this)->mCachedRootElement
= element
;
7335 const_cast<Document
*>(this)->mCachedRootElement
= nullptr;
7339 void Document::InsertChildBefore(nsIContent
* aKid
, nsIContent
* aBeforeThis
,
7340 bool aNotify
, ErrorResult
& aRv
) {
7341 if (aKid
->IsElement() && GetRootElement()) {
7342 NS_WARNING("Inserting root element when we already have one");
7343 aRv
.ThrowHierarchyRequestError("There is already a root element.");
7347 nsINode::InsertChildBefore(aKid
, aBeforeThis
, aNotify
, aRv
);
7350 void Document::RemoveChildNode(nsIContent
* aKid
, bool aNotify
) {
7351 Maybe
<mozAutoDocUpdate
> updateBatch
;
7352 if (aKid
->IsElement()) {
7353 updateBatch
.emplace(this, aNotify
);
7354 // Destroy the link map up front before we mess with the child list.
7355 DestroyElementMaps();
7358 // Preemptively clear mCachedRootElement, since we may be about to remove it
7359 // from our child list, and we don't want to return this maybe-obsolete value
7360 // from any GetRootElement() calls that happen inside of RemoveChildNode().
7361 // (NOTE: for this to be useful, RemoveChildNode() must NOT trigger any
7362 // GetRootElement() calls until after it's removed the child from mChildren.
7363 // Any call before that point would restore this soon-to-be-obsolete cached
7364 // answer, and our clearing here would be fruitless.)
7365 mCachedRootElement
= nullptr;
7366 nsINode::RemoveChildNode(aKid
, aNotify
);
7367 MOZ_ASSERT(mCachedRootElement
!= aKid
,
7368 "Stale pointer in mCachedRootElement, after we tried to clear it "
7369 "(maybe somebody called GetRootElement() too early?)");
7372 void Document::AddStyleSheetToStyleSets(StyleSheet
& aSheet
) {
7373 if (mStyleSetFilled
) {
7374 EnsureStyleSet().AddDocStyleSheet(aSheet
);
7375 ApplicableStylesChanged();
7379 void Document::RecordShadowStyleChange(ShadowRoot
& aShadowRoot
) {
7380 EnsureStyleSet().RecordShadowStyleChange(aShadowRoot
);
7381 ApplicableStylesChanged(/* aKnownInShadowTree= */ true);
7384 void Document::ApplicableStylesChanged(bool aKnownInShadowTree
) {
7385 // TODO(emilio): if we decide to resolve style in display: none iframes, then
7386 // we need to always track style changes and remove the mStyleSetFilled.
7387 if (!mStyleSetFilled
) {
7390 if (!aKnownInShadowTree
) {
7391 MarkUserFontSetDirty();
7393 PresShell
* ps
= GetPresShell();
7398 ps
->EnsureStyleFlush();
7399 nsPresContext
* pc
= ps
->GetPresContext();
7404 if (!aKnownInShadowTree
) {
7405 pc
->MarkCounterStylesDirty();
7406 pc
->MarkFontFeatureValuesDirty();
7407 pc
->MarkFontPaletteValuesDirty();
7409 pc
->RestyleManager()->NextRestyleIsForCSSRuleChanges();
7412 void Document::RemoveStyleSheetFromStyleSets(StyleSheet
& aSheet
) {
7413 if (mStyleSetFilled
) {
7414 mStyleSet
->RemoveStyleSheet(aSheet
);
7415 ApplicableStylesChanged();
7419 void Document::InsertSheetAt(size_t aIndex
, StyleSheet
& aSheet
) {
7420 DocumentOrShadowRoot::InsertSheetAt(aIndex
, aSheet
);
7422 if (aSheet
.IsApplicable()) {
7423 AddStyleSheetToStyleSets(aSheet
);
7427 void Document::StyleSheetApplicableStateChanged(StyleSheet
& aSheet
) {
7428 const bool applicable
= aSheet
.IsApplicable();
7429 // If we're actually in the document style sheet list
7430 if (StyleOrderIndexOfSheet(aSheet
) >= 0) {
7432 AddStyleSheetToStyleSets(aSheet
);
7434 RemoveStyleSheetFromStyleSets(aSheet
);
7439 void Document::PostStyleSheetApplicableStateChangeEvent(StyleSheet
& aSheet
) {
7440 if (!StyleSheetChangeEventsEnabled()) {
7444 StyleSheetApplicableStateChangeEventInit init
;
7445 init
.mBubbles
= true;
7446 init
.mCancelable
= true;
7447 init
.mStylesheet
= &aSheet
;
7448 init
.mApplicable
= aSheet
.IsApplicable();
7450 RefPtr
<StyleSheetApplicableStateChangeEvent
> event
=
7451 StyleSheetApplicableStateChangeEvent::Constructor(
7452 this, u
"StyleSheetApplicableStateChanged"_ns
, init
);
7453 event
->SetTrusted(true);
7454 event
->SetTarget(this);
7455 RefPtr
<AsyncEventDispatcher
> asyncDispatcher
=
7456 new AsyncEventDispatcher(this, event
.forget(), ChromeOnlyDispatch::eYes
);
7457 asyncDispatcher
->PostDOMEvent();
7460 void Document::PostStyleSheetRemovedEvent(StyleSheet
& aSheet
) {
7461 if (!StyleSheetChangeEventsEnabled()) {
7465 StyleSheetRemovedEventInit init
;
7466 init
.mBubbles
= true;
7467 init
.mCancelable
= false;
7468 init
.mStylesheet
= &aSheet
;
7470 RefPtr
<StyleSheetRemovedEvent
> event
=
7471 StyleSheetRemovedEvent::Constructor(this, u
"StyleSheetRemoved"_ns
, init
);
7472 event
->SetTrusted(true);
7473 event
->SetTarget(this);
7474 RefPtr
<AsyncEventDispatcher
> asyncDispatcher
=
7475 new AsyncEventDispatcher(this, event
.forget(), ChromeOnlyDispatch::eYes
);
7476 asyncDispatcher
->PostDOMEvent();
7479 void Document::PostCustomPropertyRegistered(
7480 const PropertyDefinition
& aDefinition
) {
7481 if (!StyleSheetChangeEventsEnabled()) {
7485 CSSCustomPropertyRegisteredEventInit init
;
7486 init
.mBubbles
= true;
7487 init
.mCancelable
= false;
7489 InspectorCSSPropertyDefinition property
;
7491 property
.mName
.Append(aDefinition
.mName
);
7492 property
.mSyntax
.Append(aDefinition
.mSyntax
);
7493 property
.mInherits
= aDefinition
.mInherits
;
7494 if (aDefinition
.mInitialValue
.WasPassed()) {
7495 property
.mInitialValue
.Append(aDefinition
.mInitialValue
.Value());
7497 property
.mInitialValue
.SetIsVoid(true);
7499 property
.mFromJS
= true;
7500 init
.mPropertyDefinition
= property
;
7502 RefPtr
<CSSCustomPropertyRegisteredEvent
> event
=
7503 CSSCustomPropertyRegisteredEvent::Constructor(
7504 this, u
"csscustompropertyregistered"_ns
, init
);
7505 event
->SetTrusted(true);
7506 event
->SetTarget(this);
7507 RefPtr
<AsyncEventDispatcher
> asyncDispatcher
=
7508 new AsyncEventDispatcher(this, event
.forget(), ChromeOnlyDispatch::eYes
);
7509 asyncDispatcher
->PostDOMEvent();
7512 static int32_t FindSheet(const nsTArray
<RefPtr
<StyleSheet
>>& aSheets
,
7513 nsIURI
* aSheetURI
) {
7514 for (int32_t i
= aSheets
.Length() - 1; i
>= 0; i
--) {
7516 nsIURI
* uri
= aSheets
[i
]->GetSheetURI();
7518 if (uri
&& NS_SUCCEEDED(uri
->Equals(aSheetURI
, &bEqual
)) && bEqual
)
7525 nsresult
Document::LoadAdditionalStyleSheet(additionalSheetType aType
,
7526 nsIURI
* aSheetURI
) {
7527 MOZ_ASSERT(aSheetURI
, "null arg");
7529 // Checking if we have loaded this one already.
7530 if (FindSheet(mAdditionalSheets
[aType
], aSheetURI
) >= 0)
7531 return NS_ERROR_INVALID_ARG
;
7533 // Loading the sheet sync.
7534 RefPtr
<css::Loader
> loader
= new css::Loader(GetDocGroup());
7536 css::SheetParsingMode parsingMode
;
7538 case Document::eAgentSheet
:
7539 parsingMode
= css::eAgentSheetFeatures
;
7542 case Document::eUserSheet
:
7543 parsingMode
= css::eUserSheetFeatures
;
7546 case Document::eAuthorSheet
:
7547 parsingMode
= css::eAuthorSheetFeatures
;
7551 MOZ_CRASH("impossible value for aType");
7554 auto result
= loader
->LoadSheetSync(aSheetURI
, parsingMode
,
7555 css::Loader::UseSystemPrincipal::Yes
);
7556 if (result
.isErr()) {
7557 return result
.unwrapErr();
7560 RefPtr
<StyleSheet
> sheet
= result
.unwrap();
7562 sheet
->SetAssociatedDocumentOrShadowRoot(this);
7563 MOZ_ASSERT(sheet
->IsApplicable());
7565 return AddAdditionalStyleSheet(aType
, sheet
);
7568 nsresult
Document::AddAdditionalStyleSheet(additionalSheetType aType
,
7569 StyleSheet
* aSheet
) {
7570 if (mAdditionalSheets
[aType
].Contains(aSheet
)) {
7571 return NS_ERROR_INVALID_ARG
;
7574 if (!aSheet
->IsApplicable()) {
7575 return NS_ERROR_INVALID_ARG
;
7578 mAdditionalSheets
[aType
].AppendElement(aSheet
);
7580 if (mStyleSetFilled
) {
7581 EnsureStyleSet().AppendStyleSheet(*aSheet
);
7582 ApplicableStylesChanged();
7587 void Document::RemoveAdditionalStyleSheet(additionalSheetType aType
,
7588 nsIURI
* aSheetURI
) {
7589 MOZ_ASSERT(aSheetURI
);
7591 nsTArray
<RefPtr
<StyleSheet
>>& sheets
= mAdditionalSheets
[aType
];
7593 int32_t i
= FindSheet(mAdditionalSheets
[aType
], aSheetURI
);
7595 RefPtr
<StyleSheet
> sheetRef
= std::move(sheets
[i
]);
7596 sheets
.RemoveElementAt(i
);
7598 if (!mIsGoingAway
) {
7599 MOZ_ASSERT(sheetRef
->IsApplicable());
7600 if (mStyleSetFilled
) {
7601 EnsureStyleSet().RemoveStyleSheet(*sheetRef
);
7602 ApplicableStylesChanged();
7605 sheetRef
->ClearAssociatedDocumentOrShadowRoot();
7609 nsIGlobalObject
* Document::GetScopeObject() const {
7610 nsCOMPtr
<nsIGlobalObject
> scope(do_QueryReferent(mScopeObject
));
7614 DocGroup
* Document::GetDocGroupOrCreate() {
7615 if (!mDocGroup
&& GetBrowsingContext()) {
7616 BrowsingContextGroup
* group
= GetBrowsingContext()->Group();
7619 nsAutoCString docGroupKey
;
7620 nsresult rv
= mozilla::dom::DocGroup::GetKey(
7621 NodePrincipal(), group
->IsPotentiallyCrossOriginIsolated(),
7623 if (NS_SUCCEEDED(rv
)) {
7624 mDocGroup
= group
->AddDocument(docGroupKey
, this);
7630 void Document::SetScopeObject(nsIGlobalObject
* aGlobal
) {
7631 mScopeObject
= do_GetWeakReference(aGlobal
);
7633 mHasHadScriptHandlingObject
= true;
7635 nsPIDOMWindowInner
* window
= aGlobal
->GetAsInnerWindow();
7640 // Same origin data documents should have the same docGroup as their scope
7642 if (mLoadedAsData
&& window
->GetExtantDoc() &&
7643 window
->GetExtantDoc() != this &&
7644 window
->GetExtantDoc()->NodePrincipal() == NodePrincipal()) {
7645 DocGroup
* docGroup
= window
->GetExtantDoc()->GetDocGroup();
7649 mDocGroup
= docGroup
;
7650 mDocGroup
->AddDocument(this);
7652 MOZ_ASSERT(mDocGroup
== docGroup
,
7653 "Data document has a mismatched doc group?");
7656 AssertDocGroupMatchesKey();
7661 MOZ_ASSERT_UNREACHABLE(
7662 "Scope window doesn't have DocGroup when creating data document?");
7663 // ... but fall through to be safe.
7666 BrowsingContextGroup
* browsingContextGroup
=
7667 window
->GetBrowsingContextGroup();
7669 // We should already have the principal, and now that we have been added
7670 // to a window, we should be able to join a DocGroup!
7671 nsAutoCString docGroupKey
;
7672 nsresult rv
= mozilla::dom::DocGroup::GetKey(
7674 browsingContextGroup
->IsPotentiallyCrossOriginIsolated(), docGroupKey
);
7676 if (NS_SUCCEEDED(rv
)) {
7677 MOZ_RELEASE_ASSERT(mDocGroup
->MatchesKey(docGroupKey
));
7679 MOZ_RELEASE_ASSERT(mDocGroup
->GetBrowsingContextGroup() ==
7680 browsingContextGroup
);
7682 mDocGroup
= browsingContextGroup
->AddDocument(docGroupKey
, this);
7684 MOZ_ASSERT(mDocGroup
);
7688 mNodeInfoManager
->GetArenaAllocator(),
7689 mNodeInfoManager
->GetArenaAllocator() == mDocGroup
->ArenaAllocator());
7693 bool Document::ContainsEMEContent() {
7694 nsPIDOMWindowInner
* win
= GetInnerWindow();
7695 // Note this case is different from checking just media elements in that
7696 // it covers when we've created MediaKeys but not associated them with a
7698 return win
&& win
->HasActiveMediaKeysInstance();
7701 bool Document::ContainsMSEContent() {
7702 bool containsMSE
= false;
7704 auto check
= [&containsMSE
](nsISupports
* aSupports
) {
7705 nsCOMPtr
<nsIContent
> content(do_QueryInterface(aSupports
));
7706 if (auto* mediaElem
= HTMLMediaElement::FromNodeOrNull(content
)) {
7707 RefPtr
<MediaSource
> ms
= mediaElem
->GetMozMediaSourceObject();
7714 EnumerateActivityObservers(check
);
7718 static void NotifyActivityChangedCallback(nsISupports
* aSupports
) {
7719 nsCOMPtr
<nsIContent
> content(do_QueryInterface(aSupports
));
7720 if (auto* mediaElem
= HTMLMediaElement::FromNodeOrNull(content
)) {
7721 mediaElem
->NotifyOwnerDocumentActivityChanged();
7723 nsCOMPtr
<nsIDocumentActivity
> objectDocumentActivity(
7724 do_QueryInterface(aSupports
));
7725 if (objectDocumentActivity
) {
7726 objectDocumentActivity
->NotifyOwnerDocumentActivityChanged();
7728 nsCOMPtr
<nsIImageLoadingContent
> imageLoadingContent(
7729 do_QueryInterface(aSupports
));
7730 if (imageLoadingContent
) {
7732 static_cast<nsImageLoadingContent
*>(imageLoadingContent
.get());
7733 ilc
->NotifyOwnerDocumentActivityChanged();
7738 void Document::NotifyActivityChanged() {
7739 EnumerateActivityObservers(NotifyActivityChangedCallback
);
7742 void Document::SetContainer(nsDocShell
* aContainer
) {
7744 mDocumentContainer
= aContainer
;
7746 mDocumentContainer
= WeakPtr
<nsDocShell
>();
7750 aContainer
&& aContainer
->GetBrowsingContext()->IsChrome();
7752 NotifyActivityChanged();
7754 // IsTopLevelWindowInactive depends on the docshell, so
7755 // update the cached value now that it's available.
7756 UpdateDocumentStates(DocumentState::WINDOW_INACTIVE
, false);
7761 BrowsingContext
* context
= aContainer
->GetBrowsingContext();
7762 MOZ_ASSERT_IF(context
&& mDocGroup
,
7763 context
->Group() == mDocGroup
->GetBrowsingContextGroup());
7764 if (context
&& context
->IsContent()) {
7765 SetIsTopLevelContentDocument(context
->IsTopContent());
7766 SetIsContentDocument(true);
7768 SetIsTopLevelContentDocument(false);
7769 SetIsContentDocument(false);
7773 nsISupports
* Document::GetContainer() const {
7774 return static_cast<nsIDocShell
*>(mDocumentContainer
);
7777 void Document::SetScriptGlobalObject(
7778 nsIScriptGlobalObject
* aScriptGlobalObject
) {
7779 MOZ_ASSERT(aScriptGlobalObject
|| !mAnimationController
||
7780 mAnimationController
->IsPausedByType(
7781 SMILTimeContainer::PAUSE_PAGEHIDE
|
7782 SMILTimeContainer::PAUSE_BEGIN
),
7783 "Clearing window pointer while animations are unpaused");
7785 if (mScriptGlobalObject
&& !aScriptGlobalObject
) {
7786 // We're detaching from the window. We need to grab a pointer to
7787 // our layout history state now.
7788 mLayoutHistoryState
= GetLayoutHistoryState();
7790 // Also make sure to remove our onload blocker now if we haven't done it yet
7791 if (mOnloadBlockCount
!= 0) {
7792 nsCOMPtr
<nsILoadGroup
> loadGroup
= GetDocumentLoadGroup();
7794 loadGroup
->RemoveRequest(mOnloadBlocker
, nullptr, NS_OK
);
7798 if (GetController().isSome()) {
7799 if (imgLoader
* loader
= nsContentUtils::GetImgLoaderForDocument(this)) {
7800 loader
->ClearCacheForControlledDocument(this);
7803 // We may become controlled again if this document comes back out
7804 // of bfcache. Clear our state to allow that to happen. Only
7805 // clear this flag if we are actually controlled, though, so pages
7806 // that were force reloaded don't become controlled when they
7807 // come out of bfcache.
7808 mMaybeServiceWorkerControlled
= false;
7811 if (GetWindowContext()) {
7812 // The document is about to lose its window, so this is a good time to
7813 // send our page use counters, while we still have access to our
7816 // (We also do this in nsGlobalWindowInner::FreeInnerObjects(), which
7817 // catches some cases of documents losing their window that don't
7819 SendPageUseCounters();
7823 // BlockOnload() might be called before mScriptGlobalObject is set.
7824 // We may need to add the blocker once mScriptGlobalObject is set.
7825 bool needOnloadBlocker
= !mScriptGlobalObject
&& aScriptGlobalObject
;
7827 mScriptGlobalObject
= aScriptGlobalObject
;
7829 if (needOnloadBlocker
) {
7830 EnsureOnloadBlocker();
7833 UpdateFrameRequestCallbackSchedulingState();
7835 if (aScriptGlobalObject
) {
7836 // Go back to using the docshell for the layout history state
7837 mLayoutHistoryState
= nullptr;
7838 SetScopeObject(aScriptGlobalObject
);
7839 mHasHadDefaultView
= true;
7841 if (mAllowDNSPrefetch
) {
7842 nsCOMPtr
<nsIDocShell
> docShell(mDocumentContainer
);
7845 nsCOMPtr
<nsIWebNavigation
> webNav
=
7846 do_GetInterface(aScriptGlobalObject
);
7847 NS_ASSERTION(SameCOMIdentity(webNav
, docShell
),
7848 "Unexpected container or script global?");
7850 bool allowDNSPrefetch
;
7851 docShell
->GetAllowDNSPrefetch(&allowDNSPrefetch
);
7852 mAllowDNSPrefetch
= allowDNSPrefetch
;
7856 // If we are set in a window that is already focused we should remember this
7857 // as the time the document gained focus.
7858 if (HasFocus(IgnoreErrors())) {
7859 SetLastFocusTime(TimeStamp::Now());
7863 // Remember the pointer to our window (or lack there of), to avoid
7864 // having to QI every time it's asked for.
7865 nsCOMPtr
<nsPIDOMWindowInner
> window
= do_QueryInterface(mScriptGlobalObject
);
7868 // Now that we know what our window is, we can flush the CSP errors to the
7869 // Web Console. We are flushing all messages that occurred and were stored in
7870 // the queue prior to this point.
7872 static_cast<nsCSPContext
*>(mCSP
.get())->flushConsoleMessages();
7875 nsCOMPtr
<nsIHttpChannelInternal
> internalChannel
=
7876 do_QueryInterface(GetChannel());
7877 if (internalChannel
) {
7878 nsCOMArray
<nsISecurityConsoleMessage
> messages
;
7879 DebugOnly
<nsresult
> rv
= internalChannel
->TakeAllSecurityMessages(messages
);
7880 MOZ_ASSERT(NS_SUCCEEDED(rv
));
7881 SendToConsole(messages
);
7884 // Set our visibility state, but do not fire the event. This is correct
7885 // because either we're coming out of bfcache (in which case IsVisible() will
7886 // still test false at this point and no state change will happen) or we're
7887 // doing the initial document load and don't want to fire the event for this
7890 // When the visibility is changed, notify it to observers.
7891 // Some observers need the notification, for example HTMLMediaElement uses
7892 // it to update internal media resource allocation.
7893 // When video is loaded via VideoDocument, HTMLMediaElement and MediaDecoder
7894 // creation are already done before Document::SetScriptGlobalObject() call.
7895 // MediaDecoder decides whether starting decoding is decided based on
7896 // document's visibility. When the MediaDecoder is created,
7897 // Document::SetScriptGlobalObject() is not yet called and document is
7898 // hidden state. Therefore the MediaDecoder decides that decoding is
7899 // not yet necessary. But soon after Document::SetScriptGlobalObject()
7900 // call, the document becomes not hidden. At the time, MediaDecoder needs
7901 // to know it and needs to start updating decoding.
7902 UpdateVisibilityState(DispatchVisibilityChange::No
);
7904 // The global in the template contents owner document should be the same.
7905 if (mTemplateContentsOwner
&& mTemplateContentsOwner
!= this) {
7906 mTemplateContentsOwner
->SetScriptGlobalObject(aScriptGlobalObject
);
7909 // Tell the script loader about the new global object.
7910 if (mScriptLoader
&& !IsTemplateContentsOwner()) {
7911 mScriptLoader
->SetGlobalObject(mScriptGlobalObject
);
7914 if (!mMaybeServiceWorkerControlled
&& mDocumentContainer
&&
7915 mScriptGlobalObject
&& GetChannel()) {
7916 // If we are shift-reloaded, don't associate with a ServiceWorker.
7917 if (mDocumentContainer
->IsForceReloading()) {
7918 NS_WARNING("Page was shift reloaded, skipping ServiceWorker control");
7922 mMaybeServiceWorkerControlled
= true;
7926 nsIScriptGlobalObject
* Document::GetScriptHandlingObjectInternal() const {
7927 MOZ_ASSERT(!mScriptGlobalObject
,
7928 "Do not call this when mScriptGlobalObject is set!");
7929 if (mHasHadDefaultView
) {
7933 nsCOMPtr
<nsIScriptGlobalObject
> scriptHandlingObject
=
7934 do_QueryReferent(mScopeObject
);
7935 nsCOMPtr
<nsPIDOMWindowInner
> win
= do_QueryInterface(scriptHandlingObject
);
7937 nsPIDOMWindowOuter
* outer
= win
->GetOuterWindow();
7938 if (!outer
|| outer
->GetCurrentInnerWindow() != win
) {
7939 NS_WARNING("Wrong inner/outer window combination!");
7943 return scriptHandlingObject
;
7945 void Document::SetScriptHandlingObject(nsIScriptGlobalObject
* aScriptObject
) {
7946 NS_ASSERTION(!mScriptGlobalObject
|| mScriptGlobalObject
== aScriptObject
,
7947 "Wrong script object!");
7948 if (aScriptObject
) {
7949 SetScopeObject(aScriptObject
);
7950 mHasHadDefaultView
= false;
7954 nsPIDOMWindowOuter
* Document::GetWindowInternal() const {
7955 MOZ_ASSERT(!mWindow
, "This should not be called when mWindow is not null!");
7956 // Let's use mScriptGlobalObject. Even if the document is already removed from
7957 // the docshell, the outer window might be still obtainable from the it.
7958 nsCOMPtr
<nsPIDOMWindowOuter
> win
;
7959 if (mRemovedFromDocShell
) {
7960 // The docshell returns the outer window we are done.
7961 nsCOMPtr
<nsIDocShell
> kungFuDeathGrip(mDocumentContainer
);
7962 if (kungFuDeathGrip
) {
7963 win
= kungFuDeathGrip
->GetWindow();
7966 if (nsCOMPtr
<nsPIDOMWindowInner
> inner
=
7967 do_QueryInterface(mScriptGlobalObject
)) {
7968 // mScriptGlobalObject is always the inner window, let's get the outer.
7969 win
= inner
->GetOuterWindow();
7976 bool Document::InternalAllowXULXBL() {
7977 if (nsContentUtils::AllowXULXBLForPrincipal(NodePrincipal())) {
7978 mAllowXULXBL
= eTriTrue
;
7982 mAllowXULXBL
= eTriFalse
;
7986 // Note: We don't hold a reference to the document observer; we assume
7987 // that it has a live reference to the document.
7988 void Document::AddObserver(nsIDocumentObserver
* aObserver
) {
7989 NS_ASSERTION(mObservers
.IndexOf(aObserver
) == nsTArray
<int>::NoIndex
,
7990 "Observer already in the list");
7991 mObservers
.AppendElement(aObserver
);
7992 AddMutationObserver(aObserver
);
7995 bool Document::RemoveObserver(nsIDocumentObserver
* aObserver
) {
7996 // If we're in the process of destroying the document (and we're
7997 // informing the observers of the destruction), don't remove the
7998 // observers from the list. This is not a big deal, since we
7999 // don't hold a live reference to the observers.
8000 if (!mInDestructor
) {
8001 RemoveMutationObserver(aObserver
);
8002 return mObservers
.RemoveElement(aObserver
);
8005 return mObservers
.Contains(aObserver
);
8008 void Document::BeginUpdate() {
8010 nsContentUtils::AddScriptBlocker();
8011 NS_DOCUMENT_NOTIFY_OBSERVERS(BeginUpdate
, (this));
8014 void Document::EndUpdate() {
8015 const bool reset
= !mPendingMaybeEditingStateChanged
;
8016 mPendingMaybeEditingStateChanged
= true;
8018 NS_DOCUMENT_NOTIFY_OBSERVERS(EndUpdate
, (this));
8022 nsContentUtils::RemoveScriptBlocker();
8024 if (mXULBroadcastManager
) {
8025 mXULBroadcastManager
->MaybeBroadcast();
8029 mPendingMaybeEditingStateChanged
= false;
8031 MaybeEditingStateChanged();
8034 void Document::BeginLoad() {
8035 if (IsEditingOn()) {
8036 // Reset() blows away all event listeners in the document, and our
8037 // editor relies heavily on those. Midas is turned on, to make it
8038 // work, re-initialize it to give it a chance to add its event
8042 EditingStateChanged();
8045 MOZ_ASSERT(!mDidCallBeginLoad
);
8046 mDidCallBeginLoad
= true;
8048 // Block onload here to prevent having to deal with blocking and
8049 // unblocking it while we know the document is loading.
8051 mDidFireDOMContentLoaded
= false;
8052 BlockDOMContentLoaded();
8054 if (mScriptLoader
) {
8055 mScriptLoader
->BeginDeferringScripts();
8058 NS_DOCUMENT_NOTIFY_OBSERVERS(BeginLoad
, (this));
8061 void Document::MozSetImageElement(const nsAString
& aImageElementId
,
8062 Element
* aElement
) {
8063 if (aImageElementId
.IsEmpty()) return;
8065 // Hold a script blocker while calling SetImageElement since that can call
8066 // out to id-observers
8067 nsAutoScriptBlocker scriptBlocker
;
8069 IdentifierMapEntry
* entry
= mIdentifierMap
.PutEntry(aImageElementId
);
8071 entry
->SetImageElement(aElement
);
8072 if (entry
->IsEmpty()) {
8073 mIdentifierMap
.RemoveEntry(entry
);
8078 void Document::DispatchContentLoadedEvents() {
8079 // If you add early returns from this method, make sure you're
8080 // calling UnblockOnload properly.
8082 // Unpin references to preloaded images
8083 mPreloadingImages
.Clear();
8085 // DOM manipulation after content loaded should not care if the element
8086 // came from the preloader.
8087 mPreloadedPreconnects
.Clear();
8090 mTiming
->NotifyDOMContentLoadedStart(Document::GetDocumentURI());
8093 // Dispatch observer notification to notify observers document is interactive.
8094 nsCOMPtr
<nsIObserverService
> os
= mozilla::services::GetObserverService();
8096 nsIPrincipal
* principal
= NodePrincipal();
8097 os
->NotifyObservers(ToSupports(this),
8098 principal
->IsSystemPrincipal()
8099 ? "chrome-document-interactive"
8100 : "content-document-interactive",
8104 // Fire a DOM event notifying listeners that this document has been
8105 // loaded (excluding images and other loads initiated by this
8107 nsContentUtils::DispatchTrustedEvent(this, this, u
"DOMContentLoaded"_ns
,
8108 CanBubble::eYes
, Cancelable::eNo
);
8110 if (auto* const window
= GetInnerWindow()) {
8111 const RefPtr
<ServiceWorkerContainer
> serviceWorker
=
8112 window
->Navigator()->ServiceWorker();
8114 // This could cause queued messages from a service worker to get
8115 // dispatched on serviceWorker.
8116 serviceWorker
->StartMessages();
8119 if (MayStartLayout()) {
8120 MaybeResolveReadyForIdle();
8124 mTiming
->NotifyDOMContentLoadedEnd(Document::GetDocumentURI());
8127 // If this document is a [i]frame, fire a DOMFrameContentLoaded
8128 // event on all parent documents notifying that the HTML (excluding
8129 // other external files such as images and stylesheets) in a frame
8130 // has finished loading.
8132 // target_frame is the [i]frame element that will be used as the
8133 // target for the event. It's the [i]frame whose content is done
8135 nsCOMPtr
<Element
> target_frame
= GetEmbedderElement();
8137 if (target_frame
&& target_frame
->IsInComposedDoc()) {
8138 nsCOMPtr
<Document
> parent
= target_frame
->OwnerDoc();
8140 RefPtr
<Event
> event
;
8142 IgnoredErrorResult ignored
;
8143 event
= parent
->CreateEvent(u
"Events"_ns
, CallerType::System
, ignored
);
8147 event
->InitEvent(u
"DOMFrameContentLoaded"_ns
, true, true);
8149 event
->SetTarget(target_frame
);
8150 event
->SetTrusted(true);
8152 // To dispatch this event we must manually call
8153 // EventDispatcher::Dispatch() on the ancestor document since the
8154 // target is not in the same document, so the event would never reach
8155 // the ancestor document if we used the normal event
8156 // dispatching code.
8158 WidgetEvent
* innerEvent
= event
->WidgetEventPtr();
8160 nsEventStatus status
= nsEventStatus_eIgnore
;
8162 if (RefPtr
<nsPresContext
> context
= parent
->GetPresContext()) {
8163 EventDispatcher::Dispatch(parent
, context
, innerEvent
, event
,
8169 parent
= parent
->GetInProcessParentDocument();
8173 nsPIDOMWindowInner
* inner
= GetInnerWindow();
8175 inner
->NoteDOMContentLoaded();
8179 if (mMaybeServiceWorkerControlled
) {
8180 using mozilla::dom::ServiceWorkerManager
;
8181 RefPtr
<ServiceWorkerManager
> swm
= ServiceWorkerManager::GetInstance();
8183 Maybe
<ClientInfo
> clientInfo
= GetClientInfo();
8184 if (clientInfo
.isSome()) {
8185 swm
->MaybeCheckNavigationUpdate(clientInfo
.ref());
8190 if (mSetCompleteAfterDOMContentLoaded
) {
8191 SetReadyStateInternal(ReadyState::READYSTATE_COMPLETE
);
8192 mSetCompleteAfterDOMContentLoaded
= false;
8195 UnblockOnload(true);
8198 void Document::EndLoad() {
8199 bool turnOnEditing
=
8200 mParser
&& (IsInDesignMode() || mContentEditableCount
> 0);
8203 // only assert if nothing stopped the load on purpose
8204 if (!mParserAborted
) {
8205 nsContentSecurityUtils::AssertAboutPageHasCSP(this);
8209 // EndLoad may have been called without a matching call to BeginLoad, in the
8210 // case of a failed parse (for example, due to timeout). In such a case, we
8211 // still want to execute part of this code to do appropriate cleanup, but we
8212 // gate part of it because it is intended to match 1-for-1 with calls to
8213 // BeginLoad. We have an explicit flag bit for this purpose, since it's
8214 // complicated and error prone to derive this condition from other related
8215 // flags that can be manipulated outside of a BeginLoad/EndLoad pair.
8217 // Part 1: Code that always executes to cleanup end of parsing, whether
8218 // that parsing was successful or not.
8220 // Drop the ref to our parser, if any, but keep hold of the sink so that we
8221 // can flush it from FlushPendingNotifications as needed. We might have to
8222 // do that to get a StartLayout() to happen.
8224 mWeakSink
= do_GetWeakReference(mParser
->GetContentSink());
8228 // Update the attributes on the PerformanceNavigationTiming before notifying
8229 // the onload observers.
8230 if (nsPIDOMWindowInner
* window
= GetInnerWindow()) {
8231 if (RefPtr
<Performance
> performance
= window
->GetPerformance()) {
8232 performance
->UpdateNavigationTimingEntry();
8236 NS_DOCUMENT_NOTIFY_OBSERVERS(EndLoad
, (this));
8238 // Part 2: Code that only executes when this EndLoad matches a BeginLoad.
8240 if (!mDidCallBeginLoad
) {
8243 mDidCallBeginLoad
= false;
8245 UnblockDOMContentLoaded();
8247 if (turnOnEditing
) {
8248 EditingStateChanged();
8252 // This is a document that's not in a window. For example, this could be an
8253 // XMLHttpRequest responseXML document, or a document created via DOMParser
8254 // or DOMImplementation. We don't reach this code normally for such
8255 // documents (which is not obviously correct), but can reach it via
8256 // document.open()/document.close().
8258 // Such documents don't fire load events, but per spec should set their
8259 // readyState to "complete" when parsing and all loading of subresources is
8260 // done. Parsing is done now, and documents not in a window don't load
8261 // subresources, so just go ahead and mark ourselves as complete.
8262 SetReadyStateInternal(Document::READYSTATE_COMPLETE
,
8263 /* updateTimingInformation = */ false);
8265 // Reset mSkipLoadEventAfterClose just in case.
8266 mSkipLoadEventAfterClose
= false;
8270 void Document::UnblockDOMContentLoaded() {
8271 MOZ_ASSERT(mBlockDOMContentLoaded
);
8272 if (--mBlockDOMContentLoaded
!= 0 || mDidFireDOMContentLoaded
) {
8276 MOZ_LOG(gDocumentLeakPRLog
, LogLevel::Debug
,
8277 ("DOCUMENT %p UnblockDOMContentLoaded", this));
8279 mDidFireDOMContentLoaded
= true;
8280 if (PresShell
* presShell
= GetPresShell()) {
8281 presShell
->GetRefreshDriver()->NotifyDOMContentLoaded();
8284 MOZ_ASSERT(mReadyState
== READYSTATE_INTERACTIVE
);
8285 if (!mSynchronousDOMContentLoaded
) {
8286 MOZ_RELEASE_ASSERT(NS_IsMainThread());
8287 nsCOMPtr
<nsIRunnable
> ev
=
8288 NewRunnableMethod("Document::DispatchContentLoadedEvents", this,
8289 &Document::DispatchContentLoadedEvents
);
8290 Dispatch(ev
.forget());
8292 DispatchContentLoadedEvents();
8296 void Document::ElementStateChanged(Element
* aElement
, ElementState aStateMask
) {
8297 MOZ_ASSERT(!nsContentUtils::IsSafeToRunScript(),
8298 "Someone forgot a scriptblocker");
8299 NS_DOCUMENT_NOTIFY_OBSERVERS(ElementStateChanged
,
8300 (this, aElement
, aStateMask
));
8303 void Document::RuleChanged(StyleSheet
& aSheet
, css::Rule
*,
8304 StyleRuleChangeKind
) {
8305 if (aSheet
.IsApplicable()) {
8306 ApplicableStylesChanged();
8310 void Document::RuleAdded(StyleSheet
& aSheet
, css::Rule
& aRule
) {
8311 if (aRule
.IsIncompleteImportRule()) {
8315 if (aSheet
.IsApplicable()) {
8316 ApplicableStylesChanged();
8320 void Document::ImportRuleLoaded(dom::CSSImportRule
& aRule
, StyleSheet
& aSheet
) {
8321 if (aSheet
.IsApplicable()) {
8322 ApplicableStylesChanged();
8326 void Document::RuleRemoved(StyleSheet
& aSheet
, css::Rule
& aRule
) {
8327 if (aSheet
.IsApplicable()) {
8328 ApplicableStylesChanged();
8332 static Element
* GetCustomContentContainer(PresShell
* aPresShell
) {
8333 if (!aPresShell
|| !aPresShell
->GetCanvasFrame()) {
8337 return aPresShell
->GetCanvasFrame()->GetCustomContentContainer();
8340 already_AddRefed
<AnonymousContent
> Document::InsertAnonymousContent(
8341 bool aForce
, ErrorResult
& aRv
) {
8342 RefPtr
<PresShell
> shell
= GetPresShell();
8343 if (aForce
&& !GetCustomContentContainer(shell
)) {
8344 FlushPendingNotifications(FlushType::Layout
);
8345 shell
= GetPresShell();
8348 nsAutoScriptBlocker scriptBlocker
;
8350 RefPtr
<AnonymousContent
> anonContent
= AnonymousContent::Create(*this);
8352 aRv
.Throw(NS_ERROR_OUT_OF_MEMORY
);
8356 mAnonymousContents
.AppendElement(anonContent
);
8358 if (RefPtr
<Element
> container
= GetCustomContentContainer(shell
)) {
8359 // If the container is empty and we have other anon content we should be
8360 // about to show all the other anonymous content nodes.
8361 if (container
->HasChildren() || mAnonymousContents
.Length() == 1) {
8362 container
->AppendChildTo(anonContent
->Host(), true, IgnoreErrors());
8363 if (auto* canvasFrame
= shell
->GetCanvasFrame()) {
8364 canvasFrame
->ShowCustomContentContainer();
8369 return anonContent
.forget();
8372 static void RemoveAnonContentFromCanvas(AnonymousContent
& aAnonContent
,
8373 PresShell
* aPresShell
) {
8374 RefPtr
<Element
> container
= GetCustomContentContainer(aPresShell
);
8378 container
->RemoveChild(*aAnonContent
.Host(), IgnoreErrors());
8381 void Document::RemoveAnonymousContent(AnonymousContent
& aContent
) {
8382 nsAutoScriptBlocker scriptBlocker
;
8384 auto index
= mAnonymousContents
.IndexOf(&aContent
);
8385 if (index
== mAnonymousContents
.NoIndex
) {
8389 mAnonymousContents
.RemoveElementAt(index
);
8390 RemoveAnonContentFromCanvas(aContent
, GetPresShell());
8392 if (mAnonymousContents
.IsEmpty() &&
8393 GetCustomContentContainer(GetPresShell())) {
8394 GetPresShell()->GetCanvasFrame()->HideCustomContentContainer();
8398 Element
* Document::GetAnonRootIfInAnonymousContentContainer(
8399 nsINode
* aNode
) const {
8400 if (!aNode
->IsInNativeAnonymousSubtree()) {
8404 PresShell
* presShell
= GetPresShell();
8405 if (!presShell
|| !presShell
->GetCanvasFrame()) {
8409 nsAutoScriptBlocker scriptBlocker
;
8410 nsCOMPtr
<Element
> customContainer
=
8411 presShell
->GetCanvasFrame()->GetCustomContentContainer();
8412 if (!customContainer
) {
8416 // An arbitrary number of elements can be inserted as children of the custom
8417 // container frame. We want the one that was added that contains aNode, so
8418 // we need to keep track of the last child separately using |child| here.
8419 nsINode
* child
= aNode
;
8420 nsINode
* parent
= aNode
->GetParentNode();
8421 while (parent
&& parent
->IsInNativeAnonymousSubtree()) {
8422 if (parent
== customContainer
) {
8423 return Element::FromNode(child
);
8426 parent
= child
->GetParentNode();
8431 Maybe
<ClientInfo
> Document::GetClientInfo() const {
8432 if (const Document
* orig
= GetOriginalDocument()) {
8433 if (Maybe
<ClientInfo
> info
= orig
->GetClientInfo()) {
8438 if (nsPIDOMWindowInner
* inner
= GetInnerWindow()) {
8439 return inner
->GetClientInfo();
8442 return Maybe
<ClientInfo
>();
8445 Maybe
<ClientState
> Document::GetClientState() const {
8446 if (const Document
* orig
= GetOriginalDocument()) {
8447 if (Maybe
<ClientState
> state
= orig
->GetClientState()) {
8452 if (nsPIDOMWindowInner
* inner
= GetInnerWindow()) {
8453 return inner
->GetClientState();
8456 return Maybe
<ClientState
>();
8459 Maybe
<ServiceWorkerDescriptor
> Document::GetController() const {
8460 if (const Document
* orig
= GetOriginalDocument()) {
8461 if (Maybe
<ServiceWorkerDescriptor
> controller
= orig
->GetController()) {
8466 if (nsPIDOMWindowInner
* inner
= GetInnerWindow()) {
8467 return inner
->GetController();
8470 return Maybe
<ServiceWorkerDescriptor
>();
8474 // Document interface
8476 DocumentType
* Document::GetDoctype() const {
8477 for (nsIContent
* child
= GetFirstChild(); child
;
8478 child
= child
->GetNextSibling()) {
8479 if (child
->NodeType() == DOCUMENT_TYPE_NODE
) {
8480 return static_cast<DocumentType
*>(child
);
8486 DOMImplementation
* Document::GetImplementation(ErrorResult
& rv
) {
8487 if (!mDOMImplementation
) {
8488 nsCOMPtr
<nsIURI
> uri
;
8489 NS_NewURI(getter_AddRefs(uri
), "about:blank");
8491 rv
.Throw(NS_ERROR_OUT_OF_MEMORY
);
8494 bool hasHadScriptObject
= true;
8495 nsIScriptGlobalObject
* scriptObject
=
8496 GetScriptHandlingObject(hasHadScriptObject
);
8497 if (!scriptObject
&& hasHadScriptObject
) {
8498 rv
.Throw(NS_ERROR_UNEXPECTED
);
8501 mDOMImplementation
= new DOMImplementation(
8502 this, scriptObject
? scriptObject
: GetScopeObject(), uri
, uri
);
8505 return mDOMImplementation
;
8508 bool IsLowercaseASCII(const nsAString
& aValue
) {
8509 int32_t len
= aValue
.Length();
8510 for (int32_t i
= 0; i
< len
; ++i
) {
8511 char16_t c
= aValue
[i
];
8512 if (!(0x0061 <= (c
) && ((c
) <= 0x007a))) {
8519 already_AddRefed
<Element
> Document::CreateElement(
8520 const nsAString
& aTagName
, const ElementCreationOptionsOrString
& aOptions
,
8522 rv
= nsContentUtils::CheckQName(aTagName
, false);
8527 bool needsLowercase
= IsHTMLDocument() && !IsLowercaseASCII(aTagName
);
8528 nsAutoString lcTagName
;
8529 if (needsLowercase
) {
8530 nsContentUtils::ASCIIToLower(aTagName
, lcTagName
);
8533 const nsString
* is
= nullptr;
8534 PseudoStyleType pseudoType
= PseudoStyleType::NotPseudo
;
8535 if (aOptions
.IsElementCreationOptions()) {
8536 const ElementCreationOptions
& options
=
8537 aOptions
.GetAsElementCreationOptions();
8539 if (options
.mIs
.WasPassed()) {
8540 is
= &options
.mIs
.Value();
8543 // Check 'pseudo' and throw an exception if it's not one allowed
8544 // with CSS_PSEUDO_ELEMENT_IS_JS_CREATED_NAC.
8545 if (options
.mPseudo
.WasPassed()) {
8546 Maybe
<PseudoStyleType
> type
=
8547 nsCSSPseudoElements::GetPseudoType(options
.mPseudo
.Value());
8548 if (!type
|| *type
== PseudoStyleType::NotPseudo
||
8549 !nsCSSPseudoElements::PseudoElementIsJSCreatedNAC(*type
)) {
8550 rv
.ThrowNotSupportedError("Invalid pseudo-element");
8557 RefPtr
<Element
> elem
= CreateElem(needsLowercase
? lcTagName
: aTagName
,
8558 nullptr, mDefaultElementType
, is
);
8560 if (pseudoType
!= PseudoStyleType::NotPseudo
) {
8561 elem
->SetPseudoElementType(pseudoType
);
8564 return elem
.forget();
8567 already_AddRefed
<Element
> Document::CreateElementNS(
8568 const nsAString
& aNamespaceURI
, const nsAString
& aQualifiedName
,
8569 const ElementCreationOptionsOrString
& aOptions
, ErrorResult
& rv
) {
8570 RefPtr
<mozilla::dom::NodeInfo
> nodeInfo
;
8571 rv
= nsContentUtils::GetNodeInfoFromQName(aNamespaceURI
, aQualifiedName
,
8572 mNodeInfoManager
, ELEMENT_NODE
,
8573 getter_AddRefs(nodeInfo
));
8578 const nsString
* is
= nullptr;
8579 if (aOptions
.IsElementCreationOptions()) {
8580 const ElementCreationOptions
& options
=
8581 aOptions
.GetAsElementCreationOptions();
8582 if (options
.mIs
.WasPassed()) {
8583 is
= &options
.mIs
.Value();
8587 nsCOMPtr
<Element
> element
;
8588 rv
= NS_NewElement(getter_AddRefs(element
), nodeInfo
.forget(),
8589 NOT_FROM_PARSER
, is
);
8594 return element
.forget();
8597 already_AddRefed
<Element
> Document::CreateXULElement(
8598 const nsAString
& aTagName
, const ElementCreationOptionsOrString
& aOptions
,
8600 aRv
= nsContentUtils::CheckQName(aTagName
, false);
8605 const nsString
* is
= nullptr;
8606 if (aOptions
.IsElementCreationOptions()) {
8607 const ElementCreationOptions
& options
=
8608 aOptions
.GetAsElementCreationOptions();
8609 if (options
.mIs
.WasPassed()) {
8610 is
= &options
.mIs
.Value();
8614 RefPtr
<Element
> elem
= CreateElem(aTagName
, nullptr, kNameSpaceID_XUL
, is
);
8616 aRv
.Throw(NS_ERROR_NOT_AVAILABLE
);
8619 return elem
.forget();
8622 already_AddRefed
<nsTextNode
> Document::CreateEmptyTextNode() const {
8623 RefPtr
<nsTextNode
> text
= new (mNodeInfoManager
) nsTextNode(mNodeInfoManager
);
8624 return text
.forget();
8627 already_AddRefed
<nsTextNode
> Document::CreateTextNode(
8628 const nsAString
& aData
) const {
8629 RefPtr
<nsTextNode
> text
= new (mNodeInfoManager
) nsTextNode(mNodeInfoManager
);
8630 // Don't notify; this node is still being created.
8631 text
->SetText(aData
, false);
8632 return text
.forget();
8635 already_AddRefed
<DocumentFragment
> Document::CreateDocumentFragment() const {
8636 RefPtr
<DocumentFragment
> frag
=
8637 new (mNodeInfoManager
) DocumentFragment(mNodeInfoManager
);
8638 return frag
.forget();
8641 // Unfortunately, bareword "Comment" is ambiguous with some Mac system headers.
8642 already_AddRefed
<dom::Comment
> Document::CreateComment(
8643 const nsAString
& aData
) const {
8644 RefPtr
<dom::Comment
> comment
=
8645 new (mNodeInfoManager
) dom::Comment(mNodeInfoManager
);
8647 // Don't notify; this node is still being created.
8648 comment
->SetText(aData
, false);
8649 return comment
.forget();
8652 already_AddRefed
<CDATASection
> Document::CreateCDATASection(
8653 const nsAString
& aData
, ErrorResult
& rv
) {
8654 if (IsHTMLDocument()) {
8655 rv
.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR
);
8659 if (FindInReadable(u
"]]>"_ns
, aData
)) {
8660 rv
.Throw(NS_ERROR_DOM_INVALID_CHARACTER_ERR
);
8664 RefPtr
<CDATASection
> cdata
=
8665 new (mNodeInfoManager
) CDATASection(mNodeInfoManager
);
8667 // Don't notify; this node is still being created.
8668 cdata
->SetText(aData
, false);
8670 return cdata
.forget();
8673 already_AddRefed
<ProcessingInstruction
> Document::CreateProcessingInstruction(
8674 const nsAString
& aTarget
, const nsAString
& aData
, ErrorResult
& rv
) const {
8675 nsresult res
= nsContentUtils::CheckQName(aTarget
, false);
8676 if (NS_FAILED(res
)) {
8681 if (FindInReadable(u
"?>"_ns
, aData
)) {
8682 rv
.Throw(NS_ERROR_DOM_INVALID_CHARACTER_ERR
);
8686 RefPtr
<ProcessingInstruction
> pi
=
8687 NS_NewXMLProcessingInstruction(mNodeInfoManager
, aTarget
, aData
);
8692 already_AddRefed
<Attr
> Document::CreateAttribute(const nsAString
& aName
,
8694 if (!mNodeInfoManager
) {
8695 rv
.Throw(NS_ERROR_NOT_INITIALIZED
);
8699 nsresult res
= nsContentUtils::CheckQName(aName
, false);
8700 if (NS_FAILED(res
)) {
8706 if (IsHTMLDocument()) {
8707 nsContentUtils::ASCIIToLower(aName
, name
);
8712 RefPtr
<mozilla::dom::NodeInfo
> nodeInfo
;
8713 res
= mNodeInfoManager
->GetNodeInfo(name
, nullptr, kNameSpaceID_None
,
8714 ATTRIBUTE_NODE
, getter_AddRefs(nodeInfo
));
8715 if (NS_FAILED(res
)) {
8720 RefPtr
<Attr
> attribute
=
8721 new (mNodeInfoManager
) Attr(nullptr, nodeInfo
.forget(), u
""_ns
);
8722 return attribute
.forget();
8725 already_AddRefed
<Attr
> Document::CreateAttributeNS(
8726 const nsAString
& aNamespaceURI
, const nsAString
& aQualifiedName
,
8728 RefPtr
<mozilla::dom::NodeInfo
> nodeInfo
;
8729 rv
= nsContentUtils::GetNodeInfoFromQName(aNamespaceURI
, aQualifiedName
,
8730 mNodeInfoManager
, ATTRIBUTE_NODE
,
8731 getter_AddRefs(nodeInfo
));
8736 RefPtr
<Attr
> attribute
=
8737 new (mNodeInfoManager
) Attr(nullptr, nodeInfo
.forget(), u
""_ns
);
8738 return attribute
.forget();
8741 void Document::ScheduleForPresAttrEvaluation(Element
* aElement
) {
8742 MOZ_ASSERT(aElement
->IsInComposedDoc());
8743 DebugOnly
<bool> inserted
= mLazyPresElements
.EnsureInserted(aElement
);
8744 MOZ_ASSERT(inserted
);
8745 if (aElement
->HasServoData()) {
8746 // TODO(emilio): RESTYLE_SELF is too strong, there should be no need to
8747 // re-selector-match, but right now this is needed to pick up the new mapped
8748 // attributes. We need something like RESTYLE_STYLE_ATTRIBUTE but for mapped
8750 nsLayoutUtils::PostRestyleEvent(aElement
, RestyleHint::RESTYLE_SELF
,
8755 void Document::UnscheduleForPresAttrEvaluation(Element
* aElement
) {
8756 mLazyPresElements
.Remove(aElement
);
8759 void Document::DoResolveScheduledPresAttrs() {
8760 MOZ_ASSERT(!mLazyPresElements
.IsEmpty());
8761 for (Element
* el
: mLazyPresElements
) {
8762 MOZ_ASSERT(el
->IsInComposedDoc(),
8763 "Un-schedule when removing from the document");
8764 MOZ_ASSERT(el
->IsPendingMappedAttributeEvaluation());
8765 if (auto* svg
= SVGElement::FromNode(el
)) {
8766 // SVG does its own (very similar) thing, for now at least.
8767 svg
->UpdateMappedDeclarationBlock();
8769 MappedDeclarationsBuilder
builder(*el
, *this,
8770 el
->GetMappedAttributeStyle());
8771 auto function
= el
->GetAttributeMappingFunction();
8773 el
->SetMappedDeclarationBlock(builder
.TakeDeclarationBlock());
8775 MOZ_ASSERT(!el
->IsPendingMappedAttributeEvaluation());
8777 mLazyPresElements
.Clear();
8780 already_AddRefed
<nsSimpleContentList
> Document::BlockedNodesByClassifier()
8782 RefPtr
<nsSimpleContentList
> list
= new nsSimpleContentList(nullptr);
8784 for (const nsWeakPtr
& weakNode
: mBlockedNodesByClassifier
) {
8785 if (nsCOMPtr
<nsIContent
> node
= do_QueryReferent(weakNode
)) {
8786 // Consider only nodes to which we have managed to get strong references.
8787 // Coping with nullptrs since it's expected for nodes to disappear when
8788 // nobody else is referring to them.
8789 list
->AppendElement(node
);
8793 return list
.forget();
8796 void Document::GetSelectedStyleSheetSet(nsAString
& aSheetSet
) {
8797 aSheetSet
.Truncate();
8799 // Look through our sheets, find the selected set title
8800 size_t count
= SheetCount();
8802 for (size_t index
= 0; index
< count
; index
++) {
8803 StyleSheet
* sheet
= SheetAt(index
);
8804 NS_ASSERTION(sheet
, "Null sheet in sheet list!");
8806 if (sheet
->Disabled()) {
8807 // Disabled sheets don't affect the currently selected set
8811 sheet
->GetTitle(title
);
8813 if (aSheetSet
.IsEmpty()) {
8815 } else if (!title
.IsEmpty() && !aSheetSet
.Equals(title
)) {
8816 // Sheets from multiple sets enabled; return null string, per spec.
8817 SetDOMStringToNull(aSheetSet
);
8823 void Document::SetSelectedStyleSheetSet(const nsAString
& aSheetSet
) {
8824 if (DOMStringIsNull(aSheetSet
)) {
8828 // Must update mLastStyleSheetSet before doing anything else with stylesheets
8830 mLastStyleSheetSet
= aSheetSet
;
8831 EnableStyleSheetsForSetInternal(aSheetSet
, true);
8834 void Document::SetPreferredStyleSheetSet(const nsAString
& aSheetSet
) {
8835 mPreferredStyleSheetSet
= aSheetSet
;
8836 // Only mess with our stylesheets if we don't have a lastStyleSheetSet, per
8838 if (DOMStringIsNull(mLastStyleSheetSet
)) {
8839 // Calling EnableStyleSheetsForSetInternal, not SetSelectedStyleSheetSet,
8840 // per spec. The idea here is that we're changing our preferred set and
8841 // that shouldn't change the value of lastStyleSheetSet. Also, we're
8842 // using the Internal version so we can update the CSSLoader and not have
8843 // to worry about null strings.
8844 EnableStyleSheetsForSetInternal(aSheetSet
, true);
8848 DOMStringList
* Document::StyleSheetSets() {
8849 if (!mStyleSheetSetList
) {
8850 mStyleSheetSetList
= new DOMStyleSheetSetList(this);
8852 return mStyleSheetSetList
;
8855 void Document::EnableStyleSheetsForSet(const nsAString
& aSheetSet
) {
8856 // Per spec, passing in null is a no-op.
8857 if (!DOMStringIsNull(aSheetSet
)) {
8858 // Note: must make sure to not change the CSSLoader's preferred sheet --
8859 // that value should be equal to either our lastStyleSheetSet (if that's
8860 // non-null) or to our preferredStyleSheetSet. And this method doesn't
8861 // change either of those.
8862 EnableStyleSheetsForSetInternal(aSheetSet
, false);
8866 void Document::EnableStyleSheetsForSetInternal(const nsAString
& aSheetSet
,
8867 bool aUpdateCSSLoader
) {
8868 size_t count
= SheetCount();
8870 for (size_t index
= 0; index
< count
; index
++) {
8871 StyleSheet
* sheet
= SheetAt(index
);
8872 NS_ASSERTION(sheet
, "Null sheet in sheet list!");
8874 sheet
->GetTitle(title
);
8875 if (!title
.IsEmpty()) {
8876 sheet
->SetEnabled(title
.Equals(aSheetSet
));
8879 if (aUpdateCSSLoader
) {
8880 CSSLoader()->DocumentStyleSheetSetChanged();
8882 if (EnsureStyleSet().StyleSheetsHaveChanged()) {
8883 ApplicableStylesChanged();
8887 void Document::GetCharacterSet(nsAString
& aCharacterSet
) const {
8888 nsAutoCString charset
;
8889 GetDocumentCharacterSet()->Name(charset
);
8890 CopyASCIItoUTF16(charset
, aCharacterSet
);
8893 already_AddRefed
<nsINode
> Document::ImportNode(nsINode
& aNode
, bool aDeep
,
8894 ErrorResult
& rv
) const {
8895 nsINode
* imported
= &aNode
;
8897 switch (imported
->NodeType()) {
8898 case DOCUMENT_NODE
: {
8901 case DOCUMENT_FRAGMENT_NODE
:
8902 case ATTRIBUTE_NODE
:
8904 case PROCESSING_INSTRUCTION_NODE
:
8906 case CDATA_SECTION_NODE
:
8908 case DOCUMENT_TYPE_NODE
: {
8909 return imported
->Clone(aDeep
, mNodeInfoManager
, rv
);
8912 NS_WARNING("Don't know how to clone this nodetype for importNode.");
8916 rv
.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR
);
8920 already_AddRefed
<nsRange
> Document::CreateRange(ErrorResult
& rv
) {
8921 return nsRange::Create(this, 0, this, 0, rv
);
8924 already_AddRefed
<NodeIterator
> Document::CreateNodeIterator(
8925 nsINode
& aRoot
, uint32_t aWhatToShow
, NodeFilter
* aFilter
,
8926 ErrorResult
& rv
) const {
8927 RefPtr
<NodeIterator
> iterator
=
8928 new NodeIterator(&aRoot
, aWhatToShow
, aFilter
);
8929 return iterator
.forget();
8932 already_AddRefed
<TreeWalker
> Document::CreateTreeWalker(nsINode
& aRoot
,
8933 uint32_t aWhatToShow
,
8934 NodeFilter
* aFilter
,
8935 ErrorResult
& rv
) const {
8936 RefPtr
<TreeWalker
> walker
= new TreeWalker(&aRoot
, aWhatToShow
, aFilter
);
8937 return walker
.forget();
8940 already_AddRefed
<Location
> Document::GetLocation() const {
8941 nsCOMPtr
<nsPIDOMWindowInner
> w
= do_QueryInterface(mScriptGlobalObject
);
8947 return do_AddRef(w
->Location());
8950 already_AddRefed
<nsIURI
> Document::GetDomainURI() {
8951 nsIPrincipal
* principal
= NodePrincipal();
8953 nsCOMPtr
<nsIURI
> uri
;
8954 principal
->GetDomain(getter_AddRefs(uri
));
8956 return uri
.forget();
8958 auto* basePrin
= BasePrincipal::Cast(principal
);
8959 basePrin
->GetURI(getter_AddRefs(uri
));
8960 return uri
.forget();
8963 void Document::GetDomain(nsAString
& aDomain
) {
8964 nsCOMPtr
<nsIURI
> uri
= GetDomainURI();
8971 nsAutoCString hostName
;
8972 nsresult rv
= nsContentUtils::GetHostOrIPv6WithBrackets(uri
, hostName
);
8973 if (NS_SUCCEEDED(rv
)) {
8974 CopyUTF8toUTF16(hostName
, aDomain
);
8976 // If we can't get the host from the URI (e.g. about:, javascript:,
8977 // etc), just return an empty string.
8982 void Document::SetDomain(const nsAString
& aDomain
, ErrorResult
& rv
) {
8983 if (!GetBrowsingContext()) {
8984 // If our browsing context is null; disallow setting domain
8985 rv
.Throw(NS_ERROR_DOM_SECURITY_ERR
);
8989 if (mSandboxFlags
& SANDBOXED_DOMAIN
) {
8990 // We're sandboxed; disallow setting domain
8991 rv
.Throw(NS_ERROR_DOM_SECURITY_ERR
);
8995 if (!FeaturePolicyUtils::IsFeatureAllowed(this, u
"document-domain"_ns
)) {
8996 rv
.Throw(NS_ERROR_DOM_SECURITY_ERR
);
9000 if (aDomain
.IsEmpty()) {
9001 rv
.Throw(NS_ERROR_DOM_SECURITY_ERR
);
9005 nsCOMPtr
<nsIURI
> uri
= GetDomainURI();
9007 rv
.Throw(NS_ERROR_FAILURE
);
9011 // Check new domain - must be a superdomain of the current host
9012 // For example, a page from foo.bar.com may set domain to bar.com,
9013 // but not to ar.com, baz.com, or fi.foo.bar.com.
9015 nsCOMPtr
<nsIURI
> newURI
= RegistrableDomainSuffixOfInternal(aDomain
, uri
);
9017 // Error: illegal domain
9018 rv
.Throw(NS_ERROR_DOM_SECURITY_ERR
);
9022 if (GetBrowsingContext()->Group()->IsPotentiallyCrossOriginIsolated()) {
9023 WarnOnceAbout(Document::eDocumentSetDomainNotAllowed
);
9027 MOZ_ALWAYS_SUCCEEDS(NodePrincipal()->SetDomain(newURI
));
9028 MOZ_ALWAYS_SUCCEEDS(PartitionedPrincipal()->SetDomain(newURI
));
9029 if (WindowGlobalChild
* wgc
= GetWindowGlobalChild()) {
9030 wgc
->SendSetDocumentDomain(WrapNotNull(newURI
));
9034 already_AddRefed
<nsIURI
> Document::CreateInheritingURIForHost(
9035 const nsACString
& aHostString
) {
9036 if (aHostString
.IsEmpty()) {
9041 nsCOMPtr
<nsIURI
> uri
= GetDomainURI();
9047 rv
= NS_MutateURI(uri
)
9049 .SetPort(-1) // we want to reset the port number if needed.
9050 .SetHostPort(aHostString
)
9052 if (NS_FAILED(rv
)) {
9056 return uri
.forget();
9059 already_AddRefed
<nsIURI
> Document::RegistrableDomainSuffixOfInternal(
9060 const nsAString
& aNewDomain
, nsIURI
* aOrigHost
) {
9061 if (NS_WARN_IF(!aOrigHost
)) {
9065 nsCOMPtr
<nsIURI
> newURI
=
9066 CreateInheritingURIForHost(NS_ConvertUTF16toUTF8(aNewDomain
));
9068 // Error: failed to parse input domain
9072 if (!IsValidDomain(aOrigHost
, newURI
)) {
9073 // Error: illegal domain
9077 nsAutoCString domain
;
9078 if (NS_FAILED(newURI
->GetAsciiHost(domain
))) {
9082 return CreateInheritingURIForHost(domain
);
9086 bool Document::IsValidDomain(nsIURI
* aOrigHost
, nsIURI
* aNewURI
) {
9087 // Check new domain - must be a superdomain of the current host
9088 // For example, a page from foo.bar.com may set domain to bar.com,
9089 // but not to ar.com, baz.com, or fi.foo.bar.com.
9090 nsAutoCString current
;
9091 nsAutoCString domain
;
9092 if (NS_FAILED(aOrigHost
->GetAsciiHost(current
))) {
9095 if (NS_FAILED(aNewURI
->GetAsciiHost(domain
))) {
9099 bool ok
= current
.Equals(domain
);
9100 if (current
.Length() > domain
.Length() && StringEndsWith(current
, domain
) &&
9101 current
.CharAt(current
.Length() - domain
.Length() - 1) == '.') {
9102 // We're golden if the new domain is the current page's base domain or a
9104 nsCOMPtr
<nsIEffectiveTLDService
> tldService
=
9105 do_GetService(NS_EFFECTIVETLDSERVICE_CONTRACTID
);
9110 nsAutoCString currentBaseDomain
;
9112 tldService
->GetBaseDomain(aOrigHost
, 0, currentBaseDomain
));
9113 NS_ASSERTION(StringEndsWith(domain
, currentBaseDomain
) ==
9114 (domain
.Length() >= currentBaseDomain
.Length()),
9115 "uh-oh! slight optimization wasn't valid somehow!");
9116 ok
= ok
&& domain
.Length() >= currentBaseDomain
.Length();
9122 Element
* Document::GetHtmlElement() const {
9123 Element
* rootElement
= GetRootElement();
9124 if (rootElement
&& rootElement
->IsHTMLElement(nsGkAtoms::html
))
9129 Element
* Document::GetHtmlChildElement(nsAtom
* aTag
) {
9130 Element
* html
= GetHtmlElement();
9131 if (!html
) return nullptr;
9133 // Look for the element with aTag inside html. This needs to run
9134 // forwards to find the first such element.
9135 for (nsIContent
* child
= html
->GetFirstChild(); child
;
9136 child
= child
->GetNextSibling()) {
9137 if (child
->IsHTMLElement(aTag
)) return child
->AsElement();
9142 nsGenericHTMLElement
* Document::GetBody() {
9143 Element
* html
= GetHtmlElement();
9148 for (nsIContent
* child
= html
->GetFirstChild(); child
;
9149 child
= child
->GetNextSibling()) {
9150 if (child
->IsHTMLElement(nsGkAtoms::body
) ||
9151 child
->IsHTMLElement(nsGkAtoms::frameset
)) {
9152 return static_cast<nsGenericHTMLElement
*>(child
);
9159 void Document::SetBody(nsGenericHTMLElement
* newBody
, ErrorResult
& rv
) {
9160 nsCOMPtr
<Element
> root
= GetRootElement();
9162 // The body element must be either a body tag or a frameset tag. And we must
9163 // have a root element to be able to add kids to it.
9165 !newBody
->IsAnyOfHTMLElements(nsGkAtoms::body
, nsGkAtoms::frameset
)) {
9166 rv
.ThrowHierarchyRequestError(
9167 "The new body must be either a body tag or frameset tag.");
9172 rv
.ThrowHierarchyRequestError("No root element.");
9176 // Use DOM methods so that we pass through the appropriate security checks.
9177 nsCOMPtr
<Element
> currentBody
= GetBody();
9179 root
->ReplaceChild(*newBody
, *currentBody
, rv
);
9181 root
->AppendChild(*newBody
, rv
);
9185 HTMLSharedElement
* Document::GetHead() {
9186 return static_cast<HTMLSharedElement
*>(GetHeadElement());
9189 Element
* Document::GetTitleElement() {
9190 // mMayHaveTitleElement will have been set to true if any HTML or SVG
9191 // <title> element has been bound to this document. So if it's false,
9192 // we know there is nothing to do here. This avoids us having to search
9193 // the whole DOM if someone calls document.title on a large document
9195 if (!mMayHaveTitleElement
) {
9199 Element
* root
= GetRootElement();
9200 if (root
&& root
->IsSVGElement(nsGkAtoms::svg
)) {
9201 // In SVG, the document's title must be a child
9202 for (nsIContent
* child
= root
->GetFirstChild(); child
;
9203 child
= child
->GetNextSibling()) {
9204 if (child
->IsSVGElement(nsGkAtoms::title
)) {
9205 return child
->AsElement();
9211 // We check the HTML namespace even for non-HTML documents, except SVG. This
9212 // matches the spec and the behavior of all tested browsers.
9213 for (nsINode
* node
= GetFirstChild(); node
; node
= node
->GetNextNode(this)) {
9214 if (node
->IsHTMLElement(nsGkAtoms::title
)) {
9215 return node
->AsElement();
9221 void Document::GetTitle(nsAString
& aTitle
) {
9224 Element
* rootElement
= GetRootElement();
9229 if (rootElement
->IsXULElement()) {
9230 rootElement
->GetAttr(nsGkAtoms::title
, aTitle
);
9231 } else if (Element
* title
= GetTitleElement()) {
9232 nsContentUtils::GetNodeTextContent(title
, false, aTitle
);
9237 aTitle
.CompressWhitespace();
9240 void Document::SetTitle(const nsAString
& aTitle
, ErrorResult
& aRv
) {
9241 Element
* rootElement
= GetRootElement();
9246 if (rootElement
->IsXULElement()) {
9248 rootElement
->SetAttr(kNameSpaceID_None
, nsGkAtoms::title
, aTitle
, true);
9252 Maybe
<mozAutoDocUpdate
> updateBatch
;
9253 nsCOMPtr
<Element
> title
= GetTitleElement();
9254 if (rootElement
->IsSVGElement(nsGkAtoms::svg
)) {
9256 // Batch updates so that mutation events don't change "the title
9257 // element" under us
9258 updateBatch
.emplace(this, true);
9259 RefPtr
<mozilla::dom::NodeInfo
> titleInfo
= mNodeInfoManager
->GetNodeInfo(
9260 nsGkAtoms::title
, nullptr, kNameSpaceID_SVG
, ELEMENT_NODE
);
9261 NS_NewSVGElement(getter_AddRefs(title
), titleInfo
.forget(),
9266 rootElement
->InsertChildBefore(title
, rootElement
->GetFirstChild(), true,
9269 } else if (rootElement
->IsHTMLElement()) {
9271 // Batch updates so that mutation events don't change "the title
9272 // element" under us
9273 updateBatch
.emplace(this, true);
9274 Element
* head
= GetHeadElement();
9279 RefPtr
<mozilla::dom::NodeInfo
> titleInfo
;
9280 titleInfo
= mNodeInfoManager
->GetNodeInfo(
9281 nsGkAtoms::title
, nullptr, kNameSpaceID_XHTML
, ELEMENT_NODE
);
9282 title
= NS_NewHTMLTitleElement(titleInfo
.forget());
9287 head
->AppendChildTo(title
, true, IgnoreErrors());
9293 aRv
= nsContentUtils::SetNodeTextContent(title
, aTitle
, false);
9296 class Document::TitleChangeEvent final
: public Runnable
{
9298 explicit TitleChangeEvent(Document
* aDoc
)
9299 : Runnable("Document::TitleChangeEvent"),
9303 mBlockOnload(aDoc
->IsInChromeDocShell())
9307 mDoc
->BlockOnload();
9311 NS_IMETHOD
Run() final
{
9315 const RefPtr
<Document
> doc
= mDoc
;
9316 const bool blockOnload
= mBlockOnload
;
9318 doc
->DoNotifyPossibleTitleChange();
9320 doc
->UnblockOnload(/* aFireSync = */ true);
9328 mDoc
->UnblockOnload(/* aFireSync = */ false);
9335 // Weak, caller is responsible for calling Revoke() when needed.
9337 // title changes should block the load event on chrome docshells, so that the
9338 // window title is consistently set by the time the top window is displayed.
9339 // Otherwise, some window manager integrations don't work properly,
9341 const bool mBlockOnload
= false;
9344 void Document::NotifyPossibleTitleChange(bool aBoundTitleElement
) {
9345 NS_ASSERTION(!mInUnlinkOrDeletion
|| !aBoundTitleElement
,
9346 "Setting a title while unlinking or destroying the element?");
9347 if (mInUnlinkOrDeletion
) {
9351 if (aBoundTitleElement
) {
9352 mMayHaveTitleElement
= true;
9355 if (mPendingTitleChangeEvent
.IsPending()) {
9359 MOZ_RELEASE_ASSERT(NS_IsMainThread());
9360 RefPtr
<TitleChangeEvent
> event
= new TitleChangeEvent(this);
9361 if (NS_WARN_IF(NS_FAILED(Dispatch(do_AddRef(event
))))) {
9365 mPendingTitleChangeEvent
= std::move(event
);
9368 void Document::DoNotifyPossibleTitleChange() {
9369 if (!mPendingTitleChangeEvent
.IsPending()) {
9372 // Make sure the pending runnable method is cleared.
9373 mPendingTitleChangeEvent
.Forget();
9374 mHaveFiredTitleChange
= true;
9379 if (RefPtr
<PresShell
> presShell
= GetPresShell()) {
9380 nsCOMPtr
<nsISupports
> container
=
9381 presShell
->GetPresContext()->GetContainerWeak();
9383 if (nsCOMPtr
<nsIBaseWindow
> docShellWin
= do_QueryInterface(container
)) {
9384 docShellWin
->SetTitle(title
);
9389 if (WindowGlobalChild
* child
= GetWindowGlobalChild()) {
9390 child
->SendUpdateDocumentTitle(title
);
9393 // Fire a DOM event for the title change.
9394 nsContentUtils::DispatchChromeEvent(this, this, u
"DOMTitleChanged"_ns
,
9395 CanBubble::eYes
, Cancelable::eYes
);
9397 nsCOMPtr
<nsIObserverService
> obs
= mozilla::services::GetObserverService();
9399 obs
->NotifyObservers(ToSupports(this), "document-title-changed", nullptr);
9403 already_AddRefed
<MediaQueryList
> Document::MatchMedia(
9404 const nsACString
& aMediaQueryList
, CallerType aCallerType
) {
9405 RefPtr
<MediaQueryList
> result
=
9406 new MediaQueryList(this, aMediaQueryList
, aCallerType
);
9408 mDOMMediaQueryLists
.insertBack(result
);
9410 return result
.forget();
9413 void Document::SetMayStartLayout(bool aMayStartLayout
) {
9414 mMayStartLayout
= aMayStartLayout
;
9415 if (MayStartLayout()) {
9416 // Before starting layout, check whether we're a toplevel chrome
9417 // window. If we are, setup some state so that we don't have to restyle
9418 // the whole tree after StartLayout.
9419 if (nsCOMPtr
<nsIAppWindow
> win
= GetAppWindowIfToplevelChrome()) {
9420 // We're the chrome document!
9421 win
->BeforeStartLayout();
9423 ReadyState state
= GetReadyStateEnum();
9424 if (state
>= READYSTATE_INTERACTIVE
) {
9425 // DOMContentLoaded has fired already.
9426 MaybeResolveReadyForIdle();
9430 MaybeEditingStateChanged();
9433 nsresult
Document::InitializeFrameLoader(nsFrameLoader
* aLoader
) {
9434 mInitializableFrameLoaders
.RemoveElement(aLoader
);
9435 // Don't even try to initialize.
9436 if (mInDestructor
) {
9438 "Trying to initialize a frame loader while"
9439 "document is being deleted");
9440 return NS_ERROR_FAILURE
;
9443 mInitializableFrameLoaders
.AppendElement(aLoader
);
9444 if (!mFrameLoaderRunner
) {
9445 mFrameLoaderRunner
=
9446 NewRunnableMethod("Document::MaybeInitializeFinalizeFrameLoaders", this,
9447 &Document::MaybeInitializeFinalizeFrameLoaders
);
9448 NS_ENSURE_TRUE(mFrameLoaderRunner
, NS_ERROR_OUT_OF_MEMORY
);
9449 nsContentUtils::AddScriptRunner(mFrameLoaderRunner
);
9454 nsresult
Document::FinalizeFrameLoader(nsFrameLoader
* aLoader
,
9455 nsIRunnable
* aFinalizer
) {
9456 mInitializableFrameLoaders
.RemoveElement(aLoader
);
9457 if (mInDestructor
) {
9458 return NS_ERROR_FAILURE
;
9461 LogRunnable::LogDispatch(aFinalizer
);
9462 mFrameLoaderFinalizers
.AppendElement(aFinalizer
);
9463 if (!mFrameLoaderRunner
) {
9464 mFrameLoaderRunner
=
9465 NewRunnableMethod("Document::MaybeInitializeFinalizeFrameLoaders", this,
9466 &Document::MaybeInitializeFinalizeFrameLoaders
);
9467 NS_ENSURE_TRUE(mFrameLoaderRunner
, NS_ERROR_OUT_OF_MEMORY
);
9468 nsContentUtils::AddScriptRunner(mFrameLoaderRunner
);
9473 void Document::MaybeInitializeFinalizeFrameLoaders() {
9474 if (mDelayFrameLoaderInitialization
) {
9475 // This method will be recalled when !mDelayFrameLoaderInitialization.
9476 mFrameLoaderRunner
= nullptr;
9480 // We're not in an update, but it is not safe to run scripts, so
9481 // postpone frameloader initialization and finalization.
9482 if (!nsContentUtils::IsSafeToRunScript()) {
9483 if (!mInDestructor
&& !mFrameLoaderRunner
&&
9484 (mInitializableFrameLoaders
.Length() ||
9485 mFrameLoaderFinalizers
.Length())) {
9486 mFrameLoaderRunner
= NewRunnableMethod(
9487 "Document::MaybeInitializeFinalizeFrameLoaders", this,
9488 &Document::MaybeInitializeFinalizeFrameLoaders
);
9489 nsContentUtils::AddScriptRunner(mFrameLoaderRunner
);
9493 mFrameLoaderRunner
= nullptr;
9495 // Don't use a temporary array for mInitializableFrameLoaders, because
9496 // loading a frame may cause some other frameloader to be removed from the
9497 // array. But be careful to keep the loader alive when starting the load!
9498 while (mInitializableFrameLoaders
.Length()) {
9499 RefPtr
<nsFrameLoader
> loader
= mInitializableFrameLoaders
[0];
9500 mInitializableFrameLoaders
.RemoveElementAt(0);
9501 NS_ASSERTION(loader
, "null frameloader in the array?");
9502 loader
->ReallyStartLoading();
9505 uint32_t length
= mFrameLoaderFinalizers
.Length();
9507 nsTArray
<nsCOMPtr
<nsIRunnable
>> finalizers
=
9508 std::move(mFrameLoaderFinalizers
);
9509 for (uint32_t i
= 0; i
< length
; ++i
) {
9510 LogRunnable::Run
run(finalizers
[i
]);
9511 finalizers
[i
]->Run();
9516 void Document::TryCancelFrameLoaderInitialization(nsIDocShell
* aShell
) {
9517 uint32_t length
= mInitializableFrameLoaders
.Length();
9518 for (uint32_t i
= 0; i
< length
; ++i
) {
9519 if (mInitializableFrameLoaders
[i
]->GetExistingDocShell() == aShell
) {
9520 mInitializableFrameLoaders
.RemoveElementAt(i
);
9526 void Document::SetPrototypeDocument(nsXULPrototypeDocument
* aPrototype
) {
9527 mPrototypeDocument
= aPrototype
;
9528 mSynchronousDOMContentLoaded
= true;
9531 nsIPermissionDelegateHandler
* Document::PermDelegateHandler() {
9532 return GetPermissionDelegateHandler();
9535 Document
* Document::RequestExternalResource(
9536 nsIURI
* aURI
, nsIReferrerInfo
* aReferrerInfo
, nsINode
* aRequestingNode
,
9537 ExternalResourceLoad
** aPendingLoad
) {
9538 MOZ_ASSERT(aURI
, "Must have a URI");
9539 MOZ_ASSERT(aRequestingNode
, "Must have a node");
9540 MOZ_ASSERT(aReferrerInfo
, "Must have a referrerInfo");
9541 if (mDisplayDocument
) {
9542 return mDisplayDocument
->RequestExternalResource(
9543 aURI
, aReferrerInfo
, aRequestingNode
, aPendingLoad
);
9546 return mExternalResourceMap
.RequestResource(
9547 aURI
, aReferrerInfo
, aRequestingNode
, this, aPendingLoad
);
9550 void Document::EnumerateExternalResources(SubDocEnumFunc aCallback
) {
9551 mExternalResourceMap
.EnumerateResources(aCallback
);
9554 SMILAnimationController
* Document::GetAnimationController() {
9555 // We create the animation controller lazily because most documents won't want
9556 // one and only SVG documents and the like will call this
9557 if (mAnimationController
) return mAnimationController
;
9558 // Refuse to create an Animation Controller for data documents.
9559 if (mLoadedAsData
) return nullptr;
9561 mAnimationController
= new SMILAnimationController(this);
9563 // If there's a presContext then check the animation mode and pause if
9565 nsPresContext
* context
= GetPresContext();
9566 if (mAnimationController
&& context
&&
9567 context
->ImageAnimationMode() == imgIContainer::kDontAnimMode
) {
9568 mAnimationController
->Pause(SMILTimeContainer::PAUSE_USERPREF
);
9571 // If we're hidden (or being hidden), notify the newly-created animation
9572 // controller. (Skip this check for SVG-as-an-image documents, though,
9573 // because they don't get OnPageShow / OnPageHide calls).
9574 if (!mIsShowing
&& !mIsBeingUsedAsImage
) {
9575 mAnimationController
->OnPageHide();
9578 return mAnimationController
;
9581 ScrollTimelineAnimationTracker
*
9582 Document::GetOrCreateScrollTimelineAnimationTracker() {
9583 if (!mScrollTimelineAnimationTracker
) {
9584 mScrollTimelineAnimationTracker
= new ScrollTimelineAnimationTracker(this);
9587 return mScrollTimelineAnimationTracker
;
9591 * Retrieve the "direction" property of the document.
9595 void Document::GetDir(nsAString
& aDirection
) const {
9596 aDirection
.Truncate();
9597 Element
* rootElement
= GetHtmlElement();
9599 static_cast<nsGenericHTMLElement
*>(rootElement
)->GetDir(aDirection
);
9604 * Set the "direction" property of the document.
9608 void Document::SetDir(const nsAString
& aDirection
) {
9609 Element
* rootElement
= GetHtmlElement();
9611 rootElement
->SetAttr(kNameSpaceID_None
, nsGkAtoms::dir
, aDirection
, true);
9615 nsIHTMLCollection
* Document::Images() {
9617 mImages
= new nsContentList(this, kNameSpaceID_XHTML
, nsGkAtoms::img
,
9623 nsIHTMLCollection
* Document::Embeds() {
9625 mEmbeds
= new nsContentList(this, kNameSpaceID_XHTML
, nsGkAtoms::embed
,
9631 static bool MatchLinks(Element
* aElement
, int32_t aNamespaceID
, nsAtom
* aAtom
,
9633 return aElement
->IsAnyOfHTMLElements(nsGkAtoms::a
, nsGkAtoms::area
) &&
9634 aElement
->HasAttr(nsGkAtoms::href
);
9637 nsIHTMLCollection
* Document::Links() {
9639 mLinks
= new nsContentList(this, MatchLinks
, nullptr, nullptr);
9644 nsIHTMLCollection
* Document::Forms() {
9646 // Please keep this in sync with nsHTMLDocument::GetFormsAndFormControls.
9647 mForms
= new nsContentList(this, kNameSpaceID_XHTML
, nsGkAtoms::form
,
9654 nsIHTMLCollection
* Document::Scripts() {
9656 mScripts
= new nsContentList(this, kNameSpaceID_XHTML
, nsGkAtoms::script
,
9662 nsIHTMLCollection
* Document::Applets() {
9664 mApplets
= new nsEmptyContentList(this);
9669 static bool MatchAnchors(Element
* aElement
, int32_t aNamespaceID
, nsAtom
* aAtom
,
9671 return aElement
->IsHTMLElement(nsGkAtoms::a
) &&
9672 aElement
->HasAttr(nsGkAtoms::name
);
9675 nsIHTMLCollection
* Document::Anchors() {
9677 mAnchors
= new nsContentList(this, MatchAnchors
, nullptr, nullptr);
9682 mozilla::dom::Nullable
<mozilla::dom::WindowProxyHolder
> Document::Open(
9683 const nsAString
& aURL
, const nsAString
& aName
, const nsAString
& aFeatures
,
9685 MOZ_ASSERT(nsContentUtils::CanCallerAccess(this),
9686 "XOW should have caught this!");
9688 nsCOMPtr
<nsPIDOMWindowInner
> window
= GetInnerWindow();
9690 rv
.Throw(NS_ERROR_DOM_INVALID_ACCESS_ERR
);
9693 nsCOMPtr
<nsPIDOMWindowOuter
> outer
=
9694 nsPIDOMWindowOuter::GetFromCurrentInner(window
);
9696 rv
.Throw(NS_ERROR_NOT_INITIALIZED
);
9699 RefPtr
<nsGlobalWindowOuter
> win
= nsGlobalWindowOuter::Cast(outer
);
9700 RefPtr
<BrowsingContext
> newBC
;
9701 rv
= win
->OpenJS(aURL
, aName
, aFeatures
, getter_AddRefs(newBC
));
9705 return WindowProxyHolder(std::move(newBC
));
9708 Document
* Document::Open(const Optional
<nsAString
>& /* unused */,
9709 const Optional
<nsAString
>& /* unused */,
9710 ErrorResult
& aError
) {
9712 // <https://html.spec.whatwg.org/multipage/dynamic-markup-insertion.html#document-open-steps>
9714 MOZ_ASSERT(nsContentUtils::CanCallerAccess(this),
9715 "XOW should have caught this!");
9717 // Step 1 -- throw if we're an XML document.
9718 if (!IsHTMLDocument() || mDisableDocWrite
) {
9719 aError
.Throw(NS_ERROR_DOM_INVALID_STATE_ERR
);
9723 // Step 2 -- throw if dynamic markup insertion should throw.
9724 if (ShouldThrowOnDynamicMarkupInsertion()) {
9725 aError
.Throw(NS_ERROR_DOM_INVALID_STATE_ERR
);
9729 // Step 3 -- get the entry document, so we can use it for security checks.
9730 nsCOMPtr
<Document
> callerDoc
= GetEntryDocument();
9732 // If we're called from C++ or in some other way without an originating
9733 // document we can't do a document.open w/o changing the principal of the
9734 // document to something like about:blank (as that's the only sane thing to
9735 // do when we don't know the origin of this call), and since we can't
9736 // change the principals of a document for security reasons we'll have to
9737 // refuse to go ahead with this call.
9739 aError
.Throw(NS_ERROR_DOM_SECURITY_ERR
);
9743 // Step 4 -- make sure we're same-origin (not just same origin-domain) with
9744 // the entry document.
9745 if (!callerDoc
->NodePrincipal()->Equals(NodePrincipal())) {
9746 aError
.Throw(NS_ERROR_DOM_SECURITY_ERR
);
9750 // Step 5 -- if we have an active parser with a nonzero script nesting level,
9752 if ((mParser
&& mParser
->HasNonzeroScriptNestingLevel()) || mParserAborted
) {
9756 // Step 6 -- check for open() during unload. Per spec, this is just a check
9757 // of the ignore-opens-during-unload counter, but our unload event code
9758 // doesn't affect that counter yet (unlike pagehide and beforeunload, which
9759 // do), so we check for unload directly.
9760 if (ShouldIgnoreOpens()) {
9764 RefPtr
<nsDocShell
> shell(mDocumentContainer
);
9767 shell
->GetIsInUnload(&inUnload
);
9773 // At this point we know this is a valid-enough document.open() call
9774 // and not a no-op. Increment our use counter.
9775 SetUseCounter(eUseCounter_custom_DocumentOpen
);
9777 // Step 7 -- stop existing navigation of our browsing context (and all other
9778 // loads it's doing) if we're the active document of our browsing context.
9779 // Note that we do not want to stop anything if there is no existing
9781 if (shell
&& IsCurrentActiveDocument() &&
9782 shell
->GetIsAttemptingToNavigate()) {
9783 shell
->Stop(nsIWebNavigation::STOP_NETWORK
);
9785 // The Stop call may have cancelled the onload blocker request or
9786 // prevented it from getting added, so we need to make sure it gets added
9787 // to the document again otherwise the document could have a non-zero
9788 // onload block count without the onload blocker request being in the
9790 EnsureOnloadBlocker();
9793 // Step 8 -- clear event listeners out of our DOM tree
9794 for (nsINode
* node
: ShadowIncludingTreeIterator(*this)) {
9795 if (EventListenerManager
* elm
= node
->GetExistingListenerManager()) {
9796 elm
->RemoveAllListeners();
9800 // Step 9 -- clear event listeners from our window, if we have one.
9802 // Note that we explicitly want the inner window, and only if we're its
9803 // document. We want to do this (per spec) even when we're not the "active
9804 // document", so we can't go through GetWindow(), because it might forward to
9806 if (nsPIDOMWindowInner
* win
= GetInnerWindow()) {
9807 if (win
->GetExtantDoc() == this) {
9808 if (EventListenerManager
* elm
=
9809 nsGlobalWindowInner::Cast(win
)->GetExistingListenerManager()) {
9810 elm
->RemoveAllListeners();
9815 // If we have a parser that has a zero script nesting level, we need to
9816 // properly terminate it. We do that after we've removed all the event
9817 // listeners (so termination won't trigger event listeners if it does
9818 // something to the DOM), but before we remove all elements from the document
9819 // (so if termination does modify the DOM in some way we will just blow it
9820 // away immediately. See the similar code in WriteCommon that handles the
9821 // !IsInsertionPointDefined() case and should stay in sync with this code.
9823 MOZ_ASSERT(!mParser
->HasNonzeroScriptNestingLevel(),
9824 "Why didn't we take the early return?");
9825 // Make sure we don't re-enter.
9826 IgnoreOpensDuringUnload
ignoreOpenGuard(this);
9827 mParser
->Terminate();
9828 MOZ_RELEASE_ASSERT(!mParser
, "mParser should have been null'd out");
9831 // Step 10 -- remove all our DOM kids without firing any mutation events.
9833 // We want to ignore any recursive calls to Open() that happen while
9834 // disconnecting the node tree. The spec doesn't say to do this, but the
9835 // spec also doesn't envision unload events on subframes firing while we do
9836 // this, while all browsers fire them in practice. See
9837 // <https://github.com/whatwg/html/issues/4611>.
9838 IgnoreOpensDuringUnload
ignoreOpenGuard(this);
9839 DisconnectNodeTree();
9842 // Step 11 -- if we're the current document in our docshell, do the
9843 // equivalent of pushState() with the new URL we should have.
9844 if (shell
&& IsCurrentActiveDocument()) {
9845 nsCOMPtr
<nsIURI
> newURI
= callerDoc
->GetDocumentURI();
9846 if (callerDoc
!= this) {
9847 nsCOMPtr
<nsIURI
> noFragmentURI
;
9848 nsresult rv
= NS_GetURIWithoutRef(newURI
, getter_AddRefs(noFragmentURI
));
9849 if (NS_WARN_IF(NS_FAILED(rv
))) {
9853 newURI
= std::move(noFragmentURI
);
9856 // UpdateURLAndHistory might do various member-setting, so make sure we're
9857 // holding strong refs to all the refcounted args on the stack. We can
9858 // assume that our caller is holding on to "this" already.
9859 nsCOMPtr
<nsIURI
> currentURI
= GetDocumentURI();
9861 nsresult rv
= currentURI
->Equals(newURI
, &equalURIs
);
9862 if (NS_WARN_IF(NS_FAILED(rv
))) {
9866 nsCOMPtr
<nsIStructuredCloneContainer
> stateContainer(mStateObjectContainer
);
9867 rv
= shell
->UpdateURLAndHistory(this, newURI
, stateContainer
, u
""_ns
,
9868 /* aReplace = */ true, currentURI
,
9870 if (NS_WARN_IF(NS_FAILED(rv
))) {
9875 // And use the security info of the caller document as well, since
9876 // it's the thing providing our data.
9877 mSecurityInfo
= callerDoc
->GetSecurityInfo();
9879 // This is not mentioned in the spec, but I think that's a spec bug. See
9880 // <https://github.com/whatwg/html/issues/4299>. In any case, since our
9881 // URL may be changing away from about:blank here, we really want to unset
9882 // this flag no matter what, since only about:blank can be an initial
9884 SetIsInitialDocument(false);
9886 // And let our docloader know that it will need to track our load event.
9887 nsDocShell::Cast(shell
)->SetDocumentOpenedButNotLoaded();
9890 // Per spec nothing happens with our URI in other cases, though note
9891 // <https://github.com/whatwg/html/issues/4286>.
9893 // Note that we don't need to do anything here with base URIs per spec.
9894 // That said, this might be assuming that we implement
9895 // https://html.spec.whatwg.org/multipage/urls-and-fetching.html#fallback-base-url
9896 // correctly, which we don't right now for the about:blank case.
9898 // Step 12, but note <https://github.com/whatwg/html/issues/4292>.
9899 mSkipLoadEventAfterClose
= mLoadEventFiring
;
9901 // Preliminary to steps 13-16. Set our ready state to uninitialized before
9902 // we do anything else, so we can then proceed to later ready state levels.
9903 SetReadyStateInternal(READYSTATE_UNINITIALIZED
,
9904 /* updateTimingInformation = */ false);
9905 // Reset a flag that affects readyState behavior.
9906 mSetCompleteAfterDOMContentLoaded
= false;
9908 // Step 13 -- set our compat mode to standards.
9909 SetCompatibilityMode(eCompatibility_FullStandards
);
9911 // Step 14 -- create a new parser associated with document. This also does
9912 // step 16 implicitly.
9913 mParserAborted
= false;
9914 RefPtr
<nsHtml5Parser
> parser
= nsHtml5Module::NewHtml5Parser();
9916 parser
->Initialize(this, GetDocumentURI(), ToSupports(shell
), nullptr);
9917 nsresult rv
= parser
->StartExecutor();
9918 if (NS_WARN_IF(NS_FAILED(rv
))) {
9923 // Clear out our form control state, because the state of controls
9924 // in the pre-open() document should not affect the state of
9925 // controls that are now going to be written.
9926 mLayoutHistoryState
= nullptr;
9929 // Prepare the docshell and the document viewer for the impending
9930 // out-of-band document.write()
9931 shell
->PrepareForNewContentModel();
9933 nsCOMPtr
<nsIDocumentViewer
> viewer
;
9934 shell
->GetDocViewer(getter_AddRefs(viewer
));
9936 viewer
->LoadStart(this);
9941 SetReadyStateInternal(Document::READYSTATE_LOADING
,
9942 /* updateTimingInformation = */ false);
9944 // Step 16 happened with step 14 above.
9950 void Document::Close(ErrorResult
& rv
) {
9951 if (!IsHTMLDocument()) {
9952 // No calling document.close() on XHTML!
9954 rv
.Throw(NS_ERROR_DOM_INVALID_STATE_ERR
);
9958 if (ShouldThrowOnDynamicMarkupInsertion()) {
9959 rv
.Throw(NS_ERROR_DOM_INVALID_STATE_ERR
);
9963 if (!mParser
|| !mParser
->IsScriptCreated()) {
9968 rv
= (static_cast<nsHtml5Parser
*>(mParser
.get()))
9969 ->Parse(u
""_ns
, nullptr, true);
9973 void Document::WriteCommon(const Sequence
<nsString
>& aText
,
9974 bool aNewlineTerminate
, mozilla::ErrorResult
& rv
) {
9975 // Fast path the common case
9976 if (aText
.Length() == 1) {
9977 WriteCommon(aText
[0], aNewlineTerminate
, rv
);
9979 // XXXbz it would be nice if we could pass all the strings to the parser
9980 // without having to do all this copying and then ask it to start
9983 for (size_t i
= 0; i
< aText
.Length(); ++i
) {
9984 text
.Append(aText
[i
]);
9986 WriteCommon(text
, aNewlineTerminate
, rv
);
9990 void Document::WriteCommon(const nsAString
& aText
, bool aNewlineTerminate
,
9994 // Assert that we do not use or accidentally introduce doc.write()
9995 // in system privileged context or in any of our about: pages.
9996 nsCOMPtr
<nsIPrincipal
> principal
= NodePrincipal();
9997 bool isAboutOrPrivContext
= principal
->IsSystemPrincipal();
9998 if (!isAboutOrPrivContext
) {
9999 if (principal
->SchemeIs("about")) {
10000 // about:blank inherits the security contetext and this assertion
10001 // is only meant for actual about: pages.
10002 nsAutoCString host
;
10003 principal
->GetHost(host
);
10004 isAboutOrPrivContext
= !host
.EqualsLiteral("blank");
10007 // Some automated tests use an empty string to kick off some parsing
10008 // mechansims, but they do not do any harm since they use an empty string.
10009 MOZ_ASSERT(!isAboutOrPrivContext
|| aText
.IsEmpty(),
10010 "do not use doc.write in privileged context!");
10014 mTooDeepWriteRecursion
=
10015 (mWriteLevel
> NS_MAX_DOCUMENT_WRITE_DEPTH
|| mTooDeepWriteRecursion
);
10016 if (NS_WARN_IF(mTooDeepWriteRecursion
)) {
10017 aRv
.Throw(NS_ERROR_UNEXPECTED
);
10021 if (!IsHTMLDocument() || mDisableDocWrite
) {
10022 // No calling document.write*() on XHTML!
10024 aRv
.Throw(NS_ERROR_DOM_INVALID_STATE_ERR
);
10028 if (ShouldThrowOnDynamicMarkupInsertion()) {
10029 aRv
.Throw(NS_ERROR_DOM_INVALID_STATE_ERR
);
10033 if (mParserAborted
) {
10034 // Hixie says aborting the parser doesn't undefine the insertion point.
10035 // However, since we null out mParser in that case, we track the
10036 // theoretically defined insertion point using mParserAborted.
10040 // Implement Step 4.1 of:
10041 // https://html.spec.whatwg.org/multipage/dynamic-markup-insertion.html#document-write-steps
10042 if (ShouldIgnoreOpens()) {
10046 void* key
= GenerateParserKey();
10047 if (mParser
&& !mParser
->IsInsertionPointDefined()) {
10048 if (mIgnoreDestructiveWritesCounter
) {
10049 // Instead of implying a call to document.open(), ignore the call.
10050 nsContentUtils::ReportToConsole(
10051 nsIScriptError::warningFlag
, "DOM Events"_ns
, this,
10052 nsContentUtils::eDOM_PROPERTIES
, "DocumentWriteIgnored");
10055 // The spec doesn't tell us to ignore opens from here, but we need to
10056 // ensure opens are ignored here. See similar code in Open() that handles
10057 // the case of an existing parser which is not currently running script and
10058 // should stay in sync with this code.
10059 IgnoreOpensDuringUnload
ignoreOpenGuard(this);
10060 mParser
->Terminate();
10061 MOZ_RELEASE_ASSERT(!mParser
, "mParser should have been null'd out");
10065 if (mIgnoreDestructiveWritesCounter
) {
10066 // Instead of implying a call to document.open(), ignore the call.
10067 nsContentUtils::ReportToConsole(
10068 nsIScriptError::warningFlag
, "DOM Events"_ns
, this,
10069 nsContentUtils::eDOM_PROPERTIES
, "DocumentWriteIgnored");
10075 // If Open() fails, or if it didn't create a parser (as it won't
10076 // if the user chose to not discard the current document through
10077 // onbeforeunload), don't write anything.
10078 if (aRv
.Failed() || !mParser
) {
10083 static constexpr auto new_line
= u
"\n"_ns
;
10087 // This could be done with less code, but for performance reasons it
10088 // makes sense to have the code for two separate Parse() calls here
10089 // since the concatenation of strings costs more than we like. And
10090 // why pay that price when we don't need to?
10091 if (aNewlineTerminate
) {
10092 aRv
= (static_cast<nsHtml5Parser
*>(mParser
.get()))
10093 ->Parse(aText
+ new_line
, key
, false);
10096 (static_cast<nsHtml5Parser
*>(mParser
.get()))->Parse(aText
, key
, false);
10101 mTooDeepWriteRecursion
= (mWriteLevel
!= 0 && mTooDeepWriteRecursion
);
10104 void Document::Write(const Sequence
<nsString
>& aText
, ErrorResult
& rv
) {
10105 WriteCommon(aText
, false, rv
);
10108 void Document::Writeln(const Sequence
<nsString
>& aText
, ErrorResult
& rv
) {
10109 WriteCommon(aText
, true, rv
);
10112 void* Document::GenerateParserKey(void) {
10113 if (!mScriptLoader
) {
10114 // If we don't have a script loader, then the parser probably isn't parsing
10115 // anything anyway, so just return null.
10119 // The script loader provides us with the currently executing script element,
10120 // which is guaranteed to be unique per script.
10121 nsIScriptElement
* script
= mScriptLoader
->GetCurrentParserInsertedScript();
10122 if (script
&& mParser
&& mParser
->IsScriptCreated()) {
10123 nsCOMPtr
<nsIParser
> creatorParser
= script
->GetCreatorParser();
10124 if (creatorParser
!= mParser
) {
10125 // Make scripts that aren't inserted by the active parser of this document
10126 // participate in the context of the script that document.open()ed
10135 bool Document::MatchNameAttribute(Element
* aElement
, int32_t aNamespaceID
,
10136 nsAtom
* aAtom
, void* aData
) {
10137 MOZ_ASSERT(aElement
, "Must have element to work with!");
10139 if (!aElement
->HasName()) {
10143 nsString
* elementName
= static_cast<nsString
*>(aData
);
10144 return aElement
->GetNameSpaceID() == kNameSpaceID_XHTML
&&
10145 aElement
->AttrValueIs(kNameSpaceID_None
, nsGkAtoms::name
, *elementName
,
10150 void* Document::UseExistingNameString(nsINode
* aRootNode
,
10151 const nsString
* aName
) {
10152 return const_cast<nsString
*>(aName
);
10155 nsresult
Document::GetDocumentURI(nsString
& aDocumentURI
) const {
10156 if (mDocumentURI
) {
10158 nsresult rv
= mDocumentURI
->GetSpec(uri
);
10159 NS_ENSURE_SUCCESS(rv
, rv
);
10161 CopyUTF8toUTF16(uri
, aDocumentURI
);
10163 aDocumentURI
.Truncate();
10170 nsresult
Document::GetURL(nsString
& aURL
) const { return GetDocumentURI(aURL
); }
10172 void Document::GetDocumentURIFromJS(nsString
& aDocumentURI
,
10173 CallerType aCallerType
,
10174 ErrorResult
& aRv
) const {
10175 if (!mChromeXHRDocURI
|| aCallerType
!= CallerType::System
) {
10176 aRv
= GetDocumentURI(aDocumentURI
);
10181 nsresult res
= mChromeXHRDocURI
->GetSpec(uri
);
10182 if (NS_FAILED(res
)) {
10186 CopyUTF8toUTF16(uri
, aDocumentURI
);
10189 nsIURI
* Document::GetDocumentURIObject() const {
10190 if (!mChromeXHRDocURI
) {
10191 return GetDocumentURI();
10194 return mChromeXHRDocURI
;
10197 void Document::GetCompatMode(nsString
& aCompatMode
) const {
10198 NS_ASSERTION(mCompatMode
== eCompatibility_NavQuirks
||
10199 mCompatMode
== eCompatibility_AlmostStandards
||
10200 mCompatMode
== eCompatibility_FullStandards
,
10201 "mCompatMode is neither quirks nor strict for this document");
10203 if (mCompatMode
== eCompatibility_NavQuirks
) {
10204 aCompatMode
.AssignLiteral("BackCompat");
10206 aCompatMode
.AssignLiteral("CSS1Compat");
10211 } // namespace mozilla
10213 void nsDOMAttributeMap::BlastSubtreeToPieces(nsINode
* aNode
) {
10214 if (Element
* element
= Element::FromNode(aNode
)) {
10215 if (const nsDOMAttributeMap
* map
= element
->GetAttributeMap()) {
10219 // Use an iterator to get an arbitrary attribute from the
10220 // cache. The iterator must be destroyed before any other
10221 // operations on mAttributeCache, to avoid hash table
10223 auto iter
= map
->mAttributeCache
.ConstIter();
10227 attr
= iter
.UserData();
10230 BlastSubtreeToPieces(attr
);
10232 mozilla::DebugOnly
<nsresult
> rv
=
10233 element
->UnsetAttr(attr
->NodeInfo()->NamespaceID(),
10234 attr
->NodeInfo()->NameAtom(), false);
10236 // XXX Should we abort here?
10237 NS_ASSERTION(NS_SUCCEEDED(rv
), "Uh-oh, UnsetAttr shouldn't fail!");
10241 if (mozilla::dom::ShadowRoot
* shadow
= element
->GetShadowRoot()) {
10242 BlastSubtreeToPieces(shadow
);
10243 element
->UnattachShadow();
10247 while (aNode
->HasChildren()) {
10248 nsIContent
* node
= aNode
->GetFirstChild();
10249 BlastSubtreeToPieces(node
);
10250 aNode
->RemoveChildNode(node
, false);
10254 namespace mozilla::dom
{
10256 nsINode
* Document::AdoptNode(nsINode
& aAdoptedNode
, ErrorResult
& rv
,
10257 bool aAcceptShadowRoot
) {
10258 OwningNonNull
<nsINode
> adoptedNode
= aAdoptedNode
;
10259 if (adoptedNode
->IsShadowRoot() && !aAcceptShadowRoot
) {
10260 rv
.ThrowHierarchyRequestError("The adopted node is a shadow root.");
10264 // Scope firing mutation events so that we don't carry any state that
10267 if (nsCOMPtr
<nsINode
> parent
= adoptedNode
->GetParentNode()) {
10268 nsContentUtils::MaybeFireNodeRemoved(adoptedNode
, parent
);
10272 nsAutoScriptBlocker scriptBlocker
;
10274 switch (adoptedNode
->NodeType()) {
10275 case ATTRIBUTE_NODE
: {
10276 // Remove from ownerElement.
10277 OwningNonNull
<Attr
> adoptedAttr
= static_cast<Attr
&>(*adoptedNode
);
10279 nsCOMPtr
<Element
> ownerElement
= adoptedAttr
->GetOwnerElement();
10284 if (ownerElement
) {
10285 OwningNonNull
<Attr
> newAttr
=
10286 ownerElement
->RemoveAttributeNode(*adoptedAttr
, rv
);
10294 case DOCUMENT_FRAGMENT_NODE
:
10296 case PROCESSING_INSTRUCTION_NODE
:
10298 case CDATA_SECTION_NODE
:
10300 case DOCUMENT_TYPE_NODE
: {
10301 // Don't allow adopting a node's anonymous subtree out from under it.
10302 if (adoptedNode
->IsRootOfNativeAnonymousSubtree()) {
10303 rv
.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR
);
10307 // We don't want to adopt an element into its own contentDocument or into
10308 // a descendant contentDocument, so we check if the frameElement of this
10309 // document or any of its parents is the adopted node or one of its
10311 RefPtr
<BrowsingContext
> bc
= GetBrowsingContext();
10313 nsCOMPtr
<nsINode
> node
= bc
->GetEmbedderElement();
10314 if (node
&& node
->IsInclusiveDescendantOf(adoptedNode
)) {
10315 rv
.ThrowHierarchyRequestError(
10316 "Trying to adopt a node into its own contentDocument or a "
10317 "descendant contentDocument.");
10321 if (XRE_IsParentProcess()) {
10322 bc
= bc
->Canonical()->GetParentCrossChromeBoundary();
10324 bc
= bc
->GetParent();
10328 // Remove from parent.
10329 nsCOMPtr
<nsINode
> parent
= adoptedNode
->GetParentNode();
10331 parent
->RemoveChildNode(adoptedNode
->AsContent(), true);
10333 MOZ_ASSERT(!adoptedNode
->IsInUncomposedDoc());
10338 case DOCUMENT_NODE
: {
10339 rv
.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR
);
10343 NS_WARNING("Don't know how to adopt this nodetype for adoptNode.");
10345 rv
.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR
);
10350 nsCOMPtr
<Document
> oldDocument
= adoptedNode
->OwnerDoc();
10351 bool sameDocument
= oldDocument
== this;
10354 JS::Rooted
<JSObject
*> newScope(cx
, nullptr);
10355 if (!sameDocument
) {
10356 newScope
= GetWrapper();
10357 if (!newScope
&& GetScopeObject() && GetScopeObject()->HasJSGlobal()) {
10358 // Make sure cx is in a semi-sane compartment before we call WrapNative.
10359 // It's kind of irrelevant, given that we're passing aAllowWrapping =
10360 // false, and documents should always insist on being wrapped in an
10361 // canonical scope. But we try to pass something sane anyway.
10362 JSObject
* globalObject
= GetScopeObject()->GetGlobalJSObject();
10363 JSAutoRealm
ar(cx
, globalObject
);
10364 JS::Rooted
<JS::Value
> v(cx
);
10365 rv
= nsContentUtils::WrapNative(cx
, ToSupports(this), this, &v
,
10366 /* aAllowWrapping = */ false);
10367 if (rv
.Failed()) return nullptr;
10368 newScope
= &v
.toObject();
10372 adoptedNode
->Adopt(sameDocument
? nullptr : mNodeInfoManager
, newScope
, rv
);
10374 // Disconnect all nodes from their parents, since some have the old document
10375 // as their ownerDocument and some have this as their ownerDocument.
10376 nsDOMAttributeMap::BlastSubtreeToPieces(adoptedNode
);
10380 MOZ_ASSERT(adoptedNode
->OwnerDoc() == this,
10381 "Should still be in the document we just got adopted into");
10383 return adoptedNode
;
10386 bool Document::UseWidthDeviceWidthFallbackViewport() const { return false; }
10388 static Maybe
<LayoutDeviceToScreenScale
> ParseScaleString(
10389 const nsString
& aScaleString
) {
10390 // https://drafts.csswg.org/css-device-adapt/#min-scale-max-scale
10391 if (aScaleString
.EqualsLiteral("device-width") ||
10392 aScaleString
.EqualsLiteral("device-height")) {
10393 return Some(LayoutDeviceToScreenScale(10.0f
));
10394 } else if (aScaleString
.EqualsLiteral("yes")) {
10395 return Some(LayoutDeviceToScreenScale(1.0f
));
10396 } else if (aScaleString
.EqualsLiteral("no")) {
10397 return Some(LayoutDeviceToScreenScale(ViewportMinScale()));
10398 } else if (aScaleString
.IsEmpty()) {
10402 nsresult scaleErrorCode
;
10403 float scale
= aScaleString
.ToFloatAllowTrailingChars(&scaleErrorCode
);
10404 if (NS_FAILED(scaleErrorCode
)) {
10405 return Some(LayoutDeviceToScreenScale(ViewportMinScale()));
10411 return Some(clamped(LayoutDeviceToScreenScale(scale
), ViewportMinScale(),
10412 ViewportMaxScale()));
10415 void Document::ParseScalesInViewportMetaData(
10416 const ViewportMetaData
& aViewportMetaData
) {
10417 Maybe
<LayoutDeviceToScreenScale
> scale
;
10419 scale
= ParseScaleString(aViewportMetaData
.mInitialScale
);
10420 mScaleFloat
= scale
.valueOr(LayoutDeviceToScreenScale(0.0f
));
10421 mValidScaleFloat
= scale
.isSome();
10423 scale
= ParseScaleString(aViewportMetaData
.mMaximumScale
);
10424 // Chrome uses '5' for the fallback value of maximum-scale, we might
10425 // consider matching it in future.
10426 // https://cs.chromium.org/chromium/src/third_party/blink/renderer/core/html/html_meta_element.cc?l=452&rcl=65ca4278b42d269ca738fc93ef7ae04a032afeb0
10427 mScaleMaxFloat
= scale
.valueOr(ViewportMaxScale());
10428 mValidMaxScale
= scale
.isSome();
10430 scale
= ParseScaleString(aViewportMetaData
.mMinimumScale
);
10431 mScaleMinFloat
= scale
.valueOr(ViewportMinScale());
10432 mValidMinScale
= scale
.isSome();
10434 // Resolve min-zoom and max-zoom values.
10435 // https://drafts.csswg.org/css-device-adapt/#constraining-min-max-zoom
10436 if (mValidMaxScale
&& mValidMinScale
) {
10437 mScaleMaxFloat
= std::max(mScaleMinFloat
, mScaleMaxFloat
);
10441 void Document::ParseWidthAndHeightInMetaViewport(const nsAString
& aWidthString
,
10442 const nsAString
& aHeightString
,
10443 bool aHasValidScale
) {
10444 // The width and height properties
10445 // https://drafts.csswg.org/css-device-adapt/#width-and-height-properties
10447 // The width and height viewport <META> properties are translated into width
10448 // and height descriptors, setting the min-width/min-height value to
10449 // extend-to-zoom and the max-width/max-height value to the length from the
10450 // viewport <META> property as follows:
10452 // 1. Non-negative number values are translated to pixel lengths, clamped to
10453 // the range: [1px, 10000px]
10454 // 2. Negative number values are dropped
10455 // 3. device-width and device-height translate to 100vw and 100vh respectively
10456 // 4. Other keywords and unknown values are also dropped
10457 mMinWidth
= nsViewportInfo::kAuto
;
10458 mMaxWidth
= nsViewportInfo::kAuto
;
10459 if (!aWidthString
.IsEmpty()) {
10460 mMinWidth
= nsViewportInfo::kExtendToZoom
;
10461 if (aWidthString
.EqualsLiteral("device-width")) {
10462 mMaxWidth
= nsViewportInfo::kDeviceSize
;
10464 nsresult widthErrorCode
;
10465 mMaxWidth
= aWidthString
.ToInteger(&widthErrorCode
);
10466 if (NS_FAILED(widthErrorCode
)) {
10467 mMaxWidth
= nsViewportInfo::kAuto
;
10468 } else if (mMaxWidth
>= 0.0f
) {
10469 mMaxWidth
= clamped(mMaxWidth
, CSSCoord(1.0f
), CSSCoord(10000.0f
));
10471 mMaxWidth
= nsViewportInfo::kAuto
;
10474 } else if (aHasValidScale
) {
10475 if (aHeightString
.IsEmpty()) {
10476 mMinWidth
= nsViewportInfo::kExtendToZoom
;
10477 mMaxWidth
= nsViewportInfo::kExtendToZoom
;
10479 } else if (aHeightString
.IsEmpty() && UseWidthDeviceWidthFallbackViewport()) {
10480 mMinWidth
= nsViewportInfo::kExtendToZoom
;
10481 mMaxWidth
= nsViewportInfo::kDeviceSize
;
10484 mMinHeight
= nsViewportInfo::kAuto
;
10485 mMaxHeight
= nsViewportInfo::kAuto
;
10486 if (!aHeightString
.IsEmpty()) {
10487 mMinHeight
= nsViewportInfo::kExtendToZoom
;
10488 if (aHeightString
.EqualsLiteral("device-height")) {
10489 mMaxHeight
= nsViewportInfo::kDeviceSize
;
10491 nsresult heightErrorCode
;
10492 mMaxHeight
= aHeightString
.ToInteger(&heightErrorCode
);
10493 if (NS_FAILED(heightErrorCode
)) {
10494 mMaxHeight
= nsViewportInfo::kAuto
;
10495 } else if (mMaxHeight
>= 0.0f
) {
10496 mMaxHeight
= clamped(mMaxHeight
, CSSCoord(1.0f
), CSSCoord(10000.0f
));
10498 mMaxHeight
= nsViewportInfo::kAuto
;
10504 nsViewportInfo
Document::GetViewportInfo(const ScreenIntSize
& aDisplaySize
) {
10505 MOZ_ASSERT(mPresShell
);
10507 // Compute the CSS-to-LayoutDevice pixel scale as the product of the
10508 // widget scale and the full zoom.
10509 nsPresContext
* context
= mPresShell
->GetPresContext();
10510 // When querying the full zoom, get it from the device context rather than
10511 // directly from the pres context, because the device context's value can
10512 // include an adjustment necessary to keep the number of app units per device
10513 // pixel an integer, and we want the adjusted value.
10514 float fullZoom
= context
? context
->DeviceContext()->GetFullZoom() : 1.0;
10515 fullZoom
= (fullZoom
== 0.0) ? 1.0 : fullZoom
;
10516 CSSToLayoutDeviceScale layoutDeviceScale
=
10517 context
? context
->CSSToDevPixelScale() : CSSToLayoutDeviceScale(1);
10519 CSSToScreenScale defaultScale
=
10520 layoutDeviceScale
* LayoutDeviceToScreenScale(1.0);
10522 // Special behaviour for desktop mode, provided we are not on an about: page,
10524 const bool fullscreen
= Fullscreen();
10525 auto* bc
= GetBrowsingContext();
10526 if (bc
&& bc
->ForceDesktopViewport() && !IsAboutPage() && !fullscreen
) {
10527 CSSCoord viewportWidth
=
10528 StaticPrefs::browser_viewport_desktopWidth() / fullZoom
;
10529 CSSToScreenScale
scaleToFit(aDisplaySize
.width
/ viewportWidth
);
10530 float aspectRatio
= (float)aDisplaySize
.height
/ aDisplaySize
.width
;
10531 CSSSize
viewportSize(viewportWidth
, viewportWidth
* aspectRatio
);
10532 ScreenIntSize fakeDesktopSize
= RoundedToInt(viewportSize
* scaleToFit
);
10533 return nsViewportInfo(fakeDesktopSize
, scaleToFit
,
10534 nsViewportInfo::ZoomFlag::AllowZoom
,
10535 nsViewportInfo::ZoomBehaviour::Mobile
,
10536 nsViewportInfo::AutoScaleFlag::AutoScale
);
10539 // We ignore viewport meta tage etc when in fullscreen, see bug 1696717.
10540 if (fullscreen
|| !nsLayoutUtils::ShouldHandleMetaViewport(this)) {
10541 return nsViewportInfo(aDisplaySize
, defaultScale
,
10542 nsLayoutUtils::AllowZoomingForDocument(this)
10543 ? nsViewportInfo::ZoomFlag::AllowZoom
10544 : nsViewportInfo::ZoomFlag::DisallowZoom
,
10545 StaticPrefs::apz_allow_zooming_out()
10546 ? nsViewportInfo::ZoomBehaviour::Mobile
10547 : nsViewportInfo::ZoomBehaviour::Desktop
);
10550 // In cases where the width of the CSS viewport is less than or equal to the
10551 // width of the display (i.e. width <= device-width) then we disable
10552 // double-tap-to-zoom behaviour. See bug 941995 for details.
10554 switch (mViewportType
) {
10555 case DisplayWidthHeight
:
10556 return nsViewportInfo(aDisplaySize
, defaultScale
,
10557 nsViewportInfo::ZoomFlag::AllowZoom
,
10558 nsViewportInfo::ZoomBehaviour::Mobile
);
10560 // We might early exit if the viewport is empty. Even if we don't,
10561 // at the end of this case we'll note that it was empty. Later, when
10562 // we're using the cached values, this will trigger alternate code paths.
10563 if (!mLastModifiedViewportMetaData
) {
10564 // If the docType specifies that we are on a site optimized for mobile,
10565 // then we want to return specially crafted defaults for the viewport
10567 if (RefPtr
<DocumentType
> docType
= GetDoctype()) {
10568 nsAutoString docId
;
10569 docType
->GetPublicId(docId
);
10570 if ((docId
.Find(u
"WAP") != -1) || (docId
.Find(u
"Mobile") != -1) ||
10571 (docId
.Find(u
"WML") != -1)) {
10572 // We're making an assumption that the docType can't change here
10573 mViewportType
= DisplayWidthHeight
;
10574 return nsViewportInfo(aDisplaySize
, defaultScale
,
10575 nsViewportInfo::ZoomFlag::AllowZoom
,
10576 nsViewportInfo::ZoomBehaviour::Mobile
);
10580 nsAutoString handheldFriendly
;
10581 GetHeaderData(nsGkAtoms::handheldFriendly
, handheldFriendly
);
10582 if (handheldFriendly
.EqualsLiteral("true")) {
10583 mViewportType
= DisplayWidthHeight
;
10584 return nsViewportInfo(aDisplaySize
, defaultScale
,
10585 nsViewportInfo::ZoomFlag::AllowZoom
,
10586 nsViewportInfo::ZoomBehaviour::Mobile
);
10590 ViewportMetaData metaData
= GetViewportMetaData();
10592 // Parse initial-scale, minimum-scale and maximum-scale.
10593 ParseScalesInViewportMetaData(metaData
);
10595 // Parse width and height properties
10596 // This function sets m{Min,Max}{Width,Height}.
10597 ParseWidthAndHeightInMetaViewport(metaData
.mWidth
, metaData
.mHeight
,
10601 if ((metaData
.mUserScalable
.EqualsLiteral("0")) ||
10602 (metaData
.mUserScalable
.EqualsLiteral("no")) ||
10603 (metaData
.mUserScalable
.EqualsLiteral("false"))) {
10604 mAllowZoom
= false;
10607 // Resolve viewport-fit value.
10608 // https://drafts.csswg.org/css-round-display/#viewport-fit-descriptor
10609 mViewportFit
= ViewportFitType::Auto
;
10610 if (!metaData
.mViewportFit
.IsEmpty()) {
10611 if (metaData
.mViewportFit
.EqualsLiteral("contain")) {
10612 mViewportFit
= ViewportFitType::Contain
;
10613 } else if (metaData
.mViewportFit
.EqualsLiteral("cover")) {
10614 mViewportFit
= ViewportFitType::Cover
;
10618 mWidthStrEmpty
= metaData
.mWidth
.IsEmpty();
10620 mViewportType
= Specified
;
10625 LayoutDeviceToScreenScale effectiveMinScale
= mScaleMinFloat
;
10626 LayoutDeviceToScreenScale effectiveMaxScale
= mScaleMaxFloat
;
10627 bool effectiveValidMaxScale
= mValidMaxScale
;
10629 nsViewportInfo::ZoomFlag effectiveZoomFlag
=
10630 mAllowZoom
? nsViewportInfo::ZoomFlag::AllowZoom
10631 : nsViewportInfo::ZoomFlag::DisallowZoom
;
10632 if (StaticPrefs::browser_ui_zoom_force_user_scalable()) {
10633 // If the pref to force user-scalable is enabled, we ignore the values
10634 // from the meta-viewport tag for these properties and just assume they
10635 // allow the page to be scalable. Note in particular that this code is
10636 // in the "Specified" branch of the enclosing switch statement, so that
10637 // calls to GetViewportInfo always use the latest value of the
10638 // browser_ui_zoom_force_user_scalable pref. Other codepaths that
10639 // return nsViewportInfo instances are all consistent with
10640 // browser_ui_zoom_force_user_scalable() already.
10641 effectiveMinScale
= ViewportMinScale();
10642 effectiveMaxScale
= ViewportMaxScale();
10643 effectiveValidMaxScale
= true;
10644 effectiveZoomFlag
= nsViewportInfo::ZoomFlag::AllowZoom
;
10647 // Returns extend-zoom value which is MIN(mScaleFloat, mScaleMaxFloat).
10648 auto ComputeExtendZoom
= [&]() -> float {
10649 if (mValidScaleFloat
&& effectiveValidMaxScale
) {
10650 return std::min(mScaleFloat
.scale
, effectiveMaxScale
.scale
);
10652 if (mValidScaleFloat
) {
10653 return mScaleFloat
.scale
;
10655 if (effectiveValidMaxScale
) {
10656 return effectiveMaxScale
.scale
;
10658 return nsViewportInfo::kAuto
;
10661 // Resolving 'extend-to-zoom'
10662 // https://drafts.csswg.org/css-device-adapt/#resolve-extend-to-zoom
10663 float extendZoom
= ComputeExtendZoom();
10665 CSSCoord minWidth
= mMinWidth
;
10666 CSSCoord maxWidth
= mMaxWidth
;
10667 CSSCoord minHeight
= mMinHeight
;
10668 CSSCoord maxHeight
= mMaxHeight
;
10670 // aDisplaySize is in screen pixels; convert them to CSS pixels for the
10671 // viewport size. We need to use this scaled size for any clamping of
10672 // width or height.
10673 CSSSize displaySize
= ScreenSize(aDisplaySize
) / defaultScale
;
10675 // Our min and max width and height values are mostly as specified by
10676 // the viewport declaration, but we make an exception for max width.
10677 // Max width, if auto, and if there's no initial-scale, will be set
10678 // to a default size. This is to support legacy site design with no
10679 // viewport declaration, and to do that using the same scheme as
10680 // Chrome does, in order to maintain web compatibility. Since the
10681 // default size has a complicated calculation, we fixup the maxWidth
10682 // value after setting it, above.
10683 if (maxWidth
== nsViewportInfo::kAuto
&& !mValidScaleFloat
) {
10684 if (bc
&& bc
->TouchEventsOverride() == TouchEventsOverride::Enabled
&&
10686 // If RDM and touch simulation are active, then use the simulated
10687 // screen width to accommodate for cases where the screen width is
10688 // larger than the desktop viewport default.
10689 maxWidth
= nsViewportInfo::Max(
10690 displaySize
.width
, StaticPrefs::browser_viewport_desktopWidth());
10692 maxWidth
= StaticPrefs::browser_viewport_desktopWidth();
10694 // Divide by fullZoom to stretch CSS pixel size of viewport in order
10695 // to keep device pixel size unchanged after full zoom applied.
10697 maxWidth
/= fullZoom
;
10699 // We set minWidth to ExtendToZoom, which will cause our later width
10700 // calculation to expand to maxWidth, if scale restrictions allow it.
10701 minWidth
= nsViewportInfo::kExtendToZoom
;
10704 // Resolve device-width and device-height first.
10705 if (maxWidth
== nsViewportInfo::kDeviceSize
) {
10706 maxWidth
= displaySize
.width
;
10708 if (maxHeight
== nsViewportInfo::kDeviceSize
) {
10709 maxHeight
= displaySize
.height
;
10711 if (extendZoom
== nsViewportInfo::kAuto
) {
10712 if (maxWidth
== nsViewportInfo::kExtendToZoom
) {
10713 maxWidth
= nsViewportInfo::kAuto
;
10715 if (maxHeight
== nsViewportInfo::kExtendToZoom
) {
10716 maxHeight
= nsViewportInfo::kAuto
;
10718 if (minWidth
== nsViewportInfo::kExtendToZoom
) {
10719 minWidth
= maxWidth
;
10721 if (minHeight
== nsViewportInfo::kExtendToZoom
) {
10722 minHeight
= maxHeight
;
10725 CSSSize extendSize
= displaySize
/ extendZoom
;
10726 if (maxWidth
== nsViewportInfo::kExtendToZoom
) {
10727 maxWidth
= extendSize
.width
;
10729 if (maxHeight
== nsViewportInfo::kExtendToZoom
) {
10730 maxHeight
= extendSize
.height
;
10732 if (minWidth
== nsViewportInfo::kExtendToZoom
) {
10733 minWidth
= nsViewportInfo::Max(extendSize
.width
, maxWidth
);
10735 if (minHeight
== nsViewportInfo::kExtendToZoom
) {
10736 minHeight
= nsViewportInfo::Max(extendSize
.height
, maxHeight
);
10740 // Resolve initial width and height from min/max descriptors
10741 // https://drafts.csswg.org/css-device-adapt/#resolve-initial-width-height
10742 CSSCoord width
= nsViewportInfo::kAuto
;
10743 if (minWidth
!= nsViewportInfo::kAuto
||
10744 maxWidth
!= nsViewportInfo::kAuto
) {
10745 width
= nsViewportInfo::Max(
10746 minWidth
, nsViewportInfo::Min(maxWidth
, displaySize
.width
));
10748 CSSCoord height
= nsViewportInfo::kAuto
;
10749 if (minHeight
!= nsViewportInfo::kAuto
||
10750 maxHeight
!= nsViewportInfo::kAuto
) {
10751 height
= nsViewportInfo::Max(
10752 minHeight
, nsViewportInfo::Min(maxHeight
, displaySize
.height
));
10755 // Resolve width value
10756 // https://drafts.csswg.org/css-device-adapt/#resolve-width
10757 if (width
== nsViewportInfo::kAuto
) {
10758 if (height
== nsViewportInfo::kAuto
|| aDisplaySize
.height
== 0) {
10759 width
= displaySize
.width
;
10761 width
= height
* aDisplaySize
.width
/ aDisplaySize
.height
;
10765 // Resolve height value
10766 // https://drafts.csswg.org/css-device-adapt/#resolve-height
10767 if (height
== nsViewportInfo::kAuto
) {
10768 if (aDisplaySize
.width
== 0) {
10769 height
= displaySize
.height
;
10771 height
= width
* aDisplaySize
.height
/ aDisplaySize
.width
;
10774 MOZ_ASSERT(width
!= nsViewportInfo::kAuto
&&
10775 height
!= nsViewportInfo::kAuto
);
10777 CSSSize
size(width
, height
);
10779 CSSToScreenScale scaleFloat
= mScaleFloat
* layoutDeviceScale
;
10780 CSSToScreenScale scaleMinFloat
= effectiveMinScale
* layoutDeviceScale
;
10781 CSSToScreenScale scaleMaxFloat
= effectiveMaxScale
* layoutDeviceScale
;
10783 nsViewportInfo::AutoSizeFlag sizeFlag
=
10784 nsViewportInfo::AutoSizeFlag::FixedSize
;
10785 if (mMaxWidth
== nsViewportInfo::kDeviceSize
||
10786 (mWidthStrEmpty
&& (mMaxHeight
== nsViewportInfo::kDeviceSize
||
10787 mScaleFloat
.scale
== 1.0f
)) ||
10788 (!mWidthStrEmpty
&& mMaxWidth
== nsViewportInfo::kAuto
&&
10790 sizeFlag
= nsViewportInfo::AutoSizeFlag::AutoSize
;
10793 // FIXME: Resolving width and height should be done above 'Resolve width
10794 // value' and 'Resolve height value'.
10795 if (sizeFlag
== nsViewportInfo::AutoSizeFlag::AutoSize
) {
10796 size
= displaySize
;
10799 // The purpose of clamping the viewport width to a minimum size is to
10800 // prevent page authors from setting it to a ridiculously small value.
10801 // If the page is actually being rendered in a very small area (as might
10802 // happen in e.g. Android 8's picture-in-picture mode), we don't want to
10803 // prevent the viewport from taking on that size.
10804 CSSSize effectiveMinSize
= Min(CSSSize(kViewportMinSize
), displaySize
);
10806 size
.width
= clamped(size
.width
, effectiveMinSize
.width
,
10807 float(kViewportMaxSize
.width
));
10809 // Also recalculate the default zoom, if it wasn't specified in the
10810 // metadata, and the width is specified.
10811 if (!mValidScaleFloat
&& !mWidthStrEmpty
) {
10812 CSSToScreenScale
bestFitScale(float(aDisplaySize
.width
) / size
.width
);
10813 scaleFloat
= (scaleFloat
> bestFitScale
) ? scaleFloat
: bestFitScale
;
10816 size
.height
= clamped(size
.height
, effectiveMinSize
.height
,
10817 float(kViewportMaxSize
.height
));
10819 // In cases of user-scalable=no, if we have a positive scale, clamp it to
10820 // min and max, and then use the clamped value for the scale, the min, and
10821 // the max. If we don't have a positive scale, assert that we are setting
10822 // the auto scale flag.
10823 if (effectiveZoomFlag
== nsViewportInfo::ZoomFlag::DisallowZoom
&&
10824 scaleFloat
> CSSToScreenScale(0.0f
)) {
10825 scaleFloat
= scaleMinFloat
= scaleMaxFloat
=
10826 clamped(scaleFloat
, scaleMinFloat
, scaleMaxFloat
);
10829 scaleFloat
> CSSToScreenScale(0.0f
) || !mValidScaleFloat
,
10830 "If we don't have a positive scale, we should be using auto scale.");
10832 // We need to perform a conversion, but only if the initial or maximum
10833 // scale were set explicitly by the user.
10834 if (mValidScaleFloat
&& scaleFloat
>= scaleMinFloat
&&
10835 scaleFloat
<= scaleMaxFloat
) {
10836 CSSSize displaySize
= ScreenSize(aDisplaySize
) / scaleFloat
;
10837 size
.width
= std::max(size
.width
, displaySize
.width
);
10838 size
.height
= std::max(size
.height
, displaySize
.height
);
10839 } else if (effectiveValidMaxScale
) {
10840 CSSSize displaySize
= ScreenSize(aDisplaySize
) / scaleMaxFloat
;
10841 size
.width
= std::max(size
.width
, displaySize
.width
);
10842 size
.height
= std::max(size
.height
, displaySize
.height
);
10845 return nsViewportInfo(
10846 scaleFloat
, scaleMinFloat
, scaleMaxFloat
, size
, sizeFlag
,
10847 mValidScaleFloat
? nsViewportInfo::AutoScaleFlag::FixedScale
10848 : nsViewportInfo::AutoScaleFlag::AutoScale
,
10849 effectiveZoomFlag
, mViewportFit
);
10853 ViewportMetaData
Document::GetViewportMetaData() const {
10854 return mLastModifiedViewportMetaData
? *mLastModifiedViewportMetaData
10855 : ViewportMetaData();
10858 void Document::SetMetaViewportData(UniquePtr
<ViewportMetaData
> aData
) {
10859 mLastModifiedViewportMetaData
= std::move(aData
);
10860 // Trigger recomputation of the nsViewportInfo the next time it's queried.
10861 mViewportType
= Unknown
;
10863 AsyncEventDispatcher::RunDOMEventWhenSafe(
10864 *this, u
"DOMMetaViewportFitChanged"_ns
, CanBubble::eYes
,
10865 ChromeOnlyDispatch::eYes
);
10868 EventListenerManager
* Document::GetOrCreateListenerManager() {
10869 if (!mListenerManager
) {
10871 new EventListenerManager(static_cast<EventTarget
*>(this));
10872 SetFlags(NODE_HAS_LISTENERMANAGER
);
10875 return mListenerManager
;
10878 EventListenerManager
* Document::GetExistingListenerManager() const {
10879 return mListenerManager
;
10882 void Document::GetEventTargetParent(EventChainPreVisitor
& aVisitor
) {
10883 aVisitor
.mCanHandle
= true;
10884 // FIXME! This is a hack to make middle mouse paste working also in Editor.
10886 aVisitor
.mForceContentDispatch
= true;
10888 // Load events must not propagate to |window| object, see bug 335251.
10889 if (aVisitor
.mEvent
->mMessage
!= eLoad
) {
10890 nsGlobalWindowOuter
* window
= nsGlobalWindowOuter::Cast(GetWindow());
10891 aVisitor
.SetParentTarget(
10892 window
? window
->GetTargetForEventTargetChain() : nullptr, false);
10896 already_AddRefed
<Event
> Document::CreateEvent(const nsAString
& aEventType
,
10897 CallerType aCallerType
,
10898 ErrorResult
& rv
) const {
10899 nsPresContext
* presContext
= GetPresContext();
10901 // Create event even without presContext.
10903 EventDispatcher::CreateEvent(const_cast<Document
*>(this), presContext
,
10904 nullptr, aEventType
, aCallerType
);
10906 rv
.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR
);
10909 WidgetEvent
* e
= ev
->WidgetEventPtr();
10910 e
->mFlags
.mBubbles
= false;
10911 e
->mFlags
.mCancelable
= false;
10912 return ev
.forget();
10915 void Document::FlushPendingNotifications(FlushType aType
) {
10916 mozilla::ChangesToFlush
flush(aType
, aType
>= FlushType::Style
);
10917 FlushPendingNotifications(flush
);
10920 void Document::FlushPendingNotifications(mozilla::ChangesToFlush aFlush
) {
10921 FlushType flushType
= aFlush
.mFlushType
;
10923 RefPtr
<Document
> documentOnStack
= this;
10925 // We need to flush the sink for non-HTML documents (because the XML
10926 // parser still does insertion with deferred notifications). We
10927 // also need to flush the sink if this is a layout-related flush, to
10928 // make sure that layout is started as needed. But we can skip that
10929 // part if we have no presshell or if it's already done an initial
10931 if ((!IsHTMLDocument() || (flushType
> FlushType::ContentAndNotify
&&
10932 mPresShell
&& !mPresShell
->DidInitialize())) &&
10933 (mParser
|| mWeakSink
)) {
10934 nsCOMPtr
<nsIContentSink
> sink
;
10936 sink
= mParser
->GetContentSink();
10938 sink
= do_QueryReferent(mWeakSink
);
10940 mWeakSink
= nullptr;
10943 // Determine if it is safe to flush the sink notifications
10944 // by determining if it safe to flush all the presshells.
10945 if (sink
&& (flushType
== FlushType::Content
|| IsSafeToFlush())) {
10946 sink
->FlushPendingNotifications(flushType
);
10950 // Should we be flushing pending binding constructors in here?
10952 if (flushType
<= FlushType::ContentAndNotify
) {
10953 // Nothing to do here
10957 // If we have a parent we must flush the parent too to ensure that our
10958 // container is reflowed if its size was changed.
10960 // We do it only if the subdocument and the parent can observe each other
10961 // synchronously (that is, if we're not cross-origin), to avoid work that is
10962 // not observable, and if the parent document has finished loading all its
10963 // render-blocking stylesheets and may start laying out the document, to avoid
10964 // unnecessary flashes of unstyled content on the parent document. Note that
10965 // this last bit means that size-dependent media queries in this document may
10966 // produce incorrect results temporarily.
10968 // But if it's not safe to flush ourselves, then don't flush the parent, since
10969 // that can cause things like resizes of our frame's widget, which we can't
10970 // handle while flushing is unsafe.
10971 if (StyleOrLayoutObservablyDependsOnParentDocumentLayout() &&
10972 mParentDocument
->MayStartLayout() && IsSafeToFlush()) {
10973 ChangesToFlush parentFlush
= aFlush
;
10974 if (flushType
>= FlushType::Style
) {
10975 // Since media queries mean that a size change of our container can affect
10976 // style, we need to promote a style flush on ourself to a layout flush on
10977 // our parent, since we need our container to be the correct size to
10978 // determine the correct style.
10979 parentFlush
.mFlushType
= std::max(FlushType::Layout
, flushType
);
10981 mParentDocument
->FlushPendingNotifications(parentFlush
);
10984 if (RefPtr
<PresShell
> presShell
= GetPresShell()) {
10985 presShell
->FlushPendingNotifications(aFlush
);
10989 void Document::FlushExternalResources(FlushType aType
) {
10991 aType
>= FlushType::Style
,
10992 "should only need to flush for style or higher in external resources");
10993 if (GetDisplayDocument()) {
10997 auto flush
= [aType
](Document
& aDoc
) {
10998 aDoc
.FlushPendingNotifications(aType
);
10999 return CallState::Continue
;
11002 EnumerateExternalResources(flush
);
11005 void Document::SetXMLDeclaration(const char16_t
* aVersion
,
11006 const char16_t
* aEncoding
,
11007 const int32_t aStandalone
) {
11008 if (!aVersion
|| *aVersion
== '\0') {
11009 mXMLDeclarationBits
= 0;
11013 mXMLDeclarationBits
= XML_DECLARATION_BITS_DECLARATION_EXISTS
;
11015 if (aEncoding
&& *aEncoding
!= '\0') {
11016 mXMLDeclarationBits
|= XML_DECLARATION_BITS_ENCODING_EXISTS
;
11019 if (aStandalone
== 1) {
11020 mXMLDeclarationBits
|= XML_DECLARATION_BITS_STANDALONE_EXISTS
|
11021 XML_DECLARATION_BITS_STANDALONE_YES
;
11022 } else if (aStandalone
== 0) {
11023 mXMLDeclarationBits
|= XML_DECLARATION_BITS_STANDALONE_EXISTS
;
11027 void Document::GetXMLDeclaration(nsAString
& aVersion
, nsAString
& aEncoding
,
11028 nsAString
& aStandalone
) {
11029 aVersion
.Truncate();
11030 aEncoding
.Truncate();
11031 aStandalone
.Truncate();
11033 if (!(mXMLDeclarationBits
& XML_DECLARATION_BITS_DECLARATION_EXISTS
)) {
11037 // always until we start supporting 1.1 etc.
11038 aVersion
.AssignLiteral("1.0");
11040 if (mXMLDeclarationBits
& XML_DECLARATION_BITS_ENCODING_EXISTS
) {
11041 // This is what we have stored, not necessarily what was written
11043 GetCharacterSet(aEncoding
);
11046 if (mXMLDeclarationBits
& XML_DECLARATION_BITS_STANDALONE_EXISTS
) {
11047 if (mXMLDeclarationBits
& XML_DECLARATION_BITS_STANDALONE_YES
) {
11048 aStandalone
.AssignLiteral("yes");
11050 aStandalone
.AssignLiteral("no");
11055 void Document::AddColorSchemeMeta(HTMLMetaElement
& aMeta
) {
11056 mColorSchemeMetaTags
.Insert(aMeta
);
11057 RecomputeColorScheme();
11060 void Document::RemoveColorSchemeMeta(HTMLMetaElement
& aMeta
) {
11061 mColorSchemeMetaTags
.RemoveElement(aMeta
);
11062 RecomputeColorScheme();
11065 void Document::RecomputeColorScheme() {
11066 auto oldColorScheme
= mColorSchemeBits
;
11067 mColorSchemeBits
= 0;
11068 const nsTArray
<HTMLMetaElement
*>& elements
= mColorSchemeMetaTags
;
11069 for (const HTMLMetaElement
* el
: elements
) {
11070 nsAutoString content
;
11071 if (!el
->GetAttr(nsGkAtoms::content
, content
)) {
11075 NS_ConvertUTF16toUTF8
contentU8(content
);
11076 if (Servo_ColorScheme_Parse(&contentU8
, &mColorSchemeBits
)) {
11081 if (mColorSchemeBits
== oldColorScheme
) {
11085 if (nsPresContext
* pc
= GetPresContext()) {
11086 // This affects system colors, which are inherited, so we need to recascade.
11087 pc
->RebuildAllStyleData(nsChangeHint(0), RestyleHint::RecascadeSubtree());
11091 bool Document::IsScriptEnabled() const {
11092 // If this document is sandboxed without 'allow-scripts'
11093 // script is not enabled
11094 if (HasScriptsBlockedBySandbox()) {
11098 nsCOMPtr
<nsIScriptGlobalObject
> globalObject
=
11099 do_QueryInterface(GetInnerWindow());
11100 if (!globalObject
|| !globalObject
->HasJSGlobal()) {
11104 return xpc::Scriptability::Get(globalObject
->GetGlobalJSObjectPreserveColor())
11108 void Document::RetrieveRelevantHeaders(nsIChannel
* aChannel
) {
11109 PRTime modDate
= 0;
11112 nsCOMPtr
<nsIHttpChannel
> httpChannel
;
11113 rv
= GetHttpChannelHelper(aChannel
, getter_AddRefs(httpChannel
));
11114 if (NS_WARN_IF(NS_FAILED(rv
))) {
11120 rv
= httpChannel
->GetResponseHeader("last-modified"_ns
, tmp
);
11122 if (NS_SUCCEEDED(rv
)) {
11124 PRStatus st
= PR_ParseTimeString(tmp
.get(), true, &time
);
11125 if (st
== PR_SUCCESS
) {
11130 static const char* const headers
[] = {
11131 "default-style", "content-style-type", "content-language",
11132 "content-disposition", "refresh", "x-dns-prefetch-control",
11133 "x-frame-options", "origin-trial",
11134 // add more http headers if you need
11135 // XXXbz don't add content-location support without reading bug
11136 // 238654 and its dependencies/dups first.
11139 nsAutoCString headerVal
;
11140 const char* const* name
= headers
;
11142 rv
= httpChannel
->GetResponseHeader(nsDependentCString(*name
), headerVal
);
11143 if (NS_SUCCEEDED(rv
) && !headerVal
.IsEmpty()) {
11144 RefPtr
<nsAtom
> key
= NS_Atomize(*name
);
11145 SetHeaderData(key
, NS_ConvertASCIItoUTF16(headerVal
));
11150 nsCOMPtr
<nsIFileChannel
> fileChannel
= do_QueryInterface(aChannel
);
11152 nsCOMPtr
<nsIFile
> file
;
11153 fileChannel
->GetFile(getter_AddRefs(file
));
11156 rv
= file
->GetLastModifiedTime(&msecs
);
11158 if (NS_SUCCEEDED(rv
)) {
11159 modDate
= msecs
* int64_t(PR_USEC_PER_MSEC
);
11163 nsAutoCString contentDisp
;
11164 rv
= aChannel
->GetContentDispositionHeader(contentDisp
);
11165 if (NS_SUCCEEDED(rv
)) {
11166 SetHeaderData(nsGkAtoms::headerContentDisposition
,
11167 NS_ConvertASCIItoUTF16(contentDisp
));
11172 mLastModified
.Truncate();
11173 if (modDate
!= 0) {
11174 GetFormattedTimeString(modDate
, mLastModified
);
11178 void Document::ProcessMETATag(HTMLMetaElement
* aMetaElement
) {
11179 // set any HTTP-EQUIV data into document's header data as well as url
11180 nsAutoString header
;
11181 aMetaElement
->GetAttr(nsGkAtoms::httpEquiv
, header
);
11182 if (!header
.IsEmpty()) {
11183 // Ignore META REFRESH when document is sandboxed from automatic features.
11184 nsContentUtils::ASCIIToLower(header
);
11185 if (nsGkAtoms::refresh
->Equals(header
) &&
11186 (GetSandboxFlags() & SANDBOXED_AUTOMATIC_FEATURES
)) {
11190 nsAutoString result
;
11191 aMetaElement
->GetAttr(nsGkAtoms::content
, result
);
11192 if (!result
.IsEmpty()) {
11193 RefPtr
<nsAtom
> fieldAtom(NS_Atomize(header
));
11194 SetHeaderData(fieldAtom
, result
);
11198 if (aMetaElement
->AttrValueIs(kNameSpaceID_None
, nsGkAtoms::name
,
11199 nsGkAtoms::handheldFriendly
, eIgnoreCase
)) {
11200 nsAutoString result
;
11201 aMetaElement
->GetAttr(nsGkAtoms::content
, result
);
11202 if (!result
.IsEmpty()) {
11203 nsContentUtils::ASCIIToLower(result
);
11204 SetHeaderData(nsGkAtoms::handheldFriendly
, result
);
11209 already_AddRefed
<Element
> Document::CreateElem(const nsAString
& aName
,
11211 int32_t aNamespaceID
,
11212 const nsAString
* aIs
) {
11214 nsAutoString qName
;
11216 aPrefix
->ToString(qName
);
11219 qName
.Append(aName
);
11221 // Note: "a:b:c" is a valid name in non-namespaces XML, and
11222 // Document::CreateElement can call us with such a name and no prefix,
11223 // which would cause an error if we just used true here.
11224 bool nsAware
= aPrefix
!= nullptr || aNamespaceID
!= GetDefaultNamespaceID();
11225 NS_ASSERTION(NS_SUCCEEDED(nsContentUtils::CheckQName(qName
, nsAware
)),
11226 "Don't pass invalid prefixes to Document::CreateElem, "
11230 RefPtr
<mozilla::dom::NodeInfo
> nodeInfo
;
11231 mNodeInfoManager
->GetNodeInfo(aName
, aPrefix
, aNamespaceID
, ELEMENT_NODE
,
11232 getter_AddRefs(nodeInfo
));
11233 NS_ENSURE_TRUE(nodeInfo
, nullptr);
11235 nsCOMPtr
<Element
> element
;
11236 nsresult rv
= NS_NewElement(getter_AddRefs(element
), nodeInfo
.forget(),
11237 NOT_FROM_PARSER
, aIs
);
11238 return NS_SUCCEEDED(rv
) ? element
.forget() : nullptr;
11241 bool Document::IsSafeToFlush() const {
11242 PresShell
* presShell
= GetPresShell();
11246 return presShell
->IsSafeToFlush();
11249 void Document::Sanitize() {
11250 // Sanitize the document by resetting all (current and former) password fields
11251 // and any form fields with autocomplete=off to their default values. We do
11252 // this now, instead of when the presentation is restored, to offer some
11253 // protection in case there is ever an exploit that allows a cached document
11254 // to be accessed from a different document.
11256 // First locate all input elements, regardless of whether they are
11257 // in a form, and reset the password and autocomplete=off elements.
11259 RefPtr
<nsContentList
> nodes
= GetElementsByTagName(u
"input"_ns
);
11261 nsAutoString value
;
11263 uint32_t length
= nodes
->Length(true);
11264 for (uint32_t i
= 0; i
< length
; ++i
) {
11265 NS_ASSERTION(nodes
->Item(i
), "null item in node list!");
11267 RefPtr
<HTMLInputElement
> input
=
11268 HTMLInputElement::FromNodeOrNull(nodes
->Item(i
));
11269 if (!input
) continue;
11271 input
->GetAttr(nsGkAtoms::autocomplete
, value
);
11272 if (value
.LowerCaseEqualsLiteral("off") || input
->HasBeenTypePassword()) {
11277 // Now locate all _form_ elements that have autocomplete=off and reset them
11278 nodes
= GetElementsByTagName(u
"form"_ns
);
11280 length
= nodes
->Length(true);
11281 for (uint32_t i
= 0; i
< length
; ++i
) {
11282 // Reset() may change the list dynamically.
11283 RefPtr
<HTMLFormElement
> form
=
11284 HTMLFormElement::FromNodeOrNull(nodes
->Item(i
));
11285 if (!form
) continue;
11287 form
->GetAttr(nsGkAtoms::autocomplete
, value
);
11288 if (value
.LowerCaseEqualsLiteral("off")) form
->Reset();
11292 void Document::EnumerateSubDocuments(SubDocEnumFunc aCallback
) {
11293 if (!mSubDocuments
) {
11297 // PLDHashTable::Iterator can't handle modifications while iterating so we
11298 // copy all entries to an array first before calling any callbacks.
11299 AutoTArray
<RefPtr
<Document
>, 8> subdocs
;
11300 for (auto iter
= mSubDocuments
->Iter(); !iter
.Done(); iter
.Next()) {
11301 auto entry
= static_cast<SubDocMapEntry
*>(iter
.Get());
11302 if (Document
* subdoc
= entry
->mSubDocument
) {
11303 subdocs
.AppendElement(subdoc
);
11306 for (auto& subdoc
: subdocs
) {
11307 if (aCallback(*subdoc
) == CallState::Stop
) {
11313 void Document::CollectDescendantDocuments(
11314 nsTArray
<RefPtr
<Document
>>& aDescendants
, nsDocTestFunc aCallback
) const {
11315 if (!mSubDocuments
) {
11319 for (auto iter
= mSubDocuments
->Iter(); !iter
.Done(); iter
.Next()) {
11320 auto entry
= static_cast<SubDocMapEntry
*>(iter
.Get());
11321 const Document
* subdoc
= entry
->mSubDocument
;
11323 if (aCallback(subdoc
)) {
11324 aDescendants
.AppendElement(entry
->mSubDocument
);
11326 subdoc
->CollectDescendantDocuments(aDescendants
, aCallback
);
11331 bool Document::CanSavePresentation(nsIRequest
* aNewRequest
,
11332 uint32_t& aBFCacheCombo
,
11333 bool aIncludeSubdocuments
,
11334 bool aAllowUnloadListeners
) {
11337 if (!IsBFCachingAllowed()) {
11338 aBFCacheCombo
|= BFCacheStatus::NOT_ALLOWED
;
11343 if (MOZ_UNLIKELY(MOZ_LOG_TEST(gPageCacheLog
, LogLevel::Verbose
))) {
11344 if (mDocumentURI
) {
11345 mDocumentURI
->GetSpec(uri
);
11349 if (EventHandlingSuppressed()) {
11350 MOZ_LOG(gPageCacheLog
, mozilla::LogLevel::Verbose
,
11351 ("Save of %s blocked on event handling suppression", uri
.get()));
11352 aBFCacheCombo
|= BFCacheStatus::EVENT_HANDLING_SUPPRESSED
;
11356 // Do not allow suspended windows to be placed in the
11357 // bfcache. This method is also used to verify a document
11358 // coming out of the bfcache is ok to restore, though. So
11359 // we only want to block suspend windows that aren't also
11361 nsPIDOMWindowInner
* win
= GetInnerWindow();
11362 if (win
&& win
->IsSuspended() && !win
->IsFrozen()) {
11363 MOZ_LOG(gPageCacheLog
, mozilla::LogLevel::Verbose
,
11364 ("Save of %s blocked on suspended Window", uri
.get()));
11365 aBFCacheCombo
|= BFCacheStatus::SUSPENDED
;
11369 nsCOMPtr
<nsIChannel
> channel
= do_QueryInterface(aNewRequest
);
11370 bool thirdParty
= false;
11371 // Currently some other mobile browsers seem to bfcache only cross-domain
11372 // pages, but bfcache those also when there are unload event listeners, so
11373 // this is trying to match that behavior as much as possible.
11374 bool allowUnloadListeners
=
11375 aAllowUnloadListeners
&&
11376 StaticPrefs::docshell_shistory_bfcache_allow_unload_listeners() &&
11377 (!channel
|| (NS_SUCCEEDED(NodePrincipal()->IsThirdPartyChannel(
11378 channel
, &thirdParty
)) &&
11381 // Check our event listener manager for unload/beforeunload listeners.
11382 nsCOMPtr
<EventTarget
> piTarget
= do_QueryInterface(mScriptGlobalObject
);
11383 if (!allowUnloadListeners
&& piTarget
) {
11384 EventListenerManager
* manager
= piTarget
->GetExistingListenerManager();
11386 if (manager
->HasUnloadListeners()) {
11387 MOZ_LOG(gPageCacheLog
, mozilla::LogLevel::Verbose
,
11388 ("Save of %s blocked due to unload handlers", uri
.get()));
11389 aBFCacheCombo
|= BFCacheStatus::UNLOAD_LISTENER
;
11392 if (manager
->HasBeforeUnloadListeners()) {
11393 if (!mozilla::SessionHistoryInParent() ||
11395 docshell_shistory_bfcache_ship_allow_beforeunload_listeners()) {
11397 gPageCacheLog
, mozilla::LogLevel::Verbose
,
11398 ("Save of %s blocked due to beforeUnload handlers", uri
.get()));
11399 aBFCacheCombo
|= BFCacheStatus::BEFOREUNLOAD_LISTENER
;
11406 // Check if we have pending network requests
11407 nsCOMPtr
<nsILoadGroup
> loadGroup
= GetDocumentLoadGroup();
11409 nsCOMPtr
<nsISimpleEnumerator
> requests
;
11410 loadGroup
->GetRequests(getter_AddRefs(requests
));
11412 bool hasMore
= false;
11414 // We want to bail out if we have any requests other than aNewRequest (or
11415 // in the case when aNewRequest is a part of a multipart response the base
11416 // channel the multipart response is coming in on).
11417 nsCOMPtr
<nsIChannel
> baseChannel
;
11418 nsCOMPtr
<nsIMultiPartChannel
> part(do_QueryInterface(aNewRequest
));
11420 part
->GetBaseChannel(getter_AddRefs(baseChannel
));
11423 while (NS_SUCCEEDED(requests
->HasMoreElements(&hasMore
)) && hasMore
) {
11424 nsCOMPtr
<nsISupports
> elem
;
11425 requests
->GetNext(getter_AddRefs(elem
));
11427 nsCOMPtr
<nsIRequest
> request
= do_QueryInterface(elem
);
11428 if (request
&& request
!= aNewRequest
&& request
!= baseChannel
) {
11429 // Favicon loads don't need to block caching.
11430 nsCOMPtr
<nsIChannel
> channel
= do_QueryInterface(request
);
11432 nsCOMPtr
<nsILoadInfo
> li
= channel
->LoadInfo();
11433 if (li
->InternalContentPolicyType() ==
11434 nsIContentPolicy::TYPE_INTERNAL_IMAGE_FAVICON
) {
11439 if (MOZ_UNLIKELY(MOZ_LOG_TEST(gPageCacheLog
, LogLevel::Verbose
))) {
11440 nsAutoCString requestName
;
11441 request
->GetName(requestName
);
11442 MOZ_LOG(gPageCacheLog
, LogLevel::Verbose
,
11443 ("Save of %s blocked because document has request %s",
11444 uri
.get(), requestName
.get()));
11446 aBFCacheCombo
|= BFCacheStatus::REQUEST
;
11452 // Check if we have active GetUserMedia use
11453 if (MediaManager::Exists() && win
&&
11454 MediaManager::Get()->IsWindowStillActive(win
->WindowID())) {
11455 MOZ_LOG(gPageCacheLog
, mozilla::LogLevel::Verbose
,
11456 ("Save of %s blocked due to GetUserMedia", uri
.get()));
11457 aBFCacheCombo
|= BFCacheStatus::ACTIVE_GET_USER_MEDIA
;
11462 // Check if we have active PeerConnections
11463 if (win
&& win
->HasActivePeerConnections()) {
11464 MOZ_LOG(gPageCacheLog
, mozilla::LogLevel::Verbose
,
11465 ("Save of %s blocked due to PeerConnection", uri
.get()));
11466 aBFCacheCombo
|= BFCacheStatus::ACTIVE_PEER_CONNECTION
;
11469 #endif // MOZ_WEBRTC
11471 // Don't save presentations for documents containing EME content, so that
11472 // CDMs reliably shutdown upon user navigation.
11473 if (ContainsEMEContent()) {
11474 aBFCacheCombo
|= BFCacheStatus::CONTAINS_EME_CONTENT
;
11478 // Don't save presentations for documents containing MSE content, to
11479 // reduce memory usage.
11480 if (ContainsMSEContent()) {
11481 MOZ_LOG(gPageCacheLog
, mozilla::LogLevel::Verbose
,
11482 ("Save of %s blocked due to MSE use", uri
.get()));
11483 aBFCacheCombo
|= BFCacheStatus::CONTAINS_MSE_CONTENT
;
11487 if (aIncludeSubdocuments
&& mSubDocuments
) {
11488 for (auto iter
= mSubDocuments
->Iter(); !iter
.Done(); iter
.Next()) {
11489 auto entry
= static_cast<SubDocMapEntry
*>(iter
.Get());
11490 Document
* subdoc
= entry
->mSubDocument
;
11492 uint32_t subDocBFCacheCombo
= 0;
11493 // The aIgnoreRequest we were passed is only for us, so don't pass it on.
11495 subdoc
? subdoc
->CanSavePresentation(nullptr, subDocBFCacheCombo
,
11496 true, allowUnloadListeners
)
11499 MOZ_LOG(gPageCacheLog
, mozilla::LogLevel::Verbose
,
11500 ("Save of %s blocked due to subdocument blocked", uri
.get()));
11501 aBFCacheCombo
|= subDocBFCacheCombo
;
11507 if (!mozilla::BFCacheInParent()) {
11508 // BFCache is currently not compatible with remote subframes (bug 1609324)
11509 if (RefPtr
<BrowsingContext
> browsingContext
= GetBrowsingContext()) {
11510 for (auto& child
: browsingContext
->Children()) {
11511 if (!child
->IsInProcess()) {
11512 aBFCacheCombo
|= BFCacheStatus::CONTAINS_REMOTE_SUBFRAMES
;
11521 auto* globalWindow
= nsGlobalWindowInner::Cast(win
);
11522 #ifdef MOZ_WEBSPEECH
11523 if (globalWindow
->HasActiveSpeechSynthesis()) {
11524 MOZ_LOG(gPageCacheLog
, mozilla::LogLevel::Verbose
,
11525 ("Save of %s blocked due to Speech use", uri
.get()));
11526 aBFCacheCombo
|= BFCacheStatus::HAS_ACTIVE_SPEECH_SYNTHESIS
;
11530 if (globalWindow
->HasUsedVR()) {
11531 MOZ_LOG(gPageCacheLog
, mozilla::LogLevel::Verbose
,
11532 ("Save of %s blocked due to having used VR", uri
.get()));
11533 aBFCacheCombo
|= BFCacheStatus::HAS_USED_VR
;
11537 if (win
->HasActiveLocks()) {
11539 gPageCacheLog
, mozilla::LogLevel::Verbose
,
11540 ("Save of %s blocked due to having active lock requests", uri
.get()));
11541 aBFCacheCombo
|= BFCacheStatus::ACTIVE_LOCK
;
11545 if (win
->HasActiveWebTransports()) {
11546 MOZ_LOG(gPageCacheLog
, mozilla::LogLevel::Verbose
,
11547 ("Save of %s blocked due to WebTransport", uri
.get()));
11548 aBFCacheCombo
|= BFCacheStatus::ACTIVE_WEBTRANSPORT
;
11556 void Document::Destroy() {
11557 // The ContentViewer wants to release the document now. So, tell our content
11558 // to drop any references to the document so that it can be destroyed.
11559 if (mIsGoingAway
) {
11563 ReportDocumentUseCounters();
11565 SetDevToolsWatchingDOMMutations(false);
11567 mIsGoingAway
= true;
11569 ScriptLoader()->Destroy();
11570 SetScriptGlobalObject(nullptr);
11571 RemovedFromDocShell();
11573 bool oldVal
= mInUnlinkOrDeletion
;
11574 mInUnlinkOrDeletion
= true;
11577 uint32_t oldChildCount
= GetChildCount();
11580 for (nsIContent
* child
= GetFirstChild(); child
;
11581 child
= child
->GetNextSibling()) {
11582 child
->DestroyContent();
11583 MOZ_ASSERT(child
->GetParentNode() == this);
11585 MOZ_ASSERT(oldChildCount
== GetChildCount());
11586 MOZ_ASSERT(!mSubDocuments
|| mSubDocuments
->EntryCount() == 0);
11588 mInUnlinkOrDeletion
= oldVal
;
11590 mLayoutHistoryState
= nullptr;
11592 if (mOriginalDocument
) {
11593 mOriginalDocument
->mLatestStaticClone
= nullptr;
11596 if (IsStaticDocument()) {
11597 RemoveProperty(nsGkAtoms::printisfocuseddoc
);
11598 RemoveProperty(nsGkAtoms::printselectionranges
);
11601 // Shut down our external resource map. We might not need this for
11602 // leak-fixing if we fix nsDocumentViewer to do cycle-collection, but
11603 // tearing down all those frame trees right now is the right thing to do.
11604 mExternalResourceMap
.Shutdown();
11606 // Manually break cycles via promise's global object pointer.
11607 mReadyForIdle
= nullptr;
11608 mOrientationPendingPromise
= nullptr;
11610 // To break cycles.
11611 mPreloadService
.ClearAllPreloads();
11613 if (mDocumentL10n
) {
11614 mDocumentL10n
->Destroy();
11622 void Document::RemovedFromDocShell() {
11623 mEditingState
= EditingState::eOff
;
11625 if (mRemovedFromDocShell
) return;
11627 mRemovedFromDocShell
= true;
11628 NotifyActivityChanged();
11630 for (nsIContent
* child
= GetFirstChild(); child
;
11631 child
= child
->GetNextSibling()) {
11632 child
->SaveSubtreeState();
11635 nsIDocShell
* docShell
= GetDocShell();
11637 docShell
->SynchronizeLayoutHistoryState();
11641 already_AddRefed
<nsILayoutHistoryState
> Document::GetLayoutHistoryState()
11643 nsCOMPtr
<nsILayoutHistoryState
> state
;
11644 if (!mScriptGlobalObject
) {
11645 state
= mLayoutHistoryState
;
11647 nsCOMPtr
<nsIDocShell
> docShell(mDocumentContainer
);
11649 docShell
->GetLayoutHistoryState(getter_AddRefs(state
));
11653 return state
.forget();
11656 void Document::EnsureOnloadBlocker() {
11657 // If mScriptGlobalObject is null, we shouldn't be messing with the loadgroup
11658 // -- it's not ours.
11659 if (mOnloadBlockCount
!= 0 && mScriptGlobalObject
) {
11660 nsCOMPtr
<nsILoadGroup
> loadGroup
= GetDocumentLoadGroup();
11662 // Check first to see if mOnloadBlocker is in the loadgroup.
11663 nsCOMPtr
<nsISimpleEnumerator
> requests
;
11664 loadGroup
->GetRequests(getter_AddRefs(requests
));
11666 bool hasMore
= false;
11667 while (NS_SUCCEEDED(requests
->HasMoreElements(&hasMore
)) && hasMore
) {
11668 nsCOMPtr
<nsISupports
> elem
;
11669 requests
->GetNext(getter_AddRefs(elem
));
11670 nsCOMPtr
<nsIRequest
> request
= do_QueryInterface(elem
);
11671 if (request
&& request
== mOnloadBlocker
) {
11676 // Not in the loadgroup, so add it.
11677 loadGroup
->AddRequest(mOnloadBlocker
, nullptr);
11682 void Document::BlockOnload() {
11683 if (mDisplayDocument
) {
11684 mDisplayDocument
->BlockOnload();
11688 // If mScriptGlobalObject is null, we shouldn't be messing with the loadgroup
11689 // -- it's not ours.
11690 if (mOnloadBlockCount
== 0 && mScriptGlobalObject
) {
11691 if (nsCOMPtr
<nsILoadGroup
> loadGroup
= GetDocumentLoadGroup()) {
11692 loadGroup
->AddRequest(mOnloadBlocker
, nullptr);
11695 ++mOnloadBlockCount
;
11698 void Document::UnblockOnload(bool aFireSync
) {
11699 if (mDisplayDocument
) {
11700 mDisplayDocument
->UnblockOnload(aFireSync
);
11704 --mOnloadBlockCount
;
11706 if (mOnloadBlockCount
== 0) {
11707 if (mScriptGlobalObject
) {
11708 // Only manipulate the loadgroup in this case, because if
11709 // mScriptGlobalObject is null, it's not ours.
11711 // Increment mOnloadBlockCount, since DoUnblockOnload will decrement it
11712 ++mOnloadBlockCount
;
11715 PostUnblockOnloadEvent();
11717 } else if (mIsBeingUsedAsImage
) {
11718 // To correctly unblock onload for a document that contains an SVG
11719 // image, we need to know when all of the SVG document's resources are
11720 // done loading, in a way comparable to |window.onload|. We fire this
11721 // event to indicate that the SVG should be considered fully loaded.
11722 // Because scripting is disabled on SVG-as-image documents, this event
11723 // is not accessible to content authors. (See bug 837315.)
11724 RefPtr
<AsyncEventDispatcher
> asyncDispatcher
=
11725 new AsyncEventDispatcher(this, u
"MozSVGAsImageDocumentLoad"_ns
,
11726 CanBubble::eNo
, ChromeOnlyDispatch::eNo
);
11727 asyncDispatcher
->PostDOMEvent();
11732 class nsUnblockOnloadEvent
: public Runnable
{
11734 explicit nsUnblockOnloadEvent(Document
* aDoc
)
11735 : mozilla::Runnable("nsUnblockOnloadEvent"), mDoc(aDoc
) {}
11736 NS_IMETHOD
Run() override
{
11737 mDoc
->DoUnblockOnload();
11742 RefPtr
<Document
> mDoc
;
11745 void Document::PostUnblockOnloadEvent() {
11746 MOZ_RELEASE_ASSERT(NS_IsMainThread());
11747 nsCOMPtr
<nsIRunnable
> evt
= new nsUnblockOnloadEvent(this);
11748 nsresult rv
= Dispatch(evt
.forget());
11749 if (NS_SUCCEEDED(rv
)) {
11750 // Stabilize block count so we don't post more events while this one is up
11751 ++mOnloadBlockCount
;
11753 NS_WARNING("failed to dispatch nsUnblockOnloadEvent");
11757 void Document::DoUnblockOnload() {
11758 MOZ_ASSERT(!mDisplayDocument
, "Shouldn't get here for resource document");
11759 MOZ_ASSERT(mOnloadBlockCount
!= 0,
11760 "Shouldn't have a count of zero here, since we stabilized in "
11761 "PostUnblockOnloadEvent");
11763 --mOnloadBlockCount
;
11765 if (mOnloadBlockCount
!= 0) {
11766 // We blocked again after the last unblock. Nothing to do here. We'll
11767 // post a new event when we unblock again.
11771 // If mScriptGlobalObject is null, we shouldn't be messing with the loadgroup
11772 // -- it's not ours.
11773 if (mScriptGlobalObject
) {
11774 if (nsCOMPtr
<nsILoadGroup
> loadGroup
= GetDocumentLoadGroup()) {
11775 loadGroup
->RemoveRequest(mOnloadBlocker
, nullptr, NS_OK
);
11780 nsIContent
* Document::GetContentInThisDocument(nsIFrame
* aFrame
) const {
11781 for (nsIFrame
* f
= aFrame
; f
;
11782 f
= nsLayoutUtils::GetParentOrPlaceholderForCrossDoc(f
)) {
11783 nsIContent
* content
= f
->GetContent();
11788 if (content
->OwnerDoc() == this) {
11791 // We must be in a subdocument so jump directly to the root frame.
11792 // GetParentOrPlaceholderForCrossDoc gets called immediately to jump up to
11793 // the containing document.
11794 f
= f
->PresContext()->GetPresShell()->GetRootFrame();
11800 void Document::DispatchPageTransition(EventTarget
* aDispatchTarget
,
11801 const nsAString
& aType
, bool aInFrameSwap
,
11802 bool aPersisted
, bool aOnlySystemGroup
) {
11803 if (!aDispatchTarget
) {
11807 PageTransitionEventInit init
;
11808 init
.mBubbles
= true;
11809 init
.mCancelable
= true;
11810 init
.mPersisted
= aPersisted
;
11811 init
.mInFrameSwap
= aInFrameSwap
;
11813 RefPtr
<PageTransitionEvent
> event
=
11814 PageTransitionEvent::Constructor(this, aType
, init
);
11816 event
->SetTrusted(true);
11817 event
->SetTarget(this);
11818 if (aOnlySystemGroup
) {
11819 event
->WidgetEventPtr()->mFlags
.mOnlySystemGroupDispatchInContent
= true;
11821 EventDispatcher::DispatchDOMEvent(aDispatchTarget
, nullptr, event
, nullptr,
11825 void Document::OnPageShow(bool aPersisted
, EventTarget
* aDispatchStartTarget
,
11826 bool aOnlySystemGroup
) {
11827 if (MOZ_LOG_TEST(gSHIPBFCacheLog
, LogLevel::Debug
)) {
11829 if (GetDocumentURI()) {
11830 uri
= GetDocumentURI()->GetSpecOrDefault();
11832 MOZ_LOG(gSHIPBFCacheLog
, LogLevel::Debug
,
11833 ("Document::OnPageShow [%s] persisted=%i", uri
.get(), aPersisted
));
11836 const bool inFrameLoaderSwap
= !!aDispatchStartTarget
;
11837 MOZ_DIAGNOSTIC_ASSERT(
11838 inFrameLoaderSwap
==
11839 (mDocumentContainer
&& mDocumentContainer
->InFrameSwap()));
11841 Element
* root
= GetRootElement();
11842 if (aPersisted
&& root
) {
11843 // Send out notifications that our <link> elements are attached.
11844 RefPtr
<nsContentList
> links
=
11845 NS_GetContentList(root
, kNameSpaceID_XHTML
, u
"link"_ns
);
11847 uint32_t linkCount
= links
->Length(true);
11848 for (uint32_t i
= 0; i
< linkCount
; ++i
) {
11849 static_cast<HTMLLinkElement
*>(links
->Item(i
, false))->LinkAdded();
11854 if (!inFrameLoaderSwap
) {
11856 ImageTracker()->SetAnimatingState(true);
11859 // Set mIsShowing before firing events, in case those event handlers
11864 UpdateVisibilityState();
11867 NotifyActivityChanged();
11869 auto notifyExternal
= [aPersisted
](Document
& aExternalResource
) {
11870 aExternalResource
.OnPageShow(aPersisted
, nullptr);
11871 return CallState::Continue
;
11873 EnumerateExternalResources(notifyExternal
);
11875 if (mAnimationController
) {
11876 mAnimationController
->OnPageShow();
11879 if (!mIsBeingUsedAsImage
) {
11880 // Dispatch observer notification to notify observers page is shown.
11881 nsCOMPtr
<nsIObserverService
> os
= mozilla::services::GetObserverService();
11883 nsIPrincipal
* principal
= NodePrincipal();
11884 os
->NotifyObservers(ToSupports(this),
11885 principal
->IsSystemPrincipal() ? "chrome-page-shown"
11886 : "content-page-shown",
11890 nsCOMPtr
<EventTarget
> target
= aDispatchStartTarget
;
11892 target
= do_QueryInterface(GetWindow());
11894 DispatchPageTransition(target
, u
"pageshow"_ns
, inFrameLoaderSwap
,
11895 aPersisted
, aOnlySystemGroup
);
11899 static void DispatchFullscreenChange(Document
& aDocument
, nsINode
* aTarget
) {
11900 if (nsPresContext
* presContext
= aDocument
.GetPresContext()) {
11901 auto pendingEvent
= MakeUnique
<PendingFullscreenEvent
>(
11902 FullscreenEventType::Change
, &aDocument
, aTarget
);
11903 presContext
->RefreshDriver()->ScheduleFullscreenEvent(
11904 std::move(pendingEvent
));
11908 void Document::OnPageHide(bool aPersisted
, EventTarget
* aDispatchStartTarget
,
11909 bool aOnlySystemGroup
) {
11910 if (MOZ_LOG_TEST(gSHIPBFCacheLog
, LogLevel::Debug
)) {
11912 if (GetDocumentURI()) {
11913 uri
= GetDocumentURI()->GetSpecOrDefault();
11915 MOZ_LOG(gSHIPBFCacheLog
, LogLevel::Debug
,
11916 ("Document::OnPageHide %s persisted=%i", uri
.get(), aPersisted
));
11919 const bool inFrameLoaderSwap
= !!aDispatchStartTarget
;
11920 MOZ_DIAGNOSTIC_ASSERT(
11921 inFrameLoaderSwap
==
11922 (mDocumentContainer
&& mDocumentContainer
->InFrameSwap()));
11924 if (mAnimationController
) {
11925 mAnimationController
->OnPageHide();
11928 if (!inFrameLoaderSwap
) {
11930 // We do not stop the animations (bug 1024343) when the page is refreshing
11931 // while being dragged out.
11932 ImageTracker()->SetAnimatingState(false);
11935 // Set mIsShowing before firing events, in case those event handlers
11937 mIsShowing
= false;
11943 if (!mIsBeingUsedAsImage
) {
11944 // Dispatch observer notification to notify observers page is hidden.
11945 nsCOMPtr
<nsIObserverService
> os
= mozilla::services::GetObserverService();
11947 nsIPrincipal
* principal
= NodePrincipal();
11948 os
->NotifyObservers(ToSupports(this),
11949 principal
->IsSystemPrincipal()
11950 ? "chrome-page-hidden"
11951 : "content-page-hidden",
11955 // Now send out a PageHide event.
11956 nsCOMPtr
<EventTarget
> target
= aDispatchStartTarget
;
11958 target
= do_QueryInterface(GetWindow());
11961 PageUnloadingEventTimeStamp
timeStamp(this);
11962 DispatchPageTransition(target
, u
"pagehide"_ns
, inFrameLoaderSwap
,
11963 aPersisted
, aOnlySystemGroup
);
11967 if (!inFrameLoaderSwap
) {
11968 UpdateVisibilityState();
11971 auto notifyExternal
= [aPersisted
](Document
& aExternalResource
) {
11972 aExternalResource
.OnPageHide(aPersisted
, nullptr);
11973 return CallState::Continue
;
11975 EnumerateExternalResources(notifyExternal
);
11976 NotifyActivityChanged();
11978 ClearPendingFullscreenRequests(this);
11979 if (Fullscreen()) {
11980 // If this document was fullscreen, we should exit fullscreen in this
11981 // doctree branch. This ensures that if the user navigates while in
11982 // fullscreen mode we don't leave its still visible ancestor documents
11983 // in fullscreen mode. So exit fullscreen in the document's fullscreen
11984 // root document, as this will exit fullscreen in all the root's
11985 // descendant documents. Note that documents are removed from the
11986 // doctree by the time OnPageHide() is called, so we must store a
11987 // reference to the root (in Document::mFullscreenRoot) since we can't
11988 // just traverse the doctree to get the root.
11989 Document::ExitFullscreenInDocTree(this);
11991 // Since the document is removed from the doctree before OnPageHide() is
11992 // called, ExitFullscreen() can't traverse from the root down to *this*
11993 // document, so we must manually call CleanupFullscreenState() below too.
11994 // Note that CleanupFullscreenState() clears Document::mFullscreenRoot,
11995 // so we *must* call it after ExitFullscreen(), not before.
11996 // OnPageHide() is called in every hidden (i.e. descendant) document,
11997 // so calling CleanupFullscreenState() here will ensure all hidden
11998 // documents have their fullscreen state reset.
11999 CleanupFullscreenState();
12001 // The fullscreenchange event is to be queued in the refresh driver,
12002 // however a hidden page wouldn't trigger that again, so it makes no
12003 // sense to dispatch such event here.
12007 void Document::WillDispatchMutationEvent(nsINode
* aTarget
) {
12009 mSubtreeModifiedDepth
!= 0 || mSubtreeModifiedTargets
.Count() == 0,
12010 "mSubtreeModifiedTargets not cleared after dispatching?");
12011 ++mSubtreeModifiedDepth
;
12013 // MayDispatchMutationEvent is often called just before this method,
12014 // so it has already appended the node to mSubtreeModifiedTargets.
12015 int32_t count
= mSubtreeModifiedTargets
.Count();
12016 if (!count
|| mSubtreeModifiedTargets
[count
- 1] != aTarget
) {
12017 mSubtreeModifiedTargets
.AppendObject(aTarget
);
12022 void Document::MutationEventDispatched(nsINode
* aTarget
) {
12023 if (--mSubtreeModifiedDepth
) {
12027 int32_t count
= mSubtreeModifiedTargets
.Count();
12032 nsPIDOMWindowInner
* window
= GetInnerWindow();
12034 !window
->HasMutationListeners(NS_EVENT_BITS_MUTATION_SUBTREEMODIFIED
)) {
12035 mSubtreeModifiedTargets
.Clear();
12039 nsCOMArray
<nsINode
> realTargets
;
12040 for (nsINode
* possibleTarget
: mSubtreeModifiedTargets
) {
12041 if (possibleTarget
->ChromeOnlyAccess()) {
12045 nsINode
* commonAncestor
= nullptr;
12046 int32_t realTargetCount
= realTargets
.Count();
12047 for (int32_t j
= 0; j
< realTargetCount
; ++j
) {
12048 commonAncestor
= nsContentUtils::GetClosestCommonInclusiveAncestor(
12049 possibleTarget
, realTargets
[j
]);
12050 if (commonAncestor
) {
12051 realTargets
.ReplaceObjectAt(commonAncestor
, j
);
12055 if (!commonAncestor
) {
12056 realTargets
.AppendObject(possibleTarget
);
12060 mSubtreeModifiedTargets
.Clear();
12062 for (const nsCOMPtr
<nsINode
>& target
: realTargets
) {
12063 InternalMutationEvent
mutation(true, eLegacySubtreeModified
);
12064 // MOZ_KnownLive due to bug 1620312
12065 AsyncEventDispatcher::RunDOMEventWhenSafe(MOZ_KnownLive(*target
), mutation
);
12069 void Document::DestroyElementMaps() {
12071 mStyledLinksCleared
= true;
12073 mStyledLinks
.Clear();
12074 // Notify ID change listeners before clearing the identifier map.
12075 for (auto iter
= mIdentifierMap
.Iter(); !iter
.Done(); iter
.Next()) {
12076 iter
.Get()->ClearAndNotify();
12078 mIdentifierMap
.Clear();
12079 mComposedShadowRoots
.Clear();
12080 mResponsiveContent
.Clear();
12081 IncrementExpandoGeneration(*this);
12084 void Document::RefreshLinkHrefs() {
12085 // Get a list of all links we know about. We will reset them, which will
12086 // remove them from the document, so we need a copy of what is in the
12088 const nsTArray
<Link
*> linksToNotify
= ToArray(mStyledLinks
);
12090 // Reset all of our styled links.
12091 nsAutoScriptBlocker scriptBlocker
;
12092 for (Link
* link
: linksToNotify
) {
12093 link
->ResetLinkState(true);
12097 nsresult
Document::CloneDocHelper(Document
* clone
) const {
12098 clone
->mIsStaticDocument
= mCreatingStaticClone
;
12101 nsresult rv
= clone
->Init(NodePrincipal(), mPartitionedPrincipal
);
12102 NS_ENSURE_SUCCESS(rv
, rv
);
12104 if (mCreatingStaticClone
) {
12105 if (mOriginalDocument
) {
12106 clone
->mOriginalDocument
= mOriginalDocument
;
12108 clone
->mOriginalDocument
= const_cast<Document
*>(this);
12110 clone
->mOriginalDocument
->mLatestStaticClone
= clone
;
12111 clone
->mOriginalDocument
->mStaticCloneCount
++;
12113 nsCOMPtr
<nsILoadGroup
> loadGroup
;
12115 // |mDocumentContainer| is the container of the document that is being
12116 // created and not the original container. See CreateStaticClone function().
12117 nsCOMPtr
<nsIDocumentLoader
> docLoader(mDocumentContainer
);
12119 docLoader
->GetLoadGroup(getter_AddRefs(loadGroup
));
12121 nsCOMPtr
<nsIChannel
> channel
= GetChannel();
12122 nsCOMPtr
<nsIURI
> uri
;
12124 NS_GetFinalChannelURI(channel
, getter_AddRefs(uri
));
12126 uri
= Document::GetDocumentURI();
12128 clone
->mChannel
= channel
;
12129 clone
->mShouldResistFingerprinting
= mShouldResistFingerprinting
;
12131 clone
->ResetToURI(uri
, loadGroup
, NodePrincipal(), mPartitionedPrincipal
);
12134 clone
->mIsSrcdocDocument
= mIsSrcdocDocument
;
12135 clone
->SetContainer(mDocumentContainer
);
12137 // Setup the navigation time. This will be needed by any animations in the
12138 // document, even if they are only paused.
12139 MOZ_ASSERT(!clone
->GetNavigationTiming(),
12140 "Navigation time was already set?");
12142 RefPtr
<nsDOMNavigationTiming
> timing
=
12143 mTiming
->CloneNavigationTime(nsDocShell::Cast(clone
->GetDocShell()));
12144 clone
->SetNavigationTiming(timing
);
12146 clone
->SetCsp(mCSP
);
12149 // Now ensure that our clone has the same URI, base URI, and principal as us.
12150 // We do this after the mCreatingStaticClone block above, because that block
12151 // can set the base URI to an incorrect value in cases when base URI
12152 // information came from the channel. So we override explicitly, and do it
12153 // for all these properties, in case ResetToURI messes with any of the rest of
12155 clone
->SetDocumentURI(Document::GetDocumentURI());
12156 clone
->SetChromeXHRDocURI(mChromeXHRDocURI
);
12157 clone
->mActiveStoragePrincipal
= mActiveStoragePrincipal
;
12158 clone
->mActiveCookiePrincipal
= mActiveCookiePrincipal
;
12159 // NOTE(emilio): Intentionally setting this to the GetDocBaseURI rather than
12160 // just mDocumentBaseURI, so that srcdoc iframes get the right base URI even
12161 // when printed standalone via window.print() (where there won't be a parent
12162 // document to grab the URI from).
12163 clone
->mDocumentBaseURI
= GetDocBaseURI();
12164 clone
->SetChromeXHRDocBaseURI(mChromeXHRDocBaseURI
);
12165 clone
->mReferrerInfo
=
12166 static_cast<dom::ReferrerInfo
*>(mReferrerInfo
.get())->Clone();
12167 clone
->mPreloadReferrerInfo
= clone
->mReferrerInfo
;
12169 bool hasHadScriptObject
= true;
12170 nsIScriptGlobalObject
* scriptObject
=
12171 GetScriptHandlingObject(hasHadScriptObject
);
12172 NS_ENSURE_STATE(scriptObject
|| !hasHadScriptObject
);
12173 if (mCreatingStaticClone
) {
12174 // If we're doing a static clone (print, print preview), then we're going to
12175 // be setting a scope object after the clone. It's better to set it only
12176 // once, so we don't do that here. However, we do want to act as if there is
12177 // a script handling object. So we set mHasHadScriptHandlingObject.
12178 clone
->mHasHadScriptHandlingObject
= true;
12179 } else if (scriptObject
) {
12180 clone
->SetScriptHandlingObject(scriptObject
);
12182 clone
->SetScopeObject(GetScopeObject());
12184 // Make the clone a data document
12185 clone
->SetLoadedAsData(
12187 /* aConsiderForMemoryReporting */ !mCreatingStaticClone
);
12191 // State from Document
12192 clone
->mCharacterSet
= mCharacterSet
;
12193 clone
->mCharacterSetSource
= mCharacterSetSource
;
12194 clone
->SetCompatibilityMode(mCompatMode
);
12195 clone
->mBidiOptions
= mBidiOptions
;
12196 clone
->mContentLanguage
= mContentLanguage
;
12197 clone
->SetContentType(GetContentTypeInternal());
12198 clone
->mSecurityInfo
= mSecurityInfo
;
12200 // State from Document
12201 clone
->mType
= mType
;
12202 clone
->mXMLDeclarationBits
= mXMLDeclarationBits
;
12203 clone
->mBaseTarget
= mBaseTarget
;
12208 void Document::NotifyLoading(bool aNewParentIsLoading
,
12209 const ReadyState
& aCurrentState
,
12210 ReadyState aNewState
) {
12211 // Mirror the top-level loading state down to all subdocuments
12212 bool was_loading
= mAncestorIsLoading
||
12213 aCurrentState
== READYSTATE_LOADING
||
12214 aCurrentState
== READYSTATE_INTERACTIVE
;
12215 bool is_loading
= aNewParentIsLoading
|| aNewState
== READYSTATE_LOADING
||
12216 aNewState
== READYSTATE_INTERACTIVE
; // new value for state
12217 bool set_load_state
= was_loading
!= is_loading
;
12220 gTimeoutDeferralLog
, mozilla::LogLevel::Debug
,
12221 ("NotifyLoading for doc %p: currentAncestor: %d, newParent: %d, "
12222 "currentState %d newState: %d, was_loading: %d, is_loading: %d, "
12223 "set_load_state: %d",
12224 (void*)this, mAncestorIsLoading
, aNewParentIsLoading
, (int)aCurrentState
,
12225 (int)aNewState
, was_loading
, is_loading
, set_load_state
));
12227 mAncestorIsLoading
= aNewParentIsLoading
;
12228 if (set_load_state
&& StaticPrefs::dom_timeout_defer_during_load()) {
12229 // Tell our innerwindow (and thus TimeoutManager)
12230 nsPIDOMWindowInner
* inner
= GetInnerWindow();
12232 inner
->SetActiveLoadingState(is_loading
);
12234 BrowsingContext
* context
= GetBrowsingContext();
12236 // Don't use PreOrderWalk to mirror this down; go down one level as a
12237 // time so we can set mAncestorIsLoading and take into account the
12238 // readystates of the subdocument. In the child process it will call
12239 // NotifyLoading() to notify the innerwindow/TimeoutManager, and then
12240 // iterate it's children
12241 for (auto& child
: context
->Children()) {
12242 MOZ_LOG(gTimeoutDeferralLog
, mozilla::LogLevel::Debug
,
12243 ("bc: %p SetAncestorLoading(%d)", (void*)child
, is_loading
));
12244 // Setting ancestor loading on a discarded browsing context has no
12246 Unused
<< child
->SetAncestorLoading(is_loading
);
12252 void Document::SetReadyStateInternal(ReadyState aReadyState
,
12253 bool aUpdateTimingInformation
) {
12254 if (aReadyState
== READYSTATE_UNINITIALIZED
) {
12255 // Transition back to uninitialized happens only to keep assertions happy
12256 // right before readyState transitions to something else. Make this
12257 // transition undetectable by Web content.
12258 mReadyState
= aReadyState
;
12262 if (IsTopLevelContentDocument()) {
12263 if (aReadyState
== READYSTATE_LOADING
) {
12264 AddToplevelLoadingDocument(this);
12265 } else if (aReadyState
== READYSTATE_COMPLETE
) {
12266 RemoveToplevelLoadingDocument(this);
12270 if (aUpdateTimingInformation
&& READYSTATE_LOADING
== aReadyState
) {
12271 SetLoadingOrRestoredFromBFCacheTimeStampToNow();
12273 NotifyLoading(mAncestorIsLoading
, mReadyState
, aReadyState
);
12274 mReadyState
= aReadyState
;
12275 if (aUpdateTimingInformation
&& mTiming
) {
12276 switch (aReadyState
) {
12277 case READYSTATE_LOADING
:
12278 mTiming
->NotifyDOMLoading(GetDocumentURI());
12280 case READYSTATE_INTERACTIVE
:
12281 mTiming
->NotifyDOMInteractive(GetDocumentURI());
12283 case READYSTATE_COMPLETE
:
12284 mTiming
->NotifyDOMComplete(GetDocumentURI());
12287 MOZ_ASSERT_UNREACHABLE("Unexpected ReadyState value");
12291 // At the time of loading start, we don't have timing object, record time.
12293 if (READYSTATE_INTERACTIVE
== aReadyState
&&
12294 NodePrincipal()->IsSystemPrincipal()) {
12295 if (!mXULPersist
&& XRE_IsParentProcess()) {
12296 mXULPersist
= new XULPersist(this);
12297 mXULPersist
->Init();
12299 if (!mChromeObserver
) {
12300 mChromeObserver
= new ChromeObserver(this);
12301 mChromeObserver
->Init();
12305 if (aUpdateTimingInformation
) {
12306 RecordNavigationTiming(aReadyState
);
12309 AsyncEventDispatcher::RunDOMEventWhenSafe(
12310 *this, u
"readystatechange"_ns
, CanBubble::eNo
, ChromeOnlyDispatch::eNo
);
12313 void Document::GetReadyState(nsAString
& aReadyState
) const {
12314 switch (mReadyState
) {
12315 case READYSTATE_LOADING
:
12316 aReadyState
.AssignLiteral(u
"loading");
12318 case READYSTATE_INTERACTIVE
:
12319 aReadyState
.AssignLiteral(u
"interactive");
12321 case READYSTATE_COMPLETE
:
12322 aReadyState
.AssignLiteral(u
"complete");
12325 aReadyState
.AssignLiteral(u
"uninitialized");
12329 void Document::SuppressEventHandling(uint32_t aIncrease
) {
12330 mEventsSuppressed
+= aIncrease
;
12331 if (mEventsSuppressed
== aIncrease
) {
12332 if (WindowGlobalChild
* wgc
= GetWindowGlobalChild()) {
12333 wgc
->BlockBFCacheFor(BFCacheStatus::EVENT_HANDLING_SUPPRESSED
);
12336 UpdateFrameRequestCallbackSchedulingState();
12337 for (uint32_t i
= 0; i
< aIncrease
; ++i
) {
12338 ScriptLoader()->AddExecuteBlocker();
12341 auto suppressInSubDoc
= [aIncrease
](Document
& aSubDoc
) {
12342 aSubDoc
.SuppressEventHandling(aIncrease
);
12343 return CallState::Continue
;
12346 EnumerateSubDocuments(suppressInSubDoc
);
12349 void Document::NotifyAbortedLoad() {
12350 // If we still have outstanding work blocking DOMContentLoaded,
12351 // then don't try to change the readystate now, but wait until
12352 // they finish and then do so.
12353 if (mBlockDOMContentLoaded
> 0 && !mDidFireDOMContentLoaded
) {
12354 mSetCompleteAfterDOMContentLoaded
= true;
12358 // Otherwise we're fully done at this point, so set the
12359 // readystate to complete.
12360 if (GetReadyStateEnum() == Document::READYSTATE_INTERACTIVE
) {
12361 SetReadyStateInternal(Document::READYSTATE_COMPLETE
);
12365 MOZ_CAN_RUN_SCRIPT
static void FireOrClearDelayedEvents(
12366 nsTArray
<nsCOMPtr
<Document
>>&& aDocuments
, bool aFireEvents
) {
12367 RefPtr
<nsFocusManager
> fm
= nsFocusManager::GetFocusManager();
12368 if (MOZ_UNLIKELY(!fm
)) {
12372 nsTArray
<nsCOMPtr
<Document
>> documents
= std::move(aDocuments
);
12373 for (uint32_t i
= 0; i
< documents
.Length(); ++i
) {
12374 nsCOMPtr
<Document
> document
= std::move(documents
[i
]);
12375 // NB: Don't bother trying to fire delayed events on documents that were
12376 // closed before this event ran.
12377 if (!document
->EventHandlingSuppressed()) {
12378 fm
->FireDelayedEvents(document
);
12379 RefPtr
<PresShell
> presShell
= document
->GetPresShell();
12381 // Only fire events for active documents.
12382 bool fire
= aFireEvents
&& document
->GetInnerWindow() &&
12383 document
->GetInnerWindow()->IsCurrentInnerWindow();
12384 presShell
->FireOrClearDelayedEvents(fire
);
12386 document
->FireOrClearPostMessageEvents(aFireEvents
);
12391 void Document::PreloadPictureClosed() {
12392 MOZ_ASSERT(mPreloadPictureDepth
> 0);
12393 mPreloadPictureDepth
--;
12394 if (mPreloadPictureDepth
== 0) {
12395 mPreloadPictureFoundSource
.SetIsVoid(true);
12399 void Document::PreloadPictureImageSource(const nsAString
& aSrcsetAttr
,
12400 const nsAString
& aSizesAttr
,
12401 const nsAString
& aTypeAttr
,
12402 const nsAString
& aMediaAttr
) {
12403 // Nested pictures are not valid syntax, so while we'll eventually load them,
12404 // it's not worth tracking sources mixed between nesting levels to preload
12405 // them effectively.
12406 if (mPreloadPictureDepth
== 1 && mPreloadPictureFoundSource
.IsVoid()) {
12407 // <picture> selects the first matching source, so if this returns a URI we
12408 // needn't consider new sources until a new <picture> is encountered.
12409 bool found
= HTMLImageElement::SelectSourceForTagWithAttrs(
12410 this, true, VoidString(), aSrcsetAttr
, aSizesAttr
, aTypeAttr
,
12411 aMediaAttr
, mPreloadPictureFoundSource
);
12412 if (found
&& mPreloadPictureFoundSource
.IsVoid()) {
12413 // Found an empty source, which counts
12414 mPreloadPictureFoundSource
.SetIsVoid(false);
12419 already_AddRefed
<nsIURI
> Document::ResolvePreloadImage(
12420 nsIURI
* aBaseURI
, const nsAString
& aSrcAttr
, const nsAString
& aSrcsetAttr
,
12421 const nsAString
& aSizesAttr
, bool* aIsImgSet
) {
12422 nsString sourceURL
;
12424 if (mPreloadPictureDepth
== 1 && !mPreloadPictureFoundSource
.IsVoid()) {
12425 // We're in a <picture> element and found a URI from a source previous to
12426 // this image, use it.
12427 sourceURL
= mPreloadPictureFoundSource
;
12430 // Otherwise try to use this <img> as a source
12431 HTMLImageElement::SelectSourceForTagWithAttrs(
12432 this, false, aSrcAttr
, aSrcsetAttr
, aSizesAttr
, VoidString(),
12433 VoidString(), sourceURL
);
12434 isImgSet
= !aSrcsetAttr
.IsEmpty();
12437 // Empty sources are not loaded by <img> (i.e. not resolved to the baseURI)
12438 if (sourceURL
.IsEmpty()) {
12442 // Construct into URI using passed baseURI (the parser may know of base URI
12443 // changes that have not reached us)
12445 nsCOMPtr
<nsIURI
> uri
;
12446 rv
= nsContentUtils::NewURIWithDocumentCharset(getter_AddRefs(uri
), sourceURL
,
12448 if (NS_FAILED(rv
)) {
12452 *aIsImgSet
= isImgSet
;
12454 // We don't clear mPreloadPictureFoundSource because subsequent <img> tags in
12455 // this this <picture> share the same <sources> (though this is not valid per
12457 return uri
.forget();
12460 void Document::PreLoadImage(nsIURI
* aUri
, const nsAString
& aCrossOriginAttr
,
12461 ReferrerPolicyEnum aReferrerPolicy
, bool aIsImgSet
,
12462 bool aLinkPreload
, uint64_t aEarlyHintPreloaderId
) {
12463 nsLoadFlags loadFlags
= nsIRequest::LOAD_NORMAL
|
12464 nsContentUtils::CORSModeToLoadImageFlags(
12465 Element::StringToCORSMode(aCrossOriginAttr
));
12467 nsContentPolicyType policyType
=
12468 aIsImgSet
? nsIContentPolicy::TYPE_IMAGESET
12469 : nsIContentPolicy::TYPE_INTERNAL_IMAGE_PRELOAD
;
12471 nsCOMPtr
<nsIReferrerInfo
> referrerInfo
=
12472 ReferrerInfo::CreateFromDocumentAndPolicyOverride(this, aReferrerPolicy
);
12474 RefPtr
<imgRequestProxy
> request
;
12476 nsLiteralString initiator
= aEarlyHintPreloaderId
12477 ? u
"early-hints"_ns
12478 : (aLinkPreload
? u
"link"_ns
: u
"img"_ns
);
12480 nsresult rv
= nsContentUtils::LoadImage(
12481 aUri
, static_cast<nsINode
*>(this), this, NodePrincipal(), 0, referrerInfo
,
12482 nullptr /* no observer */, loadFlags
, initiator
, getter_AddRefs(request
),
12483 policyType
, false /* urgent */, aLinkPreload
, aEarlyHintPreloaderId
);
12485 // Pin image-reference to avoid evicting it from the img-cache before
12486 // the "real" load occurs. Unpinned in DispatchContentLoadedEvents and
12488 if (!aLinkPreload
&& NS_SUCCEEDED(rv
)) {
12489 mPreloadingImages
.InsertOrUpdate(aUri
, std::move(request
));
12493 void Document::MaybePreLoadImage(nsIURI
* aUri
,
12494 const nsAString
& aCrossOriginAttr
,
12495 ReferrerPolicyEnum aReferrerPolicy
,
12496 bool aIsImgSet
, bool aLinkPreload
) {
12497 const CORSMode corsMode
= dom::Element::StringToCORSMode(aCrossOriginAttr
);
12498 if (aLinkPreload
) {
12499 // Check if the image was already preloaded in this document to avoid
12500 // duplicate preloading.
12501 PreloadHashKey key
=
12502 PreloadHashKey::CreateAsImage(aUri
, NodePrincipal(), corsMode
);
12503 if (!mPreloadService
.PreloadExists(key
)) {
12504 PreLoadImage(aUri
, aCrossOriginAttr
, aReferrerPolicy
, aIsImgSet
,
12510 // Early exit if the img is already present in the img-cache
12511 // which indicates that the "real" load has already started and
12512 // that we shouldn't preload it.
12513 if (nsContentUtils::IsImageAvailable(aUri
, NodePrincipal(), corsMode
, this)) {
12517 // Image not in cache - trigger preload
12518 PreLoadImage(aUri
, aCrossOriginAttr
, aReferrerPolicy
, aIsImgSet
, aLinkPreload
,
12522 void Document::MaybePreconnect(nsIURI
* aOrigURI
, mozilla::CORSMode aCORSMode
) {
12523 if (!StaticPrefs::network_preconnect()) {
12527 NS_MutateURI
mutator(aOrigURI
);
12528 if (NS_FAILED(mutator
.GetStatus())) {
12532 // The URI created here is used in 2 contexts. One is nsISpeculativeConnect
12533 // which ignores the path and uses only the origin. The other is for the
12534 // document mPreloadedPreconnects de-duplication hash. Anonymous vs
12535 // non-Anonymous preconnects create different connections on the wire and
12536 // therefore should not be considred duplicates of each other and we
12537 // normalize the path before putting it in the hash to accomplish that.
12539 if (aCORSMode
== CORS_ANONYMOUS
) {
12540 mutator
.SetPathQueryRef("/anonymous"_ns
);
12542 mutator
.SetPathQueryRef("/"_ns
);
12545 nsCOMPtr
<nsIURI
> uri
;
12546 nsresult rv
= mutator
.Finalize(uri
);
12547 if (NS_FAILED(rv
)) {
12551 const bool existingEntryFound
=
12552 mPreloadedPreconnects
.WithEntryHandle(uri
, [](auto&& entry
) {
12556 entry
.Insert(true);
12559 if (existingEntryFound
) {
12563 nsCOMPtr
<nsISpeculativeConnect
> speculator
=
12564 mozilla::components::IO::Service();
12569 OriginAttributes oa
;
12570 StoragePrincipalHelper::GetOriginAttributesForNetworkState(this, oa
);
12571 speculator
->SpeculativeConnectWithOriginAttributesNative(
12572 uri
, std::move(oa
), nullptr, aCORSMode
== CORS_ANONYMOUS
);
12575 void Document::ForgetImagePreload(nsIURI
* aURI
) {
12576 // Checking count is faster than hashing the URI in the common
12577 // case of empty table.
12578 if (mPreloadingImages
.Count() != 0) {
12579 nsCOMPtr
<imgIRequest
> req
;
12580 mPreloadingImages
.Remove(aURI
, getter_AddRefs(req
));
12582 // Make sure to cancel the request so imagelib knows it's gone.
12583 req
->CancelAndForgetObserver(NS_BINDING_ABORTED
);
12588 void Document::UpdateDocumentStates(DocumentState aMaybeChangedStates
,
12590 const DocumentState oldStates
= mState
;
12591 if (aMaybeChangedStates
.HasAtLeastOneOfStates(
12592 DocumentState::ALL_LOCALEDIR_BITS
)) {
12593 mState
&= ~DocumentState::ALL_LOCALEDIR_BITS
;
12594 if (IsDocumentRightToLeft()) {
12595 mState
|= DocumentState::RTL_LOCALE
;
12597 mState
|= DocumentState::LTR_LOCALE
;
12601 if (aMaybeChangedStates
.HasAtLeastOneOfStates(DocumentState::LWTHEME
)) {
12602 if (ComputeDocumentLWTheme()) {
12603 mState
|= DocumentState::LWTHEME
;
12605 mState
&= ~DocumentState::LWTHEME
;
12609 if (aMaybeChangedStates
.HasState(DocumentState::WINDOW_INACTIVE
)) {
12610 BrowsingContext
* bc
= GetBrowsingContext();
12611 if (!bc
|| !bc
->GetIsActiveBrowserWindow()) {
12612 mState
|= DocumentState::WINDOW_INACTIVE
;
12614 mState
&= ~DocumentState::WINDOW_INACTIVE
;
12618 const DocumentState changedStates
= oldStates
^ mState
;
12619 if (aNotify
&& !changedStates
.IsEmpty()) {
12620 if (PresShell
* ps
= GetObservingPresShell()) {
12621 ps
->DocumentStatesChanged(changedStates
);
12629 * Stub for LoadSheet(), since all we want is to get the sheet into
12630 * the CSSLoader's style cache
12632 class StubCSSLoaderObserver final
: public nsICSSLoaderObserver
{
12633 ~StubCSSLoaderObserver() = default;
12637 StyleSheetLoaded(StyleSheet
*, bool, nsresult
) override
{ return NS_OK
; }
12640 NS_IMPL_ISUPPORTS(StubCSSLoaderObserver
, nsICSSLoaderObserver
)
12644 SheetPreloadStatus
Document::PreloadStyle(
12645 nsIURI
* uri
, const Encoding
* aEncoding
, const nsAString
& aCrossOriginAttr
,
12646 const enum ReferrerPolicy aReferrerPolicy
, const nsAString
& aNonce
,
12647 const nsAString
& aIntegrity
, css::StylePreloadKind aKind
,
12648 uint64_t aEarlyHintPreloaderId
, const nsAString
& aFetchPriority
) {
12649 MOZ_ASSERT(aKind
!= css::StylePreloadKind::None
);
12651 // The CSSLoader will retain this object after we return.
12652 nsCOMPtr
<nsICSSLoaderObserver
> obs
= new StubCSSLoaderObserver();
12654 nsCOMPtr
<nsIReferrerInfo
> referrerInfo
=
12655 ReferrerInfo::CreateFromDocumentAndPolicyOverride(this, aReferrerPolicy
);
12657 // Charset names are always ASCII.
12658 auto result
= CSSLoader()->LoadSheet(
12659 uri
, aKind
, aEncoding
, referrerInfo
, obs
, aEarlyHintPreloaderId
,
12660 Element::StringToCORSMode(aCrossOriginAttr
), aNonce
, aIntegrity
,
12661 nsGenericHTMLElement::ToFetchPriority(aFetchPriority
));
12662 if (result
.isErr()) {
12663 return SheetPreloadStatus::Errored
;
12665 RefPtr
<StyleSheet
> sheet
= result
.unwrap();
12666 if (sheet
->IsComplete()) {
12667 return SheetPreloadStatus::AlreadyComplete
;
12669 return SheetPreloadStatus::InProgress
;
12672 RefPtr
<StyleSheet
> Document::LoadChromeSheetSync(nsIURI
* uri
) {
12674 ->LoadSheetSync(uri
, css::eAuthorSheetFeatures
)
12675 .unwrapOr(nullptr);
12678 void Document::ResetDocumentDirection() {
12679 if (!nsContentUtils::IsChromeDoc(this)) {
12682 UpdateDocumentStates(DocumentState::ALL_LOCALEDIR_BITS
, true);
12685 bool Document::IsDocumentRightToLeft() {
12686 if (!nsContentUtils::IsChromeDoc(this)) {
12689 // setting the localedir attribute on the root element forces a
12690 // specific direction for the document.
12691 Element
* element
= GetRootElement();
12693 static Element::AttrValuesArray strings
[] = {nsGkAtoms::ltr
, nsGkAtoms::rtl
,
12695 switch (element
->FindAttrValueIn(kNameSpaceID_None
, nsGkAtoms::localedir
,
12696 strings
, eCaseMatters
)) {
12702 break; // otherwise, not a valid value, so fall through
12706 if (!mDocumentURI
->SchemeIs("chrome") && !mDocumentURI
->SchemeIs("about") &&
12707 !mDocumentURI
->SchemeIs("resource")) {
12711 return intl::LocaleService::GetInstance()->IsAppLocaleRTL();
12714 class nsDelayedEventDispatcher
: public Runnable
{
12716 explicit nsDelayedEventDispatcher(nsTArray
<nsCOMPtr
<Document
>>&& aDocuments
)
12717 : mozilla::Runnable("nsDelayedEventDispatcher"),
12718 mDocuments(std::move(aDocuments
)) {}
12719 virtual ~nsDelayedEventDispatcher() = default;
12721 // MOZ_CAN_RUN_SCRIPT_BOUNDARY until Runnable::Run is MOZ_CAN_RUN_SCRIPT. See
12723 MOZ_CAN_RUN_SCRIPT_BOUNDARY NS_IMETHOD
Run() override
{
12724 FireOrClearDelayedEvents(std::move(mDocuments
), true);
12729 nsTArray
<nsCOMPtr
<Document
>> mDocuments
;
12732 static void GetAndUnsuppressSubDocuments(
12733 Document
& aDocument
, nsTArray
<nsCOMPtr
<Document
>>& aDocuments
) {
12734 if (aDocument
.EventHandlingSuppressed() > 0) {
12735 aDocument
.DecreaseEventSuppression();
12736 aDocument
.ScriptLoader()->RemoveExecuteBlocker();
12738 aDocuments
.AppendElement(&aDocument
);
12739 auto recurse
= [&aDocuments
](Document
& aSubDoc
) {
12740 GetAndUnsuppressSubDocuments(aSubDoc
, aDocuments
);
12741 return CallState::Continue
;
12743 aDocument
.EnumerateSubDocuments(recurse
);
12746 void Document::UnsuppressEventHandlingAndFireEvents(bool aFireEvents
) {
12747 nsTArray
<nsCOMPtr
<Document
>> documents
;
12748 GetAndUnsuppressSubDocuments(*this, documents
);
12750 for (nsCOMPtr
<Document
>& doc
: documents
) {
12751 if (!doc
->EventHandlingSuppressed()) {
12752 if (WindowGlobalChild
* wgc
= doc
->GetWindowGlobalChild()) {
12753 wgc
->UnblockBFCacheFor(BFCacheStatus::EVENT_HANDLING_SUPPRESSED
);
12756 MOZ_ASSERT(NS_IsMainThread());
12757 nsTArray
<RefPtr
<net::ChannelEventQueue
>> queues
=
12758 std::move(doc
->mSuspendedQueues
);
12759 for (net::ChannelEventQueue
* queue
: queues
) {
12763 // If there have been any events driven by the refresh driver which were
12764 // delayed due to events being suppressed in this document, make sure
12765 // there is a refresh scheduled soon so the events will run.
12766 if (doc
->mHasDelayedRefreshEvent
) {
12767 doc
->mHasDelayedRefreshEvent
= false;
12769 if (doc
->mPresShell
) {
12770 nsRefreshDriver
* rd
=
12771 doc
->mPresShell
->GetPresContext()->RefreshDriver();
12772 rd
->RunDelayedEventsSoon();
12779 MOZ_RELEASE_ASSERT(NS_IsMainThread());
12780 nsCOMPtr
<nsIRunnable
> ded
=
12781 new nsDelayedEventDispatcher(std::move(documents
));
12782 Dispatch(ded
.forget());
12784 FireOrClearDelayedEvents(std::move(documents
), false);
12788 bool Document::AreClipboardCommandsUnconditionallyEnabled() const {
12789 return IsHTMLOrXHTML() && !nsContentUtils::IsChromeDoc(this);
12792 void Document::AddSuspendedChannelEventQueue(net::ChannelEventQueue
* aQueue
) {
12793 MOZ_ASSERT(NS_IsMainThread());
12794 MOZ_ASSERT(EventHandlingSuppressed());
12795 mSuspendedQueues
.AppendElement(aQueue
);
12798 bool Document::SuspendPostMessageEvent(PostMessageEvent
* aEvent
) {
12799 MOZ_ASSERT(NS_IsMainThread());
12801 if (EventHandlingSuppressed() || !mSuspendedPostMessageEvents
.IsEmpty()) {
12802 mSuspendedPostMessageEvents
.AppendElement(aEvent
);
12808 void Document::FireOrClearPostMessageEvents(bool aFireEvents
) {
12809 nsTArray
<RefPtr
<PostMessageEvent
>> events
=
12810 std::move(mSuspendedPostMessageEvents
);
12813 for (PostMessageEvent
* event
: events
) {
12819 void Document::SetSuppressedEventListener(EventListener
* aListener
) {
12820 mSuppressedEventListener
= aListener
;
12821 auto setOnSubDocs
= [&](Document
& aDocument
) {
12822 aDocument
.SetSuppressedEventListener(aListener
);
12823 return CallState::Continue
;
12825 EnumerateSubDocuments(setOnSubDocs
);
12828 bool Document::IsActive() const {
12829 return mDocumentContainer
&& !mRemovedFromDocShell
&& GetBrowsingContext() &&
12830 !GetBrowsingContext()->IsInBFCache();
12833 nsISupports
* Document::GetCurrentContentSink() {
12834 return mParser
? mParser
->GetContentSink() : nullptr;
12837 Document
* Document::GetTemplateContentsOwner() {
12838 if (!mTemplateContentsOwner
) {
12839 bool hasHadScriptObject
= true;
12840 nsIScriptGlobalObject
* scriptObject
=
12841 GetScriptHandlingObject(hasHadScriptObject
);
12843 nsCOMPtr
<Document
> document
;
12844 nsresult rv
= NS_NewDOMDocument(
12845 getter_AddRefs(document
),
12846 u
""_ns
, // aNamespaceURI
12847 u
""_ns
, // aQualifiedName
12848 nullptr, // aDoctype
12849 Document::GetDocumentURI(), Document::GetDocBaseURI(), NodePrincipal(),
12850 true, // aLoadedAsData
12851 scriptObject
, // aEventObject
12852 IsHTMLDocument() ? DocumentFlavorHTML
: DocumentFlavorXML
);
12853 NS_ENSURE_SUCCESS(rv
, nullptr);
12855 mTemplateContentsOwner
= document
;
12856 NS_ENSURE_TRUE(mTemplateContentsOwner
, nullptr);
12858 if (!scriptObject
) {
12859 mTemplateContentsOwner
->SetScopeObject(GetScopeObject());
12862 mTemplateContentsOwner
->mHasHadScriptHandlingObject
= hasHadScriptObject
;
12864 // Set |mTemplateContentsOwner| as the template contents owner of itself so
12865 // that it is the template contents owner of nested template elements.
12866 mTemplateContentsOwner
->mTemplateContentsOwner
= mTemplateContentsOwner
;
12869 MOZ_ASSERT(mTemplateContentsOwner
->IsTemplateContentsOwner());
12870 return mTemplateContentsOwner
;
12873 // https://html.spec.whatwg.org/#the-autofocus-attribute
12874 void Document::ElementWithAutoFocusInserted(Element
* aAutoFocusCandidate
) {
12875 BrowsingContext
* bc
= GetBrowsingContext();
12880 // If target is not fully active, then return.
12881 if (!IsCurrentActiveDocument()) {
12885 // If target's active sandboxing flag set has the sandboxed automatic features
12886 // browsing context flag, then return.
12887 if (GetSandboxFlags() & SANDBOXED_AUTOMATIC_FEATURES
) {
12891 // For each ancestorBC of target's browsing context's ancestor browsing
12892 // contexts: if ancestorBC's active document's origin is not same origin with
12893 // target's origin, then return.
12895 BrowsingContext
* parent
= bc
->GetParent();
12899 // AncestorBC is not the same site
12900 if (!parent
->IsInProcess()) {
12904 Document
* currentDocument
= bc
->GetDocument();
12905 if (!currentDocument
) {
12909 Document
* parentDocument
= parent
->GetDocument();
12910 if (!parentDocument
) {
12915 if (!currentDocument
->NodePrincipal()->Equals(
12916 parentDocument
->NodePrincipal())) {
12922 MOZ_ASSERT(bc
->IsTop());
12924 Document
* topDocument
= bc
->GetDocument();
12925 MOZ_ASSERT(topDocument
);
12926 topDocument
->AppendAutoFocusCandidateToTopDocument(aAutoFocusCandidate
);
12929 void Document::ScheduleFlushAutoFocusCandidates() {
12930 MOZ_ASSERT(mPresShell
&& mPresShell
->DidInitialize());
12931 MOZ_ASSERT(GetBrowsingContext()->IsTop());
12932 if (nsRefreshDriver
* rd
= mPresShell
->GetRefreshDriver()) {
12933 rd
->ScheduleAutoFocusFlush(this);
12937 void Document::AppendAutoFocusCandidateToTopDocument(
12938 Element
* aAutoFocusCandidate
) {
12939 MOZ_ASSERT(GetBrowsingContext()->IsTop());
12940 if (mAutoFocusFired
) {
12944 if (!HasAutoFocusCandidates()) {
12945 // PresShell may be initialized later
12946 if (mPresShell
&& mPresShell
->DidInitialize()) {
12947 ScheduleFlushAutoFocusCandidates();
12951 nsWeakPtr element
= do_GetWeakReference(aAutoFocusCandidate
);
12952 mAutoFocusCandidates
.RemoveElement(element
);
12953 mAutoFocusCandidates
.AppendElement(element
);
12956 void Document::SetAutoFocusFired() {
12957 mAutoFocusCandidates
.Clear();
12958 mAutoFocusFired
= true;
12961 // https://html.spec.whatwg.org/#flush-autofocus-candidates
12962 void Document::FlushAutoFocusCandidates() {
12963 MOZ_ASSERT(GetBrowsingContext()->IsTop());
12964 if (mAutoFocusFired
) {
12972 MOZ_ASSERT(HasAutoFocusCandidates());
12973 MOZ_ASSERT(mPresShell
->DidInitialize());
12975 nsCOMPtr
<nsPIDOMWindowOuter
> topWindow
= GetWindow();
12976 // We should be the top document
12983 // Trying to find the top window (equivalent to window.top).
12984 nsCOMPtr
<nsPIDOMWindowOuter
> top
= topWindow
->GetInProcessTop();
12985 MOZ_ASSERT(topWindow
== top
);
12989 // Don't steal the focus from the user
12990 if (topWindow
->GetFocusedElement()) {
12991 SetAutoFocusFired();
12995 MOZ_ASSERT(mDocumentURI
);
12997 // GetRef never fails
12998 nsresult rv
= mDocumentURI
->GetRef(ref
);
12999 if (NS_SUCCEEDED(rv
) &&
13000 nsContentUtils::GetTargetElement(this, NS_ConvertUTF8toUTF16(ref
))) {
13001 SetAutoFocusFired();
13005 nsTObserverArray
<nsWeakPtr
>::ForwardIterator
iter(mAutoFocusCandidates
);
13006 while (iter
.HasMore()) {
13007 nsCOMPtr
<Element
> autoFocusElement
= do_QueryReferent(iter
.GetNext());
13008 if (!autoFocusElement
) {
13011 RefPtr
<Document
> autoFocusElementDoc
= autoFocusElement
->OwnerDoc();
13012 // Get the latest info about the frame and allow scripts
13013 // to run which might affect the focusability of this element.
13014 autoFocusElementDoc
->FlushPendingNotifications(FlushType::Frames
);
13016 // Above layout flush may cause the PresShell to disappear.
13021 // Re-get the element because the ownerDoc() might have changed
13022 autoFocusElementDoc
= autoFocusElement
->OwnerDoc();
13023 BrowsingContext
* bc
= autoFocusElementDoc
->GetBrowsingContext();
13028 // If doc is not fully active, then remove element from candidates, and
13030 if (!autoFocusElementDoc
->IsCurrentActiveDocument()) {
13035 nsCOMPtr
<nsIContentSink
> sink
=
13036 do_QueryInterface(autoFocusElementDoc
->GetCurrentContentSink());
13038 nsHtml5TreeOpExecutor
* executor
=
13039 static_cast<nsHtml5TreeOpExecutor
*>(sink
->AsExecutor());
13041 // This is a HTML5 document
13042 MOZ_ASSERT(autoFocusElementDoc
->IsHTMLDocument());
13043 // If doc's script-blocking style sheet counter is greater than 0, th
13045 if (executor
->WaitForPendingSheets()) {
13046 // In this case, element is the currently-best candidate, but doc is
13047 // not ready for autofocusing. We'll try again next time flush
13048 // autofocus candidates is called.
13049 ScheduleFlushAutoFocusCandidates();
13055 // The autofocus element could be moved to a different
13057 if (bc
->Top()->GetDocument() != this) {
13063 // Let inclusiveAncestorDocuments be a list consisting of doc, plus the
13064 // active documents of each of doc's browsing context's ancestor browsing
13066 // If any Document in inclusiveAncestorDocuments has non-null target
13067 // element, then continue.
13068 bool shouldFocus
= true;
13070 Document
* doc
= bc
->GetDocument();
13072 shouldFocus
= false;
13076 nsIURI
* uri
= doc
->GetDocumentURI();
13078 shouldFocus
= false;
13083 nsresult rv
= uri
->GetRef(ref
);
13084 // If there is an element in the document tree that has an ID equal to
13086 if (NS_SUCCEEDED(rv
) &&
13087 nsContentUtils::GetTargetElement(doc
, NS_ConvertUTF8toUTF16(ref
))) {
13088 shouldFocus
= false;
13091 bc
= bc
->GetParent();
13094 if (!shouldFocus
) {
13098 MOZ_ASSERT(topWindow
);
13099 if (TryAutoFocusCandidate(*autoFocusElement
)) {
13100 // We've successfully autofocused an element, don't
13101 // need to try to focus the rest.
13102 SetAutoFocusFired();
13107 if (HasAutoFocusCandidates()) {
13108 ScheduleFlushAutoFocusCandidates();
13112 bool Document::TryAutoFocusCandidate(Element
& aElement
) {
13113 const FocusOptions options
;
13114 if (RefPtr
<Element
> target
= nsFocusManager::GetTheFocusableArea(
13115 &aElement
, nsFocusManager::ProgrammaticFocusFlags(options
))) {
13116 target
->Focus(options
, CallerType::NonSystem
, IgnoreErrors());
13123 void Document::SetScrollToRef(nsIURI
* aDocumentURI
) {
13124 if (!aDocumentURI
) {
13130 // Since all URI's that pass through here aren't URL's we can't
13131 // rely on the nsIURI implementation for providing a way for
13132 // finding the 'ref' part of the URI, we'll haveto revert to
13133 // string routines for finding the data past '#'
13135 nsresult rv
= aDocumentURI
->GetSpec(ref
);
13136 if (NS_FAILED(rv
)) {
13137 Unused
<< aDocumentURI
->GetRef(mScrollToRef
);
13141 nsReadingIterator
<char> start
, end
;
13143 ref
.BeginReading(start
);
13144 ref
.EndReading(end
);
13146 if (FindCharInReadable('#', start
, end
)) {
13147 ++start
; // Skip over the '#'
13149 mScrollToRef
= Substring(start
, end
);
13153 // https://html.spec.whatwg.org/#scrolling-to-a-fragment
13154 void Document::ScrollToRef() {
13155 if (mScrolledToRefAlready
) {
13156 RefPtr
<PresShell
> presShell
= GetPresShell();
13158 presShell
->ScrollToAnchor();
13163 // 2. If fragment is the empty string, then return the special value top of
13165 if (mScrollToRef
.IsEmpty()) {
13169 RefPtr
<PresShell
> presShell
= GetPresShell();
13174 // 3. Let potentialIndicatedElement be the result of finding a potential
13175 // indicated element given document and fragment.
13176 NS_ConvertUTF8toUTF16
ref(mScrollToRef
);
13177 auto rv
= presShell
->GoToAnchor(ref
, mChangeScrollPosWhenScrollingToRef
);
13179 // 4. If potentialIndicatedElement is not null, then return
13180 // potentialIndicatedElement.
13181 if (NS_SUCCEEDED(rv
)) {
13182 mScrolledToRefAlready
= true;
13186 // 5. Let fragmentBytes be the result of percent-decoding fragment.
13187 nsAutoCString fragmentBytes
;
13188 const bool unescaped
=
13189 NS_UnescapeURL(mScrollToRef
.Data(), mScrollToRef
.Length(),
13190 /* aFlags = */ 0, fragmentBytes
);
13192 if (!unescaped
|| fragmentBytes
.IsEmpty()) {
13193 // Another attempt is only necessary if characters were unescaped.
13197 // 6. Let decodedFragment be the result of running UTF-8 decode without BOM on
13199 nsAutoString decodedFragment
;
13200 rv
= UTF_8_ENCODING
->DecodeWithoutBOMHandling(fragmentBytes
, decodedFragment
);
13201 NS_ENSURE_SUCCESS_VOID(rv
);
13203 // 7. Set potentialIndicatedElement to the result of finding a potential
13204 // indicated element given document and decodedFragment.
13205 rv
= presShell
->GoToAnchor(decodedFragment
,
13206 mChangeScrollPosWhenScrollingToRef
);
13207 if (NS_SUCCEEDED(rv
)) {
13208 mScrolledToRefAlready
= true;
13212 void Document::RegisterActivityObserver(nsISupports
* aSupports
) {
13213 if (!mActivityObservers
) {
13214 mActivityObservers
= MakeUnique
<nsTHashSet
<nsISupports
*>>();
13216 mActivityObservers
->Insert(aSupports
);
13219 bool Document::UnregisterActivityObserver(nsISupports
* aSupports
) {
13220 if (!mActivityObservers
) {
13223 return mActivityObservers
->EnsureRemoved(aSupports
);
13226 void Document::EnumerateActivityObservers(
13227 ActivityObserverEnumerator aEnumerator
) {
13228 if (!mActivityObservers
) {
13232 const auto keyArray
=
13233 ToTArray
<nsTArray
<nsCOMPtr
<nsISupports
>>>(*mActivityObservers
);
13234 for (auto& observer
: keyArray
) {
13235 aEnumerator(observer
.get());
13239 void Document::RegisterPendingLinkUpdate(Link
* aLink
) {
13240 if (aLink
->HasPendingLinkUpdate()) {
13244 aLink
->SetHasPendingLinkUpdate();
13246 if (!mHasLinksToUpdateRunnable
&& !mFlushingPendingLinkUpdates
) {
13247 nsCOMPtr
<nsIRunnable
> event
=
13248 NewRunnableMethod("Document::FlushPendingLinkUpdates", this,
13249 &Document::FlushPendingLinkUpdates
);
13250 // Do this work in a second in the worst case.
13251 nsresult rv
= NS_DispatchToCurrentThreadQueue(event
.forget(), 1000,
13252 EventQueuePriority::Idle
);
13253 if (NS_FAILED(rv
)) {
13254 // If during shutdown posting a runnable doesn't succeed, we probably
13255 // don't need to update link states.
13258 mHasLinksToUpdateRunnable
= true;
13261 mLinksToUpdate
.InfallibleAppend(aLink
);
13264 void Document::FlushPendingLinkUpdates() {
13265 MOZ_DIAGNOSTIC_ASSERT(!mFlushingPendingLinkUpdates
);
13266 MOZ_ASSERT(mHasLinksToUpdateRunnable
);
13267 mHasLinksToUpdateRunnable
= false;
13269 auto restore
= MakeScopeExit([&] { mFlushingPendingLinkUpdates
= false; });
13270 mFlushingPendingLinkUpdates
= true;
13272 while (!mLinksToUpdate
.IsEmpty()) {
13273 LinksToUpdateList
links(std::move(mLinksToUpdate
));
13274 for (auto iter
= links
.Iter(); !iter
.Done(); iter
.Next()) {
13275 Link
* link
= iter
.Get();
13276 Element
* element
= link
->GetElement();
13277 if (element
->OwnerDoc() == this) {
13278 link
->ClearHasPendingLinkUpdate();
13279 if (element
->IsInComposedDoc()) {
13280 link
->TriggerLinkUpdate(/* aNotify = */ true);
13288 * Retrieves the node in a static-clone document that corresponds to aOrigNode,
13289 * which is a node in the original document from which aStaticClone was cloned.
13291 static nsINode
* GetCorrespondingNodeInDocument(const nsINode
* aOrigNode
,
13292 Document
& aStaticClone
) {
13293 MOZ_ASSERT(aOrigNode
);
13295 // Selections in anonymous subtrees aren't supported.
13296 if (NS_WARN_IF(aOrigNode
->IsInNativeAnonymousSubtree())) {
13300 // If the node is disconnected, this is a bug in the selection code, but it
13301 // can happen with shadow DOM so handle it.
13302 if (NS_WARN_IF(!aOrigNode
->IsInComposedDoc())) {
13306 AutoTArray
<Maybe
<uint32_t>, 32> indexArray
;
13307 const nsINode
* current
= aOrigNode
;
13308 while (const nsINode
* parent
= current
->GetParentNode()) {
13309 Maybe
<uint32_t> index
= parent
->ComputeIndexOf(current
);
13310 NS_ENSURE_TRUE(index
.isSome(), nullptr);
13311 indexArray
.AppendElement(std::move(index
));
13314 MOZ_ASSERT(current
->IsDocument() || current
->IsShadowRoot());
13315 nsINode
* correspondingNode
= [&]() -> nsINode
* {
13316 if (current
->IsDocument()) {
13317 return &aStaticClone
;
13319 const auto* shadow
= ShadowRoot::FromNode(*current
);
13323 nsINode
* correspondingHost
=
13324 GetCorrespondingNodeInDocument(shadow
->Host(), aStaticClone
);
13325 if (NS_WARN_IF(!correspondingHost
|| !correspondingHost
->IsElement())) {
13328 return correspondingHost
->AsElement()->GetShadowRoot();
13331 if (NS_WARN_IF(!correspondingNode
)) {
13334 for (const Maybe
<uint32_t>& index
: Reversed(indexArray
)) {
13335 correspondingNode
= correspondingNode
->GetChildAt_Deprecated(*index
);
13336 NS_ENSURE_TRUE(correspondingNode
, nullptr);
13338 return correspondingNode
;
13342 * Caches the selection ranges from the source document onto the static clone in
13343 * case the "Print Selection Only" functionality is invoked.
13345 * Note that we cannot use the selection obtained from GetOriginalDocument()
13346 * since that selection may have mutated after the print was invoked.
13348 * Note also that because nsRange objects point into a specific document's
13349 * nodes, we cannot reuse an array of nsRange objects across multiple static
13350 * clone documents. For that reason we cache a new array of ranges on each
13351 * static clone that we create.
13353 * TODO(emilio): This can be simplified once we don't re-clone from static
13356 * @param aSourceDoc the document from which we are caching selection ranges
13357 * @param aStaticClone the document that will hold the cache
13358 * @return true if a selection range was cached
13360 static void CachePrintSelectionRanges(const Document
& aSourceDoc
,
13361 Document
& aStaticClone
) {
13362 MOZ_ASSERT(aStaticClone
.IsStaticDocument());
13363 MOZ_ASSERT(!aStaticClone
.GetProperty(nsGkAtoms::printisfocuseddoc
));
13364 MOZ_ASSERT(!aStaticClone
.GetProperty(nsGkAtoms::printselectionranges
));
13366 bool sourceDocIsStatic
= aSourceDoc
.IsStaticDocument();
13368 // When the user opts to "Print Selection Only", the print code prefers any
13369 // selection in the static clone corresponding to the focused frame. If this
13370 // is that static clone, flag it for the printing code:
13371 const bool isFocusedDoc
= [&] {
13372 if (sourceDocIsStatic
) {
13373 return bool(aSourceDoc
.GetProperty(nsGkAtoms::printisfocuseddoc
));
13375 nsPIDOMWindowOuter
* window
= aSourceDoc
.GetWindow();
13379 nsCOMPtr
<nsPIDOMWindowOuter
> rootWindow
= window
->GetPrivateRoot();
13383 nsCOMPtr
<nsPIDOMWindowOuter
> focusedWindow
;
13384 nsFocusManager::GetFocusedDescendant(rootWindow
,
13385 nsFocusManager::eIncludeAllDescendants
,
13386 getter_AddRefs(focusedWindow
));
13387 return focusedWindow
&& focusedWindow
->GetExtantDoc() == &aSourceDoc
;
13389 if (isFocusedDoc
) {
13390 aStaticClone
.SetProperty(nsGkAtoms::printisfocuseddoc
,
13391 reinterpret_cast<void*>(true));
13394 const Selection
* origSelection
= nullptr;
13395 const nsTArray
<RefPtr
<nsRange
>>* origRanges
= nullptr;
13397 if (sourceDocIsStatic
) {
13398 origRanges
= static_cast<nsTArray
<RefPtr
<nsRange
>>*>(
13399 aSourceDoc
.GetProperty(nsGkAtoms::printselectionranges
));
13400 } else if (PresShell
* shell
= aSourceDoc
.GetPresShell()) {
13401 origSelection
= shell
->GetCurrentSelection(SelectionType::eNormal
);
13404 if (!origSelection
&& !origRanges
) {
13408 const uint32_t rangeCount
=
13409 sourceDocIsStatic
? origRanges
->Length() : origSelection
->RangeCount();
13410 auto printRanges
= MakeUnique
<nsTArray
<RefPtr
<nsRange
>>>(rangeCount
);
13412 for (const uint32_t i
: IntegerRange(rangeCount
)) {
13413 MOZ_ASSERT_IF(!sourceDocIsStatic
,
13414 origSelection
->RangeCount() == rangeCount
);
13415 const nsRange
* range
= sourceDocIsStatic
? origRanges
->ElementAt(i
).get()
13416 : origSelection
->GetRangeAt(i
);
13418 nsINode
* startContainer
= range
->GetStartContainer();
13419 nsINode
* endContainer
= range
->GetEndContainer();
13421 if (!startContainer
|| !endContainer
) {
13425 nsINode
* startNode
=
13426 GetCorrespondingNodeInDocument(startContainer
, aStaticClone
);
13428 GetCorrespondingNodeInDocument(endContainer
, aStaticClone
);
13430 if (NS_WARN_IF(!startNode
|| !endNode
)) {
13434 RefPtr
<nsRange
> clonedRange
=
13435 nsRange::Create(startNode
, range
->StartOffset(), endNode
,
13436 range
->EndOffset(), IgnoreErrors());
13437 if (clonedRange
&& !clonedRange
->Collapsed()) {
13438 printRanges
->AppendElement(std::move(clonedRange
));
13442 if (printRanges
->IsEmpty()) {
13446 aStaticClone
.SetProperty(nsGkAtoms::printselectionranges
,
13447 printRanges
.release(),
13448 nsINode::DeleteProperty
<nsTArray
<RefPtr
<nsRange
>>>);
13451 already_AddRefed
<Document
> Document::CreateStaticClone(
13452 nsIDocShell
* aCloneContainer
, nsIDocumentViewer
* aViewer
,
13453 nsIPrintSettings
* aPrintSettings
, bool* aOutHasInProcessPrintCallbacks
) {
13454 MOZ_ASSERT(!mCreatingStaticClone
);
13455 MOZ_ASSERT(!GetProperty(nsGkAtoms::adoptedsheetclones
));
13456 MOZ_DIAGNOSTIC_ASSERT(aViewer
);
13458 mCreatingStaticClone
= true;
13459 SetProperty(nsGkAtoms::adoptedsheetclones
, new AdoptedStyleSheetCloneCache(),
13460 nsINode::DeleteProperty
<AdoptedStyleSheetCloneCache
>);
13462 auto raii
= MakeScopeExit([&] {
13463 RemoveProperty(nsGkAtoms::adoptedsheetclones
);
13464 mCreatingStaticClone
= false;
13467 // Make document use different container during cloning.
13469 // FIXME(emilio): Why is this needed?
13470 RefPtr
<nsDocShell
> originalShell
= mDocumentContainer
.get();
13471 SetContainer(nsDocShell::Cast(aCloneContainer
));
13472 IgnoredErrorResult rv
;
13473 nsCOMPtr
<nsINode
> clonedNode
= CloneNode(true, rv
);
13474 SetContainer(originalShell
);
13479 nsCOMPtr
<Document
> clonedDoc
= do_QueryInterface(clonedNode
);
13484 size_t sheetsCount
= SheetCount();
13485 for (size_t i
= 0; i
< sheetsCount
; ++i
) {
13486 RefPtr
<StyleSheet
> sheet
= SheetAt(i
);
13488 if (sheet
->IsApplicable()) {
13489 RefPtr
<StyleSheet
> clonedSheet
= sheet
->Clone(nullptr, clonedDoc
);
13490 NS_WARNING_ASSERTION(clonedSheet
, "Cloning a stylesheet didn't work!");
13492 clonedDoc
->AddStyleSheet(clonedSheet
);
13497 clonedDoc
->CloneAdoptedSheetsFrom(*this);
13499 for (int t
= 0; t
< AdditionalSheetTypeCount
; ++t
) {
13500 auto& sheets
= mAdditionalSheets
[additionalSheetType(t
)];
13501 for (StyleSheet
* sheet
: sheets
) {
13502 if (sheet
->IsApplicable()) {
13503 RefPtr
<StyleSheet
> clonedSheet
= sheet
->Clone(nullptr, clonedDoc
);
13504 NS_WARNING_ASSERTION(clonedSheet
, "Cloning a stylesheet didn't work!");
13506 clonedDoc
->AddAdditionalStyleSheet(additionalSheetType(t
),
13513 // Font faces created with the JS API will not be reflected in the
13514 // stylesheets and need to be copied over to the cloned document.
13515 if (const FontFaceSet
* set
= GetFonts()) {
13516 set
->CopyNonRuleFacesTo(clonedDoc
->Fonts());
13519 clonedDoc
->mReferrerInfo
=
13520 static_cast<dom::ReferrerInfo
*>(mReferrerInfo
.get())->Clone();
13521 clonedDoc
->mPreloadReferrerInfo
= clonedDoc
->mReferrerInfo
;
13522 CachePrintSelectionRanges(*this, *clonedDoc
);
13524 // We're done with the clone, embed ourselves into the document viewer and
13525 // clone our children. The order here is pretty important, because our
13526 // document our document needs to have an owner global before we can create
13527 // the frame loaders for subdocuments.
13528 aViewer
->SetDocument(clonedDoc
);
13530 *aOutHasInProcessPrintCallbacks
|= clonedDoc
->HasPrintCallbacks();
13532 auto pendingClones
= std::move(clonedDoc
->mPendingFrameStaticClones
);
13533 for (const auto& clone
: pendingClones
) {
13534 RefPtr
<Element
> element
= do_QueryObject(clone
.mElement
);
13535 RefPtr
<nsFrameLoader
> frameLoader
=
13536 nsFrameLoader::Create(element
, /* aNetworkCreated */ false);
13538 if (NS_WARN_IF(!frameLoader
)) {
13542 clone
.mElement
->SetFrameLoader(frameLoader
);
13544 nsresult rv
= frameLoader
->FinishStaticClone(
13545 clone
.mStaticCloneOf
, aPrintSettings
, aOutHasInProcessPrintCallbacks
);
13546 Unused
<< NS_WARN_IF(NS_FAILED(rv
));
13549 return clonedDoc
.forget();
13552 void Document::UnlinkOriginalDocumentIfStatic() {
13553 if (IsStaticDocument() && mOriginalDocument
) {
13554 MOZ_ASSERT(mOriginalDocument
->mStaticCloneCount
> 0);
13555 mOriginalDocument
->mStaticCloneCount
--;
13556 mOriginalDocument
= nullptr;
13558 MOZ_ASSERT(!mOriginalDocument
);
13561 nsresult
Document::ScheduleFrameRequestCallback(FrameRequestCallback
& aCallback
,
13562 int32_t* aHandle
) {
13563 nsresult rv
= mFrameRequestManager
.Schedule(aCallback
, aHandle
);
13564 if (NS_FAILED(rv
)) {
13568 UpdateFrameRequestCallbackSchedulingState();
13572 void Document::CancelFrameRequestCallback(int32_t aHandle
) {
13573 if (mFrameRequestManager
.Cancel(aHandle
)) {
13574 UpdateFrameRequestCallbackSchedulingState();
13578 bool Document::IsCanceledFrameRequestCallback(int32_t aHandle
) const {
13579 return mFrameRequestManager
.IsCanceled(aHandle
);
13582 nsresult
Document::GetStateObject(JS::MutableHandle
<JS::Value
> aState
) {
13583 // Get the document's current state object. This is the object backing both
13584 // history.state and popStateEvent.state.
13586 // mStateObjectContainer may be null; this just means that there's no
13587 // current state object.
13589 if (!mCachedStateObjectValid
) {
13590 if (mStateObjectContainer
) {
13592 // Init with null is "OK" in the sense that it will just fail.
13593 if (!jsapi
.Init(GetScopeObject())) {
13594 return NS_ERROR_UNEXPECTED
;
13596 JS::Rooted
<JS::Value
> value(jsapi
.cx());
13598 mStateObjectContainer
->DeserializeToJsval(jsapi
.cx(), &value
);
13599 NS_ENSURE_SUCCESS(rv
, rv
);
13601 mCachedStateObject
= value
;
13602 if (!value
.isNullOrUndefined()) {
13603 mozilla::HoldJSObjects(this);
13606 mCachedStateObject
= JS::NullValue();
13608 mCachedStateObjectValid
= true;
13611 aState
.set(mCachedStateObject
);
13615 void Document::SetNavigationTiming(nsDOMNavigationTiming
* aTiming
) {
13617 if (!mLoadingOrRestoredFromBFCacheTimeStamp
.IsNull() && mTiming
) {
13618 mTiming
->SetDOMLoadingTimeStamp(GetDocumentURI(),
13619 mLoadingOrRestoredFromBFCacheTimeStamp
);
13622 // If there's already the DocumentTimeline instance, tell it since the
13623 // DocumentTimeline is based on both the navigation start time stamp and the
13624 // refresh driver timestamp.
13625 if (mDocumentTimeline
) {
13626 mDocumentTimeline
->UpdateLastRefreshDriverTime();
13630 nsContentList
* Document::ImageMapList() {
13632 mImageMaps
= new nsContentList(this, kNameSpaceID_XHTML
, nsGkAtoms::map
,
13639 #define DEPRECATED_OPERATION(_op) #_op "Warning",
13640 static const char* kDeprecationWarnings
[] = {
13641 #include "nsDeprecatedOperationList.h"
13643 #undef DEPRECATED_OPERATION
13645 #define DOCUMENT_WARNING(_op) #_op "Warning",
13646 static const char* kDocumentWarnings
[] = {
13647 #include "nsDocumentWarningList.h"
13649 #undef DOCUMENT_WARNING
13651 static UseCounter
OperationToUseCounter(DeprecatedOperations aOperation
) {
13652 switch (aOperation
) {
13653 #define DEPRECATED_OPERATION(_op) \
13654 case DeprecatedOperations::e##_op: \
13655 return eUseCounter_##_op;
13656 #include "nsDeprecatedOperationList.h"
13657 #undef DEPRECATED_OPERATION
13663 bool Document::HasWarnedAbout(DeprecatedOperations aOperation
) const {
13664 return mDeprecationWarnedAbout
[static_cast<size_t>(aOperation
)];
13667 void Document::WarnOnceAbout(
13668 DeprecatedOperations aOperation
, bool asError
/* = false */,
13669 const nsTArray
<nsString
>& aParams
/* = empty array */) const {
13670 MOZ_ASSERT(NS_IsMainThread());
13671 if (HasWarnedAbout(aOperation
)) {
13674 mDeprecationWarnedAbout
[static_cast<size_t>(aOperation
)] = true;
13675 // Don't count deprecated operations for about pages since those pages
13676 // are almost in our control, and we always need to remove uses there
13677 // before we remove the operation itself anyway.
13678 if (!IsAboutPage()) {
13679 const_cast<Document
*>(this)->SetUseCounter(
13680 OperationToUseCounter(aOperation
));
13683 asError
? nsIScriptError::errorFlag
: nsIScriptError::warningFlag
;
13684 nsContentUtils::ReportToConsole(
13685 flags
, "DOM Core"_ns
, this, nsContentUtils::eDOM_PROPERTIES
,
13686 kDeprecationWarnings
[static_cast<size_t>(aOperation
)], aParams
);
13689 bool Document::HasWarnedAbout(DocumentWarnings aWarning
) const {
13690 return mDocWarningWarnedAbout
[aWarning
];
13693 void Document::WarnOnceAbout(
13694 DocumentWarnings aWarning
, bool asError
/* = false */,
13695 const nsTArray
<nsString
>& aParams
/* = empty array */) const {
13696 MOZ_ASSERT(NS_IsMainThread());
13697 if (HasWarnedAbout(aWarning
)) {
13700 mDocWarningWarnedAbout
[aWarning
] = true;
13702 asError
? nsIScriptError::errorFlag
: nsIScriptError::warningFlag
;
13703 nsContentUtils::ReportToConsole(flags
, "DOM Core"_ns
, this,
13704 nsContentUtils::eDOM_PROPERTIES
,
13705 kDocumentWarnings
[aWarning
], aParams
);
13708 mozilla::dom::ImageTracker
* Document::ImageTracker() {
13709 if (!mImageTracker
) {
13710 mImageTracker
= new mozilla::dom::ImageTracker
;
13712 return mImageTracker
;
13715 void Document::ScheduleSVGUseElementShadowTreeUpdate(
13716 SVGUseElement
& aUseElement
) {
13717 MOZ_ASSERT(aUseElement
.IsInComposedDoc());
13719 if (MOZ_UNLIKELY(mIsStaticDocument
)) {
13720 // Printing doesn't deal well with dynamic DOM mutations.
13724 mSVGUseElementsNeedingShadowTreeUpdate
.Insert(&aUseElement
);
13726 if (PresShell
* presShell
= GetPresShell()) {
13727 presShell
->EnsureStyleFlush();
13731 void Document::DoUpdateSVGUseElementShadowTrees() {
13732 MOZ_ASSERT(!mSVGUseElementsNeedingShadowTreeUpdate
.IsEmpty());
13734 MOZ_ASSERT(!mCloningForSVGUse
);
13735 mCloningForSVGUse
= true;
13738 const auto useElementsToUpdate
= ToTArray
<nsTArray
<RefPtr
<SVGUseElement
>>>(
13739 mSVGUseElementsNeedingShadowTreeUpdate
);
13740 mSVGUseElementsNeedingShadowTreeUpdate
.Clear();
13742 for (const auto& useElement
: useElementsToUpdate
) {
13743 if (MOZ_UNLIKELY(!useElement
->IsInComposedDoc())) {
13744 // The element was in another <use> shadow tree which we processed
13745 // already and also needed an update, and is removed from the document
13746 // now, so nothing to do here.
13747 MOZ_ASSERT(useElementsToUpdate
.Length() > 1);
13750 useElement
->UpdateShadowTree();
13752 } while (!mSVGUseElementsNeedingShadowTreeUpdate
.IsEmpty());
13754 mCloningForSVGUse
= false;
13757 void Document::NotifyMediaFeatureValuesChanged() {
13758 for (RefPtr
<HTMLImageElement
> imageElement
: mResponsiveContent
) {
13759 imageElement
->MediaFeatureValuesChanged();
13763 already_AddRefed
<Touch
> Document::CreateTouch(
13764 nsGlobalWindowInner
* aView
, EventTarget
* aTarget
, int32_t aIdentifier
,
13765 int32_t aPageX
, int32_t aPageY
, int32_t aScreenX
, int32_t aScreenY
,
13766 int32_t aClientX
, int32_t aClientY
, int32_t aRadiusX
, int32_t aRadiusY
,
13767 float aRotationAngle
, float aForce
) {
13768 RefPtr
<Touch
> touch
=
13769 new Touch(aTarget
, aIdentifier
, aPageX
, aPageY
, aScreenX
, aScreenY
,
13770 aClientX
, aClientY
, aRadiusX
, aRadiusY
, aRotationAngle
, aForce
);
13771 return touch
.forget();
13774 already_AddRefed
<TouchList
> Document::CreateTouchList() {
13775 RefPtr
<TouchList
> retval
= new TouchList(ToSupports(this));
13776 return retval
.forget();
13779 already_AddRefed
<TouchList
> Document::CreateTouchList(
13780 Touch
& aTouch
, const Sequence
<OwningNonNull
<Touch
>>& aTouches
) {
13781 RefPtr
<TouchList
> retval
= new TouchList(ToSupports(this));
13782 retval
->Append(&aTouch
);
13783 for (uint32_t i
= 0; i
< aTouches
.Length(); ++i
) {
13784 retval
->Append(aTouches
[i
].get());
13786 return retval
.forget();
13789 already_AddRefed
<TouchList
> Document::CreateTouchList(
13790 const Sequence
<OwningNonNull
<Touch
>>& aTouches
) {
13791 RefPtr
<TouchList
> retval
= new TouchList(ToSupports(this));
13792 for (uint32_t i
= 0; i
< aTouches
.Length(); ++i
) {
13793 retval
->Append(aTouches
[i
].get());
13795 return retval
.forget();
13798 already_AddRefed
<nsDOMCaretPosition
> Document::CaretPositionFromPoint(
13799 float aX
, float aY
) {
13800 using FrameForPointOption
= nsLayoutUtils::FrameForPointOption
;
13802 nscoord x
= nsPresContext::CSSPixelsToAppUnits(aX
);
13803 nscoord y
= nsPresContext::CSSPixelsToAppUnits(aY
);
13806 FlushPendingNotifications(FlushType::Layout
);
13808 PresShell
* presShell
= GetPresShell();
13813 nsIFrame
* rootFrame
= presShell
->GetRootFrame();
13815 // XUL docs, unlike HTML, have no frame tree until everything's done loading
13820 nsIFrame
* ptFrame
= nsLayoutUtils::GetFrameForPoint(
13821 RelativeTo
{rootFrame
}, pt
,
13822 {{FrameForPointOption::IgnorePaintSuppression
,
13823 FrameForPointOption::IgnoreCrossDoc
}});
13828 // We require frame-relative coordinates for GetContentOffsetsFromPoint.
13829 nsPoint adjustedPoint
= pt
;
13830 if (nsLayoutUtils::TransformPoint(RelativeTo
{rootFrame
}, RelativeTo
{ptFrame
},
13832 nsLayoutUtils::TRANSFORM_SUCCEEDED
) {
13836 nsIFrame::ContentOffsets offsets
=
13837 ptFrame
->GetContentOffsetsFromPoint(adjustedPoint
);
13839 nsCOMPtr
<nsIContent
> node
= offsets
.content
;
13840 uint32_t offset
= offsets
.offset
;
13841 nsCOMPtr
<nsIContent
> anonNode
= node
;
13842 bool nodeIsAnonymous
= node
&& node
->IsInNativeAnonymousSubtree();
13843 if (nodeIsAnonymous
) {
13844 node
= ptFrame
->GetContent();
13845 nsIContent
* nonanon
= node
->FindFirstNonChromeOnlyAccessContent();
13846 HTMLTextAreaElement
* textArea
= HTMLTextAreaElement::FromNode(nonanon
);
13847 nsITextControlFrame
* textFrame
= do_QueryFrame(nonanon
->GetPrimaryFrame());
13849 // If the anonymous content node has a child, then we need to make sure
13850 // that we get the appropriate child, as otherwise the offset may not be
13851 // correct when we construct a range for it.
13852 nsCOMPtr
<nsIContent
> firstChild
= anonNode
->GetFirstChild();
13854 anonNode
= firstChild
;
13859 nsContentUtils::GetAdjustedOffsetInTextControl(ptFrame
, offset
);
13869 RefPtr
<nsDOMCaretPosition
> aCaretPos
= new nsDOMCaretPosition(node
, offset
);
13870 if (nodeIsAnonymous
) {
13871 aCaretPos
->SetAnonymousContentNode(anonNode
);
13873 return aCaretPos
.forget();
13876 bool Document::IsPotentiallyScrollable(HTMLBodyElement
* aBody
) {
13877 // We rely on correct frame information here, so need to flush frames.
13878 FlushPendingNotifications(FlushType::Frames
);
13880 // An element that is the HTML body element is potentially scrollable if all
13881 // of the following conditions are true:
13883 // The element has an associated CSS layout box.
13884 nsIFrame
* bodyFrame
= nsLayoutUtils::GetStyleFrame(aBody
);
13889 // The element's parent element's computed value of the overflow-x and
13890 // overflow-y properties are visible.
13891 MOZ_ASSERT(aBody
->GetParent() == aBody
->OwnerDoc()->GetRootElement());
13892 nsIFrame
* parentFrame
= nsLayoutUtils::GetStyleFrame(aBody
->GetParent());
13894 parentFrame
->StyleDisplay()->OverflowIsVisibleInBothAxis()) {
13898 // The element's computed value of the overflow-x or overflow-y properties is
13900 return !bodyFrame
->StyleDisplay()->OverflowIsVisibleInBothAxis();
13903 Element
* Document::GetScrollingElement() {
13904 // Keep this in sync with IsScrollingElement.
13905 if (GetCompatibilityMode() == eCompatibility_NavQuirks
) {
13906 RefPtr
<HTMLBodyElement
> body
= GetBodyElement();
13907 if (body
&& !IsPotentiallyScrollable(body
)) {
13914 return GetRootElement();
13917 bool Document::IsScrollingElement(Element
* aElement
) {
13918 // Keep this in sync with GetScrollingElement.
13919 MOZ_ASSERT(aElement
);
13921 if (GetCompatibilityMode() != eCompatibility_NavQuirks
) {
13922 return aElement
== GetRootElement();
13925 // In the common case when aElement != body, avoid refcounting.
13926 HTMLBodyElement
* body
= GetBodyElement();
13927 if (aElement
!= body
) {
13931 // Now we know body is non-null, since aElement is not null. It's the
13932 // scrolling element for the document if it itself is not potentially
13934 RefPtr
<HTMLBodyElement
> strongBody(body
);
13935 return !IsPotentiallyScrollable(strongBody
);
13938 class UnblockParsingPromiseHandler final
: public PromiseNativeHandler
{
13940 NS_DECL_CYCLE_COLLECTING_ISUPPORTS
13941 NS_DECL_CYCLE_COLLECTION_CLASS(UnblockParsingPromiseHandler
)
13943 explicit UnblockParsingPromiseHandler(Document
* aDocument
, Promise
* aPromise
,
13944 const BlockParsingOptions
& aOptions
)
13945 : mPromise(aPromise
) {
13946 nsCOMPtr
<nsIParser
> parser
= aDocument
->CreatorParserOrNull();
13948 (aOptions
.mBlockScriptCreated
|| !parser
->IsScriptCreated())) {
13949 parser
->BlockParser();
13950 mParser
= do_GetWeakReference(parser
);
13951 mDocument
= aDocument
;
13952 mDocument
->BlockOnload();
13953 mDocument
->BlockDOMContentLoaded();
13957 void ResolvedCallback(JSContext
* aCx
, JS::Handle
<JS::Value
> aValue
,
13958 ErrorResult
& aRv
) override
{
13959 MaybeUnblockParser();
13961 mPromise
->MaybeResolve(aValue
);
13964 void RejectedCallback(JSContext
* aCx
, JS::Handle
<JS::Value
> aValue
,
13965 ErrorResult
& aRv
) override
{
13966 MaybeUnblockParser();
13968 mPromise
->MaybeReject(aValue
);
13972 virtual ~UnblockParsingPromiseHandler() {
13973 // If we're being cleaned up by the cycle collector, our mDocument reference
13974 // may have been unlinked while our mParser weak reference is still alive.
13976 MaybeUnblockParser();
13981 void MaybeUnblockParser() {
13982 nsCOMPtr
<nsIParser
> parser
= do_QueryReferent(mParser
);
13984 MOZ_DIAGNOSTIC_ASSERT(mDocument
);
13985 nsCOMPtr
<nsIParser
> docParser
= mDocument
->CreatorParserOrNull();
13986 if (parser
== docParser
) {
13987 parser
->UnblockParser();
13988 parser
->ContinueInterruptedParsingAsync();
13992 // We blocked DOMContentLoaded and load events on this document. Unblock
13993 // them. Note that we want to do that no matter what's going on with the
13994 // parser state for this document. Maybe someone caused it to stop being
13995 // parsed, so CreatorParserOrNull() is returning null, but we still want
13996 // to unblock these.
13997 mDocument
->UnblockDOMContentLoaded();
13998 mDocument
->UnblockOnload(false);
14001 mDocument
= nullptr;
14005 RefPtr
<Promise
> mPromise
;
14006 RefPtr
<Document
> mDocument
;
14009 NS_IMPL_CYCLE_COLLECTION(UnblockParsingPromiseHandler
, mDocument
, mPromise
)
14011 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(UnblockParsingPromiseHandler
)
14012 NS_INTERFACE_MAP_ENTRY(nsISupports
)
14013 NS_INTERFACE_MAP_END
14015 NS_IMPL_CYCLE_COLLECTING_ADDREF(UnblockParsingPromiseHandler
)
14016 NS_IMPL_CYCLE_COLLECTING_RELEASE(UnblockParsingPromiseHandler
)
14018 already_AddRefed
<Promise
> Document::BlockParsing(
14019 Promise
& aPromise
, const BlockParsingOptions
& aOptions
, ErrorResult
& aRv
) {
14020 RefPtr
<Promise
> resultPromise
=
14021 Promise::Create(aPromise
.GetParentObject(), aRv
);
14022 if (aRv
.Failed()) {
14026 RefPtr
<PromiseNativeHandler
> promiseHandler
=
14027 new UnblockParsingPromiseHandler(this, resultPromise
, aOptions
);
14028 aPromise
.AppendNativeHandler(promiseHandler
);
14030 return resultPromise
.forget();
14033 already_AddRefed
<nsIURI
> Document::GetMozDocumentURIIfNotForErrorPages() {
14034 if (mFailedChannel
) {
14035 nsCOMPtr
<nsIURI
> failedURI
;
14036 if (NS_SUCCEEDED(mFailedChannel
->GetURI(getter_AddRefs(failedURI
)))) {
14037 return failedURI
.forget();
14041 nsCOMPtr
<nsIURI
> uri
= GetDocumentURIObject();
14046 return uri
.forget();
14049 Promise
* Document::GetDocumentReadyForIdle(ErrorResult
& aRv
) {
14050 if (mIsGoingAway
) {
14051 aRv
.Throw(NS_ERROR_NOT_AVAILABLE
);
14055 if (!mReadyForIdle
) {
14056 nsIGlobalObject
* global
= GetScopeObject();
14058 aRv
.Throw(NS_ERROR_NOT_AVAILABLE
);
14062 mReadyForIdle
= Promise::Create(global
, aRv
);
14063 if (aRv
.Failed()) {
14068 return mReadyForIdle
;
14071 void Document::MaybeResolveReadyForIdle() {
14072 IgnoredErrorResult rv
;
14073 Promise
* readyPromise
= GetDocumentReadyForIdle(rv
);
14074 if (readyPromise
) {
14075 readyPromise
->MaybeResolveWithUndefined();
14079 mozilla::dom::FeaturePolicy
* Document::FeaturePolicy() const {
14080 // The policy is created when the document is initialized. We _must_ have a
14081 // policy here even if the featurePolicy pref is off. If this assertion fails,
14082 // it means that ::FeaturePolicy() is called before ::StartDocumentLoad().
14083 MOZ_ASSERT(mFeaturePolicy
);
14084 return mFeaturePolicy
;
14087 nsIDOMXULCommandDispatcher
* Document::GetCommandDispatcher() {
14088 // Only chrome documents are allowed to use command dispatcher.
14089 if (!nsContentUtils::IsChromeDoc(this)) {
14092 if (!mCommandDispatcher
) {
14093 // Create our command dispatcher and hook it up.
14094 mCommandDispatcher
= new nsXULCommandDispatcher(this);
14096 return mCommandDispatcher
;
14099 void Document::InitializeXULBroadcastManager() {
14100 if (mXULBroadcastManager
) {
14103 mXULBroadcastManager
= new XULBroadcastManager(this);
14108 class DevToolsMutationObserver final
: public nsStubMutationObserver
{
14110 NS_DECL_NSIMUTATIONOBSERVER_ATTRIBUTECHANGED
14111 NS_DECL_NSIMUTATIONOBSERVER_CONTENTAPPENDED
14112 NS_DECL_NSIMUTATIONOBSERVER_CONTENTINSERTED
14114 // We handle this in nsContentUtils::MaybeFireNodeRemoved, since devtools
14115 // relies on the event firing _before_ the removal happens.
14116 // NS_DECL_NSIMUTATIONOBSERVER_CONTENTREMOVED
14118 // NOTE(emilio, bug 1694627): DevTools doesn't seem to deal with character
14119 // data changes right now (maybe intentionally?).
14120 // NS_DECL_NSIMUTATIONOBSERVER_CHARACTERDATACHANGED
14122 DevToolsMutationObserver() = default;
14125 void FireEvent(nsINode
* aTarget
, const nsAString
& aType
);
14127 ~DevToolsMutationObserver() = default;
14130 NS_IMPL_ISUPPORTS(DevToolsMutationObserver
, nsIMutationObserver
)
14132 void DevToolsMutationObserver::FireEvent(nsINode
* aTarget
,
14133 const nsAString
& aType
) {
14134 AsyncEventDispatcher::RunDOMEventWhenSafe(*aTarget
, aType
, CanBubble::eNo
,
14135 ChromeOnlyDispatch::eYes
,
14139 void DevToolsMutationObserver::AttributeChanged(Element
* aElement
,
14140 int32_t aNamespaceID
,
14141 nsAtom
* aAttribute
,
14143 const nsAttrValue
* aOldValue
) {
14144 FireEvent(aElement
, u
"devtoolsattrmodified"_ns
);
14147 void DevToolsMutationObserver::ContentAppended(nsIContent
* aFirstNewContent
) {
14148 for (nsIContent
* c
= aFirstNewContent
; c
; c
= c
->GetNextSibling()) {
14149 ContentInserted(c
);
14153 void DevToolsMutationObserver::ContentInserted(nsIContent
* aChild
) {
14154 FireEvent(aChild
, u
"devtoolschildinserted"_ns
);
14157 static StaticRefPtr
<DevToolsMutationObserver
> sDevToolsMutationObserver
;
14161 void Document::SetDevToolsWatchingDOMMutations(bool aValue
) {
14162 if (mDevToolsWatchingDOMMutations
== aValue
|| mIsGoingAway
) {
14165 mDevToolsWatchingDOMMutations
= aValue
;
14167 if (MOZ_UNLIKELY(!sDevToolsMutationObserver
)) {
14168 sDevToolsMutationObserver
= new DevToolsMutationObserver();
14169 ClearOnShutdown(&sDevToolsMutationObserver
);
14171 AddMutationObserver(sDevToolsMutationObserver
);
14172 } else if (sDevToolsMutationObserver
) {
14173 RemoveMutationObserver(sDevToolsMutationObserver
);
14177 void EvaluateMediaQueryLists(nsTArray
<RefPtr
<MediaQueryList
>>& aListsToNotify
,
14178 Document
& aDocument
, bool aRecurse
) {
14179 if (nsPresContext
* pc
= aDocument
.GetPresContext()) {
14180 pc
->FlushPendingMediaFeatureValuesChanged();
14183 for (MediaQueryList
* mql
: aDocument
.MediaQueryLists()) {
14184 if (mql
->EvaluateOnRenderingUpdate()) {
14185 aListsToNotify
.AppendElement(mql
);
14191 auto recurse
= [&](Document
& aSubDoc
) {
14192 EvaluateMediaQueryLists(aListsToNotify
, aSubDoc
, true);
14193 return CallState::Continue
;
14195 aDocument
.EnumerateSubDocuments(recurse
);
14198 void Document::EvaluateMediaQueriesAndReportChanges(bool aRecurse
) {
14199 AutoTArray
<RefPtr
<MediaQueryList
>, 32> mqls
;
14200 EvaluateMediaQueryLists(mqls
, *this, aRecurse
);
14201 for (auto& mql
: mqls
) {
14202 mql
->FireChangeEvent();
14206 void Document::MaybeWarnAboutZoom() {
14207 if (mHasWarnedAboutZoom
) {
14210 const bool usedZoom
= Servo_IsPropertyIdRecordedInUseCounter(
14211 mStyleUseCounters
.get(), eCSSProperty_zoom
);
14216 mHasWarnedAboutZoom
= true;
14217 nsContentUtils::ReportToConsole(nsIScriptError::warningFlag
, "Layout"_ns
,
14218 this, nsContentUtils::eLAYOUT_PROPERTIES
,
14219 "ZoomPropertyWarning");
14222 nsIHTMLCollection
* Document::Children() {
14223 if (!mChildrenCollection
) {
14224 mChildrenCollection
=
14225 new nsContentList(this, kNameSpaceID_Wildcard
, nsGkAtoms::_asterisk
,
14226 nsGkAtoms::_asterisk
, false);
14229 return mChildrenCollection
;
14232 uint32_t Document::ChildElementCount() { return Children()->Length(); }
14234 // Singleton class to manage the list of fullscreen documents which are the
14235 // root of a branch which contains fullscreen documents. We maintain this list
14236 // so that we can easily exit all windows from fullscreen when the user
14237 // presses the escape key.
14238 class FullscreenRoots
{
14240 // Adds the root of given document to the manager. Calling this method
14241 // with a document whose root is already contained has no effect.
14242 static void Add(Document
* aDoc
);
14244 // Iterates over every root in the root list, and calls aFunction, passing
14245 // each root once to aFunction. It is safe to call Add() and Remove() while
14246 // iterating over the list (i.e. in aFunction). Documents that are removed
14247 // from the manager during traversal are not traversed, and documents that
14248 // are added to the manager during traversal are also not traversed.
14249 static void ForEach(void (*aFunction
)(Document
* aDoc
));
14251 // Removes the root of a specific document from the manager.
14252 static void Remove(Document
* aDoc
);
14254 // Returns true if all roots added to the list have been removed.
14255 static bool IsEmpty();
14258 MOZ_COUNTED_DEFAULT_CTOR(FullscreenRoots
)
14259 MOZ_COUNTED_DTOR(FullscreenRoots
)
14261 using RootsArray
= nsTArray
<WeakPtr
<Document
>>;
14263 // Returns true if aRoot is in the list of fullscreen roots.
14264 static bool Contains(Document
* aRoot
);
14266 // Singleton instance of the FullscreenRoots. This is instantiated when a
14267 // root is added, and it is deleted when the last root is removed.
14268 static FullscreenRoots
* sInstance
;
14270 // List of weak pointers to roots.
14274 FullscreenRoots
* FullscreenRoots::sInstance
= nullptr;
14277 void FullscreenRoots::ForEach(void (*aFunction
)(Document
* aDoc
)) {
14281 // Create a copy of the roots array, and iterate over the copy. This is so
14282 // that if an element is removed from mRoots we don't mess up our iteration.
14283 RootsArray
roots(sInstance
->mRoots
.Clone());
14284 // Call aFunction on all entries.
14285 for (uint32_t i
= 0; i
< roots
.Length(); i
++) {
14286 nsCOMPtr
<Document
> root(roots
[i
]);
14287 // Check that the root isn't in the manager. This is so that new additions
14288 // while we were running don't get traversed.
14289 if (root
&& FullscreenRoots::Contains(root
)) {
14296 bool FullscreenRoots::Contains(Document
* aRoot
) {
14297 return sInstance
&& sInstance
->mRoots
.Contains(aRoot
);
14301 void FullscreenRoots::Add(Document
* aDoc
) {
14302 nsCOMPtr
<Document
> root
=
14303 nsContentUtils::GetInProcessSubtreeRootDocument(aDoc
);
14304 if (!FullscreenRoots::Contains(root
)) {
14306 sInstance
= new FullscreenRoots();
14308 sInstance
->mRoots
.AppendElement(root
);
14313 void FullscreenRoots::Remove(Document
* aDoc
) {
14314 nsCOMPtr
<Document
> root
=
14315 nsContentUtils::GetInProcessSubtreeRootDocument(aDoc
);
14316 if (!sInstance
|| !sInstance
->mRoots
.RemoveElement(root
)) {
14317 NS_ERROR("Should only try to remove roots which are still added!");
14320 if (sInstance
->mRoots
.IsEmpty()) {
14322 sInstance
= nullptr;
14327 bool FullscreenRoots::IsEmpty() { return !sInstance
; }
14329 // Any fullscreen change waiting for the widget to finish transition
14330 // is queued here. This is declared static instead of a member of
14331 // Document because in the majority of time, there would be at most
14332 // one document requesting or exiting fullscreen. We shouldn't waste
14333 // the space to hold for it in every document.
14334 class PendingFullscreenChangeList
{
14336 PendingFullscreenChangeList() = delete;
14338 template <typename T
>
14339 static void Add(UniquePtr
<T
> aChange
) {
14340 sList
.insertBack(aChange
.release());
14343 static const FullscreenChange
* GetLast() { return sList
.getLast(); }
14345 enum IteratorOption
{
14346 // When we are committing fullscreen changes or preparing for
14347 // that, we generally want to iterate all requests in the same
14348 // window with eDocumentsWithSameRoot option.
14349 eDocumentsWithSameRoot
,
14350 // If we are removing a document from the tree, we would only
14351 // want to remove the requests from the given document and its
14352 // descendants. For that case, use eInclusiveDescendants.
14353 eInclusiveDescendants
14356 template <typename T
>
14359 explicit Iterator(Document
* aDoc
, IteratorOption aOption
)
14360 : mCurrent(PendingFullscreenChangeList::sList
.getFirst()) {
14362 if (aDoc
->GetBrowsingContext()) {
14363 mRootBCForIteration
= aDoc
->GetBrowsingContext();
14364 if (aOption
== eDocumentsWithSameRoot
) {
14365 RefPtr
<BrowsingContext
> bc
=
14366 GetParentIgnoreChromeBoundary(mRootBCForIteration
);
14368 mRootBCForIteration
= bc
;
14369 bc
= GetParentIgnoreChromeBoundary(mRootBCForIteration
);
14377 UniquePtr
<T
> TakeAndNext() {
14378 auto thisChange
= TakeAndNextInternal();
14382 bool AtEnd() const { return mCurrent
== nullptr; }
14385 already_AddRefed
<BrowsingContext
> GetParentIgnoreChromeBoundary(
14386 BrowsingContext
* aBC
) {
14387 // Chrome BrowsingContexts are only available in the parent process, so if
14388 // we're in a content process, we only worry about the context tree.
14389 if (XRE_IsParentProcess()) {
14390 return aBC
->Canonical()->GetParentCrossChromeBoundary();
14392 return do_AddRef(aBC
->GetParent());
14395 UniquePtr
<T
> TakeAndNextInternal() {
14396 FullscreenChange
* thisChange
= mCurrent
;
14397 MOZ_ASSERT(thisChange
->Type() == T::kType
);
14398 mCurrent
= mCurrent
->removeAndGetNext();
14399 return WrapUnique(static_cast<T
*>(thisChange
));
14401 void SkipToNextMatch() {
14403 if (mCurrent
->Type() == T::kType
) {
14404 RefPtr
<BrowsingContext
> bc
=
14405 mCurrent
->Document()->GetBrowsingContext();
14407 // Always automatically drop fullscreen changes which are
14408 // from a document detached from the doc shell.
14409 UniquePtr
<T
> change
= TakeAndNextInternal();
14410 change
->MayRejectPromise("Document is not active");
14413 while (bc
&& bc
!= mRootBCForIteration
) {
14414 bc
= GetParentIgnoreChromeBoundary(bc
);
14420 // The current one either don't have matched type, or isn't
14421 // inside the given subtree, so skip this item.
14422 mCurrent
= mCurrent
->getNext();
14426 FullscreenChange
* mCurrent
;
14427 RefPtr
<BrowsingContext
> mRootBCForIteration
;
14431 static LinkedList
<FullscreenChange
> sList
;
14435 LinkedList
<FullscreenChange
> PendingFullscreenChangeList::sList
;
14437 size_t Document::CountFullscreenElements() const {
14439 for (const nsWeakPtr
& ptr
: mTopLayer
) {
14440 if (nsCOMPtr
<Element
> elem
= do_QueryReferent(ptr
)) {
14441 if (elem
->State().HasState(ElementState::FULLSCREEN
)) {
14449 // https://github.com/whatwg/html/issues/9143
14450 // We need to consider the precedence between active modal dialog, topmost auto
14451 // popover and fullscreen element once it's specified.
14452 void Document::HandleEscKey() {
14453 for (const nsWeakPtr
& weakPtr
: Reversed(mTopLayer
)) {
14454 nsCOMPtr
<Element
> element(do_QueryReferent(weakPtr
));
14455 if (RefPtr popoverHTMLEl
= nsGenericHTMLElement::FromNodeOrNull(element
)) {
14456 if (element
->IsAutoPopover() && element
->IsPopoverOpen()) {
14457 popoverHTMLEl
->HidePopover(IgnoreErrors());
14461 if (auto* dialog
= HTMLDialogElement::FromNodeOrNull(element
)) {
14462 dialog
->QueueCancelDialog();
14468 already_AddRefed
<Promise
> Document::ExitFullscreen(ErrorResult
& aRv
) {
14469 UniquePtr
<FullscreenExit
> exit
= FullscreenExit::Create(this, aRv
);
14470 RefPtr
<Promise
> promise
= exit
->GetPromise();
14471 RestorePreviousFullscreenState(std::move(exit
));
14472 return promise
.forget();
14475 static void AskWindowToExitFullscreen(Document
* aDoc
) {
14476 if (XRE_GetProcessType() == GeckoProcessType_Content
) {
14477 nsContentUtils::DispatchEventOnlyToChrome(
14478 aDoc
, aDoc
, u
"MozDOMFullscreen:Exit"_ns
, CanBubble::eYes
,
14479 Cancelable::eNo
, /* DefaultAction */ nullptr);
14481 if (nsPIDOMWindowOuter
* win
= aDoc
->GetWindow()) {
14482 win
->SetFullscreenInternal(FullscreenReason::ForFullscreenAPI
, false);
14487 class nsCallExitFullscreen
: public Runnable
{
14489 explicit nsCallExitFullscreen(Document
* aDoc
)
14490 : mozilla::Runnable("nsCallExitFullscreen"), mDoc(aDoc
) {}
14492 NS_IMETHOD
Run() final
{
14494 FullscreenRoots::ForEach(&AskWindowToExitFullscreen
);
14496 AskWindowToExitFullscreen(mDoc
);
14502 nsCOMPtr
<Document
> mDoc
;
14506 void Document::AsyncExitFullscreen(Document
* aDoc
) {
14507 MOZ_RELEASE_ASSERT(NS_IsMainThread());
14508 nsCOMPtr
<nsIRunnable
> exit
= new nsCallExitFullscreen(aDoc
);
14509 NS_DispatchToCurrentThread(exit
.forget());
14512 static uint32_t CountFullscreenSubDocuments(Document
& aDoc
) {
14513 uint32_t count
= 0;
14514 // FIXME(emilio): Should this be recursive and dig into our nested subdocs?
14515 auto subDoc
= [&count
](Document
& aSubDoc
) {
14516 if (aSubDoc
.Fullscreen()) {
14519 return CallState::Continue
;
14521 aDoc
.EnumerateSubDocuments(subDoc
);
14525 bool Document::IsFullscreenLeaf() {
14526 // A fullscreen leaf document is fullscreen, and has no fullscreen
14529 // FIXME(emilio): This doesn't seem to account for fission iframes, is that
14531 return Fullscreen() && CountFullscreenSubDocuments(*this) == 0;
14534 static Document
* GetFullscreenLeaf(Document
& aDoc
) {
14535 if (aDoc
.IsFullscreenLeaf()) {
14538 if (!aDoc
.Fullscreen()) {
14541 Document
* leaf
= nullptr;
14542 auto recurse
= [&leaf
](Document
& aSubDoc
) {
14543 leaf
= GetFullscreenLeaf(aSubDoc
);
14544 return leaf
? CallState::Stop
: CallState::Continue
;
14546 aDoc
.EnumerateSubDocuments(recurse
);
14550 static Document
* GetFullscreenLeaf(Document
* aDoc
) {
14551 if (Document
* leaf
= GetFullscreenLeaf(*aDoc
)) {
14554 // Otherwise we could be either in a non-fullscreen doc tree, or we're
14555 // below the fullscreen doc. Start the search from the root.
14556 Document
* root
= nsContentUtils::GetInProcessSubtreeRootDocument(aDoc
);
14557 return GetFullscreenLeaf(*root
);
14560 static CallState
ResetFullscreen(Document
& aDocument
) {
14561 if (Element
* fsElement
= aDocument
.GetUnretargetedFullscreenElement()) {
14562 NS_ASSERTION(CountFullscreenSubDocuments(aDocument
) <= 1,
14563 "Should have at most 1 fullscreen subdocument.");
14564 aDocument
.CleanupFullscreenState();
14565 NS_ASSERTION(!aDocument
.Fullscreen(), "Should reset fullscreen");
14566 DispatchFullscreenChange(aDocument
, fsElement
);
14567 aDocument
.EnumerateSubDocuments(ResetFullscreen
);
14569 return CallState::Continue
;
14572 // Since Document::ExitFullscreenInDocTree() could be called from
14573 // Element::UnbindFromTree() where it is not safe to synchronously run
14574 // script. This runnable is the script part of that function.
14575 class ExitFullscreenScriptRunnable
: public Runnable
{
14577 explicit ExitFullscreenScriptRunnable(Document
* aRoot
, Document
* aLeaf
)
14578 : mozilla::Runnable("ExitFullscreenScriptRunnable"),
14582 NS_IMETHOD
Run() override
{
14583 // Dispatch MozDOMFullscreen:Exited to the original fullscreen leaf
14584 // document since we want this event to follow the same path that
14585 // MozDOMFullscreen:Entered was dispatched.
14586 nsContentUtils::DispatchEventOnlyToChrome(
14587 mLeaf
, mLeaf
, u
"MozDOMFullscreen:Exited"_ns
, CanBubble::eYes
,
14588 Cancelable::eNo
, /* DefaultAction */ nullptr);
14589 // Ensure the window exits fullscreen, as long as we don't have
14590 // pending fullscreen requests.
14591 if (nsPIDOMWindowOuter
* win
= mRoot
->GetWindow()) {
14592 if (!mRoot
->HasPendingFullscreenRequests()) {
14593 win
->SetFullscreenInternal(FullscreenReason::ForForceExitFullscreen
,
14601 nsCOMPtr
<Document
> mRoot
;
14602 nsCOMPtr
<Document
> mLeaf
;
14606 void Document::ExitFullscreenInDocTree(Document
* aMaybeNotARootDoc
) {
14607 MOZ_ASSERT(aMaybeNotARootDoc
);
14609 // Unlock the pointer
14610 PointerLockManager::Unlock();
14612 // Resolve all promises which waiting for exit fullscreen.
14613 PendingFullscreenChangeList::Iterator
<FullscreenExit
> iter(
14614 aMaybeNotARootDoc
, PendingFullscreenChangeList::eDocumentsWithSameRoot
);
14615 while (!iter
.AtEnd()) {
14616 UniquePtr
<FullscreenExit
> exit
= iter
.TakeAndNext();
14617 exit
->MayResolvePromise();
14620 nsCOMPtr
<Document
> root
= aMaybeNotARootDoc
->GetFullscreenRoot();
14621 if (!root
|| !root
->Fullscreen()) {
14622 // If a document was detached before exiting from fullscreen, it is
14623 // possible that the root had left fullscreen state. In this case,
14624 // we would not get anything from the ResetFullscreen() call. Root's
14625 // not being a fullscreen doc also means the widget should have
14626 // exited fullscreen state. It means even if we do not return here,
14627 // we would actually do nothing below except crashing ourselves via
14628 // dispatching the "MozDOMFullscreen:Exited" event to an nonexistent
14633 // Record the fullscreen leaf document for MozDOMFullscreen:Exited.
14634 // See ExitFullscreenScriptRunnable::Run for details. We have to
14635 // record it here because we don't have such information after we
14636 // reset the fullscreen state below.
14637 Document
* fullscreenLeaf
= GetFullscreenLeaf(root
);
14639 // Walk the tree of fullscreen documents, and reset their fullscreen state.
14640 ResetFullscreen(*root
);
14642 NS_ASSERTION(!root
->Fullscreen(),
14643 "Fullscreen root should no longer be a fullscreen doc...");
14645 // Move the top-level window out of fullscreen mode.
14646 FullscreenRoots::Remove(root
);
14648 nsContentUtils::AddScriptRunner(
14649 new ExitFullscreenScriptRunnable(root
, fullscreenLeaf
));
14652 static void DispatchFullscreenNewOriginEvent(Document
* aDoc
) {
14653 RefPtr
<AsyncEventDispatcher
> asyncDispatcher
=
14654 new AsyncEventDispatcher(aDoc
, u
"MozDOMFullscreen:NewOrigin"_ns
,
14655 CanBubble::eYes
, ChromeOnlyDispatch::eYes
);
14656 asyncDispatcher
->PostDOMEvent();
14659 void Document::RestorePreviousFullscreenState(UniquePtr
<FullscreenExit
> aExit
) {
14660 NS_ASSERTION(!Fullscreen() || !FullscreenRoots::IsEmpty(),
14661 "Should have at least 1 fullscreen root when fullscreen!");
14663 if (!GetWindow()) {
14664 aExit
->MayRejectPromise("No active window");
14667 if (!Fullscreen() || FullscreenRoots::IsEmpty()) {
14668 aExit
->MayRejectPromise("Not in fullscreen mode");
14672 nsCOMPtr
<Document
> fullScreenDoc
= GetFullscreenLeaf(this);
14673 AutoTArray
<Element
*, 8> exitElements
;
14675 Document
* doc
= fullScreenDoc
;
14676 // Collect all subdocuments.
14677 for (; doc
!= this; doc
= doc
->GetInProcessParentDocument()) {
14678 Element
* fsElement
= doc
->GetUnretargetedFullscreenElement();
14679 MOZ_ASSERT(fsElement
,
14680 "Parent document of "
14681 "a fullscreen document without fullscreen element?");
14682 exitElements
.AppendElement(fsElement
);
14684 MOZ_ASSERT(doc
== this, "Must have reached this doc");
14685 // Collect all ancestor documents which we are going to change.
14686 for (; doc
; doc
= doc
->GetInProcessParentDocument()) {
14687 Element
* fsElement
= doc
->GetUnretargetedFullscreenElement();
14688 MOZ_ASSERT(fsElement
,
14689 "Ancestor of fullscreen document must also be in fullscreen");
14691 if (auto* iframe
= HTMLIFrameElement::FromNode(fsElement
)) {
14692 if (iframe
->FullscreenFlag()) {
14693 // If this is an iframe, and it explicitly requested
14694 // fullscreen, don't rollback it automatically.
14699 exitElements
.AppendElement(fsElement
);
14700 if (doc
->CountFullscreenElements() > 1) {
14705 Document
* lastDoc
= exitElements
.LastElement()->OwnerDoc();
14706 size_t fullscreenCount
= lastDoc
->CountFullscreenElements();
14707 if (!lastDoc
->GetInProcessParentDocument() && fullscreenCount
== 1) {
14708 // If we are fully exiting fullscreen, don't touch anything here,
14709 // just wait for the window to get out from fullscreen first.
14710 PendingFullscreenChangeList::Add(std::move(aExit
));
14711 AskWindowToExitFullscreen(this);
14715 // If fullscreen mode is updated the pointer should be unlocked
14716 PointerLockManager::Unlock();
14717 // All documents listed in the array except the last one are going to
14718 // completely exit from the fullscreen state.
14719 for (auto i
: IntegerRange(exitElements
.Length() - 1)) {
14720 exitElements
[i
]->OwnerDoc()->CleanupFullscreenState();
14722 // The last document will either rollback one fullscreen element, or
14723 // completely exit from the fullscreen state as well.
14724 Document
* newFullscreenDoc
;
14725 if (fullscreenCount
> 1) {
14726 DebugOnly
<bool> removedFullscreenElement
= lastDoc
->PopFullscreenElement();
14727 MOZ_ASSERT(removedFullscreenElement
);
14728 newFullscreenDoc
= lastDoc
;
14730 lastDoc
->CleanupFullscreenState();
14731 newFullscreenDoc
= lastDoc
->GetInProcessParentDocument();
14733 // Dispatch the fullscreenchange event to all document listed. Note
14734 // that the loop order is reversed so that events are dispatched in
14735 // the tree order as indicated in the spec.
14736 for (Element
* e
: Reversed(exitElements
)) {
14737 DispatchFullscreenChange(*e
->OwnerDoc(), e
);
14739 aExit
->MayResolvePromise();
14741 MOZ_ASSERT(newFullscreenDoc
,
14742 "If we were going to exit from fullscreen on "
14743 "all documents in this doctree, we should've asked the window to "
14744 "exit first instead of reaching here.");
14745 if (fullScreenDoc
!= newFullscreenDoc
&&
14746 !nsContentUtils::HaveEqualPrincipals(fullScreenDoc
, newFullscreenDoc
)) {
14747 // We've popped so enough off the stack that we've rolled back to
14748 // a fullscreen element in a parent document. If this document is
14749 // cross origin, dispatch an event to chrome so it knows to show
14751 DispatchFullscreenNewOriginEvent(newFullscreenDoc
);
14755 static void UpdateViewportScrollbarOverrideForFullscreen(Document
* aDoc
) {
14756 if (nsPresContext
* presContext
= aDoc
->GetPresContext()) {
14757 presContext
->UpdateViewportScrollStylesOverride();
14761 static void NotifyFullScreenChangedForMediaElement(Element
& aElement
) {
14762 // When a media element enters the fullscreen, we would like to notify that
14763 // to the media controller in order to update its status.
14764 if (auto* mediaElem
= HTMLMediaElement::FromNode(aElement
)) {
14765 mediaElem
->NotifyFullScreenChanged();
14769 void Document::CleanupFullscreenState() {
14770 while (PopFullscreenElement(UpdateViewport::No
)) {
14771 // Remove the next one if appropriate
14774 UpdateViewportScrollbarOverrideForFullscreen(this);
14775 mFullscreenRoot
= nullptr;
14777 // Restore the zoom level that was in place prior to entering fullscreen.
14778 if (PresShell
* presShell
= GetPresShell()) {
14779 if (presShell
->GetMobileViewportManager()) {
14780 presShell
->SetResolutionAndScaleTo(
14781 mSavedResolution
, ResolutionChangeOrigin::MainThreadRestore
);
14786 bool Document::PopFullscreenElement(UpdateViewport aUpdateViewport
) {
14787 Element
* removedElement
= TopLayerPop([](Element
* element
) -> bool {
14788 return element
->State().HasState(ElementState::FULLSCREEN
);
14791 if (!removedElement
) {
14795 MOZ_ASSERT(removedElement
->State().HasState(ElementState::FULLSCREEN
));
14796 removedElement
->RemoveStates(ElementState::FULLSCREEN
| ElementState::MODAL
);
14797 NotifyFullScreenChangedForMediaElement(*removedElement
);
14798 // Reset iframe fullscreen flag.
14799 if (auto* iframe
= HTMLIFrameElement::FromNode(removedElement
)) {
14800 iframe
->SetFullscreenFlag(false);
14802 if (aUpdateViewport
== UpdateViewport::Yes
) {
14803 UpdateViewportScrollbarOverrideForFullscreen(this);
14808 void Document::SetFullscreenElement(Element
& aElement
) {
14809 ElementState statesToAdd
= ElementState::FULLSCREEN
;
14810 if (!IsInChromeDocShell()) {
14811 // Don't make the document modal in chrome documents, since we don't want
14812 // the browser UI like the context menu / etc to be inert.
14813 statesToAdd
|= ElementState::MODAL
;
14815 aElement
.AddStates(statesToAdd
);
14816 TopLayerPush(aElement
);
14817 NotifyFullScreenChangedForMediaElement(aElement
);
14818 UpdateViewportScrollbarOverrideForFullscreen(this);
14821 void Document::TopLayerPush(Element
& aElement
) {
14822 const bool modal
= aElement
.State().HasState(ElementState::MODAL
);
14824 TopLayerPop(aElement
);
14825 mTopLayer
.AppendElement(do_GetWeakReference(&aElement
));
14826 NS_ASSERTION(GetTopLayerTop() == &aElement
, "Should match");
14829 aElement
.AddStates(ElementState::TOPMOST_MODAL
);
14831 bool foundExistingModalElement
= false;
14832 for (const nsWeakPtr
& weakPtr
: Reversed(mTopLayer
)) {
14833 nsCOMPtr
<Element
> element(do_QueryReferent(weakPtr
));
14834 if (element
&& element
!= &aElement
&&
14835 element
->State().HasState(ElementState::TOPMOST_MODAL
)) {
14836 element
->RemoveStates(ElementState::TOPMOST_MODAL
);
14837 foundExistingModalElement
= true;
14842 if (!foundExistingModalElement
) {
14843 Element
* root
= GetRootElement();
14844 MOZ_RELEASE_ASSERT(root
, "top layer element outside of document?");
14845 if (&aElement
!= root
) {
14846 // Add inert to the root element so that the inertness is applied to the
14847 // entire document.
14848 root
->AddStates(ElementState::INERT
);
14854 void Document::AddModalDialog(HTMLDialogElement
& aDialogElement
) {
14855 aDialogElement
.AddStates(ElementState::MODAL
);
14856 TopLayerPush(aDialogElement
);
14859 void Document::RemoveModalDialog(HTMLDialogElement
& aDialogElement
) {
14860 DebugOnly
<Element
*> removedElement
= TopLayerPop(aDialogElement
);
14861 MOZ_ASSERT(removedElement
== &aDialogElement
);
14862 aDialogElement
.RemoveStates(ElementState::MODAL
);
14865 Element
* Document::TopLayerPop(FunctionRef
<bool(Element
*)> aPredicate
) {
14866 if (mTopLayer
.IsEmpty()) {
14870 // Remove the topmost element that qualifies aPredicate; This
14871 // is required is because the top layer contains not only
14872 // fullscreen elements, but also dialog elements.
14873 Element
* removedElement
= nullptr;
14874 for (auto i
: Reversed(IntegerRange(mTopLayer
.Length()))) {
14875 nsCOMPtr
<Element
> element(do_QueryReferent(mTopLayer
[i
]));
14876 if (element
&& aPredicate(element
)) {
14877 removedElement
= element
;
14878 mTopLayer
.RemoveElementAt(i
);
14883 // Pop from the stack null elements (references to elements which have
14884 // been GC'd since they were added to the stack) and elements which are
14885 // no longer in this document.
14887 // FIXME(emilio): If this loop does something, it'd violate the assertions
14888 // from GetTopLayerTop()... What gives?
14889 while (!mTopLayer
.IsEmpty()) {
14890 Element
* element
= GetTopLayerTop();
14891 if (!element
|| element
->GetComposedDoc() != this) {
14892 mTopLayer
.RemoveLastElement();
14894 // The top element of the stack is now an in-doc element. Return here.
14899 if (!removedElement
) {
14903 const bool modal
= removedElement
->State().HasState(ElementState::MODAL
);
14906 removedElement
->RemoveStates(ElementState::TOPMOST_MODAL
);
14907 bool foundExistingModalElement
= false;
14908 for (const nsWeakPtr
& weakPtr
: Reversed(mTopLayer
)) {
14909 nsCOMPtr
<Element
> element(do_QueryReferent(weakPtr
));
14910 if (element
&& element
->State().HasState(ElementState::MODAL
)) {
14911 element
->AddStates(ElementState::TOPMOST_MODAL
);
14912 foundExistingModalElement
= true;
14916 // No more modal elements, make the document not inert anymore.
14917 if (!foundExistingModalElement
) {
14918 Element
* root
= GetRootElement();
14919 if (root
&& !root
->GetBoolAttr(nsGkAtoms::inert
)) {
14920 root
->RemoveStates(ElementState::INERT
);
14925 return removedElement
;
14928 Element
* Document::TopLayerPop(Element
& aElement
) {
14929 auto predictFunc
= [&aElement
](Element
* element
) {
14930 return element
== &aElement
;
14932 return TopLayerPop(predictFunc
);
14935 void Document::GetWireframe(bool aIncludeNodes
,
14936 Nullable
<Wireframe
>& aWireframe
) {
14937 FlushPendingNotifications(FlushType::Layout
);
14938 GetWireframeWithoutFlushing(aIncludeNodes
, aWireframe
);
14941 void Document::GetWireframeWithoutFlushing(bool aIncludeNodes
,
14942 Nullable
<Wireframe
>& aWireframe
) {
14943 using FrameForPointOptions
= nsLayoutUtils::FrameForPointOptions
;
14944 using FrameForPointOption
= nsLayoutUtils::FrameForPointOption
;
14946 PresShell
* shell
= GetPresShell();
14951 nsPresContext
* pc
= shell
->GetPresContext();
14956 nsIFrame
* rootFrame
= shell
->GetRootFrame();
14961 auto& wireframe
= aWireframe
.SetValue();
14962 wireframe
.mCanvasBackground
= shell
->ComputeCanvasBackground().mViewportColor
;
14964 FrameForPointOptions options
;
14965 options
.mBits
+= FrameForPointOption::IgnoreCrossDoc
;
14966 options
.mBits
+= FrameForPointOption::IgnorePaintSuppression
;
14967 options
.mBits
+= FrameForPointOption::OnlyVisible
;
14969 AutoTArray
<nsIFrame
*, 32> frames
;
14970 const RelativeTo relativeTo
{rootFrame
, mozilla::ViewportType::Layout
};
14971 nsLayoutUtils::GetFramesForArea(relativeTo
, pc
->GetVisibleArea(), frames
,
14974 // TODO(emilio): We could rewrite hit testing to return nsDisplayItem*s or
14975 // something perhaps, but seems hard / like it'd involve at least some extra
14976 // copying around, since they don't outlive GetFramesForArea.
14977 auto& rects
= wireframe
.mRects
.Construct();
14978 if (!rects
.SetCapacity(frames
.Length(), fallible
)) {
14981 for (nsIFrame
* frame
: Reversed(frames
)) {
14983 rectType
] = [&]() -> std::tuple
<nscolor
, WireframeRectType
> {
14984 if (frame
->IsTextFrame()) {
14985 return {frame
->StyleText()->mWebkitTextFillColor
.CalcColor(frame
),
14986 WireframeRectType::Text
};
14988 if (frame
->IsImageFrame() || frame
->IsSVGOuterSVGFrame()) {
14989 return {0, WireframeRectType::Image
};
14991 if (frame
->IsThemed()) {
14992 return {0, WireframeRectType::Background
};
14994 bool drawImage
= false;
14995 bool drawColor
= false;
14996 if (const auto* bgStyle
= nsCSSRendering::FindBackground(frame
)) {
14997 const nscolor color
= nsCSSRendering::DetermineBackgroundColor(
14998 pc
, bgStyle
, frame
, drawImage
, drawColor
);
15000 !bgStyle
->StyleBackground()->mImage
.BottomLayer().mImage
.IsNone()) {
15001 return {color
, WireframeRectType::Image
};
15003 if (drawColor
&& !frame
->IsCanvasFrame()) {
15004 // Canvas frame background already accounted for in mCanvasBackground.
15005 return {color
, WireframeRectType::Background
};
15008 return {0, WireframeRectType::Unknown
};
15011 if (rectType
== WireframeRectType::Unknown
) {
15016 CSSRect::FromAppUnits(nsLayoutUtils::TransformFrameRectToAncestor(
15017 frame
, frame
->GetRectRelativeToSelf(), relativeTo
));
15018 if ((uint32_t)r
.Area() <
15019 StaticPrefs::browser_history_wireframeAreaThreshold()) {
15023 // Can't really fail because SetCapacity succeeded.
15024 auto& taggedRect
= *rects
.AppendElement(fallible
);
15026 if (aIncludeNodes
) {
15027 if (nsIContent
* c
= frame
->GetContent()) {
15028 taggedRect
.mNode
.Construct(c
);
15031 taggedRect
.mX
= r
.x
;
15032 taggedRect
.mY
= r
.y
;
15033 taggedRect
.mWidth
= r
.width
;
15034 taggedRect
.mHeight
= r
.height
;
15035 taggedRect
.mColor
= rectColor
;
15036 taggedRect
.mType
.Construct(rectType
);
15040 Element
* Document::GetTopLayerTop() {
15041 if (mTopLayer
.IsEmpty()) {
15044 uint32_t last
= mTopLayer
.Length() - 1;
15045 nsCOMPtr
<Element
> element(do_QueryReferent(mTopLayer
[last
]));
15046 NS_ASSERTION(element
, "Should have a top layer element!");
15047 NS_ASSERTION(element
->IsInComposedDoc(),
15048 "Top layer element should be in doc");
15049 NS_ASSERTION(element
->OwnerDoc() == this,
15050 "Top layer element should be in this doc");
15054 Element
* Document::GetUnretargetedFullscreenElement() const {
15055 for (const nsWeakPtr
& weakPtr
: Reversed(mTopLayer
)) {
15056 nsCOMPtr
<Element
> element(do_QueryReferent(weakPtr
));
15057 // Per spec, the fullscreen element is the topmost element in the document’s
15058 // top layer whose fullscreen flag is set, if any, and null otherwise.
15059 if (element
&& element
->State().HasState(ElementState::FULLSCREEN
)) {
15066 nsTArray
<Element
*> Document::GetTopLayer() const {
15067 nsTArray
<Element
*> elements
;
15068 for (const nsWeakPtr
& ptr
: mTopLayer
) {
15069 if (nsCOMPtr
<Element
> elem
= do_QueryReferent(ptr
)) {
15070 elements
.AppendElement(elem
);
15076 bool Document::TopLayerContains(Element
& aElement
) const {
15077 if (mTopLayer
.IsEmpty()) {
15080 nsWeakPtr weakElement
= do_GetWeakReference(&aElement
);
15081 return mTopLayer
.Contains(weakElement
);
15084 void Document::HideAllPopoversUntil(nsINode
& aEndpoint
,
15085 bool aFocusPreviousElement
,
15086 bool aFireEvents
) {
15087 auto closeAllOpenPopovers
= [&aFocusPreviousElement
, &aFireEvents
,
15088 this]() MOZ_CAN_RUN_SCRIPT_FOR_DEFINITION
{
15089 while (RefPtr
<Element
> topmost
= GetTopmostAutoPopover()) {
15090 HidePopover(*topmost
, aFocusPreviousElement
, aFireEvents
, IgnoreErrors());
15094 if (&aEndpoint
== this) {
15095 closeAllOpenPopovers();
15099 // https://github.com/whatwg/html/pull/9198
15100 auto needRepeatingHide
= [&]() {
15101 auto autoList
= AutoPopoverList();
15102 return autoList
.Contains(&aEndpoint
) &&
15103 &aEndpoint
!= autoList
.LastElement();
15106 MOZ_ASSERT((&aEndpoint
)->IsElement() &&
15107 (&aEndpoint
)->AsElement()->IsAutoPopover());
15108 bool repeatingHide
= false;
15109 bool fireEvents
= aFireEvents
;
15111 RefPtr
<const Element
> lastToHide
= nullptr;
15112 bool foundEndpoint
= false;
15113 for (const Element
* popover
: AutoPopoverList()) {
15114 if (popover
== &aEndpoint
) {
15115 foundEndpoint
= true;
15116 } else if (foundEndpoint
) {
15117 lastToHide
= popover
;
15122 if (!foundEndpoint
) {
15123 closeAllOpenPopovers();
15127 while (lastToHide
&& lastToHide
->IsPopoverOpen()) {
15128 RefPtr
<Element
> topmost
= GetTopmostAutoPopover();
15132 HidePopover(*topmost
, aFocusPreviousElement
, fireEvents
, IgnoreErrors());
15135 repeatingHide
= needRepeatingHide();
15136 if (repeatingHide
) {
15137 fireEvents
= false;
15139 } while (repeatingHide
);
15142 MOZ_CAN_RUN_SCRIPT_BOUNDARY
void
15143 Document::HideAllPopoversWithoutRunningScript() {
15144 return HideAllPopoversUntil(*this, false, false);
15147 void Document::HidePopover(Element
& aPopover
, bool aFocusPreviousElement
,
15148 bool aFireEvents
, ErrorResult
& aRv
) {
15149 RefPtr
<nsGenericHTMLElement
> popoverHTMLEl
=
15150 nsGenericHTMLElement::FromNode(aPopover
);
15151 NS_ASSERTION(popoverHTMLEl
, "Not a HTML element");
15153 if (!popoverHTMLEl
->CheckPopoverValidity(PopoverVisibilityState::Showing
,
15158 bool wasShowingOrHiding
=
15159 popoverHTMLEl
->GetPopoverData()->IsShowingOrHiding();
15160 popoverHTMLEl
->GetPopoverData()->SetIsShowingOrHiding(true);
15161 const bool fireEvents
= aFireEvents
&& !wasShowingOrHiding
;
15162 auto cleanupHidingFlag
= MakeScopeExit([&]() {
15163 if (auto* popoverData
= popoverHTMLEl
->GetPopoverData()) {
15164 popoverData
->SetIsShowingOrHiding(wasShowingOrHiding
);
15168 if (popoverHTMLEl
->IsAutoPopover()) {
15169 HideAllPopoversUntil(*popoverHTMLEl
, aFocusPreviousElement
, fireEvents
);
15170 if (!popoverHTMLEl
->CheckPopoverValidity(PopoverVisibilityState::Showing
,
15174 // TODO: we can't always guarantee:
15175 // The last item in document's auto popover list is popoverHTMLEl.
15176 // See, https://github.com/whatwg/html/issues/9197
15177 // If popoverHTMLEl is not on top, hide popovers again without firing
15179 if (NS_WARN_IF(GetTopmostAutoPopover() != popoverHTMLEl
)) {
15180 HideAllPopoversUntil(*popoverHTMLEl
, aFocusPreviousElement
, false);
15181 if (!popoverHTMLEl
->CheckPopoverValidity(PopoverVisibilityState::Showing
,
15185 MOZ_ASSERT(GetTopmostAutoPopover() == popoverHTMLEl
,
15186 "popoverHTMLEl should be on top of auto popover list");
15190 auto* data
= popoverHTMLEl
->GetPopoverData();
15191 MOZ_ASSERT(data
, "Should have popover data");
15192 data
->SetInvoker(nullptr);
15194 // Fire beforetoggle event and re-check popover validity.
15196 // Intentionally ignore the return value here as only on open event for
15197 // beforetoggle the cancelable attribute is initialized to true.
15198 popoverHTMLEl
->FireToggleEvent(PopoverVisibilityState::Showing
,
15199 PopoverVisibilityState::Hidden
,
15200 u
"beforetoggle"_ns
);
15201 if (!popoverHTMLEl
->CheckPopoverValidity(PopoverVisibilityState::Showing
,
15207 RemovePopoverFromTopLayer(aPopover
);
15209 popoverHTMLEl
->PopoverPseudoStateUpdate(false, true);
15210 popoverHTMLEl
->GetPopoverData()->SetPopoverVisibilityState(
15211 PopoverVisibilityState::Hidden
);
15213 // Queue popover toggle event task.
15215 popoverHTMLEl
->QueuePopoverEventTask(PopoverVisibilityState::Showing
);
15218 if (aFocusPreviousElement
) {
15219 popoverHTMLEl
->FocusPreviousElementAfterHidingPopover();
15221 popoverHTMLEl
->ForgetPreviouslyFocusedElementAfterHidingPopover();
15225 nsTArray
<Element
*> Document::AutoPopoverList() const {
15226 nsTArray
<Element
*> elements
;
15227 for (const nsWeakPtr
& ptr
: mTopLayer
) {
15228 if (nsCOMPtr
<Element
> element
= do_QueryReferent(ptr
)) {
15229 if (element
&& element
->IsAutoPopover() && element
->IsPopoverOpen()) {
15230 elements
.AppendElement(element
);
15237 Element
* Document::GetTopmostAutoPopover() const {
15238 for (const nsWeakPtr
& weakPtr
: Reversed(mTopLayer
)) {
15239 nsCOMPtr
<Element
> element(do_QueryReferent(weakPtr
));
15240 if (element
&& element
->IsAutoPopover() && element
->IsPopoverOpen()) {
15247 void Document::AddToAutoPopoverList(Element
& aElement
) {
15248 MOZ_ASSERT(aElement
.IsAutoPopover());
15249 TopLayerPush(aElement
);
15252 void Document::RemoveFromAutoPopoverList(Element
& aElement
) {
15253 MOZ_ASSERT(aElement
.IsAutoPopover());
15254 TopLayerPop(aElement
);
15257 void Document::AddPopoverToTopLayer(Element
& aElement
) {
15258 MOZ_ASSERT(aElement
.GetPopoverData());
15259 TopLayerPush(aElement
);
15262 void Document::RemovePopoverFromTopLayer(Element
& aElement
) {
15263 MOZ_ASSERT(aElement
.GetPopoverData());
15264 TopLayerPop(aElement
);
15267 // Returns true if aDoc browsing context is focused.
15268 bool IsInFocusedTab(Document
* aDoc
) {
15269 BrowsingContext
* bc
= aDoc
->GetBrowsingContext();
15274 nsFocusManager
* fm
= nsFocusManager::GetFocusManager();
15279 if (XRE_IsParentProcess()) {
15280 // Keep dom/tests/mochitest/chrome/test_MozDomFullscreen_event.xhtml happy
15281 // by retaining the old code path for the parent process.
15282 nsIDocShell
* docshell
= aDoc
->GetDocShell();
15286 nsCOMPtr
<nsIDocShellTreeItem
> rootItem
;
15287 docshell
->GetInProcessRootTreeItem(getter_AddRefs(rootItem
));
15291 nsCOMPtr
<nsPIDOMWindowOuter
> rootWin
= rootItem
->GetWindow();
15296 nsCOMPtr
<nsPIDOMWindowOuter
> activeWindow
;
15297 activeWindow
= fm
->GetActiveWindow();
15298 if (!activeWindow
) {
15302 return activeWindow
== rootWin
;
15305 return fm
->GetActiveBrowsingContext() == bc
->Top();
15308 // Returns true if aDoc browsing context is focused and is also active.
15309 bool IsInActiveTab(Document
* aDoc
) {
15310 if (!IsInFocusedTab(aDoc
)) {
15314 BrowsingContext
* bc
= aDoc
->GetBrowsingContext();
15315 MOZ_ASSERT(bc
, "With no BrowsingContext, we should have failed earlier.");
15316 return bc
->IsActive();
15319 void Document::RemoteFrameFullscreenChanged(Element
* aFrameElement
) {
15320 // Ensure the frame element is the fullscreen element in this document.
15321 // If the frame element is already the fullscreen element in this document,
15322 // this has no effect.
15323 auto request
= FullscreenRequest::CreateForRemote(aFrameElement
);
15324 RequestFullscreen(std::move(request
), XRE_IsContentProcess());
15327 void Document::RemoteFrameFullscreenReverted() {
15328 UniquePtr
<FullscreenExit
> exit
= FullscreenExit::CreateForRemote(this);
15329 RestorePreviousFullscreenState(std::move(exit
));
15332 static bool HasFullscreenSubDocument(Document
& aDoc
) {
15333 uint32_t count
= CountFullscreenSubDocuments(aDoc
);
15334 NS_ASSERTION(count
<= 1,
15335 "Fullscreen docs should have at most 1 fullscreen child!");
15339 // Returns nullptr if a request for Fullscreen API is currently enabled
15340 // in the given document. Returns a static string indicates the reason
15341 // why it is not enabled otherwise.
15342 const char* Document::GetFullscreenError(CallerType aCallerType
) {
15343 if (!StaticPrefs::full_screen_api_enabled()) {
15344 return "FullscreenDeniedDisabled";
15347 if (aCallerType
== CallerType::System
) {
15348 // Chrome code can always use the fullscreen API, provided it's not
15349 // explicitly disabled.
15353 if (!IsVisible()) {
15354 return "FullscreenDeniedHidden";
15357 if (!FeaturePolicyUtils::IsFeatureAllowed(this, u
"fullscreen"_ns
)) {
15358 return "FullscreenDeniedFeaturePolicy";
15361 // Ensure that all containing elements are <iframe> and have allowfullscreen
15363 BrowsingContext
* bc
= GetBrowsingContext();
15364 if (!bc
|| !bc
->FullscreenAllowed()) {
15365 return "FullscreenDeniedContainerNotAllowed";
15371 bool Document::FullscreenElementReadyCheck(FullscreenRequest
& aRequest
) {
15372 Element
* elem
= aRequest
.Element();
15373 // Strictly speaking, this isn't part of the fullscreen element ready
15374 // check in the spec, but per steps in the spec, when an element which
15375 // is already the fullscreen element requests fullscreen, nothing
15376 // should change and no event should be dispatched, but we still need
15377 // to resolve the returned promise.
15378 Element
* fullscreenElement
= GetUnretargetedFullscreenElement();
15379 if (elem
== fullscreenElement
) {
15380 aRequest
.MayResolvePromise();
15383 if (!elem
->IsInComposedDoc()) {
15384 aRequest
.Reject("FullscreenDeniedNotInDocument");
15387 if (elem
->IsPopoverOpen()) {
15388 aRequest
.Reject("FullscreenDeniedPopoverOpen");
15391 if (elem
->OwnerDoc() != this) {
15392 aRequest
.Reject("FullscreenDeniedMovedDocument");
15395 if (!GetWindow()) {
15396 aRequest
.Reject("FullscreenDeniedLostWindow");
15399 if (const char* msg
= GetFullscreenError(aRequest
.mCallerType
)) {
15400 aRequest
.Reject(msg
);
15403 if (HasFullscreenSubDocument(*this)) {
15404 aRequest
.Reject("FullscreenDeniedSubDocFullScreen");
15407 if (elem
->IsHTMLElement(nsGkAtoms::dialog
)) {
15408 aRequest
.Reject("FullscreenDeniedHTMLDialog");
15411 if (!nsContentUtils::IsChromeDoc(this) && !IsInFocusedTab(this)) {
15412 aRequest
.Reject("FullscreenDeniedNotFocusedTab");
15418 static nsCOMPtr
<nsPIDOMWindowOuter
> GetRootWindow(Document
* aDoc
) {
15419 MOZ_ASSERT(XRE_IsParentProcess());
15420 nsIDocShell
* docShell
= aDoc
->GetDocShell();
15424 nsCOMPtr
<nsIDocShellTreeItem
> rootItem
;
15425 docShell
->GetInProcessRootTreeItem(getter_AddRefs(rootItem
));
15426 return rootItem
? rootItem
->GetWindow() : nullptr;
15429 static bool ShouldApplyFullscreenDirectly(Document
* aDoc
,
15430 nsPIDOMWindowOuter
* aRootWin
) {
15431 MOZ_ASSERT(XRE_IsParentProcess());
15432 // If we are in the chrome process, and the window has not been in
15433 // fullscreen, we certainly need to make that fullscreen first.
15434 if (!aRootWin
->GetFullScreen()) {
15437 // The iterator not being at end indicates there is still some
15438 // pending fullscreen request relates to this document. We have to
15439 // push the request to the pending queue so requests are handled
15440 // in the correct order.
15441 PendingFullscreenChangeList::Iterator
<FullscreenRequest
> iter(
15442 aDoc
, PendingFullscreenChangeList::eDocumentsWithSameRoot
);
15443 if (!iter
.AtEnd()) {
15447 // Same thing for exits. If we have any pending, we have to push
15448 // to the pending queue.
15449 PendingFullscreenChangeList::Iterator
<FullscreenExit
> iterExit(
15450 aDoc
, PendingFullscreenChangeList::eDocumentsWithSameRoot
);
15451 if (!iterExit
.AtEnd()) {
15455 // We have to apply the fullscreen state directly in this case,
15456 // because nsGlobalWindow::SetFullscreenInternal() will do nothing
15457 // if it is already in fullscreen. If we do not apply the state but
15458 // instead add it to the queue and wait for the window as normal,
15459 // we would get stuck.
15463 static bool CheckFullscreenAllowedElementType(const Element
* elem
) {
15464 // Per spec only HTML, <svg>, and <math> should be allowed, but
15465 // we also need to allow XUL elements right now.
15466 return elem
->IsHTMLElement() || elem
->IsXULElement() ||
15467 elem
->IsSVGElement(nsGkAtoms::svg
) ||
15468 elem
->IsMathMLElement(nsGkAtoms::math
);
15471 void Document::RequestFullscreen(UniquePtr
<FullscreenRequest
> aRequest
,
15472 bool aApplyFullscreenDirectly
) {
15473 if (XRE_IsContentProcess()) {
15474 RequestFullscreenInContentProcess(std::move(aRequest
),
15475 aApplyFullscreenDirectly
);
15477 RequestFullscreenInParentProcess(std::move(aRequest
),
15478 aApplyFullscreenDirectly
);
15482 void Document::RequestFullscreenInContentProcess(
15483 UniquePtr
<FullscreenRequest
> aRequest
, bool aApplyFullscreenDirectly
) {
15484 MOZ_ASSERT(XRE_IsContentProcess());
15486 // If we are in the content process, we can apply the fullscreen
15487 // state directly only if we have been in DOM fullscreen, because
15488 // otherwise we always need to notify the chrome.
15489 if (aApplyFullscreenDirectly
||
15490 nsContentUtils::GetInProcessSubtreeRootDocument(this)->Fullscreen()) {
15491 ApplyFullscreen(std::move(aRequest
));
15495 if (!CheckFullscreenAllowedElementType(aRequest
->Element())) {
15496 aRequest
->Reject("FullscreenDeniedNotHTMLSVGOrMathML");
15500 // We don't need to check element ready before this point, because
15501 // if we called ApplyFullscreen, it would check that for us.
15502 if (!FullscreenElementReadyCheck(*aRequest
)) {
15506 PendingFullscreenChangeList::Add(std::move(aRequest
));
15507 // If we are not the top level process, dispatch an event to make
15508 // our parent process go fullscreen first.
15509 Dispatch(NS_NewRunnableFunction(
15510 "Document::RequestFullscreenInContentProcess", [self
= RefPtr
{this}] {
15511 if (!self
->HasPendingFullscreenRequests()) {
15514 nsContentUtils::DispatchEventOnlyToChrome(
15515 self
, self
, u
"MozDOMFullscreen:Request"_ns
, CanBubble::eYes
,
15516 Cancelable::eNo
, /* DefaultAction */ nullptr);
15520 void Document::RequestFullscreenInParentProcess(
15521 UniquePtr
<FullscreenRequest
> aRequest
, bool aApplyFullscreenDirectly
) {
15522 MOZ_ASSERT(XRE_IsParentProcess());
15523 nsCOMPtr
<nsPIDOMWindowOuter
> rootWin
= GetRootWindow(this);
15525 aRequest
->MayRejectPromise("No active window");
15529 if (aApplyFullscreenDirectly
||
15530 ShouldApplyFullscreenDirectly(this, rootWin
)) {
15531 ApplyFullscreen(std::move(aRequest
));
15535 if (!CheckFullscreenAllowedElementType(aRequest
->Element())) {
15536 aRequest
->Reject("FullscreenDeniedNotHTMLSVGOrMathML");
15540 // See if we're waiting on an exit. If so, just make this one pending.
15541 PendingFullscreenChangeList::Iterator
<FullscreenExit
> iter(
15542 this, PendingFullscreenChangeList::eDocumentsWithSameRoot
);
15543 if (!iter
.AtEnd()) {
15544 PendingFullscreenChangeList::Add(std::move(aRequest
));
15545 rootWin
->SetFullscreenInternal(FullscreenReason::ForFullscreenAPI
, true);
15549 // We don't need to check element ready before this point, because
15550 // if we called ApplyFullscreen, it would check that for us.
15551 if (!FullscreenElementReadyCheck(*aRequest
)) {
15555 PendingFullscreenChangeList::Add(std::move(aRequest
));
15556 // Make the window fullscreen.
15557 rootWin
->SetFullscreenInternal(FullscreenReason::ForFullscreenAPI
, true);
15561 bool Document::HandlePendingFullscreenRequests(Document
* aDoc
) {
15562 bool handled
= false;
15563 PendingFullscreenChangeList::Iterator
<FullscreenRequest
> iter(
15564 aDoc
, PendingFullscreenChangeList::eDocumentsWithSameRoot
);
15565 while (!iter
.AtEnd()) {
15566 UniquePtr
<FullscreenRequest
> request
= iter
.TakeAndNext();
15567 Document
* doc
= request
->Document();
15568 if (doc
->ApplyFullscreen(std::move(request
))) {
15576 void Document::ClearPendingFullscreenRequests(Document
* aDoc
) {
15577 PendingFullscreenChangeList::Iterator
<FullscreenRequest
> iter(
15578 aDoc
, PendingFullscreenChangeList::eInclusiveDescendants
);
15579 while (!iter
.AtEnd()) {
15580 UniquePtr
<FullscreenRequest
> request
= iter
.TakeAndNext();
15581 request
->MayRejectPromise("Fullscreen request aborted");
15585 bool Document::HasPendingFullscreenRequests() {
15586 PendingFullscreenChangeList::Iterator
<FullscreenRequest
> iter(
15587 this, PendingFullscreenChangeList::eDocumentsWithSameRoot
);
15588 return !iter
.AtEnd();
15591 bool Document::ApplyFullscreen(UniquePtr
<FullscreenRequest
> aRequest
) {
15592 if (!FullscreenElementReadyCheck(*aRequest
)) {
15596 RefPtr
<Document
> doc
= aRequest
->Document();
15597 doc
->HideAllPopoversWithoutRunningScript();
15599 // Stash a reference to any existing fullscreen doc, we'll use this later
15600 // to detect if the origin which is fullscreen has changed.
15601 nsCOMPtr
<Document
> previousFullscreenDoc
= GetFullscreenLeaf(this);
15603 // Stores a list of documents which we must dispatch "fullscreenchange"
15604 // too. We're required by the spec to dispatch the events in root-to-leaf
15605 // order, but we traverse the doctree in a leaf-to-root order, so we save
15606 // references to the documents we must dispatch to so that we get the order
15608 AutoTArray
<Document
*, 8> changed
;
15610 // Remember the root document, so that if a fullscreen document is hidden
15611 // we can reset fullscreen state in the remaining visible fullscreen
15613 Document
* fullScreenRootDoc
=
15614 nsContentUtils::GetInProcessSubtreeRootDocument(this);
15616 // If a document is already in fullscreen, then unlock the mouse pointer
15617 // before setting a new document to fullscreen
15618 PointerLockManager::Unlock();
15620 // Set the fullscreen element. This sets the fullscreen style on the
15621 // element, and the fullscreen-ancestor styles on ancestors of the element
15622 // in this document.
15623 Element
* elem
= aRequest
->Element();
15624 SetFullscreenElement(*elem
);
15625 // Set the iframe fullscreen flag.
15626 if (auto* iframe
= HTMLIFrameElement::FromNode(elem
)) {
15627 iframe
->SetFullscreenFlag(true);
15629 changed
.AppendElement(this);
15631 // Propagate up the document hierarchy, setting the fullscreen element as
15632 // the element's container in ancestor documents. This also sets the
15633 // appropriate css styles as well. Note we don't propagate down the
15634 // document hierarchy, the fullscreen element (or its container) is not
15635 // visible there. Stop when we reach the root document.
15636 Document
* child
= this;
15638 child
->SetFullscreenRoot(fullScreenRootDoc
);
15640 // When entering fullscreen, reset the RCD's resolution to the intrinsic
15641 // resolution, otherwise the fullscreen content could be sized larger than
15642 // the screen (since fullscreen is implemented using position:fixed and
15643 // fixed elements are sized to the layout viewport).
15644 // This also ensures that things like video controls aren't zoomed in
15645 // when in fullscreen mode.
15646 if (PresShell
* presShell
= child
->GetPresShell()) {
15647 if (RefPtr
<MobileViewportManager
> manager
=
15648 presShell
->GetMobileViewportManager()) {
15649 // Save the previous resolution so it can be restored.
15650 child
->mSavedResolution
= presShell
->GetResolution();
15651 presShell
->SetResolutionAndScaleTo(
15652 manager
->ComputeIntrinsicResolution(),
15653 ResolutionChangeOrigin::MainThreadRestore
);
15657 NS_ASSERTION(child
->GetFullscreenRoot() == fullScreenRootDoc
,
15658 "Fullscreen root should be set!");
15659 if (child
== fullScreenRootDoc
) {
15663 Element
* element
= child
->GetEmbedderElement();
15665 // We've reached the root.No more changes need to be made
15666 // to the top layer stacks of documents further up the tree.
15670 Document
* parent
= child
->GetInProcessParentDocument();
15671 parent
->SetFullscreenElement(*element
);
15672 changed
.AppendElement(parent
);
15676 FullscreenRoots::Add(this);
15678 // If it is the first entry of the fullscreen, trigger an event so
15679 // that the UI can response to this change, e.g. hide chrome, or
15680 // notifying parent process to enter fullscreen. Note that chrome
15681 // code may also want to listen to MozDOMFullscreen:NewOrigin event
15682 // to pop up warning UI.
15683 if (!previousFullscreenDoc
) {
15684 nsContentUtils::DispatchEventOnlyToChrome(
15685 this, elem
, u
"MozDOMFullscreen:Entered"_ns
, CanBubble::eYes
,
15686 Cancelable::eNo
, /* DefaultAction */ nullptr);
15689 // The origin which is fullscreen gets changed. Trigger an event so
15690 // that the chrome knows to pop up a warning UI. Note that
15691 // previousFullscreenDoc == nullptr upon first entry, we show the warning UI
15692 // directly as soon as chrome document goes into fullscreen state. Also note
15693 // that, in a multi-process browser, the code in content process is
15694 // responsible for sending message with the origin to its parent, and the
15695 // parent shouldn't rely on this event itself.
15696 if (aRequest
->mShouldNotifyNewOrigin
&& previousFullscreenDoc
&&
15697 !nsContentUtils::HaveEqualPrincipals(previousFullscreenDoc
, this)) {
15698 DispatchFullscreenNewOriginEvent(this);
15701 // Dispatch "fullscreenchange" events. Note that the loop order is
15702 // reversed so that events are dispatched in the tree order as
15703 // indicated in the spec.
15704 for (Document
* d
: Reversed(changed
)) {
15705 DispatchFullscreenChange(*d
, d
->GetUnretargetedFullscreenElement());
15707 aRequest
->MayResolvePromise();
15711 void Document::ClearOrientationPendingPromise() {
15712 mOrientationPendingPromise
= nullptr;
15715 bool Document::SetOrientationPendingPromise(Promise
* aPromise
) {
15716 if (mIsGoingAway
) {
15720 mOrientationPendingPromise
= aPromise
;
15724 void Document::UpdateVisibilityState(DispatchVisibilityChange aDispatchEvent
) {
15725 dom::VisibilityState oldState
= mVisibilityState
;
15726 mVisibilityState
= ComputeVisibilityState();
15727 if (oldState
!= mVisibilityState
) {
15728 if (aDispatchEvent
== DispatchVisibilityChange::Yes
) {
15729 nsContentUtils::DispatchTrustedEvent(this, this, u
"visibilitychange"_ns
,
15730 CanBubble::eYes
, Cancelable::eNo
);
15732 NotifyActivityChanged();
15733 if (mVisibilityState
== dom::VisibilityState::Visible
) {
15734 MaybeActiveMediaComponents();
15737 bool visible
= !Hidden();
15738 for (auto* listener
: mWorkerListeners
) {
15739 listener
->OnVisible(visible
);
15744 void Document::AddWorkerDocumentListener(WorkerDocumentListener
* aListener
) {
15745 mWorkerListeners
.Insert(aListener
);
15746 aListener
->OnVisible(!Hidden());
15749 void Document::RemoveWorkerDocumentListener(WorkerDocumentListener
* aListener
) {
15750 mWorkerListeners
.Remove(aListener
);
15753 VisibilityState
Document::ComputeVisibilityState() const {
15754 // We have to check a few pieces of information here:
15755 // 1) Are we in bfcache (!IsVisible())? If so, nothing else matters.
15756 // 2) Do we have an outer window? If not, we're hidden. Note that we don't
15757 // want to use GetWindow here because it does weird groveling for windows
15759 // 3) Is our outer window background? If so, we're hidden.
15760 // Otherwise, we're visible.
15761 if (!IsVisible() || !mWindow
|| !mWindow
->GetOuterWindow() ||
15762 mWindow
->GetOuterWindow()->IsBackground()) {
15763 return dom::VisibilityState::Hidden
;
15766 return dom::VisibilityState::Visible
;
15769 void Document::PostVisibilityUpdateEvent() {
15770 nsCOMPtr
<nsIRunnable
> event
= NewRunnableMethod
<DispatchVisibilityChange
>(
15771 "Document::UpdateVisibilityState", this, &Document::UpdateVisibilityState
,
15772 DispatchVisibilityChange::Yes
);
15773 Dispatch(event
.forget());
15776 void Document::MaybeActiveMediaComponents() {
15777 auto* window
= GetWindow();
15778 if (!window
|| !window
->ShouldDelayMediaFromStart()) {
15781 window
->ActivateMediaComponents();
15784 void Document::DocAddSizeOfExcludingThis(nsWindowSizes
& aWindowSizes
) const {
15785 nsINode::AddSizeOfExcludingThis(aWindowSizes
,
15786 &aWindowSizes
.mDOMSizes
.mDOMOtherSize
);
15788 for (nsIContent
* kid
= GetFirstChild(); kid
; kid
= kid
->GetNextSibling()) {
15789 AddSizeOfNodeTree(*kid
, aWindowSizes
);
15792 // IMPORTANT: for our ComputedValues measurements, we want to measure
15793 // ComputedValues accessible from DOM elements before ComputedValues not
15794 // accessible from DOM elements (i.e. accessible only from the frame tree).
15796 // Therefore, the measurement of the Document superclass must happen after
15797 // the measurement of DOM nodes (above), because Document contains the
15798 // PresShell, which contains the frame tree.
15800 mPresShell
->AddSizeOfIncludingThis(aWindowSizes
);
15804 mStyleSet
->AddSizeOfIncludingThis(aWindowSizes
);
15807 aWindowSizes
.mPropertyTablesSize
+=
15808 mPropertyTable
.SizeOfExcludingThis(aWindowSizes
.mState
.mMallocSizeOf
);
15810 if (EventListenerManager
* elm
= GetExistingListenerManager()) {
15811 aWindowSizes
.mDOMEventListenersCount
+= elm
->ListenerCount();
15814 if (mNodeInfoManager
) {
15815 mNodeInfoManager
->AddSizeOfIncludingThis(aWindowSizes
);
15818 aWindowSizes
.mDOMSizes
.mDOMMediaQueryLists
+=
15819 mDOMMediaQueryLists
.sizeOfExcludingThis(
15820 aWindowSizes
.mState
.mMallocSizeOf
);
15822 for (const MediaQueryList
* mql
: mDOMMediaQueryLists
) {
15823 aWindowSizes
.mDOMSizes
.mDOMMediaQueryLists
+=
15824 mql
->SizeOfExcludingThis(aWindowSizes
.mState
.mMallocSizeOf
);
15827 DocumentOrShadowRoot::AddSizeOfExcludingThis(aWindowSizes
);
15829 for (auto& sheetArray
: mAdditionalSheets
) {
15830 AddSizeOfOwnedSheetArrayExcludingThis(aWindowSizes
, sheetArray
);
15832 // Lumping in the loader with the style-sheets size is not ideal,
15833 // but most of the things in there are in fact stylesheets, so it
15834 // doesn't seem worthwhile to separate it out.
15835 // This can be null if we've already been unlinked.
15837 aWindowSizes
.mLayoutStyleSheetsSize
+=
15838 mCSSLoader
->SizeOfIncludingThis(aWindowSizes
.mState
.mMallocSizeOf
);
15841 aWindowSizes
.mDOMSizes
.mDOMResizeObserverControllerSize
+=
15842 mResizeObservers
.ShallowSizeOfExcludingThis(
15843 aWindowSizes
.mState
.mMallocSizeOf
);
15845 if (mAttributeStyles
) {
15846 aWindowSizes
.mDOMSizes
.mDOMOtherSize
+=
15847 mAttributeStyles
->DOMSizeOfIncludingThis(
15848 aWindowSizes
.mState
.mMallocSizeOf
);
15851 if (mRadioGroupContainer
) {
15852 aWindowSizes
.mDOMSizes
.mDOMOtherSize
+=
15853 mRadioGroupContainer
->SizeOfIncludingThis(
15854 aWindowSizes
.mState
.mMallocSizeOf
);
15857 aWindowSizes
.mDOMSizes
.mDOMOtherSize
+=
15858 mStyledLinks
.ShallowSizeOfExcludingThis(
15859 aWindowSizes
.mState
.mMallocSizeOf
);
15861 // Measurement of the following members may be added later if DMD finds it
15863 // - mMidasCommandManager
15867 void Document::DocAddSizeOfIncludingThis(nsWindowSizes
& aWindowSizes
) const {
15868 aWindowSizes
.mDOMSizes
.mDOMOtherSize
+=
15869 aWindowSizes
.mState
.mMallocSizeOf(this);
15870 DocAddSizeOfExcludingThis(aWindowSizes
);
15873 void Document::AddSizeOfExcludingThis(nsWindowSizes
& aSizes
,
15874 size_t* aNodeSize
) const {
15875 // This AddSizeOfExcludingThis() overrides the one from nsINode. But
15876 // nsDocuments can only appear at the top of the DOM tree, and we use the
15877 // specialized DocAddSizeOfExcludingThis() in that case. So this should never
15883 void Document::AddSizeOfNodeTree(nsINode
& aNode
, nsWindowSizes
& aWindowSizes
) {
15884 size_t nodeSize
= 0;
15885 aNode
.AddSizeOfIncludingThis(aWindowSizes
, &nodeSize
);
15887 // This is where we transfer the nodeSize obtained from
15888 // nsINode::AddSizeOfIncludingThis() to a value in nsWindowSizes.
15889 switch (aNode
.NodeType()) {
15890 case nsINode::ELEMENT_NODE
:
15891 aWindowSizes
.mDOMSizes
.mDOMElementNodesSize
+= nodeSize
;
15893 case nsINode::TEXT_NODE
:
15894 aWindowSizes
.mDOMSizes
.mDOMTextNodesSize
+= nodeSize
;
15896 case nsINode::CDATA_SECTION_NODE
:
15897 aWindowSizes
.mDOMSizes
.mDOMCDATANodesSize
+= nodeSize
;
15899 case nsINode::COMMENT_NODE
:
15900 aWindowSizes
.mDOMSizes
.mDOMCommentNodesSize
+= nodeSize
;
15903 aWindowSizes
.mDOMSizes
.mDOMOtherSize
+= nodeSize
;
15907 if (EventListenerManager
* elm
= aNode
.GetExistingListenerManager()) {
15908 aWindowSizes
.mDOMEventListenersCount
+= elm
->ListenerCount();
15911 if (aNode
.IsContent()) {
15912 nsTArray
<nsIContent
*> anonKids
;
15913 nsContentUtils::AppendNativeAnonymousChildren(aNode
.AsContent(), anonKids
,
15914 nsIContent::eAllChildren
);
15915 for (nsIContent
* anonKid
: anonKids
) {
15916 AddSizeOfNodeTree(*anonKid
, aWindowSizes
);
15919 if (auto* element
= Element::FromNode(aNode
)) {
15920 if (ShadowRoot
* shadow
= element
->GetShadowRoot()) {
15921 AddSizeOfNodeTree(*shadow
, aWindowSizes
);
15926 // NOTE(emilio): If you feel smart and want to change this function to use
15927 // GetNextNode(), think twice, since you'd need to handle <xbl:content> in a
15928 // sane way, and kids of <content> won't point to the parent, so we'd never
15929 // find the root node where we should stop at.
15930 for (nsIContent
* kid
= aNode
.GetFirstChild(); kid
;
15931 kid
= kid
->GetNextSibling()) {
15932 AddSizeOfNodeTree(*kid
, aWindowSizes
);
15936 already_AddRefed
<Document
> Document::Constructor(const GlobalObject
& aGlobal
,
15938 nsCOMPtr
<nsIScriptGlobalObject
> global
=
15939 do_QueryInterface(aGlobal
.GetAsSupports());
15941 rv
.Throw(NS_ERROR_UNEXPECTED
);
15945 nsCOMPtr
<nsIScriptObjectPrincipal
> prin
=
15946 do_QueryInterface(aGlobal
.GetAsSupports());
15948 rv
.Throw(NS_ERROR_UNEXPECTED
);
15952 nsCOMPtr
<nsIURI
> uri
;
15953 NS_NewURI(getter_AddRefs(uri
), "about:blank");
15955 rv
.Throw(NS_ERROR_OUT_OF_MEMORY
);
15959 nsCOMPtr
<Document
> doc
;
15960 nsresult res
= NS_NewDOMDocument(getter_AddRefs(doc
), VoidString(), u
""_ns
,
15961 nullptr, uri
, uri
, prin
->GetPrincipal(),
15962 true, global
, DocumentFlavorPlain
);
15963 if (NS_FAILED(res
)) {
15968 doc
->SetReadyStateInternal(Document::READYSTATE_COMPLETE
);
15970 return doc
.forget();
15973 UniquePtr
<XPathExpression
> Document::CreateExpression(
15974 const nsAString
& aExpression
, XPathNSResolver
* aResolver
, ErrorResult
& rv
) {
15975 return XPathEvaluator()->CreateExpression(aExpression
, aResolver
, rv
);
15978 nsINode
* Document::CreateNSResolver(nsINode
& aNodeResolver
) {
15979 return XPathEvaluator()->CreateNSResolver(aNodeResolver
);
15982 already_AddRefed
<XPathResult
> Document::Evaluate(
15983 JSContext
* aCx
, const nsAString
& aExpression
, nsINode
& aContextNode
,
15984 XPathNSResolver
* aResolver
, uint16_t aType
, JS::Handle
<JSObject
*> aResult
,
15986 return XPathEvaluator()->Evaluate(aCx
, aExpression
, aContextNode
, aResolver
,
15987 aType
, aResult
, rv
);
15990 already_AddRefed
<nsIAppWindow
> Document::GetAppWindowIfToplevelChrome() const {
15991 nsCOMPtr
<nsIDocShellTreeItem
> item
= GetDocShell();
15995 nsCOMPtr
<nsIDocShellTreeOwner
> owner
;
15996 item
->GetTreeOwner(getter_AddRefs(owner
));
15997 nsCOMPtr
<nsIAppWindow
> appWin
= do_GetInterface(owner
);
16001 nsCOMPtr
<nsIDocShell
> appWinShell
;
16002 appWin
->GetDocShell(getter_AddRefs(appWinShell
));
16003 if (!SameCOMIdentity(appWinShell
, item
)) {
16006 return appWin
.forget();
16009 WindowContext
* Document::GetTopLevelWindowContext() const {
16010 WindowContext
* windowContext
= GetWindowContext();
16011 return windowContext
? windowContext
->TopWindowContext() : nullptr;
16014 Document
* Document::GetTopLevelContentDocumentIfSameProcess() {
16017 if (!mLoadedAsData
) {
16020 nsCOMPtr
<nsPIDOMWindowInner
> window
= do_QueryInterface(GetScopeObject());
16025 parent
= window
->GetExtantDoc();
16032 if (parent
->IsTopLevelContentDocument()) {
16036 // If we ever have a non-content parent before we hit a toplevel content
16037 // parent, then we're never going to find one. Just bail.
16038 if (!parent
->IsContentDocument()) {
16042 parent
= parent
->GetInProcessParentDocument();
16048 const Document
* Document::GetTopLevelContentDocumentIfSameProcess() const {
16049 return const_cast<Document
*>(this)->GetTopLevelContentDocumentIfSameProcess();
16052 void Document::PropagateImageUseCounters(Document
* aReferencingDocument
) {
16053 MOZ_ASSERT(IsBeingUsedAsImage());
16054 MOZ_ASSERT(aReferencingDocument
);
16056 if (!aReferencingDocument
->mShouldReportUseCounters
) {
16057 // No need to propagate use counters to a document that itself won't report
16062 MOZ_LOG(gUseCountersLog
, LogLevel::Debug
,
16063 ("PropagateImageUseCounters from %s to %s",
16064 nsContentUtils::TruncatedURLForDisplay(mDocumentURI
).get(),
16065 nsContentUtils::TruncatedURLForDisplay(
16066 aReferencingDocument
->mDocumentURI
)
16069 if (aReferencingDocument
->IsBeingUsedAsImage()) {
16071 "Page use counters from nested image documents may not "
16072 "propagate to the top-level document (bug 1657805)");
16075 SetCssUseCounterBits();
16076 aReferencingDocument
->mChildDocumentUseCounters
|= mUseCounters
;
16077 aReferencingDocument
->mChildDocumentUseCounters
|= mChildDocumentUseCounters
;
16080 bool Document::HasScriptsBlockedBySandbox() const {
16081 return mSandboxFlags
& SANDBOXED_SCRIPTS
;
16084 // Some use-counter sanity-checking.
16085 static_assert(size_t(eUseCounter_EndCSSProperties
) -
16086 size_t(eUseCounter_FirstCSSProperty
) ==
16087 size_t(eCSSProperty_COUNT_with_aliases
),
16088 "We should have the right amount of CSS property use counters");
16089 static_assert(size_t(eUseCounter_Count
) -
16090 size_t(eUseCounter_FirstCountedUnknownProperty
) ==
16091 size_t(CountedUnknownProperty::Count
),
16092 "We should have the right amount of counted unknown properties"
16094 static_assert(size_t(eUseCounter_Count
) * 2 ==
16095 size_t(Telemetry::HistogramUseCounterCount
),
16096 "There should be two histograms (document and page)"
16097 " for each use counter");
16099 #define ASSERT_CSS_COUNTER(id_, method_) \
16100 static_assert(size_t(eUseCounter_property_##method_) - \
16101 size_t(eUseCounter_FirstCSSProperty) == \
16103 "Order for CSS counters and CSS property id should match");
16104 #define CSS_PROP_PUBLIC_OR_PRIVATE(publicname_, privatename_) privatename_
16105 #define CSS_PROP_LONGHAND(name_, id_, method_, ...) \
16106 ASSERT_CSS_COUNTER(eCSSProperty_##id_, method_)
16107 #define CSS_PROP_SHORTHAND(name_, id_, method_, ...) \
16108 ASSERT_CSS_COUNTER(eCSSProperty_##id_, method_)
16109 #define CSS_PROP_ALIAS(name_, aliasid_, id_, method_, ...) \
16110 ASSERT_CSS_COUNTER(eCSSPropertyAlias_##aliasid_, method_)
16111 #include "mozilla/ServoCSSPropList.h"
16112 #undef CSS_PROP_ALIAS
16113 #undef CSS_PROP_SHORTHAND
16114 #undef CSS_PROP_LONGHAND
16115 #undef CSS_PROP_PUBLIC_OR_PRIVATE
16116 #undef ASSERT_CSS_COUNTER
16118 void Document::SetCssUseCounterBits() {
16119 if (StaticPrefs::layout_css_use_counters_enabled()) {
16120 for (size_t i
= 0; i
< eCSSProperty_COUNT_with_aliases
; ++i
) {
16121 auto id
= nsCSSPropertyID(i
);
16122 if (Servo_IsPropertyIdRecordedInUseCounter(mStyleUseCounters
.get(), id
)) {
16123 SetUseCounter(nsCSSProps::UseCounterFor(id
));
16128 if (StaticPrefs::layout_css_use_counters_unimplemented_enabled()) {
16129 for (size_t i
= 0; i
< size_t(CountedUnknownProperty::Count
); ++i
) {
16130 auto id
= CountedUnknownProperty(i
);
16131 if (Servo_IsUnknownPropertyRecordedInUseCounter(mStyleUseCounters
.get(),
16133 SetUseCounter(UseCounter(eUseCounter_FirstCountedUnknownProperty
+ i
));
16139 void Document::InitUseCounters() {
16140 // We can be called more than once, e.g. when session history navigation shows
16141 // us a second time.
16142 if (mUseCountersInitialized
) {
16145 mUseCountersInitialized
= true;
16147 static_assert(Telemetry::HistogramUseCounterCount
> 0);
16149 if (!ShouldIncludeInTelemetry()) {
16153 // Now we know for sure that we should report use counters from this document.
16154 mShouldReportUseCounters
= true;
16156 WindowContext
* top
= GetWindowContextForPageUseCounters();
16158 // This is the case for SVG image documents. They are not displayed in a
16159 // window, but we still do want to record document use counters for them.
16161 // Page use counter propagation is handled in PropagateImageUseCounters,
16162 // so there is no need to use the cross-process machinery to send them.
16163 MOZ_LOG(gUseCountersLog
, LogLevel::Debug
,
16164 ("InitUseCounters for a non-displayed document [%s]",
16165 nsContentUtils::TruncatedURLForDisplay(mDocumentURI
).get()));
16169 RefPtr
<WindowGlobalChild
> wgc
= GetWindowGlobalChild();
16174 MOZ_LOG(gUseCountersLog
, LogLevel::Debug
,
16175 ("InitUseCounters for a displayed document: %" PRIu64
" -> %" PRIu64
16177 wgc
->InnerWindowId(), top
->Id(),
16178 nsContentUtils::TruncatedURLForDisplay(mDocumentURI
).get()));
16180 // Inform the parent process that we will send it page use counters later on.
16181 wgc
->SendExpectPageUseCounters(top
);
16182 mShouldSendPageUseCounters
= true;
16185 // We keep separate counts for individual documents and top-level
16186 // pages to more accurately track how many web pages might break if
16187 // certain features were removed. Consider the case of a single
16188 // HTML document with several SVG images and/or iframes with
16189 // sub-documents of their own. If we maintained a single set of use
16190 // counters and all the sub-documents use a particular feature, then
16191 // telemetry would indicate that we would be breaking N documents if
16192 // that feature were removed. Whereas with a document/top-level
16193 // page split, we can see that N documents would be affected, but
16194 // only a single web page would be affected.
16196 // The difference between the values of these two histograms and the
16197 // related use counters below tell us how many pages did *not* use
16198 // the feature in question. For instance, if we see that a given
16199 // session has destroyed 30 content documents, but a particular use
16200 // counter shows only a count of 5, we can infer that the use
16201 // counter was *not* used in 25 of those 30 documents.
16203 // We do things this way, rather than accumulating a boolean flag
16204 // for each use counter, to avoid sending histograms for features
16205 // that don't get widely used. Doing things in this fashion means
16206 // smaller telemetry payloads and faster processing on the server
16208 void Document::ReportDocumentUseCounters() {
16209 if (!mShouldReportUseCounters
|| mReportedDocumentUseCounters
) {
16213 mReportedDocumentUseCounters
= true;
16215 // Note that a document is being destroyed. See the comment above for how
16216 // use counter histograms are interpreted relative to this measurement.
16217 // TOP_LEVEL_CONTENT_DOCUMENTS_DESTROYED is recorded in
16218 // WindowGlobalParent::FinishAccumulatingPageUseCounters.
16219 Telemetry::Accumulate(Telemetry::CONTENT_DOCUMENTS_DESTROYED
, 1);
16220 glean::use_counter::content_documents_destroyed
.Add();
16222 // Ask all of our resource documents to report their own document use
16224 EnumerateExternalResources([](Document
& aDoc
) {
16225 aDoc
.ReportDocumentUseCounters();
16226 return CallState::Continue
;
16229 // Copy StyleUseCounters into our document use counters.
16230 SetCssUseCounterBits();
16232 Maybe
<nsCString
> urlForLogging
;
16233 const bool dumpCounters
= StaticPrefs::dom_use_counters_dump_document();
16234 if (dumpCounters
) {
16235 urlForLogging
.emplace(
16236 nsContentUtils::TruncatedURLForDisplay(GetDocumentURI()));
16239 // Report our per-document use counters.
16240 for (int32_t c
= 0; c
< eUseCounter_Count
; ++c
) {
16241 auto uc
= static_cast<UseCounter
>(c
);
16242 if (!mUseCounters
[uc
]) {
16246 auto id
= static_cast<Telemetry::HistogramID
>(
16247 Telemetry::HistogramFirstUseCounter
+ uc
* 2);
16248 if (dumpCounters
) {
16249 printf_stderr("USE_COUNTER_DOCUMENT: %s - %s\n",
16250 Telemetry::GetHistogramName(id
), urlForLogging
->get());
16252 Telemetry::Accumulate(id
, 1);
16253 IncrementUseCounter(uc
, /* aIsPage = */ false);
16257 void Document::ReportLCP() {
16258 const nsDOMNavigationTiming
* timing
= GetNavigationTiming();
16264 TimeStamp lcpTime
= timing
->GetLargestContentfulRenderTimeStamp();
16270 mozilla::glean::perf::largest_contentful_paint
.AccumulateRawDuration(
16271 lcpTime
- timing
->GetNavigationStartTimeStamp());
16273 if (!GetChannel()) {
16277 nsCOMPtr
<nsITimedChannel
> timedChannel(do_QueryInterface(GetChannel()));
16278 if (!timedChannel
) {
16282 TimeStamp responseStart
;
16283 timedChannel
->GetResponseStart(&responseStart
);
16285 if (!responseStart
) {
16289 mozilla::glean::perf::largest_contentful_paint_from_response_start
16290 .AccumulateRawDuration(lcpTime
- responseStart
);
16292 if (profiler_thread_is_being_profiled_for_markers()) {
16293 MarkerInnerWindowId innerWindowID
=
16294 MarkerInnerWindowIdFromDocShell(GetDocShell());
16295 GetNavigationTiming()->MaybeAddLCPProfilerMarker(innerWindowID
);
16299 void Document::SendPageUseCounters() {
16300 if (!mShouldReportUseCounters
|| !mShouldSendPageUseCounters
) {
16304 // Ask all of our resource documents to send their own document use
16305 // counters to the parent process to be counted as page use counters.
16306 EnumerateExternalResources([](Document
& aDoc
) {
16307 aDoc
.SendPageUseCounters();
16308 return CallState::Continue
;
16311 // Send our use counters to the parent process to accumulate them towards the
16312 // page use counters for the top-level document.
16314 // We take our own document use counters (those in mUseCounters) and any child
16315 // document use counters (those in mChildDocumentUseCounters) that have been
16316 // explicitly propagated up to us, which includes resource documents, static
16317 // clones, and SVG images.
16318 RefPtr
<WindowGlobalChild
> wgc
= GetWindowGlobalChild();
16320 MOZ_ASSERT_UNREACHABLE(
16321 "SendPageUseCounters should be called while we still have access "
16322 "to our WindowContext");
16323 MOZ_LOG(gUseCountersLog
, LogLevel::Debug
,
16324 (" > too late to send page use counters"));
16328 MOZ_LOG(gUseCountersLog
, LogLevel::Debug
,
16329 ("Sending page use counters: from WindowContext %" PRIu64
" [%s]",
16330 wgc
->WindowContext()->Id(),
16331 nsContentUtils::TruncatedURLForDisplay(GetDocumentURI()).get()));
16333 // Copy StyleUseCounters into our document use counters.
16334 SetCssUseCounterBits();
16336 UseCounters counters
= mUseCounters
| mChildDocumentUseCounters
;
16337 wgc
->SendAccumulatePageUseCounters(counters
);
16340 bool Document::RecomputeResistFingerprinting() {
16341 mOverriddenFingerprintingSettings
.reset();
16342 const bool previous
= mShouldResistFingerprinting
;
16344 RefPtr
<BrowsingContext
> opener
=
16345 GetBrowsingContext() ? GetBrowsingContext()->GetOpener() : nullptr;
16346 // If we have a parent or opener document, defer to it only when we have a
16347 // null principal (e.g. a sandboxed iframe or a data: uri) or when the
16348 // document's principal matches. This means we will defer about:blank,
16349 // about:srcdoc, blob and same-origin iframes/popups to the parent/opener,
16350 // but not cross-origin ones. Cross-origin iframes/popups may inherit a
16351 // CookieJarSettings.mShouldRFP = false bit however, which will be respected.
16352 auto shouldInheritFrom
= [this](Document
* aDoc
) {
16353 return aDoc
&& (this->NodePrincipal()->Equals(aDoc
->NodePrincipal()) ||
16354 this->NodePrincipal()->GetIsNullPrincipal());
16357 if (shouldInheritFrom(mParentDocument
)) {
16359 nsContentUtils::ResistFingerprintingLog(), LogLevel::Debug
,
16360 ("Inside RecomputeResistFingerprinting with URI %s and deferring "
16361 "to parent document %s",
16362 GetDocumentURI() ? GetDocumentURI()->GetSpecOrDefault().get() : "null",
16363 mParentDocument
->GetDocumentURI()->GetSpecOrDefault().get()));
16364 mShouldResistFingerprinting
= mParentDocument
->ShouldResistFingerprinting(
16365 RFPTarget::IsAlwaysEnabledForPrecompute
);
16366 mOverriddenFingerprintingSettings
=
16367 mParentDocument
->mOverriddenFingerprintingSettings
;
16368 } else if (opener
&& shouldInheritFrom(opener
->GetDocument())) {
16370 nsContentUtils::ResistFingerprintingLog(), LogLevel::Debug
,
16371 ("Inside RecomputeResistFingerprinting with URI %s and deferring to "
16372 "opener document %s",
16373 GetDocumentURI() ? GetDocumentURI()->GetSpecOrDefault().get() : "null",
16374 opener
->GetDocument()->GetDocumentURI()->GetSpecOrDefault().get()));
16375 mShouldResistFingerprinting
=
16376 opener
->GetDocument()->ShouldResistFingerprinting(
16377 RFPTarget::IsAlwaysEnabledForPrecompute
);
16378 mOverriddenFingerprintingSettings
=
16379 opener
->GetDocument()->mOverriddenFingerprintingSettings
;
16380 } else if (nsContentUtils::IsChromeDoc(this)) {
16381 MOZ_LOG(nsContentUtils::ResistFingerprintingLog(), LogLevel::Debug
,
16382 ("Inside RecomputeResistFingerprinting with a ChromeDoc"));
16384 mShouldResistFingerprinting
= false;
16385 mOverriddenFingerprintingSettings
.reset();
16386 } else if (mChannel
) {
16387 MOZ_LOG(nsContentUtils::ResistFingerprintingLog(), LogLevel::Debug
,
16388 ("Inside RecomputeResistFingerprinting with URI %s",
16389 GetDocumentURI() ? GetDocumentURI()->GetSpecOrDefault().get()
16391 mShouldResistFingerprinting
= nsContentUtils::ShouldResistFingerprinting(
16392 mChannel
, RFPTarget::IsAlwaysEnabledForPrecompute
);
16394 nsCOMPtr
<nsILoadInfo
> loadInfo
= mChannel
->LoadInfo();
16395 mOverriddenFingerprintingSettings
=
16396 loadInfo
->GetOverriddenFingerprintingSettings();
16398 MOZ_LOG(nsContentUtils::ResistFingerprintingLog(), LogLevel::Debug
,
16399 ("Inside RecomputeResistFingerprinting fallback case."));
16400 // We still preserve the behavior in the fallback case. But, it means there
16401 // might be some cases we haven't considered yet and we need to investigate
16403 mShouldResistFingerprinting
= nsContentUtils::ShouldResistFingerprinting(
16404 mChannel
, RFPTarget::IsAlwaysEnabledForPrecompute
);
16405 mOverriddenFingerprintingSettings
.reset();
16408 MOZ_LOG(nsContentUtils::ResistFingerprintingLog(), LogLevel::Debug
,
16409 ("Finished RecomputeResistFingerprinting with result %x",
16410 mShouldResistFingerprinting
));
16412 bool changed
= previous
!= mShouldResistFingerprinting
;
16414 if (auto win
= nsGlobalWindowInner::Cast(GetInnerWindow())) {
16415 win
->RefreshReduceTimerPrecisionCallerType();
16421 bool Document::ShouldResistFingerprinting(RFPTarget aTarget
) const {
16422 return mShouldResistFingerprinting
&&
16423 nsRFPService::IsRFPEnabledFor(this->IsInPrivateBrowsing(), aTarget
,
16424 mOverriddenFingerprintingSettings
);
16427 void Document::RecordCanvasUsage(CanvasUsage
& aUsage
) {
16428 // Limit the number of recent canvas extraction uses that are tracked.
16429 const size_t kTrackedCanvasLimit
= 8;
16430 // Timeout between different canvas extractions.
16431 const uint64_t kTimeoutUsec
= 3000 * 1000;
16433 uint64_t now
= PR_Now();
16434 if ((mCanvasUsage
.Length() > kTrackedCanvasLimit
) ||
16435 ((now
- mLastCanvasUsage
) > kTimeoutUsec
)) {
16436 mCanvasUsage
.ClearAndRetainStorage();
16439 mCanvasUsage
.AppendElement(aUsage
);
16440 mLastCanvasUsage
= now
;
16442 nsCString originNoSuffix
;
16443 if (NS_FAILED(NodePrincipal()->GetOriginNoSuffix(originNoSuffix
))) {
16447 nsRFPService::MaybeReportCanvasFingerprinter(mCanvasUsage
, GetChannel(),
16451 void Document::RecordFontFingerprinting() {
16452 nsCString originNoSuffix
;
16453 if (NS_FAILED(NodePrincipal()->GetOriginNoSuffix(originNoSuffix
))) {
16457 nsRFPService::MaybeReportFontFingerprinter(GetChannel(), originNoSuffix
);
16460 bool Document::IsInPrivateBrowsing() const { return mIsInPrivateBrowsing
; }
16462 WindowContext
* Document::GetWindowContextForPageUseCounters() const {
16463 if (mDisplayDocument
) {
16464 // If we are a resource document, then go through it to find the
16465 // top-level document.
16466 return mDisplayDocument
->GetWindowContextForPageUseCounters();
16469 if (mOriginalDocument
) {
16470 // For static clones (print preview documents), contribute page use counters
16471 // towards the original document.
16472 return mOriginalDocument
->GetWindowContextForPageUseCounters();
16475 WindowContext
* wc
= GetTopLevelWindowContext();
16476 if (!wc
|| !wc
->GetBrowsingContext()->IsContent()) {
16483 void Document::UpdateIntersectionObservations(TimeStamp aNowTime
) {
16484 if (mIntersectionObservers
.IsEmpty()) {
16488 DOMHighResTimeStamp time
= 0;
16489 if (nsPIDOMWindowInner
* win
= GetInnerWindow()) {
16490 if (Performance
* perf
= win
->GetPerformance()) {
16491 time
= perf
->TimeStampToDOMHighResForRendering(aNowTime
);
16495 const auto observers
= ToTArray
<nsTArray
<RefPtr
<DOMIntersectionObserver
>>>(
16496 mIntersectionObservers
);
16497 for (const auto& observer
: observers
) {
16499 observer
->Update(*this, time
);
16504 void Document::ScheduleIntersectionObserverNotification() {
16505 if (mIntersectionObservers
.IsEmpty()) {
16508 MOZ_RELEASE_ASSERT(NS_IsMainThread());
16509 nsCOMPtr
<nsIRunnable
> notification
=
16510 NewRunnableMethod("Document::NotifyIntersectionObservers", this,
16511 &Document::NotifyIntersectionObservers
);
16512 Dispatch(notification
.forget());
16515 void Document::NotifyIntersectionObservers() {
16516 const auto observers
= ToTArray
<nsTArray
<RefPtr
<DOMIntersectionObserver
>>>(
16517 mIntersectionObservers
);
16518 for (const auto& observer
: observers
) {
16520 // MOZ_KnownLive because the 'observers' array guarantees to keep it
16522 MOZ_KnownLive(observer
)->Notify();
16527 DOMIntersectionObserver
& Document::EnsureLazyLoadObserver() {
16528 if (!mLazyLoadObserver
) {
16529 mLazyLoadObserver
= DOMIntersectionObserver::CreateLazyLoadObserver(*this);
16531 return *mLazyLoadObserver
;
16534 ResizeObserver
& Document::EnsureLastRememberedSizeObserver() {
16535 if (!mLastRememberedSizeObserver
) {
16536 mLastRememberedSizeObserver
=
16537 ResizeObserver::CreateLastRememberedSizeObserver(*this);
16539 return *mLastRememberedSizeObserver
;
16542 void Document::ObserveForLastRememberedSize(Element
& aElement
) {
16543 if (NS_WARN_IF(!IsActive())) {
16546 // Options are initialized with ResizeObserverBoxOptions::Content_box by
16547 // default, which is what we want.
16548 static ResizeObserverOptions options
;
16549 EnsureLastRememberedSizeObserver().Observe(aElement
, options
);
16552 void Document::UnobserveForLastRememberedSize(Element
& aElement
) {
16553 if (mLastRememberedSizeObserver
) {
16554 mLastRememberedSizeObserver
->Unobserve(aElement
);
16558 void Document::NotifyLayerManagerRecreated() {
16559 NotifyActivityChanged();
16560 EnumerateSubDocuments([](Document
& aSubDoc
) {
16561 aSubDoc
.NotifyLayerManagerRecreated();
16562 return CallState::Continue
;
16566 XPathEvaluator
* Document::XPathEvaluator() {
16567 if (!mXPathEvaluator
) {
16568 mXPathEvaluator
.reset(new dom::XPathEvaluator(this));
16570 return mXPathEvaluator
.get();
16573 already_AddRefed
<nsIDocumentEncoder
> Document::GetCachedEncoder() {
16574 return mCachedEncoder
.forget();
16577 void Document::SetCachedEncoder(already_AddRefed
<nsIDocumentEncoder
> aEncoder
) {
16578 mCachedEncoder
= aEncoder
;
16581 nsILoadContext
* Document::GetLoadContext() const { return mDocumentContainer
; }
16583 nsIDocShell
* Document::GetDocShell() const { return mDocumentContainer
; }
16585 void Document::SetStateObject(nsIStructuredCloneContainer
* scContainer
) {
16586 mStateObjectContainer
= scContainer
;
16587 mCachedStateObject
= JS::UndefinedValue();
16588 mCachedStateObjectValid
= false;
16591 bool Document::ComputeDocumentLWTheme() const {
16592 if (!NodePrincipal()->IsSystemPrincipal()) {
16596 Element
* element
= GetRootElement();
16597 return element
&& element
->AttrValueIs(kNameSpaceID_None
, nsGkAtoms::lwtheme
,
16598 nsGkAtoms::_true
, eCaseMatters
);
16601 already_AddRefed
<Element
> Document::CreateHTMLElement(nsAtom
* aTag
) {
16602 RefPtr
<mozilla::dom::NodeInfo
> nodeInfo
;
16603 nodeInfo
= mNodeInfoManager
->GetNodeInfo(aTag
, nullptr, kNameSpaceID_XHTML
,
16605 MOZ_ASSERT(nodeInfo
, "GetNodeInfo should never fail");
16607 nsCOMPtr
<Element
> element
;
16608 DebugOnly
<nsresult
> rv
=
16609 NS_NewHTMLElement(getter_AddRefs(element
), nodeInfo
.forget(),
16610 mozilla::dom::NOT_FROM_PARSER
);
16612 MOZ_ASSERT(NS_SUCCEEDED(rv
), "NS_NewHTMLElement should never fail");
16613 return element
.forget();
16616 void AutoWalkBrowsingContextGroup::SuppressBrowsingContext(
16617 BrowsingContext
* aContext
) {
16618 aContext
->PreOrderWalk([&](BrowsingContext
* aBC
) {
16619 if (nsCOMPtr
<nsPIDOMWindowOuter
> win
= aBC
->GetDOMWindow()) {
16620 if (RefPtr
<Document
> doc
= win
->GetExtantDoc()) {
16621 SuppressDocument(doc
);
16622 mDocuments
.AppendElement(doc
);
16628 void AutoWalkBrowsingContextGroup::SuppressBrowsingContextGroup(
16629 BrowsingContextGroup
* aGroup
) {
16630 for (const auto& bc
: aGroup
->Toplevels()) {
16631 SuppressBrowsingContext(bc
);
16635 nsAutoSyncOperation::nsAutoSyncOperation(Document
* aDoc
,
16636 SyncOperationBehavior aSyncBehavior
)
16637 : mSyncBehavior(aSyncBehavior
) {
16638 mMicroTaskLevel
= 0;
16639 if (CycleCollectedJSContext
* ccjs
= CycleCollectedJSContext::Get()) {
16640 mMicroTaskLevel
= ccjs
->MicroTaskLevel();
16641 ccjs
->SetMicroTaskLevel(0);
16644 mBrowsingContext
= aDoc
->GetBrowsingContext();
16645 if (InputTaskManager::CanSuspendInputEvent()) {
16646 if (auto* bcg
= aDoc
->GetDocGroup()->GetBrowsingContextGroup()) {
16647 SuppressBrowsingContextGroup(bcg
);
16649 } else if (mBrowsingContext
) {
16650 SuppressBrowsingContext(mBrowsingContext
->Top());
16652 if (mBrowsingContext
&&
16653 mSyncBehavior
== SyncOperationBehavior::eSuspendInput
&&
16654 InputTaskManager::CanSuspendInputEvent()) {
16655 mBrowsingContext
->Group()->IncInputEventSuspensionLevel();
16660 void nsAutoSyncOperation::SuppressDocument(Document
* aDoc
) {
16661 if (nsCOMPtr
<nsPIDOMWindowInner
> win
= aDoc
->GetInnerWindow()) {
16662 win
->TimeoutManager().BeginSyncOperation();
16664 aDoc
->SetIsInSyncOperation(true);
16667 void nsAutoSyncOperation::UnsuppressDocument(Document
* aDoc
) {
16668 if (nsCOMPtr
<nsPIDOMWindowInner
> win
= aDoc
->GetInnerWindow()) {
16669 win
->TimeoutManager().EndSyncOperation();
16671 aDoc
->SetIsInSyncOperation(false);
16674 nsAutoSyncOperation::~nsAutoSyncOperation() {
16675 UnsuppressDocuments();
16676 CycleCollectedJSContext
* ccjs
= CycleCollectedJSContext::Get();
16678 ccjs
->SetMicroTaskLevel(mMicroTaskLevel
);
16680 if (mBrowsingContext
&&
16681 mSyncBehavior
== SyncOperationBehavior::eSuspendInput
&&
16682 InputTaskManager::CanSuspendInputEvent()) {
16683 mBrowsingContext
->Group()->DecInputEventSuspensionLevel();
16687 void Document::SetIsInSyncOperation(bool aSync
) {
16688 if (CycleCollectedJSContext
* ccjs
= CycleCollectedJSContext::Get()) {
16689 ccjs
->UpdateMicroTaskSuppressionGeneration();
16693 ++mInSyncOperationCount
;
16695 --mInSyncOperationCount
;
16699 gfxUserFontSet
* Document::GetUserFontSet() {
16700 if (!mFontFaceSet
) {
16704 return mFontFaceSet
->GetImpl();
16707 void Document::FlushUserFontSet() {
16708 if (!mFontFaceSetDirty
) {
16712 mFontFaceSetDirty
= false;
16714 if (gfxPlatform::GetPlatform()->DownloadableFontsEnabled()) {
16715 nsTArray
<nsFontFaceRuleContainer
> rules
;
16716 RefPtr
<PresShell
> presShell
= GetPresShell();
16718 MOZ_ASSERT(mStyleSetFilled
);
16719 EnsureStyleSet().AppendFontFaceRules(rules
);
16722 if (!mFontFaceSet
&& !rules
.IsEmpty()) {
16723 mFontFaceSet
= FontFaceSet::CreateForDocument(this);
16726 bool changed
= false;
16727 if (mFontFaceSet
) {
16728 changed
= mFontFaceSet
->UpdateRules(rules
);
16731 // We need to enqueue a style change reflow (for later) to
16732 // reflect that we're modifying @font-face rules. (However,
16733 // without a reflow, nothing will happen to start any downloads
16734 // that are needed.)
16735 if (changed
&& presShell
) {
16736 if (nsPresContext
* presContext
= presShell
->GetPresContext()) {
16737 presContext
->UserFontSetUpdated();
16743 void Document::MarkUserFontSetDirty() {
16744 if (mFontFaceSetDirty
) {
16747 mFontFaceSetDirty
= true;
16748 if (PresShell
* presShell
= GetPresShell()) {
16749 presShell
->EnsureStyleFlush();
16753 FontFaceSet
* Document::Fonts() {
16754 if (!mFontFaceSet
) {
16755 mFontFaceSet
= FontFaceSet::CreateForDocument(this);
16756 FlushUserFontSet();
16758 return mFontFaceSet
;
16761 void Document::ReportHasScrollLinkedEffect(const TimeStamp
& aTimeStamp
) {
16762 MOZ_ASSERT(!aTimeStamp
.IsNull());
16764 if (!mLastScrollLinkedEffectDetectionTime
.IsNull() &&
16765 mLastScrollLinkedEffectDetectionTime
>= aTimeStamp
) {
16769 if (mLastScrollLinkedEffectDetectionTime
.IsNull()) {
16770 // Report to console just once.
16771 nsContentUtils::ReportToConsole(
16772 nsIScriptError::warningFlag
, "Async Pan/Zoom"_ns
, this,
16773 nsContentUtils::eLAYOUT_PROPERTIES
, "ScrollLinkedEffectFound3");
16776 mLastScrollLinkedEffectDetectionTime
= aTimeStamp
;
16779 bool Document::HasScrollLinkedEffect() const {
16780 if (nsPresContext
* pc
= GetPresContext()) {
16781 return mLastScrollLinkedEffectDetectionTime
==
16782 pc
->RefreshDriver()->MostRecentRefresh();
16788 void Document::SetSHEntryHasUserInteraction(bool aHasInteraction
) {
16789 if (RefPtr
<WindowContext
> topWc
= GetTopLevelWindowContext()) {
16790 // Setting has user interction on a discarded browsing context has
16792 Unused
<< topWc
->SetSHEntryHasUserInteraction(aHasInteraction
);
16796 bool Document::GetSHEntryHasUserInteraction() {
16797 if (RefPtr
<WindowContext
> topWc
= GetTopLevelWindowContext()) {
16798 return topWc
->GetSHEntryHasUserInteraction();
16803 void Document::SetUserHasInteracted() {
16804 MOZ_LOG(gUserInteractionPRLog
, LogLevel::Debug
,
16805 ("Document %p has been interacted by user.", this));
16807 // We maybe need to update the user-interaction permission.
16808 MaybeStoreUserInteractionAsPermission();
16810 // For purposes of reducing irrelevant session history entries on
16811 // the back button, we annotate entries with whether they had user
16812 // interaction. This is gated on its own flag on the WindowContext
16813 // (instead of mUserHasInteracted) to account for the fact that multiple
16814 // top-level SH entries can be associated with the same document.
16815 // Thus, whenever we create a new SH entry for this document,
16816 // this flag is reset.
16817 if (!GetSHEntryHasUserInteraction()) {
16818 nsIDocShell
* docShell
= GetDocShell();
16820 nsCOMPtr
<nsISHEntry
> currentEntry
;
16823 docShell
->GetCurrentSHEntry(getter_AddRefs(currentEntry
), &oshe
);
16824 if (!NS_WARN_IF(NS_FAILED(rv
)) && currentEntry
) {
16825 currentEntry
->SetHasUserInteraction(true);
16828 SetSHEntryHasUserInteraction(true);
16831 if (mUserHasInteracted
) {
16835 mUserHasInteracted
= true;
16838 nsCOMPtr
<nsILoadInfo
> loadInfo
= mChannel
->LoadInfo();
16839 loadInfo
->SetDocumentHasUserInteracted(true);
16841 // Tell the parent process about user interaction
16842 if (auto* wgc
= GetWindowGlobalChild()) {
16843 wgc
->SendUpdateDocumentHasUserInteracted(true);
16846 MaybeAllowStorageForOpenerAfterUserInteraction();
16849 BrowsingContext
* Document::GetBrowsingContext() const {
16850 return mDocumentContainer
? mDocumentContainer
->GetBrowsingContext()
16854 void Document::NotifyUserGestureActivation(
16855 UserActivation::Modifiers
16856 aModifiers
/* = UserActivation::Modifiers::None() */) {
16857 // https://html.spec.whatwg.org/multipage/interaction.html#activation-notification
16858 // 1. "Assert: document is fully active."
16859 RefPtr
<BrowsingContext
> currentBC
= GetBrowsingContext();
16864 RefPtr
<WindowContext
> currentWC
= GetWindowContext();
16869 // 2. "Let windows be « document's relevant global object"
16870 // Instead of assembling a list, we just call notify for wanted windows as we
16872 currentWC
->NotifyUserGestureActivation(aModifiers
);
16874 // 3. "...windows with the active window of each of document's ancestor
16876 for (WindowContext
* wc
= currentWC
; wc
; wc
= wc
->GetParentWindowContext()) {
16877 wc
->NotifyUserGestureActivation(aModifiers
);
16880 // 4. "windows with the active window of each of document's descendant
16881 // navigables, filtered to include only those navigables whose active
16882 // document's origin is same origin with document's origin"
16883 currentBC
->PreOrderWalk([&](BrowsingContext
* bc
) {
16884 WindowContext
* wc
= bc
->GetCurrentWindowContext();
16889 // Check same-origin as current document
16890 WindowGlobalChild
* wgc
= wc
->GetWindowGlobalChild();
16891 if (!wgc
|| !wgc
->IsSameOriginWith(currentWC
)) {
16895 wc
->NotifyUserGestureActivation(aModifiers
);
16899 bool Document::HasBeenUserGestureActivated() {
16900 RefPtr
<WindowContext
> wc
= GetWindowContext();
16901 return wc
&& wc
->HasBeenUserGestureActivated();
16904 DOMHighResTimeStamp
Document::LastUserGestureTimeStamp() {
16905 if (RefPtr
<WindowContext
> wc
= GetWindowContext()) {
16906 if (nsGlobalWindowInner
* innerWindow
= wc
->GetInnerWindow()) {
16907 if (Performance
* perf
= innerWindow
->GetPerformance()) {
16908 return perf
->GetDOMTiming()->TimeStampToDOMHighRes(
16909 wc
->GetUserGestureStart());
16915 "Unable to calculate DOMHighResTimeStamp for LastUserGestureTimeStamp");
16919 void Document::ClearUserGestureActivation() {
16920 if (RefPtr
<BrowsingContext
> bc
= GetBrowsingContext()) {
16922 bc
->PreOrderWalk([&](BrowsingContext
* aBC
) {
16923 if (WindowContext
* windowContext
= aBC
->GetCurrentWindowContext()) {
16924 windowContext
->NotifyResetUserGestureActivation();
16930 bool Document::HasValidTransientUserGestureActivation() const {
16931 RefPtr
<WindowContext
> wc
= GetWindowContext();
16932 return wc
&& wc
->HasValidTransientUserGestureActivation();
16935 bool Document::ConsumeTransientUserGestureActivation() {
16936 RefPtr
<WindowContext
> wc
= GetWindowContext();
16937 return wc
&& wc
->ConsumeTransientUserGestureActivation();
16940 bool Document::GetTransientUserGestureActivationModifiers(
16941 UserActivation::Modifiers
* aModifiers
) {
16942 RefPtr
<WindowContext
> wc
= GetWindowContext();
16943 return wc
&& wc
->GetTransientUserGestureActivationModifiers(aModifiers
);
16946 void Document::SetDocTreeHadMedia() {
16947 RefPtr
<WindowContext
> topWc
= GetTopLevelWindowContext();
16948 if (topWc
&& !topWc
->IsDiscarded() && !topWc
->GetDocTreeHadMedia()) {
16949 MOZ_ALWAYS_SUCCEEDS(topWc
->SetDocTreeHadMedia(true));
16953 void Document::MaybeAllowStorageForOpenerAfterUserInteraction() {
16954 if (!CookieJarSettings()->GetRejectThirdPartyContexts()) {
16958 // This will probably change for project fission, but currently this document
16959 // and the opener are on the same process. In the future, we should make this
16961 nsPIDOMWindowInner
* inner
= GetInnerWindow();
16962 if (NS_WARN_IF(!inner
)) {
16966 // We care about first-party tracking resources only.
16967 if (!nsContentUtils::IsFirstPartyTrackingResourceWindow(inner
)) {
16971 auto* outer
= nsGlobalWindowOuter::Cast(inner
->GetOuterWindow());
16972 if (NS_WARN_IF(!outer
)) {
16976 RefPtr
<BrowsingContext
> openerBC
= outer
->GetOpenerBrowsingContext();
16982 // We want to ensure the following check works for both fission mode and
16983 // non-fission mode:
16984 // "If the opener is not a 3rd party and if this window is not a 3rd party
16985 // with respect to the opener, we should not continue."
16987 // In non-fission mode, the opener and the opened window are in the same
16988 // process, we can use AntiTrackingUtils::IsThirdPartyWindow to do the check.
16989 // In fission mode, if this window is not a 3rd party with respect to the
16990 // opener, they must be in the same process, so we can still use
16991 // IsThirdPartyWindow(openerInner) to continue to check if the opener is a 3rd
16993 if (openerBC
->IsInProcess()) {
16994 nsCOMPtr
<nsPIDOMWindowOuter
> outerOpener
= openerBC
->GetDOMWindow();
16995 if (NS_WARN_IF(!outerOpener
)) {
16999 nsCOMPtr
<nsPIDOMWindowInner
> openerInner
=
17000 outerOpener
->GetCurrentInnerWindow();
17001 if (NS_WARN_IF(!openerInner
)) {
17005 RefPtr
<Document
> openerDocument
= openerInner
->GetExtantDoc();
17006 if (NS_WARN_IF(!openerDocument
)) {
17010 nsCOMPtr
<nsIURI
> openerURI
= openerDocument
->GetDocumentURI();
17011 if (NS_WARN_IF(!openerURI
)) {
17015 // If the opener is not a 3rd party and if this window is not
17016 // a 3rd party with respect to the opener, we should not continue.
17017 if (!AntiTrackingUtils::IsThirdPartyWindow(inner
, openerURI
) &&
17018 !AntiTrackingUtils::IsThirdPartyWindow(openerInner
, nullptr)) {
17023 // We don't care when the asynchronous work finishes here.
17024 Unused
<< StorageAccessAPIHelper::AllowAccessForOnChildProcess(
17025 NodePrincipal(), openerBC
,
17026 ContentBlockingNotifier::eOpenerAfterUserInteraction
);
17031 // Documents can stay alive for days. We don't want to update the permission
17032 // value at any user-interaction, and, using a timer triggered any X seconds
17033 // should be good enough. 'X' is taken from
17034 // privacy.userInteraction.document.interval pref.
17035 // We also want to store the user-interaction before shutting down, and, for
17036 // this reason, this class implements nsIAsyncShutdownBlocker interface.
17037 class UserInteractionTimer final
: public Runnable
,
17038 public nsITimerCallback
,
17039 public nsIAsyncShutdownBlocker
{
17041 NS_DECL_ISUPPORTS_INHERITED
17043 explicit UserInteractionTimer(Document
* aDocument
)
17044 : Runnable("UserInteractionTimer"),
17045 mPrincipal(aDocument
->NodePrincipal()),
17046 mDocument(aDocument
) {
17047 static int32_t userInteractionTimerId
= 0;
17048 // Blocker names must be unique. Let's create it now because when needed,
17049 // the document could be already gone.
17050 mBlockerName
.AppendPrintf("UserInteractionTimer %d for document %p",
17051 ++userInteractionTimerId
, aDocument
);
17054 // Runnable interface
17058 uint32_t interval
=
17059 StaticPrefs::privacy_userInteraction_document_interval();
17064 RefPtr
<UserInteractionTimer
> self
= this;
17066 MakeScopeExit([self
] { self
->CancelTimerAndStoreUserInteraction(); });
17068 nsresult rv
= NS_NewTimerWithCallback(
17069 getter_AddRefs(mTimer
), this, interval
* 1000, nsITimer::TYPE_ONE_SHOT
);
17070 NS_ENSURE_SUCCESS(rv
, NS_OK
);
17072 nsCOMPtr
<nsIAsyncShutdownClient
> phase
= GetShutdownPhase();
17073 NS_ENSURE_TRUE(!!phase
, NS_OK
);
17075 rv
= phase
->AddBlocker(this, NS_LITERAL_STRING_FROM_CSTRING(__FILE__
),
17076 __LINE__
, u
"UserInteractionTimer shutdown"_ns
);
17077 NS_ENSURE_SUCCESS(rv
, NS_OK
);
17083 // nsITimerCallback interface
17086 Notify(nsITimer
* aTimer
) override
{
17087 StoreUserInteraction();
17091 #ifdef MOZ_COLLECTING_RUNNABLE_TELEMETRY
17092 using nsINamed::GetName
;
17095 // nsIAsyncShutdownBlocker interface
17098 GetName(nsAString
& aName
) override
{
17099 aName
= mBlockerName
;
17104 BlockShutdown(nsIAsyncShutdownClient
* aClient
) override
{
17105 CancelTimerAndStoreUserInteraction();
17110 GetState(nsIPropertyBag
**) override
{ return NS_OK
; }
17113 ~UserInteractionTimer() = default;
17115 void StoreUserInteraction() {
17116 // Remove the shutting down blocker
17117 nsCOMPtr
<nsIAsyncShutdownClient
> phase
= GetShutdownPhase();
17119 phase
->RemoveBlocker(this);
17122 // If the document is not gone, let's reset its timer flag.
17123 nsCOMPtr
<Document
> document(mDocument
);
17125 ContentBlockingUserInteraction::Observe(mPrincipal
);
17126 document
->ResetUserInteractionTimer();
17130 void CancelTimerAndStoreUserInteraction() {
17136 StoreUserInteraction();
17139 static already_AddRefed
<nsIAsyncShutdownClient
> GetShutdownPhase() {
17140 nsCOMPtr
<nsIAsyncShutdownService
> svc
= services::GetAsyncShutdownService();
17141 NS_ENSURE_TRUE(!!svc
, nullptr);
17143 nsCOMPtr
<nsIAsyncShutdownClient
> phase
;
17144 nsresult rv
= svc
->GetXpcomWillShutdown(getter_AddRefs(phase
));
17145 NS_ENSURE_SUCCESS(rv
, nullptr);
17147 return phase
.forget();
17150 nsCOMPtr
<nsIPrincipal
> mPrincipal
;
17151 WeakPtr
<Document
> mDocument
;
17153 nsCOMPtr
<nsITimer
> mTimer
;
17155 nsString mBlockerName
;
17158 NS_IMPL_ISUPPORTS_INHERITED(UserInteractionTimer
, Runnable
, nsITimerCallback
,
17159 nsIAsyncShutdownBlocker
)
17163 void Document::MaybeStoreUserInteractionAsPermission() {
17164 // We care about user-interaction stored only for top-level documents
17165 // and documents with access to the Storage Access API
17166 if (!IsTopLevelContentDocument()) {
17168 nsresult rv
= HasStorageAccessSync(hasSA
);
17169 if (NS_FAILED(rv
) || !hasSA
) {
17174 if (!mUserHasInteracted
) {
17175 // First interaction, let's store this info now.
17176 ContentBlockingUserInteraction::Observe(NodePrincipal());
17180 if (mHasUserInteractionTimerScheduled
) {
17184 nsCOMPtr
<nsIRunnable
> task
= new UserInteractionTimer(this);
17185 nsresult rv
= NS_DispatchToCurrentThreadQueue(task
.forget(), 2500,
17186 EventQueuePriority::Idle
);
17187 if (NS_WARN_IF(NS_FAILED(rv
))) {
17191 // This value will be reset by the timer.
17192 mHasUserInteractionTimerScheduled
= true;
17195 void Document::ResetUserInteractionTimer() {
17196 mHasUserInteractionTimerScheduled
= false;
17199 bool Document::IsExtensionPage() const {
17200 return BasePrincipal::Cast(NodePrincipal())->AddonPolicy();
17203 void Document::AddResizeObserver(ResizeObserver
& aObserver
) {
17204 MOZ_ASSERT(!mResizeObservers
.Contains(&aObserver
));
17205 // Insert internal ResizeObservers before scripted ones, since they may have
17206 // observable side-effects and we don't want to expose the insertion time.
17207 if (aObserver
.HasNativeCallback()) {
17208 mResizeObservers
.InsertElementAt(0, &aObserver
);
17210 mResizeObservers
.AppendElement(&aObserver
);
17214 void Document::RemoveResizeObserver(ResizeObserver
& aObserver
) {
17215 MOZ_ASSERT(mResizeObservers
.Contains(&aObserver
));
17216 mResizeObservers
.RemoveElement(&aObserver
);
17219 PermissionDelegateHandler
* Document::GetPermissionDelegateHandler() {
17220 if (!mPermissionDelegateHandler
) {
17221 mPermissionDelegateHandler
= MakeAndAddRef
<PermissionDelegateHandler
>(this);
17224 if (!mPermissionDelegateHandler
->Initialize()) {
17225 mPermissionDelegateHandler
= nullptr;
17228 return mPermissionDelegateHandler
;
17231 void Document::ScheduleResizeObserversNotification() const {
17235 if (nsRefreshDriver
* rd
= mPresShell
->GetRefreshDriver()) {
17236 rd
->EnsureResizeObserverUpdateHappens();
17240 static void FlushLayoutForWholeBrowsingContextTree(Document
& aDoc
) {
17241 BrowsingContext
* bc
= aDoc
.GetBrowsingContext();
17242 if (bc
&& bc
->GetExtantDocument() == &aDoc
) {
17243 RefPtr
<BrowsingContext
> top
= bc
->Top();
17244 top
->PreOrderWalk([](BrowsingContext
* aCur
) {
17245 if (Document
* doc
= aCur
->GetExtantDocument()) {
17246 doc
->FlushPendingNotifications(FlushType::Layout
);
17250 // If there is no browsing context, or we're not the current document of the
17251 // browsing context, then we just flush this document itself.
17252 aDoc
.FlushPendingNotifications(FlushType::Layout
);
17256 void Document::DetermineProximityToViewportAndNotifyResizeObservers() {
17257 uint32_t shallowestTargetDepth
= 0;
17258 bool initialResetOfScrolledIntoViewFlagsDone
= false;
17260 // Flush layout, so that any callback functions' style changes / resizes
17261 // get a chance to take effect. The callback functions may do changes in its
17262 // sub-documents or ancestors, so flushing layout for the whole browsing
17263 // context tree makes sure we don't miss anyone.
17264 FlushLayoutForWholeBrowsingContextTree(*this);
17265 if (PresShell
* presShell
= GetPresShell()) {
17266 auto result
= presShell
->DetermineProximityToViewport();
17267 if (result
.mHadInitialDetermination
) {
17270 if (result
.mAnyScrollIntoViewFlag
) {
17271 // Not defined in the spec: It's possible that some elements with
17272 // content-visibility: auto were forced to be visible in order to
17273 // perform scrollIntoView() so clear their flags now and restart the
17275 // See https://github.com/w3c/csswg-drafts/issues/9337
17276 presShell
->ClearTemporarilyVisibleForScrolledIntoViewDescendantFlags();
17277 presShell
->ScheduleContentRelevancyUpdate(
17278 ContentRelevancyReason::Visible
);
17279 if (!initialResetOfScrolledIntoViewFlagsDone
) {
17280 initialResetOfScrolledIntoViewFlagsDone
= true;
17286 // To avoid infinite resize loop, we only gather all active observations
17287 // that have the depth of observed target element more than current
17288 // shallowestTargetDepth.
17289 GatherAllActiveResizeObservations(shallowestTargetDepth
);
17291 if (!HasAnyActiveResizeObservations()) {
17295 DebugOnly
<uint32_t> oldShallowestTargetDepth
= shallowestTargetDepth
;
17296 shallowestTargetDepth
= BroadcastAllActiveResizeObservations();
17297 NS_ASSERTION(oldShallowestTargetDepth
< shallowestTargetDepth
,
17298 "shallowestTargetDepth should be getting strictly deeper");
17301 if (HasAnySkippedResizeObservations()) {
17302 if (nsCOMPtr
<nsPIDOMWindowInner
> window
= GetInnerWindow()) {
17303 // Per spec, we deliver an error if the document has any skipped
17304 // observations. Also, we re-register via ScheduleNotification().
17305 RootedDictionary
<ErrorEventInit
> init(RootingCx());
17306 init
.mMessage
.AssignLiteral(
17307 "ResizeObserver loop completed with undelivered notifications.");
17308 init
.mBubbles
= false;
17309 init
.mCancelable
= false;
17311 nsEventStatus status
= nsEventStatus_eIgnore
;
17312 nsCOMPtr
<nsIScriptGlobalObject
> sgo
= do_QueryInterface(window
);
17314 if (NS_WARN_IF(sgo
->HandleScriptError(init
, &status
))) {
17315 status
= nsEventStatus_eIgnore
;
17318 // We don't fire error events at any global for non-window JS on the main
17322 // We need to deliver pending notifications in next cycle.
17323 ScheduleResizeObserversNotification();
17327 void Document::GatherAllActiveResizeObservations(uint32_t aDepth
) {
17328 for (ResizeObserver
* observer
: mResizeObservers
) {
17329 observer
->GatherActiveObservations(aDepth
);
17333 uint32_t Document::BroadcastAllActiveResizeObservations() {
17334 uint32_t shallowestTargetDepth
= std::numeric_limits
<uint32_t>::max();
17336 // Copy the observers as this invokes the callbacks and could register and
17337 // unregister observers at will.
17338 const auto observers
=
17339 ToTArray
<nsTArray
<RefPtr
<ResizeObserver
>>>(mResizeObservers
);
17340 for (const auto& observer
: observers
) {
17341 // MOZ_KnownLive because 'observers' is guaranteed to keep it
17344 // This can go away once
17345 // https://bugzilla.mozilla.org/show_bug.cgi?id=1620312 is fixed.
17346 uint32_t targetDepth
=
17347 MOZ_KnownLive(observer
)->BroadcastActiveObservations();
17348 if (targetDepth
< shallowestTargetDepth
) {
17349 shallowestTargetDepth
= targetDepth
;
17353 return shallowestTargetDepth
;
17356 bool Document::HasAnySkippedResizeObservations() const {
17357 for (const auto& observer
: mResizeObservers
) {
17358 if (observer
->HasSkippedObservations()) {
17365 bool Document::HasAnyActiveResizeObservations() const {
17366 for (const auto& observer
: mResizeObservers
) {
17367 if (observer
->HasActiveObservations()) {
17374 void Document::ClearStaleServoData() {
17375 DocumentStyleRootIterator
iter(this);
17376 while (Element
* root
= iter
.GetNextStyleRoot()) {
17377 RestyleManager::ClearServoDataFromSubtree(root
);
17381 Selection
* Document::GetSelection(ErrorResult
& aRv
) {
17382 nsCOMPtr
<nsPIDOMWindowInner
> window
= GetInnerWindow();
17387 if (!window
->IsCurrentInnerWindow()) {
17391 return nsGlobalWindowInner::Cast(window
)->GetSelection(aRv
);
17394 void Document::MakeBrowsingContextNonSynthetic() {
17395 if (BrowsingContext
* bc
= GetBrowsingContext()) {
17396 if (bc
->GetSyntheticDocumentContainer()) {
17397 Unused
<< bc
->SetSyntheticDocumentContainer(false);
17402 nsresult
Document::HasStorageAccessSync(bool& aHasStorageAccess
) {
17403 // Step 1: check if cookie permissions are available or denied to this
17404 // document's principal
17405 nsCOMPtr
<nsPIDOMWindowInner
> inner
= GetInnerWindow();
17407 aHasStorageAccess
= false;
17410 Maybe
<bool> resultBecauseCookiesApproved
=
17411 StorageAccessAPIHelper::CheckCookiesPermittedDecidesStorageAccessAPI(
17412 CookieJarSettings(), NodePrincipal());
17413 if (resultBecauseCookiesApproved
.isSome()) {
17414 if (resultBecauseCookiesApproved
.value()) {
17415 aHasStorageAccess
= true;
17418 aHasStorageAccess
= false;
17423 // Step 2: Check if the browser settings determine whether or not this
17424 // document has access to its unpartitioned cookies.
17425 bool isThirdPartyDocument
= AntiTrackingUtils::IsThirdPartyDocument(this);
17426 bool isOnThirdPartySkipList
= false;
17428 nsCOMPtr
<nsILoadInfo
> loadInfo
= mChannel
->LoadInfo();
17429 isOnThirdPartySkipList
= loadInfo
->GetStoragePermission() ==
17430 nsILoadInfo::StoragePermissionAllowListed
;
17432 bool isThirdPartyTracker
=
17433 nsContentUtils::IsThirdPartyTrackingResourceWindow(inner
);
17434 Maybe
<bool> resultBecauseBrowserSettings
=
17435 StorageAccessAPIHelper::CheckBrowserSettingsDecidesStorageAccessAPI(
17436 CookieJarSettings(), isThirdPartyDocument
, isOnThirdPartySkipList
,
17437 isThirdPartyTracker
);
17438 if (resultBecauseBrowserSettings
.isSome()) {
17439 if (resultBecauseBrowserSettings
.value()) {
17440 aHasStorageAccess
= true;
17443 aHasStorageAccess
= false;
17448 // Step 3: Check if the location of this call (embedded, top level, same-site)
17449 // determines if cookies are permitted or not.
17450 Maybe
<bool> resultBecauseCallContext
=
17451 StorageAccessAPIHelper::CheckCallingContextDecidesStorageAccessAPI(this,
17453 if (resultBecauseCallContext
.isSome()) {
17454 if (resultBecauseCallContext
.value()) {
17455 aHasStorageAccess
= true;
17458 aHasStorageAccess
= false;
17463 // Step 4: Check if the permissions for this document determine if if has
17464 // access or is denied cookies.
17465 Maybe
<bool> resultBecausePreviousPermission
=
17466 StorageAccessAPIHelper::CheckExistingPermissionDecidesStorageAccessAPI(
17468 if (resultBecausePreviousPermission
.isSome()) {
17469 if (resultBecausePreviousPermission
.value()) {
17470 aHasStorageAccess
= true;
17473 aHasStorageAccess
= false;
17477 // If you get here, we default to not giving you permission.
17478 aHasStorageAccess
= false;
17482 already_AddRefed
<mozilla::dom::Promise
> Document::HasStorageAccess(
17483 mozilla::ErrorResult
& aRv
) {
17484 nsIGlobalObject
* global
= GetScopeObject();
17486 aRv
.Throw(NS_ERROR_NOT_AVAILABLE
);
17490 RefPtr
<Promise
> promise
=
17491 Promise::Create(global
, aRv
, Promise::ePropagateUserInteraction
);
17492 if (aRv
.Failed()) {
17496 if (!IsCurrentActiveDocument()) {
17497 promise
->MaybeRejectWithInvalidStateError(
17498 "hasStorageAccess requires an active document");
17499 return promise
.forget();
17502 bool hasStorageAccess
;
17503 nsresult rv
= HasStorageAccessSync(hasStorageAccess
);
17504 if (NS_FAILED(rv
)) {
17505 promise
->MaybeRejectWithUndefined();
17507 promise
->MaybeResolve(hasStorageAccess
);
17510 return promise
.forget();
17513 RefPtr
<Document::GetContentBlockingEventsPromise
>
17514 Document::GetContentBlockingEvents() {
17515 RefPtr
<WindowGlobalChild
> wgc
= GetWindowGlobalChild();
17520 return wgc
->SendGetContentBlockingEvents()->Then(
17521 GetCurrentSerialEventTarget(), __func__
,
17522 [](const WindowGlobalChild::GetContentBlockingEventsPromise::
17523 ResolveOrRejectValue
& aValue
) {
17524 if (aValue
.IsResolve()) {
17525 return Document::GetContentBlockingEventsPromise::CreateAndResolve(
17526 aValue
.ResolveValue(), __func__
);
17529 return Document::GetContentBlockingEventsPromise::CreateAndReject(
17534 StorageAccessAPIHelper::PerformPermissionGrant
17535 Document::CreatePermissionGrantPromise(
17536 nsPIDOMWindowInner
* aInnerWindow
, nsIPrincipal
* aPrincipal
,
17537 bool aHasUserInteraction
, bool aRequireUserInteraction
,
17538 const Maybe
<nsCString
>& aTopLevelBaseDomain
, bool aFrameOnly
) {
17539 MOZ_ASSERT(aInnerWindow
);
17540 MOZ_ASSERT(aPrincipal
);
17541 RefPtr
<Document
> self(this);
17542 RefPtr
<nsPIDOMWindowInner
> inner(aInnerWindow
);
17543 RefPtr
<nsIPrincipal
> principal(aPrincipal
);
17545 return [inner
, self
, principal
, aHasUserInteraction
, aRequireUserInteraction
,
17546 aTopLevelBaseDomain
, aFrameOnly
]() {
17547 RefPtr
<StorageAccessAPIHelper::StorageAccessPermissionGrantPromise::Private
>
17548 p
= new StorageAccessAPIHelper::StorageAccessPermissionGrantPromise::
17551 RefPtr
<PWindowGlobalChild::GetStorageAccessPermissionPromise
> promise
;
17552 // Test the permission
17553 MOZ_ASSERT(XRE_IsContentProcess());
17555 WindowGlobalChild
* wgc
= inner
->GetWindowGlobalChild();
17558 promise
= wgc
->SendGetStorageAccessPermission();
17559 MOZ_ASSERT(promise
);
17561 GetCurrentSerialEventTarget(), __func__
,
17562 [self
, p
, inner
, principal
, aHasUserInteraction
,
17563 aRequireUserInteraction
, aTopLevelBaseDomain
,
17564 aFrameOnly
](uint32_t aAction
) {
17565 if (aAction
== nsIPermissionManager::ALLOW_ACTION
) {
17566 p
->Resolve(StorageAccessAPIHelper::eAllow
, __func__
);
17569 if (aAction
== nsIPermissionManager::DENY_ACTION
) {
17570 p
->Reject(false, __func__
);
17574 // We require user activation before conducting a permission request
17576 // https://privacycg.github.io/storage-access/#dom-document-requeststorageaccess
17577 // where we "If has transient activation is false: ..." immediately
17578 // before we "Let permissionState be the result of requesting
17579 // permission to use "storage-access"" from in parallel.
17580 if (!aHasUserInteraction
&& aRequireUserInteraction
) {
17581 // Report an error to the console for this case
17582 nsContentUtils::ReportToConsole(
17583 nsIScriptError::errorFlag
,
17584 nsLiteralCString("requestStorageAccess"), self
,
17585 nsContentUtils::eDOM_PROPERTIES
,
17586 "RequestStorageAccessUserGesture");
17587 p
->Reject(false, __func__
);
17591 // Create the user prompt
17592 RefPtr
<StorageAccessPermissionRequest
> sapr
=
17593 StorageAccessPermissionRequest::Create(
17594 inner
, principal
, aTopLevelBaseDomain
, aFrameOnly
,
17597 Telemetry::AccumulateCategorical(
17598 Telemetry::LABELS_STORAGE_ACCESS_API_UI::Allow
);
17599 p
->Resolve(StorageAccessAPIHelper::eAllow
, __func__
);
17603 Telemetry::AccumulateCategorical(
17604 Telemetry::LABELS_STORAGE_ACCESS_API_UI::Deny
);
17605 p
->Reject(false, __func__
);
17608 using PromptResult
= ContentPermissionRequestBase::PromptResult
;
17609 PromptResult pr
= sapr
->CheckPromptPrefs();
17611 if (pr
== PromptResult::Pending
) {
17612 // We're about to show a prompt, record the request attempt
17613 Telemetry::AccumulateCategorical(
17614 Telemetry::LABELS_STORAGE_ACCESS_API_UI::Request
);
17617 // Try to auto-grant the storage access so the user doesn't see the
17619 self
->AutomaticStorageAccessPermissionCanBeGranted(
17620 aHasUserInteraction
)
17622 GetCurrentSerialEventTarget(), __func__
,
17623 // If the autogrant check didn't fail, call this function
17625 inner
](const Document::
17626 AutomaticStorageAccessPermissionGrantPromise::
17627 ResolveOrRejectValue
& aValue
) -> void {
17628 // Make a copy because we can't modified copy-captured
17629 // lambda variables.
17630 PromptResult pr2
= pr
;
17632 // If the user didn't already click "allow" and we can
17633 // autogrant, do that!
17634 bool storageAccessCanBeGrantedAutomatically
=
17635 aValue
.IsResolve() && aValue
.ResolveValue();
17636 bool autoGrant
= false;
17637 if (pr2
== PromptResult::Pending
&&
17638 storageAccessCanBeGrantedAutomatically
) {
17639 pr2
= PromptResult::Granted
;
17642 Telemetry::AccumulateCategorical(
17643 Telemetry::LABELS_STORAGE_ACCESS_API_UI::
17644 AllowAutomatically
);
17647 // If we can complete the permission request, do so.
17648 if (pr2
!= PromptResult::Pending
) {
17649 MOZ_ASSERT_IF(pr2
!= PromptResult::Granted
,
17650 pr2
== PromptResult::Denied
);
17651 if (pr2
== PromptResult::Granted
) {
17652 StorageAccessAPIHelper::StorageAccessPromptChoices
17653 choice
= StorageAccessAPIHelper::eAllow
;
17655 choice
= StorageAccessAPIHelper::eAllowAutoGrant
;
17658 p
->Resolve(choice
, __func__
);
17660 // We capture sapr here to prevent it from destructing
17661 // before the callbacks complete.
17662 sapr
->MaybeDelayAutomaticGrants()->Then(
17663 GetCurrentSerialEventTarget(), __func__
,
17664 [p
, sapr
, choice
] {
17665 p
->Resolve(choice
, __func__
);
17667 [p
, sapr
] { p
->Reject(false, __func__
); });
17671 p
->Reject(false, __func__
);
17675 // If we get here, the auto-decision failed and we need to
17676 // wait for the user prompt to complete.
17677 sapr
->RequestDelayedTask(
17678 GetMainThreadSerialEventTarget(),
17679 ContentPermissionRequestBase::DelayedTaskType::Request
);
17682 [p
](mozilla::ipc::ResponseRejectReason aError
) {
17683 p
->Reject(false, __func__
);
17691 already_AddRefed
<mozilla::dom::Promise
> Document::RequestStorageAccess(
17692 mozilla::ErrorResult
& aRv
) {
17693 nsIGlobalObject
* global
= GetScopeObject();
17695 aRv
.Throw(NS_ERROR_NOT_AVAILABLE
);
17699 RefPtr
<Promise
> promise
= Promise::Create(global
, aRv
);
17700 if (aRv
.Failed()) {
17704 if (!IsCurrentActiveDocument()) {
17705 promise
->MaybeRejectWithInvalidStateError(
17706 "requestStorageAccess requires an active document");
17707 return promise
.forget();
17710 // Get a pointer to the inner window- We need this for convenience sake
17711 RefPtr
<nsPIDOMWindowInner
> inner
= GetInnerWindow();
17713 ConsumeTransientUserGestureActivation();
17714 promise
->MaybeRejectWithNotAllowedError(
17715 "requestStorageAccess not allowed"_ns
);
17716 return promise
.forget();
17719 // Step 1: Check if the principal calling this has a permission that lets
17720 // them use cookies or forbids them from using cookies.
17721 // This is outside of the spec of the StorageAccess API, but makes the return
17722 // values to have proper semantics.
17723 Maybe
<bool> resultBecauseCookiesApproved
=
17724 StorageAccessAPIHelper::CheckCookiesPermittedDecidesStorageAccessAPI(
17725 CookieJarSettings(), NodePrincipal());
17726 if (resultBecauseCookiesApproved
.isSome()) {
17727 if (resultBecauseCookiesApproved
.value()) {
17728 promise
->MaybeResolveWithUndefined();
17729 return promise
.forget();
17731 ConsumeTransientUserGestureActivation();
17732 promise
->MaybeRejectWithNotAllowedError(
17733 "requestStorageAccess not allowed"_ns
);
17734 return promise
.forget();
17738 // Step 2: Check if the browser settings always allow or deny cookies.
17739 // We should always return a resolved promise if the cookieBehavior is ACCEPT.
17740 // This is outside of the spec of the StorageAccess API, but makes the return
17741 // values to have proper semantics.
17742 bool isThirdPartyDocument
= AntiTrackingUtils::IsThirdPartyDocument(this);
17743 bool isOnThirdPartySkipList
= false;
17745 nsCOMPtr
<nsILoadInfo
> loadInfo
= mChannel
->LoadInfo();
17746 isOnThirdPartySkipList
= loadInfo
->GetStoragePermission() ==
17747 nsILoadInfo::StoragePermissionAllowListed
;
17749 bool isThirdPartyTracker
=
17750 nsContentUtils::IsThirdPartyTrackingResourceWindow(inner
);
17751 Maybe
<bool> resultBecauseBrowserSettings
=
17752 StorageAccessAPIHelper::CheckBrowserSettingsDecidesStorageAccessAPI(
17753 CookieJarSettings(), isThirdPartyDocument
, isOnThirdPartySkipList
,
17754 isThirdPartyTracker
);
17755 if (resultBecauseBrowserSettings
.isSome()) {
17756 if (resultBecauseBrowserSettings
.value()) {
17757 promise
->MaybeResolveWithUndefined();
17758 return promise
.forget();
17760 ConsumeTransientUserGestureActivation();
17761 promise
->MaybeRejectWithNotAllowedError(
17762 "requestStorageAccess not allowed"_ns
);
17763 return promise
.forget();
17767 // Step 3: Check if the Document calling requestStorageAccess has anything to
17768 // gain from storage access. It should be embedded, non-null, etc.
17769 Maybe
<bool> resultBecauseCallContext
=
17770 StorageAccessAPIHelper::CheckCallingContextDecidesStorageAccessAPI(this,
17772 if (resultBecauseCallContext
.isSome()) {
17773 if (resultBecauseCallContext
.value()) {
17774 promise
->MaybeResolveWithUndefined();
17775 return promise
.forget();
17777 ConsumeTransientUserGestureActivation();
17778 promise
->MaybeRejectWithNotAllowedError(
17779 "requestStorageAccess not allowed"_ns
);
17780 return promise
.forget();
17784 // Step 4: Check if we already allowed or denied storage access for this
17785 // document's storage key.
17786 Maybe
<bool> resultBecausePreviousPermission
=
17787 StorageAccessAPIHelper::CheckExistingPermissionDecidesStorageAccessAPI(
17789 if (resultBecausePreviousPermission
.isSome()) {
17790 if (resultBecausePreviousPermission
.value()) {
17791 promise
->MaybeResolveWithUndefined();
17792 return promise
.forget();
17794 ConsumeTransientUserGestureActivation();
17795 promise
->MaybeRejectWithNotAllowedError(
17796 "requestStorageAccess not allowed"_ns
);
17797 return promise
.forget();
17801 // Get pointers to some objects that will be used in the async portion
17802 RefPtr
<BrowsingContext
> bc
= GetBrowsingContext();
17803 RefPtr
<nsGlobalWindowOuter
> outer
=
17804 nsGlobalWindowOuter::Cast(inner
->GetOuterWindow());
17806 ConsumeTransientUserGestureActivation();
17807 promise
->MaybeRejectWithNotAllowedError(
17808 "requestStorageAccess not allowed"_ns
);
17809 return promise
.forget();
17811 RefPtr
<Document
> self(this);
17813 // Step 5. Start an async call to request storage access. This will either
17814 // perform an automatic decision or notify the user, then perform some follow
17815 // on work changing state to reflect the result of the API. If it resolves,
17816 // the request was granted. If it rejects it was denied.
17817 StorageAccessAPIHelper::RequestStorageAccessAsyncHelper(
17818 this, inner
, bc
, NodePrincipal(),
17819 self
->HasValidTransientUserGestureActivation(), true, true,
17820 ContentBlockingNotifier::eStorageAccessAPI
, true)
17822 GetCurrentSerialEventTarget(), __func__
,
17824 inner
->SaveStorageAccessPermissionGranted();
17825 promise
->MaybeResolveWithUndefined();
17828 self
->ConsumeTransientUserGestureActivation();
17829 promise
->MaybeRejectWithNotAllowedError(
17830 "requestStorageAccess not allowed"_ns
);
17833 return promise
.forget();
17836 already_AddRefed
<mozilla::dom::Promise
> Document::RequestStorageAccessForOrigin(
17837 const nsAString
& aThirdPartyOrigin
, const bool aRequireUserActivation
,
17838 mozilla::ErrorResult
& aRv
) {
17839 nsIGlobalObject
* global
= GetScopeObject();
17841 aRv
.Throw(NS_ERROR_NOT_AVAILABLE
);
17844 RefPtr
<Promise
> promise
= Promise::Create(global
, aRv
);
17845 if (aRv
.Failed()) {
17849 // Step 0: Check that we have user activation before proceeding to prevent
17850 // rapid calls to the API to leak information.
17851 if (aRequireUserActivation
&& !HasValidTransientUserGestureActivation()) {
17852 // Report an error to the console for this case
17853 nsContentUtils::ReportToConsole(nsIScriptError::errorFlag
,
17854 nsLiteralCString("requestStorageAccess"),
17855 this, nsContentUtils::eDOM_PROPERTIES
,
17856 "RequestStorageAccessUserGesture");
17857 ConsumeTransientUserGestureActivation();
17858 promise
->MaybeRejectWithNotAllowedError(
17859 "requestStorageAccess not allowed"_ns
);
17860 return promise
.forget();
17863 // Step 1: Check if the provided URI is different-site to this Document
17864 nsCOMPtr
<nsIURI
> thirdPartyURI
;
17865 nsresult rv
= NS_NewURI(getter_AddRefs(thirdPartyURI
), aThirdPartyOrigin
);
17866 if (NS_WARN_IF(NS_FAILED(rv
))) {
17870 bool isThirdPartyDocument
;
17871 rv
= NodePrincipal()->IsThirdPartyURI(thirdPartyURI
, &isThirdPartyDocument
);
17872 if (NS_WARN_IF(NS_FAILED(rv
))) {
17876 Maybe
<bool> resultBecauseBrowserSettings
=
17877 StorageAccessAPIHelper::CheckBrowserSettingsDecidesStorageAccessAPI(
17878 CookieJarSettings(), isThirdPartyDocument
, false, true);
17879 if (resultBecauseBrowserSettings
.isSome()) {
17880 if (resultBecauseBrowserSettings
.value()) {
17881 promise
->MaybeResolveWithUndefined();
17882 return promise
.forget();
17884 ConsumeTransientUserGestureActivation();
17885 promise
->MaybeRejectWithNotAllowedError(
17886 "requestStorageAccess not allowed"_ns
);
17887 return promise
.forget();
17890 // Step 2: Check that this Document is same-site to the top, and check that
17891 // we have user activation if we require it.
17892 Maybe
<bool> resultBecauseCallContext
= StorageAccessAPIHelper::
17893 CheckSameSiteCallingContextDecidesStorageAccessAPI(
17894 this, aRequireUserActivation
);
17895 if (resultBecauseCallContext
.isSome()) {
17896 if (resultBecauseCallContext
.value()) {
17897 promise
->MaybeResolveWithUndefined();
17898 return promise
.forget();
17900 ConsumeTransientUserGestureActivation();
17901 promise
->MaybeRejectWithNotAllowedError(
17902 "requestStorageAccess not allowed"_ns
);
17903 return promise
.forget();
17906 // Step 3: Get some useful variables that can be captured by the lambda for
17907 // the asynchronous portion
17908 RefPtr
<BrowsingContext
> bc
= GetBrowsingContext();
17909 nsCOMPtr
<nsPIDOMWindowInner
> inner
= GetInnerWindow();
17911 ConsumeTransientUserGestureActivation();
17912 promise
->MaybeRejectWithNotAllowedError(
17913 "requestStorageAccess not allowed"_ns
);
17914 return promise
.forget();
17916 RefPtr
<nsGlobalWindowOuter
> outer
=
17917 nsGlobalWindowOuter::Cast(inner
->GetOuterWindow());
17919 ConsumeTransientUserGestureActivation();
17920 promise
->MaybeRejectWithNotAllowedError(
17921 "requestStorageAccess not allowed"_ns
);
17922 return promise
.forget();
17924 nsCOMPtr
<nsIPrincipal
> principal
= BasePrincipal::CreateContentPrincipal(
17925 thirdPartyURI
, NodePrincipal()->OriginAttributesRef());
17927 ConsumeTransientUserGestureActivation();
17928 promise
->MaybeRejectWithNotAllowedError(
17929 "requestStorageAccess not allowed"_ns
);
17930 return promise
.forget();
17933 RefPtr
<Document
> self(this);
17934 bool hasUserActivation
= HasValidTransientUserGestureActivation();
17936 // Consume user activation before entering the async part of this method.
17937 // This prevents usage of other transient activation-gated APIs.
17938 ConsumeTransientUserGestureActivation();
17940 // Step 4a: Start the async part of this function. Check the cookie
17941 // permission, but this can't be done in this process. We needs the cookie
17942 // permission of the URL as if it were embedded on this page, so we need to
17943 // make this check in the ContentParent.
17944 StorageAccessAPIHelper::
17945 AsyncCheckCookiesPermittedDecidesStorageAccessAPIOnChildProcess(
17946 GetBrowsingContext(), principal
)
17948 GetCurrentSerialEventTarget(), __func__
,
17949 [inner
, thirdPartyURI
, bc
, principal
, hasUserActivation
,
17950 aRequireUserActivation
, self
,
17951 promise
](Maybe
<bool> cookieResult
) {
17952 // Handle the result of the cookie permission check that took
17953 // place in the ContentParent.
17954 if (cookieResult
.isSome()) {
17955 if (cookieResult
.value()) {
17956 return MozPromise
<int, bool, true>::CreateAndResolve(
17959 return MozPromise
<int, bool, true>::CreateAndReject(false,
17963 // Step 4b: Check for the existing storage access permission
17964 nsAutoCString type
;
17965 bool ok
= AntiTrackingUtils::CreateStoragePermissionKey(
17968 return MozPromise
<int, bool, true>::CreateAndReject(false,
17971 if (AntiTrackingUtils::CheckStoragePermission(
17972 self
->NodePrincipal(), type
,
17973 nsContentUtils::IsInPrivateBrowsing(self
), nullptr,
17975 return MozPromise
<int, bool, true>::CreateAndResolve(
17979 // Step 4c: Try to request storage access, either automatically
17980 // or with a user-prompt. This is the part that is async in the
17981 // typical requestStorageAccess function.
17982 return StorageAccessAPIHelper::RequestStorageAccessAsyncHelper(
17983 self
, inner
, bc
, principal
, hasUserActivation
,
17984 aRequireUserActivation
, false,
17985 ContentBlockingNotifier::
17986 ePrivilegeStorageAccessForOriginAPI
,
17989 // If the IPC rejects, we should reject our promise here which
17990 // will cause a rejection of the promise we already returned
17992 return MozPromise
<int, bool, true>::CreateAndReject(false,
17996 GetCurrentSerialEventTarget(), __func__
,
17997 // If the previous handlers resolved, we should reinstate user
17998 // activation and resolve the promise we returned in Step 5.
17999 [self
, inner
, promise
] {
18000 inner
->SaveStorageAccessPermissionGranted();
18001 self
->NotifyUserGestureActivation();
18002 promise
->MaybeResolveWithUndefined();
18004 // If the previous handler rejected, we should reject the promise
18005 // returned by this function.
18007 promise
->MaybeRejectWithNotAllowedError(
18008 "requestStorageAccess not allowed"_ns
);
18011 // Step 5: While the async stuff is happening, we should return the promise so
18012 // our caller can continue executing.
18013 return promise
.forget();
18016 already_AddRefed
<Promise
> Document::RequestStorageAccessUnderSite(
18017 const nsAString
& aSerializedSite
, ErrorResult
& aRv
) {
18018 nsIGlobalObject
* global
= GetScopeObject();
18020 aRv
.Throw(NS_ERROR_NOT_AVAILABLE
);
18023 RefPtr
<Promise
> promise
= Promise::Create(global
, aRv
);
18024 if (aRv
.Failed()) {
18028 // Check that we have user activation before proceeding to prevent
18029 // rapid calls to the API to leak information.
18030 if (!ConsumeTransientUserGestureActivation()) {
18031 // Report an error to the console for this case
18032 nsContentUtils::ReportToConsole(
18033 nsIScriptError::errorFlag
, "requestStorageAccess"_ns
, this,
18034 nsContentUtils::eDOM_PROPERTIES
, "RequestStorageAccessUserGesture");
18035 promise
->MaybeRejectWithUndefined();
18036 return promise
.forget();
18039 // Check if the provided URI is different-site to this Document
18040 nsCOMPtr
<nsIURI
> siteURI
;
18041 nsresult rv
= NS_NewURI(getter_AddRefs(siteURI
), aSerializedSite
);
18042 if (NS_WARN_IF(NS_FAILED(rv
))) {
18043 promise
->MaybeRejectWithUndefined();
18044 return promise
.forget();
18046 bool isCrossSiteArgument
;
18047 rv
= NodePrincipal()->IsThirdPartyURI(siteURI
, &isCrossSiteArgument
);
18048 if (NS_WARN_IF(NS_FAILED(rv
))) {
18052 if (!isCrossSiteArgument
) {
18053 promise
->MaybeRejectWithUndefined();
18054 return promise
.forget();
18057 // Check if this party has broad cookie permissions.
18058 Maybe
<bool> resultBecauseCookiesApproved
=
18059 StorageAccessAPIHelper::CheckCookiesPermittedDecidesStorageAccessAPI(
18060 CookieJarSettings(), NodePrincipal());
18061 if (resultBecauseCookiesApproved
.isSome()) {
18062 if (resultBecauseCookiesApproved
.value()) {
18063 promise
->MaybeResolveWithUndefined();
18064 return promise
.forget();
18066 promise
->MaybeRejectWithUndefined();
18067 return promise
.forget();
18070 // Check if browser settings preclude this document getting storage
18071 // access under the provided site
18072 Maybe
<bool> resultBecauseBrowserSettings
=
18073 StorageAccessAPIHelper::CheckBrowserSettingsDecidesStorageAccessAPI(
18074 CookieJarSettings(), true, false, true);
18075 if (resultBecauseBrowserSettings
.isSome()) {
18076 if (resultBecauseBrowserSettings
.value()) {
18077 promise
->MaybeResolveWithUndefined();
18078 return promise
.forget();
18080 promise
->MaybeRejectWithUndefined();
18081 return promise
.forget();
18084 // Check that this Document is same-site to the top
18085 Maybe
<bool> resultBecauseCallContext
= StorageAccessAPIHelper::
18086 CheckSameSiteCallingContextDecidesStorageAccessAPI(this, false);
18087 if (resultBecauseCallContext
.isSome()) {
18088 if (resultBecauseCallContext
.value()) {
18089 promise
->MaybeResolveWithUndefined();
18090 return promise
.forget();
18092 promise
->MaybeRejectWithUndefined();
18093 return promise
.forget();
18096 nsCOMPtr
<nsIPrincipal
> principal(NodePrincipal());
18098 // Test if the permission this is requesting is already set
18099 nsCOMPtr
<nsIPrincipal
> argumentPrincipal
=
18100 BasePrincipal::CreateContentPrincipal(
18101 siteURI
, NodePrincipal()->OriginAttributesRef());
18102 if (!argumentPrincipal
) {
18103 ConsumeTransientUserGestureActivation();
18104 promise
->MaybeRejectWithUndefined();
18105 return promise
.forget();
18107 nsCString originNoSuffix
;
18108 rv
= NodePrincipal()->GetOriginNoSuffix(originNoSuffix
);
18109 if (NS_WARN_IF(NS_FAILED(rv
))) {
18110 promise
->MaybeRejectWithUndefined();
18111 return promise
.forget();
18114 ContentChild
* cc
= ContentChild::GetSingleton();
18116 RefPtr
<Document
> self(this);
18117 cc
->SendTestStorageAccessPermission(argumentPrincipal
, originNoSuffix
)
18119 GetCurrentSerialEventTarget(), __func__
,
18121 self
](const ContentChild::TestStorageAccessPermissionPromise::
18122 ResolveValueType
& aResult
) {
18124 return StorageAccessAPIHelper::
18125 StorageAccessPermissionGrantPromise::CreateAndResolve(
18126 StorageAccessAPIHelper::eAllow
, __func__
);
18128 // Get a grant for the storage access permission that will be set
18129 // when this is completed in the embedding context
18130 nsCString serializedSite
;
18131 RefPtr
<nsEffectiveTLDService
> etld
=
18132 nsEffectiveTLDService::GetInstance();
18134 return StorageAccessAPIHelper::
18135 StorageAccessPermissionGrantPromise::CreateAndReject(
18138 nsresult rv
= etld
->GetSite(siteURI
, serializedSite
);
18139 if (NS_FAILED(rv
)) {
18140 return StorageAccessAPIHelper::
18141 StorageAccessPermissionGrantPromise::CreateAndReject(
18144 return self
->CreatePermissionGrantPromise(
18145 self
->GetInnerWindow(), self
->NodePrincipal(), true, true,
18146 Some(serializedSite
), false)();
18148 [](const ContentChild::TestStorageAccessPermissionPromise::
18149 RejectValueType
& aResult
) {
18150 return StorageAccessAPIHelper::StorageAccessPermissionGrantPromise::
18151 CreateAndReject(false, __func__
);
18154 GetCurrentSerialEventTarget(), __func__
,
18155 [promise
, principal
, siteURI
](int result
) {
18156 ContentChild
* cc
= ContentChild::GetSingleton();
18158 // TODO(bug 1778561): Make this work in non-content processes.
18159 promise
->MaybeRejectWithUndefined();
18162 // Set a permission in the parent process that this document wants
18163 // storage access under the argument's site, resolving our returned
18164 // promise on success
18165 cc
->SendSetAllowStorageAccessRequestFlag(principal
, siteURI
)
18167 GetCurrentSerialEventTarget(), __func__
,
18168 [promise
](bool success
) {
18170 promise
->MaybeResolveWithUndefined();
18172 promise
->MaybeRejectWithUndefined();
18175 [promise
](mozilla::ipc::ResponseRejectReason reason
) {
18176 promise
->MaybeRejectWithUndefined();
18179 [promise
](bool result
) { promise
->MaybeRejectWithUndefined(); });
18181 // Return the promise that is resolved in the async handler above
18182 return promise
.forget();
18185 already_AddRefed
<Promise
> Document::CompleteStorageAccessRequestFromSite(
18186 const nsAString
& aSerializedOrigin
, ErrorResult
& aRv
) {
18187 nsIGlobalObject
* global
= GetScopeObject();
18189 aRv
.Throw(NS_ERROR_NOT_AVAILABLE
);
18192 RefPtr
<Promise
> promise
= Promise::Create(global
, aRv
);
18193 if (aRv
.Failed()) {
18197 // Check that the provided URI is different-site to this Document
18198 nsCOMPtr
<nsIURI
> argumentURI
;
18199 nsresult rv
= NS_NewURI(getter_AddRefs(argumentURI
), aSerializedOrigin
);
18200 if (NS_WARN_IF(NS_FAILED(rv
))) {
18201 promise
->MaybeRejectWithUndefined();
18202 return promise
.forget();
18204 bool isCrossSiteArgument
;
18205 rv
= NodePrincipal()->IsThirdPartyURI(argumentURI
, &isCrossSiteArgument
);
18206 if (NS_WARN_IF(NS_FAILED(rv
))) {
18210 if (!isCrossSiteArgument
) {
18211 promise
->MaybeRejectWithUndefined();
18212 return promise
.forget();
18215 // Check if browser settings preclude this document getting storage
18216 // access under the provided site
18217 Maybe
<bool> resultBecauseBrowserSettings
=
18218 StorageAccessAPIHelper::CheckBrowserSettingsDecidesStorageAccessAPI(
18219 CookieJarSettings(), true, false, true);
18220 if (resultBecauseBrowserSettings
.isSome()) {
18221 if (resultBecauseBrowserSettings
.value()) {
18222 promise
->MaybeResolveWithUndefined();
18223 return promise
.forget();
18225 promise
->MaybeRejectWithUndefined();
18226 return promise
.forget();
18229 // Check that this Document is same-site to the top
18230 Maybe
<bool> resultBecauseCallContext
= StorageAccessAPIHelper::
18231 CheckSameSiteCallingContextDecidesStorageAccessAPI(this, false);
18232 if (resultBecauseCallContext
.isSome()) {
18233 if (resultBecauseCallContext
.value()) {
18234 promise
->MaybeResolveWithUndefined();
18235 return promise
.forget();
18237 promise
->MaybeRejectWithUndefined();
18238 return promise
.forget();
18241 // Create principal of the embedded site requesting storage access
18242 nsCOMPtr
<nsIPrincipal
> principal
= BasePrincipal::CreateContentPrincipal(
18243 argumentURI
, NodePrincipal()->OriginAttributesRef());
18245 promise
->MaybeRejectWithUndefined();
18246 return promise
.forget();
18249 // Get versions of these objects that we can use in lambdas for callbacks
18250 RefPtr
<Document
> self(this);
18251 RefPtr
<BrowsingContext
> bc
= GetBrowsingContext();
18252 nsCOMPtr
<nsPIDOMWindowInner
> inner
= GetInnerWindow();
18254 // Test that the permission was set by a call to RequestStorageAccessUnderSite
18255 // from a top level document that is same-site with the argument
18256 ContentChild
* cc
= ContentChild::GetSingleton();
18258 // TODO(bug 1778561): Make this work in non-content processes.
18259 promise
->MaybeRejectWithUndefined();
18260 return promise
.forget();
18262 cc
->SendTestAllowStorageAccessRequestFlag(NodePrincipal(), argumentURI
)
18264 GetCurrentSerialEventTarget(), __func__
,
18265 [inner
, bc
, self
, principal
](bool success
) {
18267 // If that resolved with true, check that we don't already have a
18268 // permission that gives cookie access.
18269 return StorageAccessAPIHelper::
18270 AsyncCheckCookiesPermittedDecidesStorageAccessAPIOnChildProcess(
18273 return MozPromise
<Maybe
<bool>, nsresult
, true>::CreateAndReject(
18274 NS_ERROR_FAILURE
, __func__
);
18276 [](mozilla::ipc::ResponseRejectReason reason
) {
18277 return MozPromise
<Maybe
<bool>, nsresult
, true>::CreateAndReject(
18278 NS_ERROR_FAILURE
, __func__
);
18281 GetCurrentSerialEventTarget(), __func__
,
18282 [inner
, bc
, principal
, self
, promise
](Maybe
<bool> cookieResult
) {
18283 // Handle the result of the cookie permission check that took place
18284 // in the ContentParent.
18285 if (cookieResult
.isSome()) {
18286 if (cookieResult
.value()) {
18287 return StorageAccessAPIHelper::
18288 StorageAccessPermissionGrantPromise::CreateAndResolve(
18289 StorageAccessAPIHelper::eAllowAutoGrant
, __func__
);
18291 return StorageAccessAPIHelper::
18292 StorageAccessPermissionGrantPromise::CreateAndReject(
18296 // Check for the existing storage access permission
18297 nsAutoCString type
;
18299 AntiTrackingUtils::CreateStoragePermissionKey(principal
, type
);
18301 return StorageAccessAPIHelper::
18302 StorageAccessPermissionGrantPromise::CreateAndReject(
18305 if (AntiTrackingUtils::CheckStoragePermission(
18306 self
->NodePrincipal(), type
,
18307 nsContentUtils::IsInPrivateBrowsing(self
), nullptr, 0)) {
18308 return StorageAccessAPIHelper::
18309 StorageAccessPermissionGrantPromise::CreateAndResolve(
18310 StorageAccessAPIHelper::eAllowAutoGrant
, __func__
);
18313 // Try to request storage access, ignoring the final checks.
18314 // We ignore the final checks because this is where the "grant"
18315 // either by prompt doorhanger or autogrant takes place. We already
18316 // gathered an equivalent grant in requestStorageAccessUnderSite.
18317 return StorageAccessAPIHelper::RequestStorageAccessAsyncHelper(
18318 self
, inner
, bc
, principal
, true, true, false,
18319 ContentBlockingNotifier::eStorageAccessAPI
, false);
18321 // If the IPC rejects, we should reject our promise here which will
18322 // cause a rejection of the promise we already returned
18324 return MozPromise
<int, bool, true>::CreateAndReject(false,
18328 GetCurrentSerialEventTarget(), __func__
,
18329 // If the previous handlers resolved, we should reinstate user
18330 // activation and resolve the promise we returned in Step 5.
18331 [self
, inner
, promise
] {
18332 inner
->SaveStorageAccessPermissionGranted();
18333 promise
->MaybeResolveWithUndefined();
18335 // If the previous handler rejected, we should reject the promise
18336 // returned by this function.
18337 [promise
] { promise
->MaybeRejectWithUndefined(); });
18339 return promise
.forget();
18342 nsTHashSet
<RefPtr
<WakeLockSentinel
>>& Document::ActiveWakeLocks(
18343 WakeLockType aType
) {
18344 return mActiveLocks
.LookupOrInsert(aType
);
18347 class UnlockAllWakeLockRunnable final
: public Runnable
{
18349 UnlockAllWakeLockRunnable(WakeLockType aType
, Document
* aDoc
)
18350 : Runnable("UnlockAllWakeLocks"), mType(aType
), mDoc(aDoc
) {}
18352 // MOZ_CAN_RUN_SCRIPT_BOUNDARY until Runnable::Run is MOZ_CAN_RUN_SCRIPT. See
18354 MOZ_CAN_RUN_SCRIPT_BOUNDARY
18355 NS_IMETHOD
Run() override
{
18356 // Move, as ReleaseWakeLock will try to remove from and possibly allow
18357 // scripts via onrelease to add to document.[[ActiveLocks]]["screen"]
18358 nsCOMPtr
<Document
> doc
= mDoc
;
18359 nsTHashSet
<RefPtr
<WakeLockSentinel
>> locks
=
18360 std::move(doc
->ActiveWakeLocks(mType
));
18361 for (const auto& lock
: locks
) {
18362 // ReleaseWakeLock runs script, which could release other locks
18363 if (!lock
->Released()) {
18364 ReleaseWakeLock(doc
, MOZ_KnownLive(lock
), mType
);
18371 ~UnlockAllWakeLockRunnable() = default;
18374 WakeLockType mType
;
18375 nsCOMPtr
<Document
> mDoc
;
18378 void Document::UnlockAllWakeLocks(WakeLockType aType
) {
18379 // Perform unlock in a runnable to prevent UnlockAll being MOZ_CAN_RUN_SCRIPT
18380 RefPtr
<UnlockAllWakeLockRunnable
> runnable
=
18381 MakeRefPtr
<UnlockAllWakeLockRunnable
>(aType
, this);
18382 NS_DispatchToMainThread(runnable
);
18385 RefPtr
<Document::AutomaticStorageAccessPermissionGrantPromise
>
18386 Document::AutomaticStorageAccessPermissionCanBeGranted(bool hasUserActivation
) {
18387 // requestStorageAccessForOrigin may not require user activation. If we don't
18388 // have user activation at this point we should always show the prompt.
18389 if (!hasUserActivation
||
18390 !StaticPrefs::privacy_antitracking_enableWebcompat()) {
18391 return AutomaticStorageAccessPermissionGrantPromise::CreateAndResolve(
18394 if (XRE_IsContentProcess()) {
18395 // In the content process, we need to ask the parent process to compute
18396 // this. The reason is that nsIPermissionManager::GetAllWithTypePrefix()
18397 // isn't accessible in the content process.
18398 ContentChild
* cc
= ContentChild::GetSingleton();
18401 return cc
->SendAutomaticStorageAccessPermissionCanBeGranted(NodePrincipal())
18402 ->Then(GetCurrentSerialEventTarget(), __func__
,
18403 [](const ContentChild::
18404 AutomaticStorageAccessPermissionCanBeGrantedPromise::
18405 ResolveOrRejectValue
& aValue
) {
18406 if (aValue
.IsResolve()) {
18407 return AutomaticStorageAccessPermissionGrantPromise::
18408 CreateAndResolve(aValue
.ResolveValue(), __func__
);
18411 return AutomaticStorageAccessPermissionGrantPromise::
18412 CreateAndReject(false, __func__
);
18416 if (XRE_IsParentProcess()) {
18417 // In the parent process, we can directly compute this.
18418 return AutomaticStorageAccessPermissionGrantPromise::CreateAndResolve(
18419 AutomaticStorageAccessPermissionCanBeGranted(NodePrincipal()),
18423 return AutomaticStorageAccessPermissionGrantPromise::CreateAndReject(
18427 bool Document::AutomaticStorageAccessPermissionCanBeGranted(
18428 nsIPrincipal
* aPrincipal
) {
18429 if (!StaticPrefs::dom_storage_access_auto_grants()) {
18433 if (!ContentBlockingUserInteraction::Exists(aPrincipal
)) {
18437 nsCOMPtr
<nsIBrowserUsage
> bu
= do_ImportESModule(
18438 "resource:///modules/BrowserUsageTelemetry.sys.mjs", fallible
);
18439 if (NS_WARN_IF(!bu
)) {
18443 uint32_t uniqueDomainsVisitedInPast24Hours
= 0;
18444 nsresult rv
= bu
->GetUniqueDomainsVisitedInPast24Hours(
18445 &uniqueDomainsVisitedInPast24Hours
);
18446 if (NS_WARN_IF(NS_FAILED(rv
))) {
18450 Maybe
<size_t> maybeOriginsThirdPartyHasAccessTo
=
18451 AntiTrackingUtils::CountSitesAllowStorageAccess(aPrincipal
);
18452 if (maybeOriginsThirdPartyHasAccessTo
.isNothing()) {
18455 size_t originsThirdPartyHasAccessTo
=
18456 maybeOriginsThirdPartyHasAccessTo
.value();
18458 // one percent of the number of top-levels origins visited in the current
18459 // session (but not to exceed 24 hours), or the value of the
18460 // dom.storage_access.max_concurrent_auto_grants preference, whichever is
18462 size_t maxConcurrentAutomaticGrants
= std::max(
18463 std::max(int(std::floor(uniqueDomainsVisitedInPast24Hours
/ 100)),
18464 StaticPrefs::dom_storage_access_max_concurrent_auto_grants()),
18467 return originsThirdPartyHasAccessTo
< maxConcurrentAutomaticGrants
;
18470 void Document::RecordNavigationTiming(ReadyState aReadyState
) {
18471 if (!XRE_IsContentProcess()) {
18474 if (!IsTopLevelContentDocument()) {
18477 // If we dont have the timing yet (mostly because the doc is still loading),
18478 // get it from docshell.
18479 RefPtr
<nsDOMNavigationTiming
> timing
= mTiming
;
18481 if (!mDocumentContainer
) {
18484 timing
= mDocumentContainer
->GetNavigationTiming();
18489 TimeStamp startTime
= timing
->GetNavigationStartTimeStamp();
18490 switch (aReadyState
) {
18491 case READYSTATE_LOADING
:
18492 if (!mDOMLoadingSet
) {
18493 Telemetry::AccumulateTimeDelta(Telemetry::TIME_TO_DOM_LOADING_MS
,
18495 mDOMLoadingSet
= true;
18498 case READYSTATE_INTERACTIVE
:
18499 if (!mDOMInteractiveSet
) {
18500 Telemetry::AccumulateTimeDelta(Telemetry::TIME_TO_DOM_INTERACTIVE_MS
,
18502 mDOMInteractiveSet
= true;
18505 case READYSTATE_COMPLETE
:
18506 if (!mDOMCompleteSet
) {
18507 Telemetry::AccumulateTimeDelta(Telemetry::TIME_TO_DOM_COMPLETE_MS
,
18509 mDOMCompleteSet
= true;
18513 NS_WARNING("Unexpected ReadyState value");
18518 void Document::ReportShadowDOMUsage() {
18519 nsPIDOMWindowInner
* inner
= GetInnerWindow();
18520 if (NS_WARN_IF(!inner
)) {
18524 WindowContext
* wc
= inner
->GetWindowContext();
18525 if (NS_WARN_IF(!wc
|| wc
->IsDiscarded())) {
18529 WindowContext
* topWc
= wc
->TopWindowContext();
18530 if (topWc
->GetHasReportedShadowDOMUsage()) {
18534 MOZ_ALWAYS_SUCCEEDS(topWc
->SetHasReportedShadowDOMUsage(true));
18538 bool Document::StorageAccessSandboxed(uint32_t aSandboxFlags
) {
18539 return StaticPrefs::dom_storage_access_enabled() &&
18540 (aSandboxFlags
& SANDBOXED_STORAGE_ACCESS
) != 0;
18543 bool Document::StorageAccessSandboxed() const {
18544 return Document::StorageAccessSandboxed(GetSandboxFlags());
18547 bool Document::GetCachedSizes(nsTabSizes
* aSizes
) {
18548 if (mCachedTabSizeGeneration
== 0 ||
18549 GetGeneration() != mCachedTabSizeGeneration
) {
18552 aSizes
->mDom
+= mCachedTabSizes
.mDom
;
18553 aSizes
->mStyle
+= mCachedTabSizes
.mStyle
;
18554 aSizes
->mOther
+= mCachedTabSizes
.mOther
;
18558 void Document::SetCachedSizes(nsTabSizes
* aSizes
) {
18559 mCachedTabSizes
.mDom
= aSizes
->mDom
;
18560 mCachedTabSizes
.mStyle
= aSizes
->mStyle
;
18561 mCachedTabSizes
.mOther
= aSizes
->mOther
;
18562 mCachedTabSizeGeneration
= GetGeneration();
18565 nsAtom
* Document::GetContentLanguageAsAtomForStyle() const {
18566 // Content-Language may be a comma-separated list of language codes,
18567 // in which case the HTML5 spec says to treat it as unknown
18568 if (mContentLanguage
&&
18569 !nsDependentAtomString(mContentLanguage
).Contains(char16_t(','))) {
18570 return GetContentLanguage();
18576 nsAtom
* Document::GetLanguageForStyle() const {
18577 if (nsAtom
* lang
= GetContentLanguageAsAtomForStyle()) {
18580 return mLanguageFromCharset
.get();
18583 void Document::GetContentLanguageForBindings(DOMString
& aString
) const {
18584 aString
.SetKnownLiveAtom(mContentLanguage
, DOMString::eTreatNullAsEmpty
);
18587 const LangGroupFontPrefs
* Document::GetFontPrefsForLang(
18588 nsAtom
* aLanguage
, bool* aNeedsToCache
) const {
18589 nsAtom
* lang
= aLanguage
? aLanguage
: mLanguageFromCharset
.get();
18590 return StaticPresData::Get()->GetFontPrefsForLang(lang
, aNeedsToCache
);
18593 void Document::DoCacheAllKnownLangPrefs() {
18594 MOZ_ASSERT(mMayNeedFontPrefsUpdate
);
18595 RefPtr
<nsAtom
> lang
= GetLanguageForStyle();
18596 StaticPresData
* data
= StaticPresData::Get();
18597 data
->GetFontPrefsForLang(lang
? lang
.get() : mLanguageFromCharset
.get());
18598 data
->GetFontPrefsForLang(nsGkAtoms::x_math
);
18599 // https://bugzilla.mozilla.org/show_bug.cgi?id=1362599#c12
18600 data
->GetFontPrefsForLang(nsGkAtoms::Unicode
);
18601 for (const auto& key
: mLanguagesUsed
) {
18602 data
->GetFontPrefsForLang(key
);
18604 mMayNeedFontPrefsUpdate
= false;
18607 void Document::RecomputeLanguageFromCharset() {
18608 nsLanguageAtomService
* service
= nsLanguageAtomService::GetService();
18609 RefPtr
<nsAtom
> language
= service
->LookupCharSet(mCharacterSet
);
18610 if (language
== nsGkAtoms::Unicode
) {
18611 language
= service
->GetLocaleLanguage();
18614 if (language
== mLanguageFromCharset
) {
18618 mMayNeedFontPrefsUpdate
= true;
18619 mLanguageFromCharset
= std::move(language
);
18622 nsICookieJarSettings
* Document::CookieJarSettings() {
18623 // If we are here, this is probably a javascript: URL document. In any case,
18624 // we must have a nsCookieJarSettings. Let's create it.
18625 if (!mCookieJarSettings
) {
18626 Document
* inProcessParent
= GetInProcessParentDocument();
18628 if (inProcessParent
) {
18629 mCookieJarSettings
= net::CookieJarSettings::Create(
18630 inProcessParent
->CookieJarSettings()->GetCookieBehavior(),
18631 mozilla::net::CookieJarSettings::Cast(
18632 inProcessParent
->CookieJarSettings())
18633 ->GetPartitionKey(),
18634 inProcessParent
->CookieJarSettings()->GetIsFirstPartyIsolated(),
18635 inProcessParent
->CookieJarSettings()
18636 ->GetIsOnContentBlockingAllowList(),
18637 inProcessParent
->CookieJarSettings()
18638 ->GetShouldResistFingerprinting());
18640 // Inherit the fingerprinting random key from the parent.
18641 nsTArray
<uint8_t> randomKey
;
18642 nsresult rv
= inProcessParent
->CookieJarSettings()
18643 ->GetFingerprintingRandomizationKey(randomKey
);
18645 if (NS_SUCCEEDED(rv
)) {
18646 net::CookieJarSettings::Cast(mCookieJarSettings
)
18647 ->SetFingerprintingRandomizationKey(randomKey
);
18650 mCookieJarSettings
= net::CookieJarSettings::Create(NodePrincipal());
18653 if (auto* wgc
= GetWindowGlobalChild()) {
18654 net::CookieJarSettingsArgs csArgs
;
18655 net::CookieJarSettings::Cast(mCookieJarSettings
)->Serialize(csArgs
);
18656 // Update cookie settings in the parent process
18657 if (!wgc
->SendUpdateCookieJarSettings(csArgs
)) {
18659 "Failed to update document's cookie jar settings on the "
18660 "WindowGlobalParent");
18665 return mCookieJarSettings
;
18668 bool Document::UsingStorageAccess() {
18669 if (WindowContext
* wc
= GetWindowContext()) {
18670 return wc
->GetUsingStorageAccess();
18673 // If we don't yet have a window context, we have to use the decision
18674 // from the Document's Channel's LoadInfo directly.
18679 nsCOMPtr
<nsILoadInfo
> loadInfo
= mChannel
->LoadInfo();
18680 return loadInfo
->GetStoragePermission() != nsILoadInfo::NoStoragePermission
;
18683 bool Document::HasStorageAccessPermissionGrantedByAllowList() {
18684 // We only care about if the document gets the storage permission via the
18685 // allow list here. So we don't check the storage access cache in the inner
18692 nsCOMPtr
<nsILoadInfo
> loadInfo
= mChannel
->LoadInfo();
18693 return loadInfo
->GetStoragePermission() ==
18694 nsILoadInfo::StoragePermissionAllowListed
;
18697 nsIPrincipal
* Document::EffectiveStoragePrincipal() const {
18699 privacy_partition_always_partition_third_party_non_cookie_storage()) {
18700 return EffectiveCookiePrincipal();
18703 nsPIDOMWindowInner
* inner
= GetInnerWindow();
18705 return NodePrincipal();
18708 // Return our cached storage principal if one exists.
18709 if (mActiveStoragePrincipal
) {
18710 return mActiveStoragePrincipal
;
18713 // Calling StorageAllowedForDocument will notify the ContentBlockLog. This
18714 // loads TrackingDBService.jsm, which in turn pulls in osfile.jsm, making us
18715 // fail // browser/base/content/test/performance/browser_startup.js. To avoid
18716 // that, we short-circuit the check here by allowing storage access to system
18717 // and addon principles, avoiding the test-failure.
18718 nsIPrincipal
* principal
= NodePrincipal();
18719 if (principal
&& (principal
->IsSystemPrincipal() ||
18720 principal
->GetIsAddonOrExpandedAddonPrincipal())) {
18721 return mActiveStoragePrincipal
= NodePrincipal();
18724 auto cookieJarSettings
= const_cast<Document
*>(this)->CookieJarSettings();
18725 if (cookieJarSettings
->GetIsOnContentBlockingAllowList()) {
18726 return mActiveStoragePrincipal
= NodePrincipal();
18729 StorageAccess storageAccess
= StorageAllowedForDocument(this);
18730 if (!ShouldPartitionStorage(storageAccess
) ||
18731 !StoragePartitioningEnabled(storageAccess
, cookieJarSettings
)) {
18732 return mActiveStoragePrincipal
= NodePrincipal();
18735 Unused
<< NS_WARN_IF(NS_FAILED(StoragePrincipalHelper::GetPrincipal(
18736 nsGlobalWindowInner::Cast(inner
),
18737 StoragePrincipalHelper::eForeignPartitionedPrincipal
,
18738 getter_AddRefs(mActiveStoragePrincipal
))));
18739 return mActiveStoragePrincipal
;
18742 nsIPrincipal
* Document::EffectiveCookiePrincipal() const {
18743 nsPIDOMWindowInner
* inner
= GetInnerWindow();
18745 return NodePrincipal();
18748 // Return our cached storage principal if one exists.
18749 if (mActiveCookiePrincipal
) {
18750 return mActiveCookiePrincipal
;
18753 // We use the lower-level ContentBlocking API here to ensure this
18754 // check doesn't send notifications.
18755 uint32_t rejectedReason
= 0;
18756 if (ShouldAllowAccessFor(inner
, GetDocumentURI(), &rejectedReason
)) {
18757 return mActiveCookiePrincipal
= NodePrincipal();
18760 // Let's use the storage principal only if we need to partition the cookie
18761 // jar. When the permission is granted, access will be different and the
18762 // normal principal will be used.
18763 if (ShouldPartitionStorage(rejectedReason
) &&
18764 !StoragePartitioningEnabled(
18765 rejectedReason
, const_cast<Document
*>(this)->CookieJarSettings())) {
18766 return mActiveCookiePrincipal
= NodePrincipal();
18769 return mActiveCookiePrincipal
= mPartitionedPrincipal
;
18772 nsIPrincipal
* Document::GetPrincipalForPrefBasedHacks() const {
18773 // If the document is sandboxed document or data: document, we should
18774 // get URI of the parent document.
18775 for (const Document
* document
= this;
18776 document
&& document
->IsContentDocument();
18777 document
= document
->GetInProcessParentDocument()) {
18778 // The document URI may be about:blank even if it comes from actual web
18779 // site. Therefore, we need to check the URI of its principal.
18780 nsIPrincipal
* principal
= document
->NodePrincipal();
18781 if (principal
->GetIsNullPrincipal()) {
18789 void Document::SetIsInitialDocument(bool aIsInitialDocument
) {
18790 mIsInitialDocumentInWindow
= aIsInitialDocument
;
18792 if (aIsInitialDocument
&& !mIsEverInitialDocumentInWindow
) {
18793 mIsEverInitialDocumentInWindow
= aIsInitialDocument
;
18796 // Asynchronously tell the parent process that we are, or are no longer, the
18797 // initial document. This happens async.
18798 if (auto* wgc
= GetWindowGlobalChild()) {
18799 wgc
->SendSetIsInitialDocument(aIsInitialDocument
);
18804 void Document::AddToplevelLoadingDocument(Document
* aDoc
) {
18805 MOZ_ASSERT(aDoc
&& aDoc
->IsTopLevelContentDocument());
18806 // Currently we're interested in foreground documents only, so bail out early.
18807 if (aDoc
->IsInBackgroundWindow() || !XRE_IsContentProcess()) {
18811 if (!sLoadingForegroundTopLevelContentDocument
) {
18812 sLoadingForegroundTopLevelContentDocument
= new AutoTArray
<Document
*, 8>();
18813 mozilla::ipc::IdleSchedulerChild
* idleScheduler
=
18814 mozilla::ipc::IdleSchedulerChild::GetMainThreadIdleScheduler();
18815 if (idleScheduler
) {
18816 idleScheduler
->SendRunningPrioritizedOperation();
18819 if (!sLoadingForegroundTopLevelContentDocument
->Contains(aDoc
)) {
18820 sLoadingForegroundTopLevelContentDocument
->AppendElement(aDoc
);
18825 void Document::RemoveToplevelLoadingDocument(Document
* aDoc
) {
18826 MOZ_ASSERT(aDoc
&& aDoc
->IsTopLevelContentDocument());
18827 if (sLoadingForegroundTopLevelContentDocument
) {
18828 sLoadingForegroundTopLevelContentDocument
->RemoveElement(aDoc
);
18829 if (sLoadingForegroundTopLevelContentDocument
->IsEmpty()) {
18830 delete sLoadingForegroundTopLevelContentDocument
;
18831 sLoadingForegroundTopLevelContentDocument
= nullptr;
18833 mozilla::ipc::IdleSchedulerChild
* idleScheduler
=
18834 mozilla::ipc::IdleSchedulerChild::GetMainThreadIdleScheduler();
18835 if (idleScheduler
) {
18836 idleScheduler
->SendPrioritizedOperationDone();
18842 ColorScheme
Document::DefaultColorScheme() const {
18843 return LookAndFeel::ColorSchemeForStyle(*this, {GetColorSchemeBits()});
18846 ColorScheme
Document::PreferredColorScheme(IgnoreRFP aIgnoreRFP
) const {
18847 if (ShouldResistFingerprinting(RFPTarget::CSSPrefersColorScheme
) &&
18848 aIgnoreRFP
== IgnoreRFP::No
) {
18849 return ColorScheme::Light
;
18852 if (nsPresContext
* pc
= GetPresContext()) {
18853 if (auto scheme
= pc
->GetOverriddenOrEmbedderColorScheme()) {
18858 return PreferenceSheet::PrefsFor(*this).mColorScheme
;
18861 bool Document::HasRecentlyStartedForegroundLoads() {
18862 if (!sLoadingForegroundTopLevelContentDocument
) {
18866 for (size_t i
= 0; i
< sLoadingForegroundTopLevelContentDocument
->Length();
18868 Document
* doc
= sLoadingForegroundTopLevelContentDocument
->ElementAt(i
);
18869 // A page loaded in foreground could be in background now.
18870 if (!doc
->IsInBackgroundWindow()) {
18871 nsPIDOMWindowInner
* win
= doc
->GetInnerWindow();
18873 Performance
* perf
= win
->GetPerformance();
18875 perf
->Now() < StaticPrefs::page_load_deprioritization_period()) {
18882 // Didn't find any loading foreground documents, just clear the array.
18883 delete sLoadingForegroundTopLevelContentDocument
;
18884 sLoadingForegroundTopLevelContentDocument
= nullptr;
18886 mozilla::ipc::IdleSchedulerChild
* idleScheduler
=
18887 mozilla::ipc::IdleSchedulerChild::GetMainThreadIdleScheduler();
18888 if (idleScheduler
) {
18889 idleScheduler
->SendPrioritizedOperationDone();
18894 void Document::AddPendingFrameStaticClone(nsFrameLoaderOwner
* aElement
,
18895 nsFrameLoader
* aStaticCloneOf
) {
18896 PendingFrameStaticClone
* clone
= mPendingFrameStaticClones
.AppendElement();
18897 clone
->mElement
= aElement
;
18898 clone
->mStaticCloneOf
= aStaticCloneOf
;
18901 bool Document::ShouldAvoidNativeTheme() const {
18902 return StaticPrefs::widget_non_native_theme_enabled() &&
18903 (!IsInChromeDocShell() || XRE_IsContentProcess());
18906 bool Document::UseRegularPrincipal() const {
18907 return EffectiveStoragePrincipal() == NodePrincipal();
18910 bool Document::HasThirdPartyChannel() {
18911 nsCOMPtr
<nsIChannel
> channel
= GetChannel();
18913 // We assume that the channel is a third-party by default.
18914 bool thirdParty
= true;
18916 nsCOMPtr
<mozIThirdPartyUtil
> thirdPartyUtil
=
18917 components::ThirdPartyUtil::Service();
18918 if (!thirdPartyUtil
) {
18922 // Check that if the channel is a third-party to its parent.
18924 thirdPartyUtil
->IsThirdPartyChannel(channel
, nullptr, &thirdParty
);
18925 if (NS_FAILED(rv
)) {
18926 // Assume third-party in case of failure
18933 if (mParentDocument
) {
18934 return mParentDocument
->HasThirdPartyChannel();
18940 bool Document::IsLikelyContentInaccessibleTopLevelAboutBlank() const {
18941 if (!mDocumentURI
|| !NS_IsAboutBlank(mDocumentURI
)) {
18944 // FIXME(emilio): This is not quite edge-case free. See bug 1860098.
18946 // For stuff in frames, that makes our per-document telemetry probes not
18947 // really reliable but doesn't affect the correctness of our page probes, so
18948 // it's not too terrible.
18949 BrowsingContext
* bc
= GetBrowsingContext();
18950 return bc
&& bc
->IsTop() && !bc
->HadOriginalOpener();
18953 bool Document::ShouldIncludeInTelemetry() const {
18954 if (!IsContentDocument() && !IsResourceDoc()) {
18958 if (IsLikelyContentInaccessibleTopLevelAboutBlank()) {
18962 nsIPrincipal
* prin
= NodePrincipal();
18963 // TODO(emilio): Should this use GetIsContentPrincipal() +
18964 // GetPrecursorPrincipal() instead (accounting for add-ons separately)?
18965 return !(prin
->GetIsAddonOrExpandedAddonPrincipal() ||
18966 prin
->IsSystemPrincipal() || prin
->SchemeIs("about") ||
18967 prin
->SchemeIs("chrome") || prin
->SchemeIs("resource"));
18970 void Document::GetConnectedShadowRoots(
18971 nsTArray
<RefPtr
<ShadowRoot
>>& aOut
) const {
18972 AppendToArray(aOut
, mComposedShadowRoots
);
18975 void Document::AddMediaElementWithMSE() {
18976 if (mMediaElementWithMSECount
++ == 0) {
18977 if (WindowGlobalChild
* wgc
= GetWindowGlobalChild()) {
18978 wgc
->BlockBFCacheFor(BFCacheStatus::CONTAINS_MSE_CONTENT
);
18983 void Document::RemoveMediaElementWithMSE() {
18984 MOZ_ASSERT(mMediaElementWithMSECount
> 0);
18985 if (--mMediaElementWithMSECount
== 0) {
18986 if (WindowGlobalChild
* wgc
= GetWindowGlobalChild()) {
18987 wgc
->UnblockBFCacheFor(BFCacheStatus::CONTAINS_MSE_CONTENT
);
18992 void Document::UnregisterFromMemoryReportingForDataDocument() {
18993 if (!mAddedToMemoryReportingAsDataDocument
) {
18996 mAddedToMemoryReportingAsDataDocument
= false;
18997 nsIGlobalObject
* global
= GetScopeObject();
18999 if (nsPIDOMWindowInner
* win
= global
->GetAsInnerWindow()) {
19000 nsGlobalWindowInner::Cast(win
)->UnregisterDataDocumentForMemoryReporting(
19005 void Document::OOPChildLoadStarted(BrowserBridgeChild
* aChild
) {
19006 MOZ_DIAGNOSTIC_ASSERT(!mOOPChildrenLoading
.Contains(aChild
));
19007 mOOPChildrenLoading
.AppendElement(aChild
);
19008 if (mOOPChildrenLoading
.Length() == 1) {
19009 // Let's block unload so that we're blocked from going into the BFCache
19010 // until the child has actually notified us that it has done loading.
19015 void Document::OOPChildLoadDone(BrowserBridgeChild
* aChild
) {
19016 // aChild will not be in the list if nsDocLoader::Stop() was called, since
19017 // that clears mOOPChildrenLoading. It also dispatches the 'load' event,
19018 // so we don't need to call DocLoaderIsEmpty in that case.
19019 if (mOOPChildrenLoading
.RemoveElement(aChild
)) {
19020 if (mOOPChildrenLoading
.IsEmpty()) {
19021 UnblockOnload(false);
19023 RefPtr
<nsDocLoader
> docLoader(mDocumentContainer
);
19025 docLoader
->OOPChildrenLoadingIsEmpty();
19030 void Document::ClearOOPChildrenLoading() {
19031 nsTArray
<const BrowserBridgeChild
*> oopChildrenLoading
;
19032 mOOPChildrenLoading
.SwapElements(oopChildrenLoading
);
19033 if (!oopChildrenLoading
.IsEmpty()) {
19034 UnblockOnload(false);
19038 bool Document::MayHaveDOMActivateListeners() const {
19039 if (nsPIDOMWindowInner
* inner
= GetInnerWindow()) {
19040 return inner
->HasDOMActivateEventListeners();
19043 // If we can't get information from the window object, default to true.
19047 HighlightRegistry
& Document::HighlightRegistry() {
19048 if (!mHighlightRegistry
) {
19049 mHighlightRegistry
= MakeRefPtr
<class HighlightRegistry
>(this);
19051 return *mHighlightRegistry
;
19054 RadioGroupContainer
& Document::OwnedRadioGroupContainer() {
19055 if (!mRadioGroupContainer
) {
19056 mRadioGroupContainer
= MakeUnique
<RadioGroupContainer
>();
19058 return *mRadioGroupContainer
;
19061 void Document::UpdateHiddenByContentVisibilityForAnimations() {
19062 for (AnimationTimeline
* timeline
: Timelines()) {
19063 timeline
->UpdateHiddenByContentVisibility();
19067 void Document::SetAllowDeclarativeShadowRoots(
19068 bool aAllowDeclarativeShadowRoots
) {
19069 mAllowDeclarativeShadowRoots
= aAllowDeclarativeShadowRoots
;
19072 bool Document::AllowsDeclarativeShadowRoots() const {
19073 return mAllowDeclarativeShadowRoots
;
19077 already_AddRefed
<Document
> Document::ParseHTMLUnsafe(GlobalObject
& aGlobal
,
19078 const nsAString
& aHTML
) {
19079 nsCOMPtr
<nsIURI
> uri
;
19080 NS_NewURI(getter_AddRefs(uri
), "about:blank");
19085 nsCOMPtr
<Document
> doc
;
19087 NS_NewHTMLDocument(getter_AddRefs(doc
), aGlobal
.GetSubjectPrincipal(),
19088 aGlobal
.GetSubjectPrincipal());
19089 if (NS_WARN_IF(NS_FAILED(rv
))) {
19093 doc
->SetAllowDeclarativeShadowRoots(true);
19094 doc
->SetDocumentURI(uri
);
19095 rv
= nsContentUtils::ParseDocumentHTML(aHTML
, doc
, false);
19096 if (NS_WARN_IF(NS_FAILED(rv
))) {
19100 return doc
.forget();
19103 } // namespace mozilla::dom