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/AbstractTimelineMarker.h"
44 #include "mozilla/AntiTrackingUtils.h"
45 #include "mozilla/ArrayIterator.h"
46 #include "mozilla/ArrayUtils.h"
47 #include "mozilla/AsyncEventDispatcher.h"
48 #include "mozilla/Base64.h"
49 #include "mozilla/BasePrincipal.h"
50 #include "mozilla/CSSEnabledState.h"
51 #include "mozilla/ContentBlockingAllowList.h"
52 #include "mozilla/ContentBlockingNotifier.h"
53 #include "mozilla/ContentBlockingUserInteraction.h"
54 #include "mozilla/ContentPrincipal.h"
55 #include "mozilla/CycleCollectedJSContext.h"
56 #include "mozilla/DebugOnly.h"
57 #include "mozilla/DocLoadingTimelineMarker.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/HTMLEditor.h"
71 #include "mozilla/HoldDropJSObjects.h"
72 #include "mozilla/IdentifierMapEntry.h"
73 #include "mozilla/InputTaskManager.h"
74 #include "mozilla/IntegerRange.h"
75 #include "mozilla/InternalMutationEvent.h"
76 #include "mozilla/Likely.h"
77 #include "mozilla/Logging.h"
78 #include "mozilla/LookAndFeel.h"
79 #include "mozilla/MacroForEach.h"
80 #include "mozilla/Maybe.h"
81 #include "mozilla/MediaFeatureChange.h"
82 #include "mozilla/MediaManager.h"
83 #include "mozilla/MemoryReporting.h"
84 #include "mozilla/NullPrincipal.h"
85 #include "mozilla/OriginAttributes.h"
86 #include "mozilla/OwningNonNull.h"
87 #include "mozilla/PendingAnimationTracker.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/RejectForeignAllowList.h"
99 #include "mozilla/RelativeTo.h"
100 #include "mozilla/RestyleManager.h"
101 #include "mozilla/ReverseIterator.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/ServoStyleSet.h"
109 #include "mozilla/ServoTypes.h"
110 #include "mozilla/SizeOfState.h"
111 #include "mozilla/Span.h"
112 #include "mozilla/Sprintf.h"
113 #include "mozilla/StaticAnalysisFunctions.h"
114 #include "mozilla/StaticPrefs_apz.h"
115 #include "mozilla/StaticPrefs_browser.h"
116 #include "mozilla/StaticPrefs_docshell.h"
117 #include "mozilla/StaticPrefs_dom.h"
118 #include "mozilla/StaticPrefs_editor.h"
119 #include "mozilla/StaticPrefs_fission.h"
120 #include "mozilla/StaticPrefs_full_screen_api.h"
121 #include "mozilla/StaticPrefs_layout.h"
122 #include "mozilla/StaticPrefs_network.h"
123 #include "mozilla/StaticPrefs_page_load.h"
124 #include "mozilla/StaticPrefs_privacy.h"
125 #include "mozilla/StaticPrefs_security.h"
126 #include "mozilla/StaticPrefs_widget.h"
127 #include "mozilla/StaticPresData.h"
128 #include "mozilla/StorageAccess.h"
129 #include "mozilla/StoragePrincipalHelper.h"
130 #include "mozilla/StyleSheet.h"
131 #include "mozilla/Telemetry.h"
132 #include "mozilla/TelemetryHistogramEnums.h"
133 #include "mozilla/TelemetryScalarEnums.h"
134 #include "mozilla/TextControlElement.h"
135 #include "mozilla/TextEditor.h"
136 #include "mozilla/TimelineConsumers.h"
137 #include "mozilla/TypedEnumBits.h"
138 #include "mozilla/URLDecorationStripper.h"
139 #include "mozilla/URLExtraData.h"
140 #include "mozilla/Unused.h"
141 #include "mozilla/css/ImageLoader.h"
142 #include "mozilla/css/Loader.h"
143 #include "mozilla/css/Rule.h"
144 #include "mozilla/css/SheetParsingMode.h"
145 #include "mozilla/dom/AnonymousContent.h"
146 #include "mozilla/dom/BlobURLProtocolHandler.h"
147 #include "mozilla/dom/BrowserChild.h"
148 #include "mozilla/dom/BrowsingContext.h"
149 #include "mozilla/dom/BrowsingContextGroup.h"
150 #include "mozilla/dom/CDATASection.h"
151 #include "mozilla/dom/CSPDictionariesBinding.h"
152 #include "mozilla/dom/CanonicalBrowsingContext.h"
153 #include "mozilla/dom/ChromeObserver.h"
154 #include "mozilla/dom/ClientInfo.h"
155 #include "mozilla/dom/ClientState.h"
156 #include "mozilla/dom/Comment.h"
157 #include "mozilla/dom/ContentChild.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/Event.h"
169 #include "mozilla/dom/EventListenerBinding.h"
170 #include "mozilla/dom/FailedCertSecurityInfoBinding.h"
171 #include "mozilla/dom/FeaturePolicy.h"
172 #include "mozilla/dom/FeaturePolicyUtils.h"
173 #include "mozilla/dom/FontFaceSet.h"
174 #include "mozilla/dom/FromParser.h"
175 #include "mozilla/dom/HighlightRegistry.h"
176 #include "mozilla/dom/HTMLAllCollection.h"
177 #include "mozilla/dom/HTMLBodyElement.h"
178 #include "mozilla/dom/HTMLCollectionBinding.h"
179 #include "mozilla/dom/HTMLDialogElement.h"
180 #include "mozilla/dom/HTMLFormElement.h"
181 #include "mozilla/dom/HTMLIFrameElement.h"
182 #include "mozilla/dom/HTMLImageElement.h"
183 #include "mozilla/dom/HTMLInputElement.h"
184 #include "mozilla/dom/HTMLLinkElement.h"
185 #include "mozilla/dom/HTMLMediaElement.h"
186 #include "mozilla/dom/HTMLMetaElement.h"
187 #include "mozilla/dom/HTMLSharedElement.h"
188 #include "mozilla/dom/HTMLTextAreaElement.h"
189 #include "mozilla/dom/ImageTracker.h"
190 #include "mozilla/dom/Link.h"
191 #include "mozilla/dom/MediaQueryList.h"
192 #include "mozilla/dom/MediaSource.h"
193 #include "mozilla/dom/MutationObservers.h"
194 #include "mozilla/dom/NameSpaceConstants.h"
195 #include "mozilla/dom/Navigator.h"
196 #include "mozilla/dom/NetErrorInfoBinding.h"
197 #include "mozilla/dom/NodeInfo.h"
198 #include "mozilla/dom/NodeIterator.h"
199 #include "mozilla/dom/PContentChild.h"
200 #include "mozilla/dom/PWindowGlobalChild.h"
201 #include "mozilla/dom/PageTransitionEvent.h"
202 #include "mozilla/dom/PageTransitionEventBinding.h"
203 #include "mozilla/dom/Performance.h"
204 #include "mozilla/dom/PermissionMessageUtils.h"
205 #include "mozilla/dom/PostMessageEvent.h"
206 #include "mozilla/dom/ProcessingInstruction.h"
207 #include "mozilla/dom/Promise.h"
208 #include "mozilla/dom/PromiseNativeHandler.h"
209 #include "mozilla/dom/ResizeObserverController.h"
210 #include "mozilla/dom/RustTypes.h"
211 #include "mozilla/dom/SVGElement.h"
212 #include "mozilla/dom/SVGDocument.h"
213 #include "mozilla/dom/SVGSVGElement.h"
214 #include "mozilla/dom/SVGUseElement.h"
215 #include "mozilla/dom/ScriptLoader.h"
216 #include "mozilla/dom/ScriptSettings.h"
217 #include "mozilla/dom/Selection.h"
218 #include "mozilla/dom/ServiceWorkerContainer.h"
219 #include "mozilla/dom/ServiceWorkerDescriptor.h"
220 #include "mozilla/dom/ServiceWorkerManager.h"
221 #include "mozilla/dom/ShadowIncludingTreeIterator.h"
222 #include "mozilla/dom/ShadowRoot.h"
223 #include "mozilla/dom/StyleSheetApplicableStateChangeEvent.h"
224 #include "mozilla/dom/StyleSheetApplicableStateChangeEventBinding.h"
225 #include "mozilla/dom/StyleSheetList.h"
226 #include "mozilla/dom/TimeoutManager.h"
227 #include "mozilla/dom/ToggleEvent.h"
228 #include "mozilla/dom/Touch.h"
229 #include "mozilla/dom/TouchEvent.h"
230 #include "mozilla/dom/TreeOrderedArrayInlines.h"
231 #include "mozilla/dom/TreeWalker.h"
232 #include "mozilla/dom/URL.h"
233 #include "mozilla/dom/UserActivation.h"
234 #include "mozilla/dom/WindowBinding.h"
235 #include "mozilla/dom/WindowContext.h"
236 #include "mozilla/dom/WindowGlobalChild.h"
237 #include "mozilla/dom/WindowProxyHolder.h"
238 #include "mozilla/dom/WorkerDocumentListener.h"
239 #include "mozilla/dom/XPathEvaluator.h"
240 #include "mozilla/dom/nsCSPContext.h"
241 #include "mozilla/dom/nsCSPUtils.h"
242 #include "mozilla/extensions/WebExtensionPolicy.h"
243 #include "mozilla/fallible.h"
244 #include "mozilla/gfx/BaseCoord.h"
245 #include "mozilla/gfx/BaseSize.h"
246 #include "mozilla/gfx/Coord.h"
247 #include "mozilla/gfx/Point.h"
248 #include "mozilla/gfx/ScaleFactor.h"
249 #include "mozilla/glean/GleanMetrics.h"
250 #include "mozilla/intl/LocaleService.h"
251 #include "mozilla/ipc/IdleSchedulerChild.h"
252 #include "mozilla/ipc/MessageChannel.h"
253 #include "mozilla/net/ChannelEventQueue.h"
254 #include "mozilla/net/CookieJarSettings.h"
255 #include "mozilla/net/NeckoChannelParams.h"
256 #include "mozilla/net/RequestContextService.h"
257 #include "nsAboutProtocolUtils.h"
258 #include "nsAlgorithm.h"
259 #include "nsAttrValue.h"
260 #include "nsAttrValueInlines.h"
261 #include "nsBaseHashtable.h"
262 #include "nsBidiUtils.h"
264 #include "nsCSSPropertyID.h"
265 #include "nsCSSProps.h"
266 #include "nsCSSPseudoElements.h"
267 #include "nsCSSRendering.h"
268 #include "nsCanvasFrame.h"
269 #include "nsCaseTreatment.h"
270 #include "nsCharsetSource.h"
271 #include "nsCommandManager.h"
272 #include "nsCommandParams.h"
273 #include "nsComponentManagerUtils.h"
274 #include "nsContentCreatorFunctions.h"
275 #include "nsContentList.h"
276 #include "nsContentPermissionHelper.h"
277 #include "nsContentSecurityUtils.h"
278 #include "nsContentUtils.h"
280 #include "nsCycleCollectionNoteChild.h"
281 #include "nsCycleCollectionTraversalCallback.h"
282 #include "nsDOMAttributeMap.h"
283 #include "nsDOMCaretPosition.h"
284 #include "nsDOMNavigationTiming.h"
285 #include "nsDOMString.h"
286 #include "nsDeviceContext.h"
287 #include "nsDocShell.h"
288 #include "nsDocShellLoadTypes.h"
289 #include "nsEffectiveTLDService.h"
291 #include "nsEscape.h"
292 #include "nsFocusManager.h"
293 #include "nsFrameLoader.h"
294 #include "nsFrameLoaderOwner.h"
295 #include "nsGenericHTMLElement.h"
296 #include "nsGlobalWindowInner.h"
297 #include "nsGlobalWindowOuter.h"
298 #include "nsHTMLCSSStyleSheet.h"
299 #include "nsHTMLDocument.h"
300 #include "nsHTMLStyleSheet.h"
301 #include "nsHtml5Module.h"
302 #include "nsHtml5Parser.h"
303 #include "nsHtml5TreeOpExecutor.h"
304 #include "nsIAsyncShutdown.h"
305 #include "nsIAuthPrompt.h"
306 #include "nsIAuthPrompt2.h"
307 #include "nsIBFCacheEntry.h"
308 #include "nsIBaseWindow.h"
309 #include "nsIBrowserChild.h"
310 #include "nsIBrowserUsage.h"
311 #include "nsICSSLoaderObserver.h"
312 #include "nsICategoryManager.h"
313 #include "nsICertOverrideService.h"
314 #include "nsIContent.h"
315 #include "nsIContentInlines.h"
316 #include "nsIContentPolicy.h"
317 #include "nsIContentSecurityPolicy.h"
318 #include "nsIContentSink.h"
319 #include "nsICookieJarSettings.h"
320 #include "nsICookieService.h"
321 #include "nsIDOMXULCommandDispatcher.h"
322 #include "nsIDocShell.h"
323 #include "nsIDocShellTreeItem.h"
324 #include "nsIDocumentActivity.h"
325 #include "nsIDocumentEncoder.h"
326 #include "nsIDocumentLoader.h"
327 #include "nsIDocumentLoaderFactory.h"
328 #include "nsIDocumentObserver.h"
329 #include "nsIDNSService.h"
330 #include "nsIEditingSession.h"
331 #include "nsIEditor.h"
332 #include "nsIEffectiveTLDService.h"
334 #include "nsIFileChannel.h"
335 #include "nsIFrame.h"
336 #include "nsIGlobalObject.h"
337 #include "nsIHTMLCollection.h"
338 #include "nsIHttpChannel.h"
339 #include "nsIHttpChannelInternal.h"
340 #include "nsIIOService.h"
341 #include "nsIImageLoadingContent.h"
342 #include "nsIInlineSpellChecker.h"
343 #include "nsIInputStreamChannel.h"
344 #include "nsIInterfaceRequestorUtils.h"
345 #include "nsILayoutHistoryState.h"
346 #include "nsIMultiPartChannel.h"
347 #include "nsIMutationObserver.h"
348 #include "nsINSSErrorsService.h"
349 #include "nsINamed.h"
350 #include "nsINodeList.h"
351 #include "nsIObjectLoadingContent.h"
352 #include "nsIObserverService.h"
353 #include "nsIPermission.h"
354 #include "nsIPrompt.h"
355 #include "nsIPropertyBag2.h"
356 #include "nsIPublicKeyPinningService.h"
357 #include "nsIReferrerInfo.h"
358 #include "nsIRefreshURI.h"
359 #include "nsIRequest.h"
360 #include "nsIRequestContext.h"
361 #include "nsIRunnable.h"
362 #include "nsISHEntry.h"
363 #include "nsIScriptElement.h"
364 #include "nsIScriptError.h"
365 #include "nsIScriptGlobalObject.h"
366 #include "nsIScriptSecurityManager.h"
367 #include "nsISecurityConsoleMessage.h"
368 #include "nsISelectionController.h"
369 #include "nsISerialEventTarget.h"
370 #include "nsISimpleEnumerator.h"
371 #include "nsISiteSecurityService.h"
372 #include "nsISocketProvider.h"
373 #include "nsISpeculativeConnect.h"
374 #include "nsIStructuredCloneContainer.h"
375 #include "nsIThread.h"
376 #include "nsITimedChannel.h"
377 #include "nsITimer.h"
378 #include "nsITransportSecurityInfo.h"
379 #include "nsIURIMutator.h"
380 #include "nsIVariant.h"
381 #include "nsIWeakReference.h"
382 #include "nsIWebNavigation.h"
383 #include "nsIWidget.h"
384 #include "nsIX509Cert.h"
385 #include "nsIX509CertValidity.h"
386 #include "nsIXMLContentSink.h"
387 #include "nsIHTMLContentSink.h"
388 #include "nsIXULRuntime.h"
389 #include "nsImageLoadingContent.h"
390 #include "nsImportModule.h"
391 #include "nsLanguageAtomService.h"
392 #include "nsLayoutUtils.h"
393 #include "nsNetCID.h"
394 #include "nsNetUtil.h"
395 #include "nsNodeInfoManager.h"
396 #include "nsObjectLoadingContent.h"
397 #include "nsPIDOMWindowInlines.h"
398 #include "nsPIWindowRoot.h"
400 #include "nsPointerHashKeys.h"
401 #include "nsPresContext.h"
402 #include "nsQueryFrame.h"
403 #include "nsQueryObject.h"
406 #include "nsRefreshDriver.h"
407 #include "nsSandboxFlags.h"
408 #include "nsSerializationHelper.h"
409 #include "nsServiceManagerUtils.h"
410 #include "nsStringFlags.h"
411 #include "nsStyleUtil.h"
412 #include "nsStringIterator.h"
413 #include "nsStyleSheetService.h"
414 #include "nsStyleStruct.h"
415 #include "nsTextNode.h"
416 #include "nsUnicharUtils.h"
417 #include "nsWrapperCache.h"
418 #include "nsWrapperCacheInlines.h"
419 #include "nsXPCOMCID.h"
420 #include "nsXULAppAPI.h"
421 #include "prthread.h"
424 #include "xpcpublic.h"
426 // XXX Must be included after mozilla/Encoding.h
427 #include "encoding_rs.h"
429 #include "mozilla/dom/XULBroadcastManager.h"
430 #include "mozilla/dom/XULPersist.h"
431 #include "nsIAppWindow.h"
432 #include "nsXULPrototypeDocument.h"
433 #include "nsXULCommandDispatcher.h"
434 #include "nsXULPopupManager.h"
435 #include "nsIDocShellTreeOwner.h"
437 #define XML_DECLARATION_BITS_DECLARATION_EXISTS (1 << 0)
438 #define XML_DECLARATION_BITS_ENCODING_EXISTS (1 << 1)
439 #define XML_DECLARATION_BITS_STANDALONE_EXISTS (1 << 2)
440 #define XML_DECLARATION_BITS_STANDALONE_YES (1 << 3)
442 #define NS_MAX_DOCUMENT_WRITE_DEPTH 20
444 mozilla::LazyLogModule
gPageCacheLog("PageCache");
445 mozilla::LazyLogModule
gSHIPBFCacheLog("SHIPBFCache");
446 mozilla::LazyLogModule
gTimeoutDeferralLog("TimeoutDefer");
447 mozilla::LazyLogModule
gUseCountersLog("UseCounters");
452 class Document::HeaderData
{
454 HeaderData(nsAtom
* aField
, const nsAString
& aData
)
455 : mField(aField
), mData(aData
) {}
458 // Delete iteratively to avoid blowing up the stack, though it shouldn't
459 // happen in practice.
460 UniquePtr
<HeaderData
> next
= std::move(mNext
);
462 next
= std::move(next
->mNext
);
466 RefPtr
<nsAtom
> mField
;
468 UniquePtr
<HeaderData
> mNext
;
471 using LinkArray
= nsTArray
<Link
*>;
473 AutoTArray
<Document
*, 8>* Document::sLoadingForegroundTopLevelContentDocument
=
476 static LazyLogModule
gDocumentLeakPRLog("DocumentLeak");
477 static LazyLogModule
gCspPRLog("CSP");
478 LazyLogModule
gUserInteractionPRLog("UserInteraction");
480 static nsresult
GetHttpChannelHelper(nsIChannel
* aChannel
,
481 nsIHttpChannel
** aHttpChannel
) {
482 nsCOMPtr
<nsIHttpChannel
> httpChannel
= do_QueryInterface(aChannel
);
484 httpChannel
.forget(aHttpChannel
);
488 nsCOMPtr
<nsIMultiPartChannel
> multipart
= do_QueryInterface(aChannel
);
490 *aHttpChannel
= nullptr;
494 nsCOMPtr
<nsIChannel
> baseChannel
;
495 nsresult rv
= multipart
->GetBaseChannel(getter_AddRefs(baseChannel
));
496 if (NS_WARN_IF(NS_FAILED(rv
))) {
500 httpChannel
= do_QueryInterface(baseChannel
);
501 httpChannel
.forget(aHttpChannel
);
508 #define NAME_NOT_VALID ((nsSimpleContentList*)1)
510 IdentifierMapEntry::IdentifierMapEntry(
511 const IdentifierMapEntry::DependentAtomOrString
* aKey
)
512 : mKey(aKey
? *aKey
: nullptr) {}
514 void IdentifierMapEntry::Traverse(
515 nsCycleCollectionTraversalCallback
* aCallback
) {
516 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*aCallback
,
517 "mIdentifierMap mNameContentList");
518 aCallback
->NoteXPCOMChild(static_cast<nsINodeList
*>(mNameContentList
));
521 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*aCallback
,
522 "mIdentifierMap mImageElement element");
523 nsIContent
* imageElement
= mImageElement
;
524 aCallback
->NoteXPCOMChild(imageElement
);
528 bool IdentifierMapEntry::IsEmpty() {
529 return mIdContentList
->IsEmpty() && !mNameContentList
&& !mChangeCallbacks
&&
533 bool IdentifierMapEntry::HasNameElement() const {
534 return mNameContentList
&& mNameContentList
->Length() != 0;
537 void IdentifierMapEntry::AddContentChangeCallback(
538 Document::IDTargetObserver aCallback
, void* aData
, bool aForImage
) {
539 if (!mChangeCallbacks
) {
540 mChangeCallbacks
= MakeUnique
<nsTHashtable
<ChangeCallbackEntry
>>();
543 ChangeCallback cc
= {aCallback
, aData
, aForImage
};
544 mChangeCallbacks
->PutEntry(cc
);
547 void IdentifierMapEntry::RemoveContentChangeCallback(
548 Document::IDTargetObserver aCallback
, void* aData
, bool aForImage
) {
549 if (!mChangeCallbacks
) return;
550 ChangeCallback cc
= {aCallback
, aData
, aForImage
};
551 mChangeCallbacks
->RemoveEntry(cc
);
552 if (mChangeCallbacks
->Count() == 0) {
553 mChangeCallbacks
= nullptr;
557 void IdentifierMapEntry::FireChangeCallbacks(Element
* aOldElement
,
558 Element
* aNewElement
,
560 if (!mChangeCallbacks
) return;
562 for (auto iter
= mChangeCallbacks
->Iter(); !iter
.Done(); iter
.Next()) {
563 IdentifierMapEntry::ChangeCallbackEntry
* entry
= iter
.Get();
564 // Don't fire image changes for non-image observers, and don't fire element
565 // changes for image observers when an image override is active.
566 if (entry
->mKey
.mForImage
? (mImageElement
&& !aImageOnly
) : aImageOnly
) {
570 if (!entry
->mKey
.mCallback(aOldElement
, aNewElement
, entry
->mKey
.mData
)) {
576 void IdentifierMapEntry::AddIdElement(Element
* aElement
) {
577 MOZ_ASSERT(aElement
, "Must have element");
578 MOZ_ASSERT(!mIdContentList
->Contains(nullptr), "Why is null in our list?");
580 size_t index
= mIdContentList
.Insert(*aElement
);
582 Element
* oldElement
= mIdContentList
->SafeElementAt(1);
583 FireChangeCallbacks(oldElement
, aElement
);
587 void IdentifierMapEntry::RemoveIdElement(Element
* aElement
) {
588 MOZ_ASSERT(aElement
, "Missing element");
590 // This should only be called while the document is in an update.
591 // Assertions near the call to this method guarantee this.
593 // This could fire in OOM situations
594 // Only assert this in HTML documents for now as XUL does all sorts of weird
596 NS_ASSERTION(!aElement
->OwnerDoc()->IsHTMLDocument() ||
597 mIdContentList
->Contains(aElement
),
598 "Removing id entry that doesn't exist");
600 // XXXbz should this ever Compact() I guess when all the content is gone
601 // we'll just get cleaned up in the natural order of things...
602 Element
* currentElement
= mIdContentList
->SafeElementAt(0);
603 mIdContentList
.RemoveElement(*aElement
);
604 if (currentElement
== aElement
) {
605 FireChangeCallbacks(currentElement
, mIdContentList
->SafeElementAt(0));
609 void IdentifierMapEntry::SetImageElement(Element
* aElement
) {
610 Element
* oldElement
= GetImageIdElement();
611 mImageElement
= aElement
;
612 Element
* newElement
= GetImageIdElement();
613 if (oldElement
!= newElement
) {
614 FireChangeCallbacks(oldElement
, newElement
, true);
618 void IdentifierMapEntry::ClearAndNotify() {
619 Element
* currentElement
= mIdContentList
->SafeElementAt(0);
620 mIdContentList
.Clear();
621 if (currentElement
) {
622 FireChangeCallbacks(currentElement
, nullptr);
624 mNameContentList
= nullptr;
626 SetImageElement(nullptr);
628 mChangeCallbacks
= nullptr;
633 class SimpleHTMLCollection final
: public nsSimpleContentList
,
634 public nsIHTMLCollection
{
636 explicit SimpleHTMLCollection(nsINode
* aRoot
) : nsSimpleContentList(aRoot
) {}
638 NS_DECL_ISUPPORTS_INHERITED
640 virtual nsINode
* GetParentObject() override
{
641 return nsSimpleContentList::GetParentObject();
643 virtual uint32_t Length() override
{ return nsSimpleContentList::Length(); }
644 virtual Element
* GetElementAt(uint32_t aIndex
) override
{
645 return mElements
.SafeElementAt(aIndex
)->AsElement();
648 virtual Element
* GetFirstNamedElement(const nsAString
& aName
,
649 bool& aFound
) override
{
651 RefPtr
<nsAtom
> name
= NS_Atomize(aName
);
652 for (uint32_t i
= 0; i
< mElements
.Length(); i
++) {
653 MOZ_DIAGNOSTIC_ASSERT(mElements
[i
]);
654 Element
* element
= mElements
[i
]->AsElement();
655 if (element
->GetID() == name
||
656 (element
->HasName() &&
657 element
->GetParsedAttr(nsGkAtoms::name
)->GetAtomValue() == name
)) {
665 virtual void GetSupportedNames(nsTArray
<nsString
>& aNames
) override
{
666 AutoTArray
<nsAtom
*, 8> atoms
;
667 for (uint32_t i
= 0; i
< mElements
.Length(); i
++) {
668 MOZ_DIAGNOSTIC_ASSERT(mElements
[i
]);
669 Element
* element
= mElements
[i
]->AsElement();
671 nsAtom
* id
= element
->GetID();
672 MOZ_ASSERT(id
!= nsGkAtoms::_empty
);
673 if (id
&& !atoms
.Contains(id
)) {
674 atoms
.AppendElement(id
);
677 if (element
->HasName()) {
678 nsAtom
* name
= element
->GetParsedAttr(nsGkAtoms::name
)->GetAtomValue();
679 MOZ_ASSERT(name
&& name
!= nsGkAtoms::_empty
);
680 if (name
&& !atoms
.Contains(name
)) {
681 atoms
.AppendElement(name
);
686 nsString
* names
= aNames
.AppendElements(atoms
.Length());
687 for (uint32_t i
= 0; i
< atoms
.Length(); i
++) {
688 atoms
[i
]->ToString(names
[i
]);
692 virtual JSObject
* GetWrapperPreserveColorInternal() override
{
693 return nsWrapperCache::GetWrapperPreserveColor();
695 virtual void PreserveWrapperInternal(
696 nsISupports
* aScriptObjectHolder
) override
{
697 nsWrapperCache::PreserveWrapper(aScriptObjectHolder
);
699 virtual JSObject
* WrapObject(JSContext
* aCx
,
700 JS::Handle
<JSObject
*> aGivenProto
) override
{
701 return HTMLCollection_Binding::Wrap(aCx
, this, aGivenProto
);
704 using nsBaseContentList::Item
;
707 virtual ~SimpleHTMLCollection() = default;
710 NS_IMPL_ISUPPORTS_INHERITED(SimpleHTMLCollection
, nsSimpleContentList
,
715 void IdentifierMapEntry::AddNameElement(nsINode
* aNode
, Element
* aElement
) {
716 if (!mNameContentList
) {
717 mNameContentList
= new dom::SimpleHTMLCollection(aNode
);
720 mNameContentList
->AppendElement(aElement
);
723 void IdentifierMapEntry::RemoveNameElement(Element
* aElement
) {
724 if (mNameContentList
) {
725 mNameContentList
->RemoveElement(aElement
);
729 bool IdentifierMapEntry::HasIdElementExposedAsHTMLDocumentProperty() const {
730 Element
* idElement
= GetIdElement();
732 nsGenericHTMLElement::ShouldExposeIdAsHTMLDocumentProperty(idElement
);
735 size_t IdentifierMapEntry::SizeOfExcludingThis(
736 MallocSizeOf aMallocSizeOf
) const {
737 return mKey
.mString
.SizeOfExcludingThisIfUnshared(aMallocSizeOf
);
740 // Helper structs for the content->subdoc map
742 class SubDocMapEntry
: public PLDHashEntryHdr
{
744 // Both of these are strong references
745 dom::Element
* mKey
; // must be first, to look like PLDHashEntryStub
746 dom::Document
* mSubDocument
;
749 class OnloadBlocker final
: public nsIRequest
{
751 OnloadBlocker() = default;
757 ~OnloadBlocker() = default;
760 NS_IMPL_ISUPPORTS(OnloadBlocker
, nsIRequest
)
763 OnloadBlocker::GetName(nsACString
& aResult
) {
764 aResult
.AssignLiteral("about:document-onload-blocker");
769 OnloadBlocker::IsPending(bool* _retval
) {
775 OnloadBlocker::GetStatus(nsresult
* status
) {
780 NS_IMETHODIMP
OnloadBlocker::SetCanceledReason(const nsACString
& aReason
) {
781 return SetCanceledReasonImpl(aReason
);
784 NS_IMETHODIMP
OnloadBlocker::GetCanceledReason(nsACString
& aReason
) {
785 return GetCanceledReasonImpl(aReason
);
788 NS_IMETHODIMP
OnloadBlocker::CancelWithReason(nsresult aStatus
,
789 const nsACString
& aReason
) {
790 return CancelWithReasonImpl(aStatus
, aReason
);
793 OnloadBlocker::Cancel(nsresult status
) { return NS_OK
; }
795 OnloadBlocker::Suspend(void) { return NS_OK
; }
797 OnloadBlocker::Resume(void) { return NS_OK
; }
800 OnloadBlocker::GetLoadGroup(nsILoadGroup
** aLoadGroup
) {
801 *aLoadGroup
= nullptr;
806 OnloadBlocker::SetLoadGroup(nsILoadGroup
* aLoadGroup
) { return NS_OK
; }
809 OnloadBlocker::GetLoadFlags(nsLoadFlags
* aLoadFlags
) {
810 *aLoadFlags
= nsIRequest::LOAD_NORMAL
;
815 OnloadBlocker::GetTRRMode(nsIRequest::TRRMode
* aTRRMode
) {
816 return GetTRRModeImpl(aTRRMode
);
820 OnloadBlocker::SetTRRMode(nsIRequest::TRRMode aTRRMode
) {
821 return SetTRRModeImpl(aTRRMode
);
825 OnloadBlocker::SetLoadFlags(nsLoadFlags aLoadFlags
) { return NS_OK
; }
827 // ==================================================================
831 ExternalResourceMap::ExternalResourceMap() : mHaveShutDown(false) {}
833 Document
* ExternalResourceMap::RequestResource(
834 nsIURI
* aURI
, nsIReferrerInfo
* aReferrerInfo
, nsINode
* aRequestingNode
,
835 Document
* aDisplayDocument
, ExternalResourceLoad
** aPendingLoad
) {
836 // If we ever start allowing non-same-origin loads here, we might need to do
837 // something interesting with aRequestingPrincipal even for the hashtable
839 MOZ_ASSERT(aURI
, "Must have a URI");
840 MOZ_ASSERT(aRequestingNode
, "Must have a node");
841 MOZ_ASSERT(aReferrerInfo
, "Must have a referrerInfo");
842 *aPendingLoad
= nullptr;
847 // First, make sure we strip the ref from aURI.
848 nsCOMPtr
<nsIURI
> clone
;
849 nsresult rv
= NS_GetURIWithoutRef(aURI
, getter_AddRefs(clone
));
850 if (NS_FAILED(rv
) || !clone
) {
854 ExternalResource
* resource
;
855 mMap
.Get(clone
, &resource
);
857 return resource
->mDocument
;
860 bool loadStartSucceeded
=
861 mPendingLoads
.WithEntryHandle(clone
, [&](auto&& loadEntry
) {
863 loadEntry
.Insert(MakeRefPtr
<PendingLoad
>(aDisplayDocument
));
865 if (NS_FAILED(loadEntry
.Data()->StartLoad(clone
, aReferrerInfo
,
871 RefPtr
<PendingLoad
> load(loadEntry
.Data());
872 load
.forget(aPendingLoad
);
875 if (!loadStartSucceeded
) {
876 // Make sure we don't thrash things by trying this load again, since
877 // chances are it failed for good reasons (security check, etc).
878 // This must be done outside the WithEntryHandle functor, as it accesses
880 AddExternalResource(clone
, nullptr, nullptr, aDisplayDocument
);
886 void ExternalResourceMap::EnumerateResources(SubDocEnumFunc aCallback
) {
887 nsTArray
<RefPtr
<Document
>> docs(mMap
.Count());
888 for (const auto& entry
: mMap
.Values()) {
889 if (Document
* doc
= entry
->mDocument
) {
890 docs
.AppendElement(doc
);
894 for (auto& doc
: docs
) {
895 if (aCallback(*doc
) == CallState::Stop
) {
901 void ExternalResourceMap::Traverse(
902 nsCycleCollectionTraversalCallback
* aCallback
) const {
903 // mPendingLoads will get cleared out as the requests complete, so
904 // no need to worry about those here.
905 for (const auto& entry
: mMap
) {
906 ExternalResourceMap::ExternalResource
* resource
= entry
.GetWeak();
908 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*aCallback
,
909 "mExternalResourceMap.mMap entry"
911 aCallback
->NoteXPCOMChild(ToSupports(resource
->mDocument
));
913 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*aCallback
,
914 "mExternalResourceMap.mMap entry"
916 aCallback
->NoteXPCOMChild(resource
->mViewer
);
918 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*aCallback
,
919 "mExternalResourceMap.mMap entry"
921 aCallback
->NoteXPCOMChild(resource
->mLoadGroup
);
925 void ExternalResourceMap::HideViewers() {
926 for (const auto& entry
: mMap
) {
927 nsCOMPtr
<nsIContentViewer
> viewer
= entry
.GetData()->mViewer
;
934 void ExternalResourceMap::ShowViewers() {
935 for (const auto& entry
: mMap
) {
936 nsCOMPtr
<nsIContentViewer
> viewer
= entry
.GetData()->mViewer
;
943 void TransferShowingState(Document
* aFromDoc
, Document
* aToDoc
) {
944 MOZ_ASSERT(aFromDoc
&& aToDoc
, "transferring showing state from/to null doc");
946 if (aFromDoc
->IsShowing()) {
947 aToDoc
->OnPageShow(true, nullptr);
951 nsresult
ExternalResourceMap::AddExternalResource(nsIURI
* aURI
,
952 nsIContentViewer
* aViewer
,
953 nsILoadGroup
* aLoadGroup
,
954 Document
* aDisplayDocument
) {
955 MOZ_ASSERT(aURI
, "Unexpected call");
956 MOZ_ASSERT((aViewer
&& aLoadGroup
) || (!aViewer
&& !aLoadGroup
),
957 "Must have both or neither");
959 RefPtr
<PendingLoad
> load
;
960 mPendingLoads
.Remove(aURI
, getter_AddRefs(load
));
964 nsCOMPtr
<Document
> doc
;
966 doc
= aViewer
->GetDocument();
967 NS_ASSERTION(doc
, "Must have a document");
969 doc
->SetDisplayDocument(aDisplayDocument
);
971 // Make sure that hiding our viewer will tear down its presentation.
972 aViewer
->SetSticky(false);
974 rv
= aViewer
->Init(nullptr, nsIntRect(0, 0, 0, 0), nullptr);
975 if (NS_SUCCEEDED(rv
)) {
976 rv
= aViewer
->Open(nullptr, nullptr);
982 aLoadGroup
= nullptr;
986 ExternalResource
* newResource
=
987 mMap
.InsertOrUpdate(aURI
, MakeUnique
<ExternalResource
>()).get();
989 newResource
->mDocument
= doc
;
990 newResource
->mViewer
= aViewer
;
991 newResource
->mLoadGroup
= aLoadGroup
;
993 if (nsPresContext
* pc
= doc
->GetPresContext()) {
994 pc
->RecomputeBrowsingContextDependentData();
996 TransferShowingState(aDisplayDocument
, doc
);
999 const nsTArray
<nsCOMPtr
<nsIObserver
>>& obs
= load
->Observers();
1000 for (uint32_t i
= 0; i
< obs
.Length(); ++i
) {
1001 obs
[i
]->Observe(ToSupports(doc
), "external-resource-document-created",
1008 NS_IMPL_ISUPPORTS(ExternalResourceMap::PendingLoad
, nsIStreamListener
,
1012 ExternalResourceMap::PendingLoad::OnStartRequest(nsIRequest
* aRequest
) {
1013 ExternalResourceMap
& map
= mDisplayDocument
->ExternalResourceMap();
1014 if (map
.HaveShutDown()) {
1015 return NS_BINDING_ABORTED
;
1018 nsCOMPtr
<nsIContentViewer
> viewer
;
1019 nsCOMPtr
<nsILoadGroup
> loadGroup
;
1021 SetupViewer(aRequest
, getter_AddRefs(viewer
), getter_AddRefs(loadGroup
));
1023 // Make sure to do this no matter what
1025 map
.AddExternalResource(mURI
, viewer
, loadGroup
, mDisplayDocument
);
1026 if (NS_FAILED(rv
)) {
1029 if (NS_FAILED(rv2
)) {
1030 mTargetListener
= nullptr;
1034 return mTargetListener
->OnStartRequest(aRequest
);
1037 nsresult
ExternalResourceMap::PendingLoad::SetupViewer(
1038 nsIRequest
* aRequest
, nsIContentViewer
** aViewer
,
1039 nsILoadGroup
** aLoadGroup
) {
1040 MOZ_ASSERT(!mTargetListener
, "Unexpected call to OnStartRequest");
1042 *aLoadGroup
= nullptr;
1044 nsCOMPtr
<nsIChannel
> chan(do_QueryInterface(aRequest
));
1045 NS_ENSURE_TRUE(chan
, NS_ERROR_UNEXPECTED
);
1047 nsCOMPtr
<nsIHttpChannel
> httpChannel(do_QueryInterface(aRequest
));
1049 bool requestSucceeded
;
1050 if (NS_FAILED(httpChannel
->GetRequestSucceeded(&requestSucceeded
)) ||
1051 !requestSucceeded
) {
1052 // Bail out on this load, since it looks like we have an HTTP error page
1053 return NS_BINDING_ABORTED
;
1058 chan
->GetContentType(type
);
1060 nsCOMPtr
<nsILoadGroup
> loadGroup
;
1061 chan
->GetLoadGroup(getter_AddRefs(loadGroup
));
1063 // Give this document its own loadgroup
1064 nsCOMPtr
<nsILoadGroup
> newLoadGroup
=
1065 do_CreateInstance(NS_LOADGROUP_CONTRACTID
);
1066 NS_ENSURE_TRUE(newLoadGroup
, NS_ERROR_OUT_OF_MEMORY
);
1067 newLoadGroup
->SetLoadGroup(loadGroup
);
1069 nsCOMPtr
<nsIInterfaceRequestor
> callbacks
;
1070 loadGroup
->GetNotificationCallbacks(getter_AddRefs(callbacks
));
1072 nsCOMPtr
<nsIInterfaceRequestor
> newCallbacks
=
1073 new LoadgroupCallbacks(callbacks
);
1074 newLoadGroup
->SetNotificationCallbacks(newCallbacks
);
1076 // This is some serious hackery cribbed from docshell
1077 nsCOMPtr
<nsICategoryManager
> catMan
=
1078 do_GetService(NS_CATEGORYMANAGER_CONTRACTID
);
1079 NS_ENSURE_TRUE(catMan
, NS_ERROR_NOT_AVAILABLE
);
1080 nsCString contractId
;
1082 catMan
->GetCategoryEntry("Gecko-Content-Viewers", type
, contractId
);
1083 NS_ENSURE_SUCCESS(rv
, rv
);
1084 nsCOMPtr
<nsIDocumentLoaderFactory
> docLoaderFactory
=
1085 do_GetService(contractId
.get());
1086 NS_ENSURE_TRUE(docLoaderFactory
, NS_ERROR_NOT_AVAILABLE
);
1088 nsCOMPtr
<nsIContentViewer
> viewer
;
1089 nsCOMPtr
<nsIStreamListener
> listener
;
1090 rv
= docLoaderFactory
->CreateInstance(
1091 "external-resource", chan
, newLoadGroup
, type
, nullptr, nullptr,
1092 getter_AddRefs(listener
), getter_AddRefs(viewer
));
1093 NS_ENSURE_SUCCESS(rv
, rv
);
1094 NS_ENSURE_TRUE(viewer
, NS_ERROR_UNEXPECTED
);
1096 nsCOMPtr
<nsIParser
> parser
= do_QueryInterface(listener
);
1098 /// We don't want to deal with the various fake documents yet
1099 return NS_ERROR_NOT_IMPLEMENTED
;
1102 // We can't handle HTML and other weird things here yet.
1103 nsIContentSink
* sink
= parser
->GetContentSink();
1104 nsCOMPtr
<nsIXMLContentSink
> xmlSink
= do_QueryInterface(sink
);
1106 return NS_ERROR_NOT_IMPLEMENTED
;
1109 listener
.swap(mTargetListener
);
1110 viewer
.forget(aViewer
);
1111 newLoadGroup
.forget(aLoadGroup
);
1116 ExternalResourceMap::PendingLoad::OnDataAvailable(nsIRequest
* aRequest
,
1117 nsIInputStream
* aStream
,
1120 // mTargetListener might be null if SetupViewer or AddExternalResource failed.
1121 NS_ENSURE_TRUE(mTargetListener
, NS_ERROR_FAILURE
);
1122 if (mDisplayDocument
->ExternalResourceMap().HaveShutDown()) {
1123 return NS_BINDING_ABORTED
;
1125 return mTargetListener
->OnDataAvailable(aRequest
, aStream
, aOffset
, aCount
);
1129 ExternalResourceMap::PendingLoad::OnStopRequest(nsIRequest
* aRequest
,
1131 // mTargetListener might be null if SetupViewer or AddExternalResource failed
1132 if (mTargetListener
) {
1133 nsCOMPtr
<nsIStreamListener
> listener
;
1134 mTargetListener
.swap(listener
);
1135 return listener
->OnStopRequest(aRequest
, aStatus
);
1141 nsresult
ExternalResourceMap::PendingLoad::StartLoad(
1142 nsIURI
* aURI
, nsIReferrerInfo
* aReferrerInfo
, nsINode
* aRequestingNode
) {
1143 MOZ_ASSERT(aURI
, "Must have a URI");
1144 MOZ_ASSERT(aRequestingNode
, "Must have a node");
1145 MOZ_ASSERT(aReferrerInfo
, "Must have a referrerInfo");
1147 nsCOMPtr
<nsILoadGroup
> loadGroup
=
1148 aRequestingNode
->OwnerDoc()->GetDocumentLoadGroup();
1150 nsresult rv
= NS_OK
;
1151 nsCOMPtr
<nsIChannel
> channel
;
1152 rv
= NS_NewChannel(getter_AddRefs(channel
), aURI
, aRequestingNode
,
1153 nsILoadInfo::SEC_REQUIRE_SAME_ORIGIN_INHERITS_SEC_CONTEXT
,
1154 nsIContentPolicy::TYPE_OTHER
,
1155 nullptr, // aPerformanceStorage
1157 NS_ENSURE_SUCCESS(rv
, rv
);
1159 nsCOMPtr
<nsIHttpChannel
> httpChannel(do_QueryInterface(channel
));
1161 rv
= httpChannel
->SetReferrerInfo(aReferrerInfo
);
1162 Unused
<< NS_WARN_IF(NS_FAILED(rv
));
1167 return channel
->AsyncOpen(this);
1170 NS_IMPL_ISUPPORTS(ExternalResourceMap::LoadgroupCallbacks
,
1171 nsIInterfaceRequestor
)
1173 #define IMPL_SHIM(_i) \
1174 NS_IMPL_ISUPPORTS(ExternalResourceMap::LoadgroupCallbacks::_i##Shim, _i)
1176 IMPL_SHIM(nsILoadContext
)
1177 IMPL_SHIM(nsIProgressEventSink
)
1178 IMPL_SHIM(nsIChannelEventSink
)
1182 #define IID_IS(_i) aIID.Equals(NS_GET_IID(_i))
1184 #define TRY_SHIM(_i) \
1187 nsCOMPtr<_i> real = do_GetInterface(mCallbacks); \
1189 return NS_NOINTERFACE; \
1191 nsCOMPtr<_i> shim = new _i##Shim(this, real); \
1192 shim.forget(aSink); \
1198 ExternalResourceMap::LoadgroupCallbacks::GetInterface(const nsIID
& aIID
,
1200 if (mCallbacks
&& (IID_IS(nsIPrompt
) || IID_IS(nsIAuthPrompt
) ||
1201 IID_IS(nsIAuthPrompt2
) || IID_IS(nsIBrowserChild
))) {
1202 return mCallbacks
->GetInterface(aIID
, aSink
);
1207 TRY_SHIM(nsILoadContext
);
1208 TRY_SHIM(nsIProgressEventSink
);
1209 TRY_SHIM(nsIChannelEventSink
);
1211 return NS_NOINTERFACE
;
1217 ExternalResourceMap::ExternalResource::~ExternalResource() {
1219 mViewer
->Close(nullptr);
1224 // ==================================================================
1226 // ==================================================================
1228 // If we ever have an nsIDocumentObserver notification for stylesheet title
1229 // changes we should update the list from that instead of overriding
1231 class DOMStyleSheetSetList final
: public DOMStringList
{
1233 explicit DOMStyleSheetSetList(Document
* aDocument
);
1235 void Disconnect() { mDocument
= nullptr; }
1237 virtual void EnsureFresh() override
;
1240 Document
* mDocument
; // Our document; weak ref. It'll let us know if it
1244 DOMStyleSheetSetList::DOMStyleSheetSetList(Document
* aDocument
)
1245 : mDocument(aDocument
) {
1246 NS_ASSERTION(mDocument
, "Must have document!");
1249 void DOMStyleSheetSetList::EnsureFresh() {
1250 MOZ_ASSERT(NS_IsMainThread());
1255 return; // Spec says "no exceptions", and we have no style sets if we have
1256 // no document, for sure
1259 size_t count
= mDocument
->SheetCount();
1261 for (size_t index
= 0; index
< count
; index
++) {
1262 StyleSheet
* sheet
= mDocument
->SheetAt(index
);
1263 NS_ASSERTION(sheet
, "Null sheet in sheet list!");
1264 sheet
->GetTitle(title
);
1265 if (!title
.IsEmpty() && !mNames
.Contains(title
) && !Add(title
)) {
1271 // ==================================================================
1272 Document::SelectorCache::SelectorCache(nsIEventTarget
* aEventTarget
)
1273 : nsExpirationTracker
<SelectorCacheKey
, 4>(1000, "Document::SelectorCache",
1276 Document::SelectorCache::~SelectorCache() { AgeAllGenerations(); }
1278 void Document::SelectorCache::NotifyExpired(SelectorCacheKey
* aSelector
) {
1279 MOZ_ASSERT(NS_IsMainThread());
1280 MOZ_ASSERT(aSelector
);
1282 // There is no guarantee that this method won't be re-entered when selector
1283 // matching is ongoing because "memory-pressure" could be notified immediately
1284 // when OOM happens according to the design of nsExpirationTracker.
1285 // The perfect solution is to delete the |aSelector| and its
1286 // RawServoSelectorList in mTable asynchronously.
1287 // We remove these objects synchronously for now because NotifyExpired() will
1288 // never be triggered by "memory-pressure" which is not implemented yet in
1289 // the stage 2 of mozalloc_handle_oom().
1290 // Once these objects are removed asynchronously, we should update the warning
1291 // added in mozalloc_handle_oom() as well.
1292 RemoveObject(aSelector
);
1293 mTable
.Remove(aSelector
->mKey
);
1297 Document::PendingFrameStaticClone::~PendingFrameStaticClone() = default;
1299 // ==================================================================
1301 // ==================================================================
1303 Document::InternalCommandDataHashtable
*
1304 Document::sInternalCommandDataHashtable
= nullptr;
1307 void Document::Shutdown() {
1308 if (sInternalCommandDataHashtable
) {
1309 sInternalCommandDataHashtable
->Clear();
1310 delete sInternalCommandDataHashtable
;
1311 sInternalCommandDataHashtable
= nullptr;
1315 Document::Document(const char* aContentType
)
1317 DocumentOrShadowRoot(this),
1318 mCharacterSet(WINDOWS_1252_ENCODING
),
1319 mCharacterSetSource(0),
1320 mParentDocument(nullptr),
1321 mCachedRootElement(nullptr),
1322 mNodeInfoManager(nullptr),
1324 mStyledLinksCleared(false),
1326 mCachedStateObjectValid(false),
1327 mBlockAllMixedContent(false),
1328 mBlockAllMixedContentPreloads(false),
1329 mUpgradeInsecureRequests(false),
1330 mUpgradeInsecurePreloads(false),
1331 mDevToolsWatchingDOMMutations(false),
1332 mBidiEnabled(false),
1333 mMayNeedFontPrefsUpdate(true),
1334 mMathMLEnabled(false),
1335 mIsInitialDocumentInWindow(false),
1336 mIgnoreDocGroupMismatches(false),
1337 mLoadedAsData(false),
1338 mAddedToMemoryReportingAsDataDocument(false),
1339 mMayStartLayout(true),
1340 mHaveFiredTitleChange(false),
1343 mRemovedFromDocShell(false),
1344 // mAllowDNSPrefetch starts true, so that we can always reliably && it
1345 // with various values that might disable it. Since we never prefetch
1346 // unless we get a window, and in that case the docshell value will get
1347 // &&-ed in, this is safe.
1348 mAllowDNSPrefetch(true),
1349 mIsStaticDocument(false),
1350 mCreatingStaticClone(false),
1351 mHasPrintCallbacks(false),
1352 mInUnlinkOrDeletion(false),
1353 mHasHadScriptHandlingObject(false),
1354 mIsBeingUsedAsImage(false),
1355 mChromeRulesEnabled(false),
1356 mInChromeDocShell(false),
1357 mIsDevToolsDocument(false),
1358 mIsSyntheticDocument(false),
1359 mHasLinksToUpdateRunnable(false),
1360 mFlushingPendingLinkUpdates(false),
1361 mMayHaveDOMMutationObservers(false),
1362 mMayHaveAnimationObservers(false),
1364 mHasUnsafeEvalCSP(false),
1365 mHasUnsafeInlineCSP(false),
1366 mHasCSPDeliveredThroughHeader(false),
1367 mBFCacheDisallowed(false),
1368 mHasHadDefaultView(false),
1369 mStyleSheetChangeEventsEnabled(false),
1370 mDevToolsAnonymousAndShadowEventsEnabled(false),
1371 mIsSrcdocDocument(false),
1372 mHasDisplayDocument(false),
1373 mFontFaceSetDirty(true),
1374 mDidFireDOMContentLoaded(true),
1375 mFrameRequestCallbacksScheduled(false),
1376 mIsTopLevelContentDocument(false),
1377 mIsContentDocument(false),
1378 mDidCallBeginLoad(false),
1379 mEncodingMenuDisabled(false),
1380 mLinksEnabled(true),
1381 mIsSVGGlyphsDocument(false),
1382 mInDestructor(false),
1383 mIsGoingAway(false),
1384 mInXBLUpdate(false),
1385 mStyleSetFilled(false),
1386 mQuirkSheetAdded(false),
1387 mContentEditableSheetAdded(false),
1388 mDesignModeSheetAdded(false),
1389 mSSApplicableStateNotificationPending(false),
1390 mMayHaveTitleElement(false),
1391 mDOMLoadingSet(false),
1392 mDOMInteractiveSet(false),
1393 mDOMCompleteSet(false),
1394 mAutoFocusFired(false),
1395 mScrolledToRefAlready(false),
1396 mChangeScrollPosWhenScrollingToRef(false),
1397 mDelayFrameLoaderInitialization(false),
1398 mSynchronousDOMContentLoaded(false),
1399 mMaybeServiceWorkerControlled(false),
1401 mValidScaleFloat(false),
1402 mValidMinScale(false),
1403 mValidMaxScale(false),
1404 mWidthStrEmpty(false),
1405 mParserAborted(false),
1406 mReportedDocumentUseCounters(false),
1407 mHasReportedShadowDOMUsage(false),
1408 mHasDelayedRefreshEvent(false),
1409 mLoadEventFiring(false),
1410 mSkipLoadEventAfterClose(false),
1411 mDisableCookieAccess(false),
1412 mDisableDocWrite(false),
1413 mTooDeepWriteRecursion(false),
1414 mPendingMaybeEditingStateChanged(false),
1415 mHasBeenEditable(false),
1416 mHasWarnedAboutZoom(false),
1417 mIsRunningExecCommand(false),
1418 mSetCompleteAfterDOMContentLoaded(false),
1419 mDidHitCompleteSheetCache(false),
1420 mUseCountersInitialized(false),
1421 mShouldReportUseCounters(false),
1422 mShouldSendPageUseCounters(false),
1423 mUserHasInteracted(false),
1424 mHasUserInteractionTimerScheduled(false),
1425 mShouldResistFingerprinting(false),
1426 mXMLDeclarationBits(0),
1427 mOnloadBlockCount(0),
1429 mContentEditableCount(0),
1430 mEditingState(EditingState::eOff
),
1431 mCompatMode(eCompatibility_FullStandards
),
1432 mReadyState(ReadyState::READYSTATE_UNINITIALIZED
),
1433 mAncestorIsLoading(false),
1434 mVisibilityState(dom::VisibilityState::Hidden
),
1436 mDefaultElementType(0),
1437 mAllowXULXBL(eTriUnset
),
1438 mSkipDTDSecurityChecks(false),
1439 mBidiOptions(IBMBIDI_DEFAULT_BIDI_OPTIONS
),
1442 mMarkedCCGeneration(0),
1443 mPresShell(nullptr),
1444 mSubtreeModifiedDepth(0),
1445 mPreloadPictureDepth(0),
1446 mEventsSuppressed(0),
1447 mIgnoreDestructiveWritesCounter(0),
1448 mStaticCloneCount(0),
1450 mBFCacheEntry(nullptr),
1451 mInSyncOperationCount(0),
1452 mBlockDOMContentLoaded(0),
1453 mUpdateNestLevel(0),
1454 mHttpsOnlyStatus(nsILoadInfo::HTTPS_ONLY_UNINITIALIZED
),
1455 mViewportType(Unknown
),
1456 mViewportFit(ViewportFitType::Auto
),
1457 mSubDocuments(nullptr),
1458 mHeaderData(nullptr),
1459 mServoRestyleRootDirtyBits(0),
1460 mThrowOnDynamicMarkupInsertionCounter(0),
1461 mIgnoreOpensDuringUnloadCounter(0),
1462 mSavedResolution(1.0f
),
1463 mSavedResolutionBeforeMVM(1.0f
),
1465 mCachedTabSizeGeneration(0),
1467 mNextControlNumber(0),
1468 mPreloadService(this),
1469 mShouldNotifyFetchSuccess(false),
1470 mShouldNotifyFormOrPasswordRemoved(false) {
1471 MOZ_LOG(gDocumentLeakPRLog
, LogLevel::Debug
, ("DOCUMENT %p created", this));
1474 SetIsConnected(true);
1476 // Create these unconditionally, they will be used to warn about the `zoom`
1477 // property, even if use counters are disabled.
1478 mStyleUseCounters
.reset(Servo_UseCounters_Create());
1480 SetContentType(nsDependentCString(aContentType
));
1482 // Start out mLastStyleSheetSet as null, per spec
1483 SetDOMStringToNull(mLastStyleSheetSet
);
1485 // void state used to differentiate an empty source from an unselected source
1486 mPreloadPictureFoundSource
.SetIsVoid(true);
1488 RecomputeLanguageFromCharset();
1490 mPreloadReferrerInfo
= new dom::ReferrerInfo(nullptr);
1491 mReferrerInfo
= new dom::ReferrerInfo(nullptr);
1495 // unused by GeckoView
1496 static bool IsAboutErrorPage(nsGlobalWindowInner
* aWin
, const char* aSpec
) {
1497 if (NS_WARN_IF(!aWin
)) {
1501 nsIURI
* uri
= aWin
->GetDocumentURI();
1502 if (NS_WARN_IF(!uri
)) {
1505 // getSpec is an expensive operation, hence we first check the scheme
1506 // to see if the caller is actually an about: page.
1507 if (!uri
->SchemeIs("about")) {
1511 nsAutoCString aboutSpec
;
1512 nsresult rv
= NS_GetAboutModuleName(uri
, aboutSpec
);
1513 NS_ENSURE_SUCCESS(rv
, false);
1515 return aboutSpec
.EqualsASCII(aSpec
);
1519 bool Document::CallerIsTrustedAboutNetError(JSContext
* aCx
, JSObject
* aObject
) {
1520 nsGlobalWindowInner
* win
= xpc::WindowOrNull(aObject
);
1522 // GeckoView uses data URLs for error pages, so for now just check for any
1524 return win
&& win
->GetDocument() && win
->GetDocument()->IsErrorPage();
1526 return win
&& IsAboutErrorPage(win
, "neterror");
1530 bool Document::CallerIsTrustedAboutHttpsOnlyError(JSContext
* aCx
,
1531 JSObject
* aObject
) {
1532 nsGlobalWindowInner
* win
= xpc::WindowOrNull(aObject
);
1534 // GeckoView uses data URLs for error pages, so for now just check for any
1536 return win
&& win
->GetDocument() && win
->GetDocument()->IsErrorPage();
1538 return win
&& IsAboutErrorPage(win
, "httpsonlyerror");
1542 already_AddRefed
<mozilla::dom::Promise
> Document::AddCertException(
1543 bool aIsTemporary
, ErrorResult
& aError
) {
1544 RefPtr
<Promise
> promise
= Promise::Create(GetScopeObject(), aError
,
1545 Promise::ePropagateUserInteraction
);
1546 if (aError
.Failed()) {
1550 nsresult rv
= NS_OK
;
1551 if (NS_WARN_IF(!mFailedChannel
)) {
1552 promise
->MaybeReject(NS_ERROR_DOM_INVALID_STATE_ERR
);
1553 return promise
.forget();
1556 nsCOMPtr
<nsIURI
> failedChannelURI
;
1557 NS_GetFinalChannelURI(mFailedChannel
, getter_AddRefs(failedChannelURI
));
1558 if (!failedChannelURI
) {
1559 promise
->MaybeReject(NS_ERROR_DOM_INVALID_STATE_ERR
);
1560 return promise
.forget();
1563 nsCOMPtr
<nsIURI
> innerURI
= NS_GetInnermostURI(failedChannelURI
);
1565 promise
->MaybeReject(NS_ERROR_DOM_INVALID_STATE_ERR
);
1566 return promise
.forget();
1570 innerURI
->GetAsciiHost(host
);
1572 innerURI
->GetPort(&port
);
1574 nsCOMPtr
<nsITransportSecurityInfo
> tsi
;
1575 rv
= mFailedChannel
->GetSecurityInfo(getter_AddRefs(tsi
));
1576 if (NS_WARN_IF(NS_FAILED(rv
))) {
1577 promise
->MaybeReject(rv
);
1578 return promise
.forget();
1580 if (NS_WARN_IF(!tsi
)) {
1581 promise
->MaybeReject(NS_ERROR_DOM_INVALID_STATE_ERR
);
1582 return promise
.forget();
1585 nsCOMPtr
<nsIX509Cert
> cert
;
1586 rv
= tsi
->GetServerCert(getter_AddRefs(cert
));
1587 if (NS_WARN_IF(NS_FAILED(rv
))) {
1588 promise
->MaybeReject(rv
);
1589 return promise
.forget();
1591 if (NS_WARN_IF(!cert
)) {
1592 promise
->MaybeReject(NS_ERROR_DOM_INVALID_STATE_ERR
);
1593 return promise
.forget();
1596 if (XRE_IsContentProcess()) {
1597 ContentChild
* cc
= ContentChild::GetSingleton();
1599 OriginAttributes
const& attrs
= NodePrincipal()->OriginAttributesRef();
1600 cc
->SendAddCertException(cert
, host
, port
, attrs
, aIsTemporary
)
1601 ->Then(GetCurrentSerialEventTarget(), __func__
,
1602 [promise
](const mozilla::MozPromise
<
1603 nsresult
, mozilla::ipc::ResponseRejectReason
,
1604 true>::ResolveOrRejectValue
& aValue
) {
1605 if (aValue
.IsResolve()) {
1606 promise
->MaybeResolve(aValue
.ResolveValue());
1608 promise
->MaybeRejectWithUndefined();
1611 return promise
.forget();
1614 if (XRE_IsParentProcess()) {
1615 nsCOMPtr
<nsICertOverrideService
> overrideService
=
1616 do_GetService(NS_CERTOVERRIDE_CONTRACTID
);
1617 if (!overrideService
) {
1618 promise
->MaybeReject(NS_ERROR_FAILURE
);
1619 return promise
.forget();
1622 OriginAttributes
const& attrs
= NodePrincipal()->OriginAttributesRef();
1623 rv
= overrideService
->RememberValidityOverride(host
, port
, attrs
, cert
,
1625 if (NS_WARN_IF(NS_FAILED(rv
))) {
1626 promise
->MaybeReject(rv
);
1627 return promise
.forget();
1630 promise
->MaybeResolveWithUndefined();
1631 return promise
.forget();
1634 promise
->MaybeReject(NS_ERROR_FAILURE
);
1635 return promise
.forget();
1638 void Document::ReloadWithHttpsOnlyException() {
1639 if (WindowGlobalChild
* wgc
= GetWindowGlobalChild()) {
1640 wgc
->SendReloadWithHttpsOnlyException();
1644 void Document::GetNetErrorInfo(NetErrorInfo
& aInfo
, ErrorResult
& aRv
) {
1645 nsresult rv
= NS_OK
;
1646 if (NS_WARN_IF(!mFailedChannel
)) {
1647 aRv
.Throw(NS_ERROR_DOM_INVALID_STATE_ERR
);
1651 nsCOMPtr
<nsITransportSecurityInfo
> tsi
;
1652 rv
= mFailedChannel
->GetSecurityInfo(getter_AddRefs(tsi
));
1653 if (NS_WARN_IF(NS_FAILED(rv
))) {
1657 if (NS_WARN_IF(!tsi
)) {
1658 aRv
.Throw(NS_ERROR_DOM_INVALID_STATE_ERR
);
1662 nsAutoString errorCodeString
;
1663 rv
= tsi
->GetErrorCodeString(errorCodeString
);
1664 if (NS_WARN_IF(NS_FAILED(rv
))) {
1668 aInfo
.mErrorCodeString
.Assign(errorCodeString
);
1671 bool Document::CallerIsTrustedAboutCertError(JSContext
* aCx
,
1672 JSObject
* aObject
) {
1673 nsGlobalWindowInner
* win
= xpc::WindowOrNull(aObject
);
1675 // GeckoView uses data URLs for error pages, so for now just check for any
1677 return win
&& win
->GetDocument() && win
->GetDocument()->IsErrorPage();
1679 return win
&& IsAboutErrorPage(win
, "certerror");
1683 bool Document::CallerCanAccessPrivilegeSSA(JSContext
* aCx
, JSObject
* aObject
) {
1684 RefPtr
<BasePrincipal
> principal
=
1685 BasePrincipal::Cast(nsContentUtils::SubjectPrincipal(aCx
));
1691 // We allow the privilege SSA to be called from system principal.
1692 if (principal
->IsSystemPrincipal()) {
1696 // We only allow calling the privilege SSA from the content script of the
1697 // webcompat extension.
1698 if (auto* policy
= principal
->ContentScriptAddonPolicy()) {
1699 nsAutoString addonID
;
1700 policy
->GetId(addonID
);
1702 return addonID
.EqualsLiteral("webcompat@mozilla.org");
1708 bool Document::IsErrorPage() const {
1709 nsCOMPtr
<nsILoadInfo
> loadInfo
= mChannel
? mChannel
->LoadInfo() : nullptr;
1710 return loadInfo
&& loadInfo
->GetLoadErrorPage();
1713 void Document::GetFailedCertSecurityInfo(FailedCertSecurityInfo
& aInfo
,
1715 nsresult rv
= NS_OK
;
1716 if (NS_WARN_IF(!mFailedChannel
)) {
1717 aRv
.Throw(NS_ERROR_DOM_INVALID_STATE_ERR
);
1721 nsCOMPtr
<nsITransportSecurityInfo
> tsi
;
1722 rv
= mFailedChannel
->GetSecurityInfo(getter_AddRefs(tsi
));
1723 if (NS_WARN_IF(NS_FAILED(rv
))) {
1727 if (NS_WARN_IF(!tsi
)) {
1728 aRv
.Throw(NS_ERROR_DOM_INVALID_STATE_ERR
);
1732 nsAutoString errorCodeString
;
1733 rv
= tsi
->GetErrorCodeString(errorCodeString
);
1734 if (NS_WARN_IF(NS_FAILED(rv
))) {
1738 aInfo
.mErrorCodeString
.Assign(errorCodeString
);
1740 nsITransportSecurityInfo::OverridableErrorCategory errorCategory
;
1741 rv
= tsi
->GetOverridableErrorCategory(&errorCategory
);
1742 if (NS_WARN_IF(NS_FAILED(rv
))) {
1746 switch (errorCategory
) {
1747 case nsITransportSecurityInfo::OverridableErrorCategory::ERROR_TRUST
:
1748 aInfo
.mOverridableErrorCategory
=
1749 dom::OverridableErrorCategory::Trust_error
;
1751 case nsITransportSecurityInfo::OverridableErrorCategory::ERROR_DOMAIN
:
1752 aInfo
.mOverridableErrorCategory
=
1753 dom::OverridableErrorCategory::Domain_mismatch
;
1755 case nsITransportSecurityInfo::OverridableErrorCategory::ERROR_TIME
:
1756 aInfo
.mOverridableErrorCategory
=
1757 dom::OverridableErrorCategory::Expired_or_not_yet_valid
;
1760 aInfo
.mOverridableErrorCategory
= dom::OverridableErrorCategory::Unset
;
1764 nsCOMPtr
<nsIX509Cert
> cert
;
1765 nsCOMPtr
<nsIX509CertValidity
> validity
;
1766 rv
= tsi
->GetServerCert(getter_AddRefs(cert
));
1767 if (NS_WARN_IF(NS_FAILED(rv
))) {
1771 if (NS_WARN_IF(!cert
)) {
1772 aRv
.Throw(NS_ERROR_DOM_INVALID_STATE_ERR
);
1776 rv
= cert
->GetValidity(getter_AddRefs(validity
));
1777 if (NS_WARN_IF(NS_FAILED(rv
))) {
1781 if (NS_WARN_IF(!validity
)) {
1782 aRv
.Throw(NS_ERROR_DOM_INVALID_STATE_ERR
);
1786 PRTime validityResult
;
1787 rv
= validity
->GetNotBefore(&validityResult
);
1788 if (NS_WARN_IF(NS_FAILED(rv
))) {
1792 aInfo
.mValidNotBefore
= DOMTimeStamp(validityResult
/ PR_USEC_PER_MSEC
);
1794 rv
= validity
->GetNotAfter(&validityResult
);
1795 if (NS_WARN_IF(NS_FAILED(rv
))) {
1799 aInfo
.mValidNotAfter
= DOMTimeStamp(validityResult
/ PR_USEC_PER_MSEC
);
1801 nsAutoString issuerCommonName
;
1802 nsAutoString certChainPEMString
;
1803 Sequence
<nsString
>& certChainStrings
= aInfo
.mCertChainStrings
.Construct();
1804 int64_t maxValidity
= std::numeric_limits
<int64_t>::max();
1805 int64_t minValidity
= 0;
1806 PRTime notBefore
, notAfter
;
1807 nsTArray
<RefPtr
<nsIX509Cert
>> failedCertArray
;
1808 rv
= tsi
->GetFailedCertChain(failedCertArray
);
1809 if (NS_WARN_IF(NS_FAILED(rv
))) {
1814 if (NS_WARN_IF(failedCertArray
.IsEmpty())) {
1815 aRv
.Throw(NS_ERROR_DOM_INVALID_STATE_ERR
);
1819 for (const auto& certificate
: failedCertArray
) {
1820 rv
= certificate
->GetIssuerCommonName(issuerCommonName
);
1821 if (NS_WARN_IF(NS_FAILED(rv
))) {
1826 rv
= certificate
->GetValidity(getter_AddRefs(validity
));
1827 if (NS_WARN_IF(NS_FAILED(rv
))) {
1831 if (NS_WARN_IF(!validity
)) {
1832 aRv
.Throw(NS_ERROR_DOM_INVALID_STATE_ERR
);
1836 rv
= validity
->GetNotBefore(¬Before
);
1837 if (NS_WARN_IF(NS_FAILED(rv
))) {
1842 rv
= validity
->GetNotAfter(¬After
);
1843 if (NS_WARN_IF(NS_FAILED(rv
))) {
1848 notBefore
= std::max(minValidity
, notBefore
);
1849 notAfter
= std::min(maxValidity
, notAfter
);
1850 nsTArray
<uint8_t> certArray
;
1851 rv
= certificate
->GetRawDER(certArray
);
1852 if (NS_WARN_IF(NS_FAILED(rv
))) {
1858 rv
= Base64Encode(reinterpret_cast<const char*>(certArray
.Elements()),
1859 certArray
.Length(), der64
);
1860 if (NS_WARN_IF(NS_FAILED(rv
))) {
1864 if (!certChainStrings
.AppendElement(der64
, fallible
)) {
1865 aRv
.Throw(NS_ERROR_OUT_OF_MEMORY
);
1870 aInfo
.mIssuerCommonName
.Assign(issuerCommonName
);
1871 aInfo
.mCertValidityRangeNotAfter
= DOMTimeStamp(notAfter
/ PR_USEC_PER_MSEC
);
1872 aInfo
.mCertValidityRangeNotBefore
=
1873 DOMTimeStamp(notBefore
/ PR_USEC_PER_MSEC
);
1876 rv
= tsi
->GetErrorCode(&errorCode
);
1877 if (NS_WARN_IF(NS_FAILED(rv
))) {
1882 nsCOMPtr
<nsINSSErrorsService
> nsserr
=
1883 do_GetService("@mozilla.org/nss_errors_service;1");
1884 if (NS_WARN_IF(!nsserr
)) {
1885 aRv
.Throw(NS_ERROR_DOM_INVALID_STATE_ERR
);
1889 rv
= nsserr
->GetXPCOMFromNSSError(errorCode
, &res
);
1890 if (NS_WARN_IF(NS_FAILED(rv
))) {
1894 rv
= nsserr
->GetErrorMessage(res
, aInfo
.mErrorMessage
);
1895 if (NS_WARN_IF(NS_FAILED(rv
))) {
1900 OriginAttributes attrs
;
1901 StoragePrincipalHelper::GetRegularPrincipalOriginAttributes(this, attrs
);
1902 nsCOMPtr
<nsIURI
> aURI
;
1903 mFailedChannel
->GetURI(getter_AddRefs(aURI
));
1904 if (XRE_IsContentProcess()) {
1905 ContentChild
* cc
= ContentChild::GetSingleton();
1907 cc
->SendIsSecureURI(aURI
, attrs
, &aInfo
.mHasHSTS
);
1909 nsCOMPtr
<nsISiteSecurityService
> sss
=
1910 do_GetService(NS_SSSERVICE_CONTRACTID
);
1911 if (NS_WARN_IF(!sss
)) {
1914 Unused
<< NS_WARN_IF(
1915 NS_FAILED(sss
->IsSecureURI(aURI
, attrs
, &aInfo
.mHasHSTS
)));
1917 nsCOMPtr
<nsIPublicKeyPinningService
> pkps
=
1918 do_GetService(NS_PKPSERVICE_CONTRACTID
);
1919 if (NS_WARN_IF(!pkps
)) {
1922 Unused
<< NS_WARN_IF(NS_FAILED(pkps
->HostHasPins(aURI
, &aInfo
.mHasHPKP
)));
1925 bool Document::IsAboutPage() const {
1926 return NodePrincipal()->SchemeIs("about");
1929 void Document::ConstructUbiNode(void* storage
) {
1930 JS::ubi::Concrete
<Document
>::construct(storage
, this);
1933 void Document::LoadEventFired() {
1934 // Object used to collect some telemetry data so we don't need to query for it
1936 glean::perf::PageLoadExtra pageLoadEventData
;
1938 // Accumulate timing data located in each document's realm and report to
1940 AccumulateJSTelemetry(pageLoadEventData
);
1942 // Collect page load timings
1943 AccumulatePageLoadTelemetry(pageLoadEventData
);
1945 // Record page load event
1946 RecordPageLoadEventTelemetry(pageLoadEventData
);
1948 // Release the JS bytecode cache from its wait on the load event, and
1949 // potentially dispatch the encoding of the bytecode.
1950 if (ScriptLoader()) {
1951 ScriptLoader()->LoadEventFired();
1955 static uint32_t ConvertToUnsignedFromDouble(double aNumber
) {
1956 return aNumber
< 0 ? 0 : static_cast<uint32_t>(aNumber
);
1959 void Document::RecordPageLoadEventTelemetry(
1960 glean::perf::PageLoadExtra
& aEventTelemetryData
) {
1961 // If the page load time is empty, then the content wasn't something we want
1962 // to report (i.e. not a top level document).
1963 if (!aEventTelemetryData
.loadTime
) {
1966 MOZ_ASSERT(IsTopLevelContentDocument());
1968 nsPIDOMWindowOuter
* window
= GetWindow();
1973 nsIDocShell
* docshell
= window
->GetDocShell();
1978 nsAutoCString loadTypeStr
;
1979 switch (docshell
->GetLoadType()) {
1981 case LOAD_NORMAL_REPLACE
:
1982 case LOAD_NORMAL_BYPASS_CACHE
:
1983 case LOAD_NORMAL_BYPASS_PROXY
:
1984 case LOAD_NORMAL_BYPASS_PROXY_AND_CACHE
:
1985 loadTypeStr
.Append("NORMAL");
1988 loadTypeStr
.Append("HISTORY");
1990 case LOAD_RELOAD_NORMAL
:
1991 case LOAD_RELOAD_BYPASS_CACHE
:
1992 case LOAD_RELOAD_BYPASS_PROXY
:
1993 case LOAD_RELOAD_BYPASS_PROXY_AND_CACHE
:
1995 case LOAD_REFRESH_REPLACE
:
1996 case LOAD_RELOAD_CHARSET_CHANGE
:
1997 case LOAD_RELOAD_CHARSET_CHANGE_BYPASS_PROXY_AND_CACHE
:
1998 case LOAD_RELOAD_CHARSET_CHANGE_BYPASS_CACHE
:
1999 loadTypeStr
.Append("RELOAD");
2002 loadTypeStr
.Append("LINK");
2004 case LOAD_STOP_CONTENT
:
2005 case LOAD_STOP_CONTENT_AND_REPLACE
:
2006 loadTypeStr
.Append("STOP");
2008 case LOAD_ERROR_PAGE
:
2009 loadTypeStr
.Append("ERROR");
2012 loadTypeStr
.Append("OTHER");
2016 nsCOMPtr
<nsIEffectiveTLDService
> tldService
=
2017 do_GetService(NS_EFFECTIVETLDSERVICE_CONTRACTID
);
2018 if (tldService
&& mReferrerInfo
&&
2019 (docshell
->GetLoadType() & nsIDocShell::LOAD_CMD_NORMAL
)) {
2020 nsAutoCString currentBaseDomain
, referrerBaseDomain
;
2021 nsCOMPtr
<nsIURI
> referrerURI
= mReferrerInfo
->GetComputedReferrer();
2023 auto result
= NS_SUCCEEDED(
2024 tldService
->GetBaseDomain(referrerURI
, 0, referrerBaseDomain
));
2026 bool sameOrigin
= false;
2027 NodePrincipal()->IsSameOrigin(referrerURI
, &sameOrigin
);
2028 aEventTelemetryData
.sameOriginNav
= mozilla::Some(sameOrigin
);
2033 aEventTelemetryData
.loadType
= mozilla::Some(loadTypeStr
);
2035 // Sending a glean ping must be done on the parent process.
2036 if (ContentChild
* cc
= ContentChild::GetSingleton()) {
2037 cc
->SendRecordPageLoadEvent(aEventTelemetryData
);
2041 void Document::AccumulatePageLoadTelemetry(
2042 glean::perf::PageLoadExtra
& aEventTelemetryDataOut
) {
2043 // Interested only in top level documents for real websites that are in the
2045 if (!ShouldIncludeInTelemetry(false) || !IsTopLevelContentDocument() ||
2046 !GetNavigationTiming() ||
2047 !GetNavigationTiming()->DocShellHasBeenActiveSinceNavigationStart()) {
2051 if (!GetChannel()) {
2055 nsCOMPtr
<nsITimedChannel
> timedChannel(do_QueryInterface(GetChannel()));
2056 if (!timedChannel
) {
2060 // Default duration is 0, use this to check for bogus negative values.
2061 const TimeDuration zeroDuration
;
2063 TimeStamp responseStart
;
2064 timedChannel
->GetResponseStart(&responseStart
);
2066 TimeStamp redirectStart
, redirectEnd
;
2067 timedChannel
->GetRedirectStart(&redirectStart
);
2068 timedChannel
->GetRedirectEnd(&redirectEnd
);
2070 uint8_t redirectCount
;
2071 timedChannel
->GetRedirectCount(&redirectCount
);
2072 if (redirectCount
) {
2073 aEventTelemetryDataOut
.redirectCount
=
2074 mozilla::Some(static_cast<uint32_t>(redirectCount
));
2077 if (!redirectStart
.IsNull() && !redirectEnd
.IsNull()) {
2078 TimeDuration redirectTime
= redirectEnd
- redirectStart
;
2079 if (redirectTime
> zeroDuration
) {
2080 aEventTelemetryDataOut
.redirectTime
=
2081 mozilla::Some(static_cast<uint32_t>(redirectTime
.ToMilliseconds()));
2085 TimeStamp dnsLookupStart
, dnsLookupEnd
;
2086 timedChannel
->GetDomainLookupStart(&dnsLookupStart
);
2087 timedChannel
->GetDomainLookupEnd(&dnsLookupEnd
);
2089 if (!dnsLookupStart
.IsNull() && !dnsLookupEnd
.IsNull()) {
2090 TimeDuration dnsLookupTime
= dnsLookupEnd
- dnsLookupStart
;
2091 if (dnsLookupTime
> zeroDuration
) {
2092 aEventTelemetryDataOut
.dnsLookupTime
=
2093 mozilla::Some(static_cast<uint32_t>(dnsLookupTime
.ToMilliseconds()));
2097 TimeStamp navigationStart
=
2098 GetNavigationTiming()->GetNavigationStartTimeStamp();
2100 if (!responseStart
|| !navigationStart
) {
2104 nsAutoCString
dnsKey("Native");
2105 nsAutoCString http3Key
;
2106 nsAutoCString http3WithPriorityKey
;
2107 nsAutoCString earlyHintKey
;
2108 nsCOMPtr
<nsIHttpChannelInternal
> httpChannel
=
2109 do_QueryInterface(GetChannel());
2111 bool resolvedByTRR
= false;
2112 Unused
<< httpChannel
->GetIsResolvedByTRR(&resolvedByTRR
);
2113 if (resolvedByTRR
) {
2114 if (nsCOMPtr
<nsIDNSService
> dns
=
2115 do_GetService(NS_DNSSERVICE_CONTRACTID
)) {
2116 dns
->GetTRRDomainKey(dnsKey
);
2118 // Failed to get the DNS service.
2119 dnsKey
= "(fail)"_ns
;
2121 aEventTelemetryDataOut
.trrDomain
= mozilla::Some(dnsKey
);
2126 if (NS_SUCCEEDED(httpChannel
->GetResponseVersion(&major
, &minor
))) {
2128 http3Key
= "http3"_ns
;
2129 nsCOMPtr
<nsIHttpChannel
> httpChannel2
= do_QueryInterface(GetChannel());
2133 httpChannel2
->GetResponseHeader("priority"_ns
, header
)) &&
2134 !header
.IsEmpty()) {
2135 http3WithPriorityKey
= "with_priority"_ns
;
2137 http3WithPriorityKey
= "without_priority"_ns
;
2139 } else if (major
== 2) {
2140 bool supportHttp3
= false;
2141 if (NS_FAILED(httpChannel
->GetSupportsHTTP3(&supportHttp3
))) {
2142 supportHttp3
= false;
2145 http3Key
= "supports_http3"_ns
;
2149 aEventTelemetryDataOut
.httpVer
= mozilla::Some(major
);
2152 uint32_t earlyHintType
= 0;
2153 Unused
<< httpChannel
->GetEarlyHintLinkType(&earlyHintType
);
2154 if (earlyHintType
& LinkStyle::ePRECONNECT
) {
2155 earlyHintKey
.Append("preconnect_"_ns
);
2157 if (earlyHintType
& LinkStyle::ePRELOAD
) {
2158 earlyHintKey
.Append("preload_"_ns
);
2159 earlyHintKey
.Append(mPreloadService
.GetEarlyHintUsed() ? "1"_ns
: "0"_ns
);
2163 TimeStamp asyncOpen
;
2164 timedChannel
->GetAsyncOpen(&asyncOpen
);
2166 Telemetry::AccumulateTimeDelta(Telemetry::DNS_PERF_FIRST_BYTE_MS
, dnsKey
,
2167 asyncOpen
, responseStart
);
2170 // First Contentful Composite
2171 if (TimeStamp firstContentfulComposite
=
2172 GetNavigationTiming()->GetFirstContentfulCompositeTimeStamp()) {
2173 Telemetry::AccumulateTimeDelta(Telemetry::PERF_FIRST_CONTENTFUL_PAINT_MS
,
2174 navigationStart
, firstContentfulComposite
);
2176 if (!http3Key
.IsEmpty()) {
2177 Telemetry::AccumulateTimeDelta(
2178 Telemetry::HTTP3_PERF_FIRST_CONTENTFUL_PAINT_MS
, http3Key
,
2179 navigationStart
, firstContentfulComposite
);
2182 if (!http3WithPriorityKey
.IsEmpty()) {
2183 Telemetry::AccumulateTimeDelta(
2184 Telemetry::H3P_PERF_FIRST_CONTENTFUL_PAINT_MS
, http3WithPriorityKey
,
2185 navigationStart
, firstContentfulComposite
);
2188 if (!earlyHintKey
.IsEmpty()) {
2189 Telemetry::AccumulateTimeDelta(
2190 Telemetry::EH_PERF_FIRST_CONTENTFUL_PAINT_MS
, earlyHintKey
,
2191 navigationStart
, firstContentfulComposite
);
2194 Telemetry::AccumulateTimeDelta(
2195 Telemetry::DNS_PERF_FIRST_CONTENTFUL_PAINT_MS
, dnsKey
, navigationStart
,
2196 firstContentfulComposite
);
2198 Telemetry::AccumulateTimeDelta(
2199 Telemetry::PERF_FIRST_CONTENTFUL_PAINT_FROM_RESPONSESTART_MS
,
2200 responseStart
, firstContentfulComposite
);
2202 TimeDuration fcpTime
= firstContentfulComposite
- navigationStart
;
2203 if (fcpTime
> zeroDuration
) {
2204 aEventTelemetryDataOut
.fcpTime
=
2205 mozilla::Some(static_cast<uint32_t>(fcpTime
.ToMilliseconds()));
2209 // DOM Content Loaded event
2210 if (TimeStamp dclEventStart
=
2211 GetNavigationTiming()->GetDOMContentLoadedEventStartTimeStamp()) {
2212 Telemetry::AccumulateTimeDelta(Telemetry::PERF_DOM_CONTENT_LOADED_TIME_MS
,
2213 navigationStart
, dclEventStart
);
2214 Telemetry::AccumulateTimeDelta(
2215 Telemetry::PERF_DOM_CONTENT_LOADED_TIME_FROM_RESPONSESTART_MS
,
2216 responseStart
, dclEventStart
);
2220 if (TimeStamp loadEventStart
=
2221 GetNavigationTiming()->GetLoadEventStartTimeStamp()) {
2222 Telemetry::AccumulateTimeDelta(Telemetry::PERF_PAGE_LOAD_TIME_MS
,
2223 navigationStart
, loadEventStart
);
2224 if (!http3Key
.IsEmpty()) {
2225 Telemetry::AccumulateTimeDelta(Telemetry::HTTP3_PERF_PAGE_LOAD_TIME_MS
,
2226 http3Key
, navigationStart
, loadEventStart
);
2229 if (!http3WithPriorityKey
.IsEmpty()) {
2230 Telemetry::AccumulateTimeDelta(Telemetry::H3P_PERF_PAGE_LOAD_TIME_MS
,
2231 http3WithPriorityKey
, navigationStart
,
2235 if (!earlyHintKey
.IsEmpty()) {
2236 Telemetry::AccumulateTimeDelta(Telemetry::EH_PERF_PAGE_LOAD_TIME_MS
,
2237 earlyHintKey
, navigationStart
,
2241 Telemetry::AccumulateTimeDelta(
2242 Telemetry::PERF_PAGE_LOAD_TIME_FROM_RESPONSESTART_MS
, responseStart
,
2245 TimeDuration responseTime
= responseStart
- navigationStart
;
2246 if (responseTime
> zeroDuration
) {
2247 aEventTelemetryDataOut
.responseTime
=
2248 mozilla::Some(static_cast<uint32_t>(responseTime
.ToMilliseconds()));
2251 TimeDuration loadTime
= loadEventStart
- navigationStart
;
2252 if (loadTime
> zeroDuration
) {
2253 aEventTelemetryDataOut
.loadTime
=
2254 mozilla::Some(static_cast<uint32_t>(loadTime
.ToMilliseconds()));
2259 void Document::AccumulateJSTelemetry(
2260 glean::perf::PageLoadExtra
& aEventTelemetryDataOut
) {
2261 if (!IsTopLevelContentDocument() || !ShouldIncludeInTelemetry(false)) {
2265 if (!GetScopeObject() || !GetScopeObject()->GetGlobalJSObject()) {
2270 JSObject
* globalObject
= GetScopeObject()->GetGlobalJSObject();
2271 JSAutoRealm
ar(cx
, globalObject
);
2272 JS::JSTimers timers
= JS::GetJSTimers(cx
);
2274 if (!timers
.executionTime
.IsZero()) {
2275 Telemetry::Accumulate(
2276 Telemetry::JS_PAGELOAD_EXECUTION_MS
,
2277 ConvertToUnsignedFromDouble(timers
.executionTime
.ToMilliseconds()));
2278 aEventTelemetryDataOut
.jsExecTime
= mozilla::Some(
2279 static_cast<uint32_t>(timers
.executionTime
.ToMilliseconds()));
2282 if (!timers
.delazificationTime
.IsZero()) {
2283 Telemetry::Accumulate(Telemetry::JS_PAGELOAD_DELAZIFICATION_MS
,
2284 ConvertToUnsignedFromDouble(
2285 timers
.delazificationTime
.ToMilliseconds()));
2288 if (!timers
.xdrEncodingTime
.IsZero()) {
2289 Telemetry::Accumulate(
2290 Telemetry::JS_PAGELOAD_XDR_ENCODING_MS
,
2291 ConvertToUnsignedFromDouble(timers
.xdrEncodingTime
.ToMilliseconds()));
2294 if (!timers
.baselineCompileTime
.IsZero()) {
2295 Telemetry::Accumulate(Telemetry::JS_PAGELOAD_BASELINE_COMPILE_MS
,
2296 ConvertToUnsignedFromDouble(
2297 timers
.baselineCompileTime
.ToMilliseconds()));
2300 if (!timers
.gcTime
.IsZero()) {
2301 Telemetry::Accumulate(
2302 Telemetry::JS_PAGELOAD_GC_MS
,
2303 ConvertToUnsignedFromDouble(timers
.gcTime
.ToMilliseconds()));
2306 if (!timers
.protectTime
.IsZero()) {
2307 Telemetry::Accumulate(
2308 Telemetry::JS_PAGELOAD_PROTECT_MS
,
2309 ConvertToUnsignedFromDouble(timers
.protectTime
.ToMilliseconds()));
2313 Document::~Document() {
2314 MOZ_LOG(gDocumentLeakPRLog
, LogLevel::Debug
, ("DOCUMENT %p destroyed", this));
2315 MOZ_ASSERT(!IsTopLevelContentDocument() || !IsResourceDoc(),
2316 "Can't be top-level and a resource doc at the same time");
2318 NS_ASSERTION(!mIsShowing
, "Destroying a currently-showing document");
2320 if (IsTopLevelContentDocument()) {
2321 RemoveToplevelLoadingDocument(this);
2323 // don't report for about: pages
2324 if (!IsAboutPage()) {
2325 // record CSP telemetry on this document
2327 Accumulate(Telemetry::CSP_DOCUMENTS_COUNT
, 1);
2329 if (mHasUnsafeInlineCSP
) {
2330 Accumulate(Telemetry::CSP_UNSAFE_INLINE_DOCUMENTS_COUNT
, 1);
2332 if (mHasUnsafeEvalCSP
) {
2333 Accumulate(Telemetry::CSP_UNSAFE_EVAL_DOCUMENTS_COUNT
, 1);
2336 if (MOZ_UNLIKELY(mMathMLEnabled
)) {
2337 ScalarAdd(Telemetry::ScalarID::MATHML_DOC_COUNT
, 1);
2340 if (IsHTMLDocument()) {
2341 switch (GetCompatibilityMode()) {
2342 case eCompatibility_FullStandards
:
2343 Telemetry::AccumulateCategorical(
2344 Telemetry::LABELS_QUIRKS_MODE::FullStandards
);
2346 case eCompatibility_AlmostStandards
:
2347 Telemetry::AccumulateCategorical(
2348 Telemetry::LABELS_QUIRKS_MODE::AlmostStandards
);
2350 case eCompatibility_NavQuirks
:
2351 Telemetry::AccumulateCategorical(
2352 Telemetry::LABELS_QUIRKS_MODE::NavQuirks
);
2355 MOZ_ASSERT_UNREACHABLE("Unknown quirks mode");
2362 mInDestructor
= true;
2363 mInUnlinkOrDeletion
= true;
2365 mozilla::DropJSObjects(this);
2367 // Clear mObservers to keep it in sync with the mutationobserver list
2370 mIntersectionObservers
.Clear();
2372 if (mStyleSheetSetList
) {
2373 mStyleSheetSetList
->Disconnect();
2376 if (mAnimationController
) {
2377 mAnimationController
->Disconnect();
2380 MOZ_ASSERT(mTimelines
.isEmpty());
2382 mParentDocument
= nullptr;
2384 // Kill the subdocument map, doing this will release its strong
2385 // references, if any.
2386 delete mSubDocuments
;
2387 mSubDocuments
= nullptr;
2389 nsAutoScriptBlocker scriptBlocker
;
2391 // Destroy link map now so we don't waste time removing
2393 DestroyElementMaps();
2395 // Invalidate cached array of child nodes
2396 InvalidateChildNodes();
2398 // We should not have child nodes when destructor is called,
2399 // since child nodes keep their owner document alive.
2400 MOZ_ASSERT(!HasChildren());
2402 mCachedRootElement
= nullptr;
2404 for (auto& sheets
: mAdditionalSheets
) {
2405 UnlinkStyleSheets(sheets
);
2408 if (mAttrStyleSheet
) {
2409 mAttrStyleSheet
->SetOwningDocument(nullptr);
2412 if (mListenerManager
) {
2413 mListenerManager
->Disconnect();
2414 UnsetFlags(NODE_HAS_LISTENERMANAGER
);
2417 if (mScriptLoader
) {
2418 mScriptLoader
->DropDocumentReference();
2422 // Could be null here if Init() failed or if we have been unlinked.
2423 mCSSLoader
->DropDocumentReference();
2426 if (mStyleImageLoader
) {
2427 mStyleImageLoader
->DropDocumentReference();
2430 if (mXULBroadcastManager
) {
2431 mXULBroadcastManager
->DropDocumentReference();
2435 mXULPersist
->DropDocumentReference();
2438 if (mPermissionDelegateHandler
) {
2439 mPermissionDelegateHandler
->DropDocumentReference();
2442 mHeaderData
= nullptr;
2444 mPendingTitleChangeEvent
.Revoke();
2446 MOZ_ASSERT(mDOMMediaQueryLists
.isEmpty(),
2447 "must not have media query lists left");
2449 if (mNodeInfoManager
) {
2450 mNodeInfoManager
->DropDocumentReference();
2454 MOZ_ASSERT(mDocGroup
->GetBrowsingContextGroup());
2455 mDocGroup
->GetBrowsingContextGroup()->RemoveDocument(this, mDocGroup
);
2458 UnlinkOriginalDocumentIfStatic();
2460 UnregisterFromMemoryReportingForDataDocument();
2463 NS_INTERFACE_TABLE_HEAD(Document
)
2464 NS_WRAPPERCACHE_INTERFACE_TABLE_ENTRY
2465 NS_INTERFACE_TABLE_BEGIN
2466 NS_INTERFACE_TABLE_ENTRY_AMBIGUOUS(Document
, nsISupports
, nsINode
)
2467 NS_INTERFACE_TABLE_ENTRY(Document
, nsINode
)
2468 NS_INTERFACE_TABLE_ENTRY(Document
, Document
)
2469 NS_INTERFACE_TABLE_ENTRY(Document
, nsIScriptObjectPrincipal
)
2470 NS_INTERFACE_TABLE_ENTRY(Document
, EventTarget
)
2471 NS_INTERFACE_TABLE_ENTRY(Document
, nsISupportsWeakReference
)
2472 NS_INTERFACE_TABLE_ENTRY(Document
, nsIRadioGroupContainer
)
2473 NS_INTERFACE_TABLE_END
2474 NS_INTERFACE_TABLE_TO_MAP_SEGUE_CYCLE_COLLECTION(Document
)
2475 NS_INTERFACE_MAP_END
2477 NS_IMPL_MAIN_THREAD_ONLY_CYCLE_COLLECTING_ADDREF(Document
)
2478 NS_IMPL_MAIN_THREAD_ONLY_CYCLE_COLLECTING_RELEASE_WITH_LAST_RELEASE(
2479 Document
, LastRelease())
2481 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_BEGIN(Document
)
2482 if (Element::CanSkip(tmp
, aRemovingAllowed
)) {
2483 EventListenerManager
* elm
= tmp
->GetExistingListenerManager();
2489 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_END
2491 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_BEGIN(Document
)
2492 return Element::CanSkipInCC(tmp
);
2493 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_END
2495 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_BEGIN(Document
)
2496 return Element::CanSkipThis(tmp
);
2497 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_END
2499 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INTERNAL(Document
)
2500 if (MOZ_UNLIKELY(cb
.WantDebugInfo())) {
2502 nsAutoCString loadedAsData
;
2503 if (tmp
->IsLoadedAsData()) {
2504 loadedAsData
.AssignLiteral("data");
2506 loadedAsData
.AssignLiteral("normal");
2508 uint32_t nsid
= tmp
->GetDefaultNamespaceID();
2510 if (tmp
->mDocumentURI
) uri
= tmp
->mDocumentURI
->GetSpecOrDefault();
2511 static const char* kNSURIs
[] = {"([none])", "(xmlns)", "(xml)",
2512 "(xhtml)", "(XLink)", "(XSLT)",
2513 "(MathML)", "(RDF)", "(XUL)"};
2514 if (nsid
< ArrayLength(kNSURIs
)) {
2515 SprintfLiteral(name
, "Document %s %s %s", loadedAsData
.get(),
2516 kNSURIs
[nsid
], uri
.get());
2518 SprintfLiteral(name
, "Document %s %s", loadedAsData
.get(), uri
.get());
2520 cb
.DescribeRefCountedNode(tmp
->mRefCnt
.get(), name
);
2522 NS_IMPL_CYCLE_COLLECTION_DESCRIBE(Document
, tmp
->mRefCnt
.get())
2525 if (!nsINode::Traverse(tmp
, cb
)) {
2526 return NS_SUCCESS_INTERRUPTED_TRAVERSE
;
2529 tmp
->mExternalResourceMap
.Traverse(&cb
);
2531 // Traverse all Document pointer members.
2532 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSecurityInfo
)
2533 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDisplayDocument
)
2534 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFontFaceSet
)
2535 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mReadyForIdle
)
2536 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocumentL10n
)
2537 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mHighlightRegistry
)
2539 // Traverse all Document nsCOMPtrs.
2540 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mParser
)
2541 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mScriptGlobalObject
)
2542 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mListenerManager
)
2543 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mStyleSheetSetList
)
2544 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mScriptLoader
)
2546 DocumentOrShadowRoot::Traverse(tmp
, cb
);
2548 for (auto& sheets
: tmp
->mAdditionalSheets
) {
2549 tmp
->TraverseStyleSheets(sheets
, "mAdditionalSheets[<origin>][i]", cb
);
2552 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mOnloadBlocker
)
2553 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mLazyLoadImageObserver
)
2554 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mLazyLoadImageObserverViewport
)
2555 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mLastRememberedSizeObserver
)
2556 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mContentVisibilityObserver
)
2557 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDOMImplementation
)
2558 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mImageMaps
)
2559 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mOrientationPendingPromise
)
2560 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mOriginalDocument
)
2561 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCachedEncoder
)
2562 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocumentTimeline
)
2563 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPendingAnimationTracker
)
2564 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mScrollTimelineAnimationTracker
)
2565 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mTemplateContentsOwner
)
2566 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mChildrenCollection
)
2567 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mImages
);
2568 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mEmbeds
);
2569 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mLinks
);
2570 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mForms
);
2571 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mScripts
);
2572 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mApplets
);
2573 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mAnchors
);
2574 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mAnonymousContents
)
2575 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCommandDispatcher
)
2576 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFeaturePolicy
)
2577 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSuppressedEventListener
)
2578 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPrototypeDocument
)
2579 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mMidasCommandManager
)
2580 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mAll
)
2581 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocGroup
)
2582 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFrameRequestManager
)
2584 // Traverse all our nsCOMArrays.
2585 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPreloadingImages
)
2587 // Traverse animation components
2588 if (tmp
->mAnimationController
) {
2589 tmp
->mAnimationController
->Traverse(&cb
);
2592 if (tmp
->mSubDocuments
) {
2593 for (auto iter
= tmp
->mSubDocuments
->Iter(); !iter
.Done(); iter
.Next()) {
2594 auto entry
= static_cast<SubDocMapEntry
*>(iter
.Get());
2596 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb
, "mSubDocuments entry->mKey");
2597 cb
.NoteXPCOMChild(entry
->mKey
);
2598 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb
,
2599 "mSubDocuments entry->mSubDocument");
2600 cb
.NoteXPCOMChild(ToSupports(entry
->mSubDocument
));
2604 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCSSLoader
)
2606 // We own only the items in mDOMMediaQueryLists that have listeners;
2607 // this reference is managed by their AddListener and RemoveListener
2609 for (MediaQueryList
* mql
= tmp
->mDOMMediaQueryLists
.getFirst(); mql
;
2610 mql
= static_cast<LinkedListElement
<MediaQueryList
>*>(mql
)->getNext()) {
2611 if (mql
->HasListeners() &&
2612 NS_SUCCEEDED(mql
->CheckCurrentGlobalCorrectness())) {
2613 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb
, "mDOMMediaQueryLists item");
2614 cb
.NoteXPCOMChild(mql
);
2618 // XXX: This should be not needed once bug 1569185 lands.
2619 for (const auto& entry
: tmp
->mL10nProtoElements
) {
2620 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb
, "mL10nProtoElements key");
2621 cb
.NoteXPCOMChild(entry
.GetKey());
2622 CycleCollectionNoteChild(cb
, entry
.GetWeak(), "mL10nProtoElements value");
2625 for (size_t i
= 0; i
< tmp
->mPendingFrameStaticClones
.Length(); ++i
) {
2626 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPendingFrameStaticClones
[i
].mElement
);
2627 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(
2628 mPendingFrameStaticClones
[i
].mStaticCloneOf
);
2630 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
2632 NS_IMPL_CYCLE_COLLECTION_CLASS(Document
)
2634 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(Document
)
2635 NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER
2636 NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mCachedStateObject
)
2637 NS_IMPL_CYCLE_COLLECTION_TRACE_END
2639 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(Document
)
2640 tmp
->mInUnlinkOrDeletion
= true;
2642 tmp
->SetStateObject(nullptr);
2644 // Clear out our external resources
2645 tmp
->mExternalResourceMap
.Shutdown();
2647 nsAutoScriptBlocker scriptBlocker
;
2649 nsINode::Unlink(tmp
);
2651 while (tmp
->HasChildren()) {
2652 // Hold a strong ref to the node when we remove it, because we may be
2653 // the last reference to it.
2654 // If this code changes, change the corresponding code in Document's
2655 // unlink impl and ContentUnbinder::UnbindSubtree.
2656 nsCOMPtr
<nsIContent
> child
= tmp
->GetLastChild();
2657 tmp
->DisconnectChild(child
);
2658 child
->UnbindFromTree();
2661 tmp
->UnlinkOriginalDocumentIfStatic();
2663 tmp
->mCachedRootElement
= nullptr; // Avoid a dangling pointer
2665 tmp
->SetScriptGlobalObject(nullptr);
2667 for (auto& sheets
: tmp
->mAdditionalSheets
) {
2668 tmp
->UnlinkStyleSheets(sheets
);
2671 NS_IMPL_CYCLE_COLLECTION_UNLINK(mSecurityInfo
)
2672 NS_IMPL_CYCLE_COLLECTION_UNLINK(mDisplayDocument
)
2673 NS_IMPL_CYCLE_COLLECTION_UNLINK(mLazyLoadImageObserver
)
2674 NS_IMPL_CYCLE_COLLECTION_UNLINK(mLazyLoadImageObserverViewport
)
2675 NS_IMPL_CYCLE_COLLECTION_UNLINK(mContentVisibilityObserver
)
2676 NS_IMPL_CYCLE_COLLECTION_UNLINK(mLastRememberedSizeObserver
)
2677 NS_IMPL_CYCLE_COLLECTION_UNLINK(mFontFaceSet
)
2678 NS_IMPL_CYCLE_COLLECTION_UNLINK(mReadyForIdle
)
2679 NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocumentL10n
)
2680 NS_IMPL_CYCLE_COLLECTION_UNLINK(mHighlightRegistry
)
2681 NS_IMPL_CYCLE_COLLECTION_UNLINK(mParser
)
2682 NS_IMPL_CYCLE_COLLECTION_UNLINK(mOnloadBlocker
)
2683 NS_IMPL_CYCLE_COLLECTION_UNLINK(mDOMImplementation
)
2684 NS_IMPL_CYCLE_COLLECTION_UNLINK(mImageMaps
)
2685 NS_IMPL_CYCLE_COLLECTION_UNLINK(mOrientationPendingPromise
)
2686 NS_IMPL_CYCLE_COLLECTION_UNLINK(mOriginalDocument
)
2687 NS_IMPL_CYCLE_COLLECTION_UNLINK(mCachedEncoder
)
2688 NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocumentTimeline
)
2689 NS_IMPL_CYCLE_COLLECTION_UNLINK(mPendingAnimationTracker
)
2690 NS_IMPL_CYCLE_COLLECTION_UNLINK(mScrollTimelineAnimationTracker
)
2691 NS_IMPL_CYCLE_COLLECTION_UNLINK(mTemplateContentsOwner
)
2692 NS_IMPL_CYCLE_COLLECTION_UNLINK(mChildrenCollection
)
2693 NS_IMPL_CYCLE_COLLECTION_UNLINK(mImages
);
2694 NS_IMPL_CYCLE_COLLECTION_UNLINK(mEmbeds
);
2695 NS_IMPL_CYCLE_COLLECTION_UNLINK(mLinks
);
2696 NS_IMPL_CYCLE_COLLECTION_UNLINK(mForms
);
2697 NS_IMPL_CYCLE_COLLECTION_UNLINK(mScripts
);
2698 NS_IMPL_CYCLE_COLLECTION_UNLINK(mApplets
);
2699 NS_IMPL_CYCLE_COLLECTION_UNLINK(mAnchors
);
2700 NS_IMPL_CYCLE_COLLECTION_UNLINK(mAnonymousContents
)
2701 NS_IMPL_CYCLE_COLLECTION_UNLINK(mCommandDispatcher
)
2702 NS_IMPL_CYCLE_COLLECTION_UNLINK(mFeaturePolicy
)
2703 NS_IMPL_CYCLE_COLLECTION_UNLINK(mSuppressedEventListener
)
2704 NS_IMPL_CYCLE_COLLECTION_UNLINK(mPrototypeDocument
)
2705 NS_IMPL_CYCLE_COLLECTION_UNLINK(mMidasCommandManager
)
2706 NS_IMPL_CYCLE_COLLECTION_UNLINK(mAll
)
2707 NS_IMPL_CYCLE_COLLECTION_UNLINK(mReferrerInfo
)
2708 NS_IMPL_CYCLE_COLLECTION_UNLINK(mPreloadReferrerInfo
)
2710 if (tmp
->mDocGroup
&& tmp
->mDocGroup
->GetBrowsingContextGroup()) {
2711 tmp
->mDocGroup
->GetBrowsingContextGroup()->RemoveDocument(tmp
,
2714 tmp
->mDocGroup
= nullptr;
2716 if (tmp
->IsTopLevelContentDocument()) {
2717 RemoveToplevelLoadingDocument(tmp
);
2720 tmp
->mParentDocument
= nullptr;
2722 NS_IMPL_CYCLE_COLLECTION_UNLINK(mPreloadingImages
)
2724 NS_IMPL_CYCLE_COLLECTION_UNLINK(mIntersectionObservers
)
2726 if (tmp
->mListenerManager
) {
2727 tmp
->mListenerManager
->Disconnect();
2728 tmp
->UnsetFlags(NODE_HAS_LISTENERMANAGER
);
2729 tmp
->mListenerManager
= nullptr;
2732 if (tmp
->mStyleSheetSetList
) {
2733 tmp
->mStyleSheetSetList
->Disconnect();
2734 tmp
->mStyleSheetSetList
= nullptr;
2737 delete tmp
->mSubDocuments
;
2738 tmp
->mSubDocuments
= nullptr;
2740 NS_IMPL_CYCLE_COLLECTION_UNLINK(mFrameRequestManager
)
2741 MOZ_RELEASE_ASSERT(!tmp
->mFrameRequestCallbacksScheduled
,
2742 "How did we get here without our presshell going away "
2745 DocumentOrShadowRoot::Unlink(tmp
);
2747 // Document has a pretty complex destructor, so we're going to
2748 // assume that *most* cycles you actually want to break somewhere
2749 // else, and not unlink an awful lot here.
2751 tmp
->mExpandoAndGeneration
.OwnerUnlinked();
2753 if (tmp
->mAnimationController
) {
2754 tmp
->mAnimationController
->Unlink();
2757 tmp
->mPendingTitleChangeEvent
.Revoke();
2759 if (tmp
->mCSSLoader
) {
2760 tmp
->mCSSLoader
->DropDocumentReference();
2761 NS_IMPL_CYCLE_COLLECTION_UNLINK(mCSSLoader
)
2764 // We own only the items in mDOMMediaQueryLists that have listeners;
2765 // this reference is managed by their AddListener and RemoveListener
2767 for (MediaQueryList
* mql
= tmp
->mDOMMediaQueryLists
.getFirst(); mql
;) {
2768 MediaQueryList
* next
=
2769 static_cast<LinkedListElement
<MediaQueryList
>*>(mql
)->getNext();
2774 tmp
->mPendingFrameStaticClones
.Clear();
2776 tmp
->mInUnlinkOrDeletion
= false;
2778 tmp
->UnregisterFromMemoryReportingForDataDocument();
2780 NS_IMPL_CYCLE_COLLECTION_UNLINK(mL10nProtoElements
)
2781 NS_IMPL_CYCLE_COLLECTION_UNLINK_WEAK_PTR
2782 NS_IMPL_CYCLE_COLLECTION_UNLINK_WEAK_REFERENCE
2783 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
2785 nsresult
Document::Init() {
2786 if (mCSSLoader
|| mStyleImageLoader
|| mNodeInfoManager
|| mScriptLoader
) {
2787 return NS_ERROR_ALREADY_INITIALIZED
;
2790 // Force initialization.
2791 mOnloadBlocker
= new OnloadBlocker();
2792 mStyleImageLoader
= new css::ImageLoader(this);
2794 mNodeInfoManager
= new nsNodeInfoManager(this);
2796 // mNodeInfo keeps NodeInfoManager alive!
2797 mNodeInfo
= mNodeInfoManager
->GetDocumentNodeInfo();
2798 NS_ENSURE_TRUE(mNodeInfo
, NS_ERROR_OUT_OF_MEMORY
);
2799 MOZ_ASSERT(mNodeInfo
->NodeType() == DOCUMENT_NODE
,
2800 "Bad NodeType in aNodeInfo");
2802 NS_ASSERTION(OwnerDoc() == this, "Our nodeinfo is busted!");
2804 mCSSLoader
= new css::Loader(this);
2805 // Assume we're not quirky, until we know otherwise
2806 mCSSLoader
->SetCompatibilityMode(eCompatibility_FullStandards
);
2808 // If after creation the owner js global is not set for a document
2809 // we use the default compartment for this document, instead of creating
2810 // wrapper in some random compartment when the document is exposed to js
2812 nsCOMPtr
<nsIGlobalObject
> global
=
2813 xpc::NativeGlobal(xpc::PrivilegedJunkScope());
2814 NS_ENSURE_TRUE(global
, NS_ERROR_FAILURE
);
2815 mScopeObject
= do_GetWeakReference(global
);
2816 MOZ_ASSERT(mScopeObject
);
2818 mScriptLoader
= new dom::ScriptLoader(this);
2820 // we need to create a policy here so getting the policy within
2821 // ::Policy() can *always* return a non null policy
2822 mFeaturePolicy
= new dom::FeaturePolicy(this);
2823 mFeaturePolicy
->SetDefaultOrigin(NodePrincipal());
2825 mStyleSet
= MakeUnique
<ServoStyleSet
>(*this);
2827 RecomputeResistFingerprinting();
2832 void Document::RemoveAllProperties() { PropertyTable().RemoveAllProperties(); }
2834 void Document::RemoveAllPropertiesFor(nsINode
* aNode
) {
2835 PropertyTable().RemoveAllPropertiesFor(aNode
);
2838 void Document::Reset(nsIChannel
* aChannel
, nsILoadGroup
* aLoadGroup
) {
2839 nsCOMPtr
<nsIURI
> uri
;
2840 nsCOMPtr
<nsIPrincipal
> principal
;
2841 nsCOMPtr
<nsIPrincipal
> partitionedPrincipal
;
2843 // Note: this code is duplicated in PrototypeDocumentContentSink::Init and
2844 // nsScriptSecurityManager::GetChannelResultPrincipals.
2845 // Note: this should match the uri used for the OnNewURI call in
2846 // nsDocShell::CreateContentViewer.
2847 NS_GetFinalChannelURI(aChannel
, getter_AddRefs(uri
));
2849 nsIScriptSecurityManager
* securityManager
=
2850 nsContentUtils::GetSecurityManager();
2851 if (securityManager
) {
2852 securityManager
->GetChannelResultPrincipals(
2853 aChannel
, getter_AddRefs(principal
),
2854 getter_AddRefs(partitionedPrincipal
));
2858 bool equal
= principal
->Equals(partitionedPrincipal
);
2860 principal
= MaybeDowngradePrincipal(principal
);
2862 partitionedPrincipal
= principal
;
2864 partitionedPrincipal
= MaybeDowngradePrincipal(partitionedPrincipal
);
2867 ResetToURI(uri
, aLoadGroup
, principal
, partitionedPrincipal
);
2869 // Note that, since mTiming does not change during a reset, the
2870 // navigationStart time remains unchanged and therefore any future new
2871 // timeline will have the same global clock time as the old one.
2872 mDocumentTimeline
= nullptr;
2874 if (nsCOMPtr
<nsIPropertyBag2
> bag
= do_QueryInterface(aChannel
)) {
2875 if (nsCOMPtr
<nsIURI
> baseURI
= do_GetProperty(bag
, u
"baseURI"_ns
)) {
2876 mDocumentBaseURI
= baseURI
.forget();
2877 mChromeXHRDocBaseURI
= nullptr;
2881 mChannel
= aChannel
;
2882 RecomputeResistFingerprinting();
2885 void Document::DisconnectNodeTree() {
2886 // Delete references to sub-documents and kill the subdocument map,
2887 // if any. This is not strictly needed, but makes the node tree
2888 // teardown a bit faster.
2889 delete mSubDocuments
;
2890 mSubDocuments
= nullptr;
2892 bool oldVal
= mInUnlinkOrDeletion
;
2893 mInUnlinkOrDeletion
= true;
2894 { // Scope for update
2895 MOZ_AUTO_DOC_UPDATE(this, true);
2897 // Destroy link map now so we don't waste time removing
2899 DestroyElementMaps();
2901 // Invalidate cached array of child nodes
2902 InvalidateChildNodes();
2904 while (HasChildren()) {
2905 nsMutationGuard::DidMutate();
2906 nsCOMPtr
<nsIContent
> content
= GetLastChild();
2907 nsIContent
* previousSibling
= content
->GetPreviousSibling();
2908 DisconnectChild(content
);
2909 if (content
== mCachedRootElement
) {
2910 // Immediately clear mCachedRootElement, now that it's been removed
2911 // from mChildren, so that GetRootElement() will stop returning this
2913 mCachedRootElement
= nullptr;
2915 MutationObservers::NotifyContentRemoved(this, content
, previousSibling
);
2916 content
->UnbindFromTree();
2918 MOZ_ASSERT(!mCachedRootElement
,
2919 "After removing all children, there should be no root elem");
2921 mInUnlinkOrDeletion
= oldVal
;
2924 void Document::ResetToURI(nsIURI
* aURI
, nsILoadGroup
* aLoadGroup
,
2925 nsIPrincipal
* aPrincipal
,
2926 nsIPrincipal
* aPartitionedPrincipal
) {
2927 MOZ_ASSERT(aURI
, "Null URI passed to ResetToURI");
2928 MOZ_ASSERT(!!aPrincipal
== !!aPartitionedPrincipal
);
2930 MOZ_LOG(gDocumentLeakPRLog
, LogLevel::Debug
,
2931 ("DOCUMENT %p ResetToURI %s", this, aURI
->GetSpecOrDefault().get()));
2933 mSecurityInfo
= nullptr;
2935 nsCOMPtr
<nsILoadGroup
> group
= do_QueryReferent(mDocumentLoadGroup
);
2936 if (!aLoadGroup
|| group
!= aLoadGroup
) {
2937 mDocumentLoadGroup
= nullptr;
2940 DisconnectNodeTree();
2942 // Reset our stylesheets
2943 ResetStylesheetsToURI(aURI
);
2945 // Release the listener manager
2946 if (mListenerManager
) {
2947 mListenerManager
->Disconnect();
2948 mListenerManager
= nullptr;
2951 // Release the stylesheets list.
2952 mDOMStyleSheets
= nullptr;
2954 // Release our principal after tearing down the document, rather than before.
2955 // This ensures that, during teardown, the document and the dying window
2956 // (which already nulled out its document pointer and cached the principal)
2957 // have matching principals.
2958 SetPrincipals(nullptr, nullptr);
2960 // Clear the original URI so SetDocumentURI sets it.
2961 mOriginalURI
= nullptr;
2963 SetDocumentURI(aURI
);
2964 mChromeXHRDocURI
= nullptr;
2965 // If mDocumentBaseURI is null, Document::GetBaseURI() returns
2967 mDocumentBaseURI
= nullptr;
2968 mChromeXHRDocBaseURI
= nullptr;
2970 // Check if the current document is the top-level DevTools document.
2971 // For inner DevTools frames, mIsDevToolsDocument will be set when
2972 // calling SetDocumentParent.
2973 if (aURI
&& aURI
->SchemeIs("about") &&
2974 aURI
->GetSpecOrDefault().EqualsLiteral("about:devtools-toolbox")) {
2975 mIsDevToolsDocument
= true;
2979 mDocumentLoadGroup
= do_GetWeakReference(aLoadGroup
);
2980 // there was an assertion here that aLoadGroup was not null. This
2981 // is no longer valid: nsDocShell::SetDocument does not create a
2982 // load group, and it works just fine
2984 // XXXbz what does "just fine" mean exactly? And given that there
2985 // is no nsDocShell::SetDocument, what is this talking about?
2987 if (IsContentDocument()) {
2988 // Inform the associated request context about this load start so
2989 // any of its internal load progress flags gets reset.
2990 nsCOMPtr
<nsIRequestContextService
> rcsvc
=
2991 net::RequestContextService::GetOrCreate();
2993 nsCOMPtr
<nsIRequestContext
> rc
;
2994 rcsvc
->GetRequestContextFromLoadGroup(aLoadGroup
, getter_AddRefs(rc
));
3002 mLastModified
.Truncate();
3003 // XXXbz I guess we're assuming that the caller will either pass in
3004 // a channel with a useful type or call SetContentType?
3005 SetContentType(""_ns
);
3006 mContentLanguage
.Truncate();
3007 mBaseTarget
.Truncate();
3009 mXMLDeclarationBits
= 0;
3011 // Now get our new principal
3013 SetPrincipals(aPrincipal
, aPartitionedPrincipal
);
3015 nsIScriptSecurityManager
* securityManager
=
3016 nsContentUtils::GetSecurityManager();
3017 if (securityManager
) {
3018 nsCOMPtr
<nsILoadContext
> loadContext(mDocumentContainer
);
3020 if (!loadContext
&& aLoadGroup
) {
3021 nsCOMPtr
<nsIInterfaceRequestor
> cbs
;
3022 aLoadGroup
->GetNotificationCallbacks(getter_AddRefs(cbs
));
3023 loadContext
= do_GetInterface(cbs
);
3026 MOZ_ASSERT(loadContext
,
3027 "must have a load context or pass in an explicit principal");
3029 nsCOMPtr
<nsIPrincipal
> principal
;
3030 nsresult rv
= securityManager
->GetLoadContextContentPrincipal(
3031 mDocumentURI
, loadContext
, getter_AddRefs(principal
));
3032 if (NS_SUCCEEDED(rv
)) {
3033 SetPrincipals(principal
, principal
);
3039 mFontFaceSet
->RefreshStandardFontLoadPrincipal();
3042 // Refresh the principal on the realm.
3043 if (nsPIDOMWindowInner
* win
= GetInnerWindow()) {
3044 nsGlobalWindowInner::Cast(win
)->RefreshRealmPrincipal();
3048 already_AddRefed
<nsIPrincipal
> Document::MaybeDowngradePrincipal(
3049 nsIPrincipal
* aPrincipal
) {
3054 // We can't load a document with an expanded principal. If we're given one,
3055 // automatically downgrade it to the last principal it subsumes (which is the
3056 // extension principal, in the case of extension content scripts).
3057 auto* basePrin
= BasePrincipal::Cast(aPrincipal
);
3058 if (basePrin
->Is
<ExpandedPrincipal
>()) {
3059 MOZ_DIAGNOSTIC_ASSERT(false,
3060 "Should never try to create a document with "
3061 "an expanded principal");
3063 auto* expanded
= basePrin
->As
<ExpandedPrincipal
>();
3064 return do_AddRef(expanded
->AllowList().LastElement());
3067 if (aPrincipal
->IsSystemPrincipal() && mDocumentContainer
) {
3068 // We basically want the parent document here, but because this is very
3069 // early in the load, GetInProcessParentDocument() returns null, so we use
3070 // the docshell hierarchy to get this information instead.
3071 if (RefPtr
<BrowsingContext
> parent
=
3072 mDocumentContainer
->GetBrowsingContext()->GetParent()) {
3073 auto* parentWin
= nsGlobalWindowOuter::Cast(parent
->GetDOMWindow());
3074 if (!parentWin
|| !parentWin
->GetPrincipal()->IsSystemPrincipal()) {
3075 nsCOMPtr
<nsIPrincipal
> nullPrincipal
=
3076 NullPrincipal::CreateWithoutOriginAttributes();
3077 return nullPrincipal
.forget();
3081 nsCOMPtr
<nsIPrincipal
> principal(aPrincipal
);
3082 return principal
.forget();
3085 size_t Document::FindDocStyleSheetInsertionPoint(const StyleSheet
& aSheet
) {
3086 nsStyleSheetService
* sheetService
= nsStyleSheetService::GetInstance();
3088 // lowest index first
3089 int32_t newDocIndex
= StyleOrderIndexOfSheet(aSheet
);
3091 size_t count
= mStyleSet
->SheetCount(StyleOrigin::Author
);
3093 for (; index
< count
; index
++) {
3094 auto* sheet
= mStyleSet
->SheetAt(StyleOrigin::Author
, index
);
3096 int32_t sheetDocIndex
= StyleOrderIndexOfSheet(*sheet
);
3097 if (sheetDocIndex
> newDocIndex
) {
3101 // If the sheet is not owned by the document it can be an author
3102 // sheet registered at nsStyleSheetService or an additional author
3103 // sheet on the document, which means the new
3104 // doc sheet should end up before it.
3105 if (sheetDocIndex
< 0) {
3107 auto& authorSheets
= *sheetService
->AuthorStyleSheets();
3108 if (authorSheets
.IndexOf(sheet
) != authorSheets
.NoIndex
) {
3112 if (sheet
== GetFirstAdditionalAuthorSheet()) {
3121 void Document::ResetStylesheetsToURI(nsIURI
* aURI
) {
3124 ClearAdoptedStyleSheets();
3126 auto ClearSheetList
= [&](nsTArray
<RefPtr
<StyleSheet
>>& aSheetList
) {
3127 for (auto& sheet
: Reversed(aSheetList
)) {
3128 sheet
->ClearAssociatedDocumentOrShadowRoot();
3129 if (mStyleSetFilled
) {
3130 mStyleSet
->RemoveStyleSheet(*sheet
);
3135 ClearSheetList(mStyleSheets
);
3136 for (auto& sheets
: mAdditionalSheets
) {
3137 ClearSheetList(sheets
);
3139 if (mStyleSetFilled
) {
3140 if (auto* ss
= nsStyleSheetService::GetInstance()) {
3141 for (auto& sheet
: Reversed(*ss
->AuthorStyleSheets())) {
3142 MOZ_ASSERT(!sheet
->GetAssociatedDocumentOrShadowRoot());
3143 if (sheet
->IsApplicable()) {
3144 mStyleSet
->RemoveStyleSheet(*sheet
);
3150 // Now reset our inline style and attribute sheets.
3151 if (mAttrStyleSheet
) {
3152 mAttrStyleSheet
->Reset();
3153 mAttrStyleSheet
->SetOwningDocument(this);
3155 mAttrStyleSheet
= new nsHTMLStyleSheet(this);
3158 if (!mStyleAttrStyleSheet
) {
3159 mStyleAttrStyleSheet
= new nsHTMLCSSStyleSheet();
3162 if (mStyleSetFilled
) {
3163 FillStyleSetDocumentSheets();
3165 if (mStyleSet
->StyleSheetsHaveChanged()) {
3166 ApplicableStylesChanged();
3171 static void AppendSheetsToStyleSet(
3172 ServoStyleSet
* aStyleSet
, const nsTArray
<RefPtr
<StyleSheet
>>& aSheets
) {
3173 for (StyleSheet
* sheet
: Reversed(aSheets
)) {
3174 aStyleSet
->AppendStyleSheet(*sheet
);
3178 void Document::FillStyleSetUserAndUASheets() {
3179 // Make sure this does the same thing as PresShell::Add{User,Agent}Sheet wrt
3182 // The document will fill in the document sheets when we create the presshell
3183 auto* cache
= GlobalStyleSheetCache::Singleton();
3185 nsStyleSheetService
* sheetService
= nsStyleSheetService::GetInstance();
3186 MOZ_ASSERT(sheetService
,
3187 "should never be creating a StyleSet after the style sheet "
3188 "service has gone");
3190 for (StyleSheet
* sheet
: *sheetService
->UserStyleSheets()) {
3191 mStyleSet
->AppendStyleSheet(*sheet
);
3194 StyleSheet
* sheet
= IsInChromeDocShell() ? cache
->GetUserChromeSheet()
3195 : cache
->GetUserContentSheet();
3197 mStyleSet
->AppendStyleSheet(*sheet
);
3200 mStyleSet
->AppendStyleSheet(*cache
->UASheet());
3202 if (MOZ_LIKELY(NodeInfoManager()->MathMLEnabled())) {
3203 mStyleSet
->AppendStyleSheet(*cache
->MathMLSheet());
3206 if (MOZ_LIKELY(NodeInfoManager()->SVGEnabled())) {
3207 mStyleSet
->AppendStyleSheet(*cache
->SVGSheet());
3210 mStyleSet
->AppendStyleSheet(*cache
->HTMLSheet());
3212 if (nsLayoutUtils::ShouldUseNoFramesSheet(this)) {
3213 mStyleSet
->AppendStyleSheet(*cache
->NoFramesSheet());
3216 mStyleSet
->AppendStyleSheet(*cache
->CounterStylesSheet());
3218 // Only load the full XUL sheet if we'll need it.
3219 if (LoadsFullXULStyleSheetUpFront()) {
3220 mStyleSet
->AppendStyleSheet(*cache
->XULSheet());
3223 mStyleSet
->AppendStyleSheet(*cache
->FormsSheet());
3224 mStyleSet
->AppendStyleSheet(*cache
->ScrollbarsSheet());
3226 for (StyleSheet
* sheet
: *sheetService
->AgentStyleSheets()) {
3227 mStyleSet
->AppendStyleSheet(*sheet
);
3230 MOZ_ASSERT(!mQuirkSheetAdded
);
3231 if (NeedsQuirksSheet()) {
3232 mStyleSet
->AppendStyleSheet(*cache
->QuirkSheet());
3233 mQuirkSheetAdded
= true;
3237 void Document::FillStyleSet() {
3238 MOZ_ASSERT(!mStyleSetFilled
);
3239 FillStyleSetUserAndUASheets();
3240 FillStyleSetDocumentSheets();
3241 mStyleSetFilled
= true;
3244 void Document::RemoveContentEditableStyleSheets() {
3245 MOZ_ASSERT(IsHTMLOrXHTML());
3247 auto* cache
= GlobalStyleSheetCache::Singleton();
3248 bool changed
= false;
3249 if (mDesignModeSheetAdded
) {
3250 mStyleSet
->RemoveStyleSheet(*cache
->DesignModeSheet());
3251 mDesignModeSheetAdded
= false;
3254 if (mContentEditableSheetAdded
) {
3255 mStyleSet
->RemoveStyleSheet(*cache
->ContentEditableSheet());
3256 mContentEditableSheetAdded
= false;
3260 MOZ_ASSERT(mStyleSetFilled
);
3261 ApplicableStylesChanged();
3265 void Document::AddContentEditableStyleSheetsToStyleSet(bool aDesignMode
) {
3266 MOZ_ASSERT(IsHTMLOrXHTML());
3267 MOZ_DIAGNOSTIC_ASSERT(mStyleSetFilled
,
3268 "Caller should ensure we're being rendered");
3270 auto* cache
= GlobalStyleSheetCache::Singleton();
3271 bool changed
= false;
3272 if (!mContentEditableSheetAdded
) {
3273 mStyleSet
->AppendStyleSheet(*cache
->ContentEditableSheet());
3274 mContentEditableSheetAdded
= true;
3277 if (mDesignModeSheetAdded
!= aDesignMode
) {
3278 if (mDesignModeSheetAdded
) {
3279 mStyleSet
->RemoveStyleSheet(*cache
->DesignModeSheet());
3281 mStyleSet
->AppendStyleSheet(*cache
->DesignModeSheet());
3283 mDesignModeSheetAdded
= !mDesignModeSheetAdded
;
3287 ApplicableStylesChanged();
3291 void Document::FillStyleSetDocumentSheets() {
3292 MOZ_ASSERT(mStyleSet
->SheetCount(StyleOrigin::Author
) == 0,
3293 "Style set already has document sheets?");
3295 // Sheets are added in reverse order to avoid worst-case time complexity when
3296 // looking up the index of a sheet.
3298 // Note that usually appending is faster (rebuilds less stuff in the
3299 // styleset), but in this case it doesn't matter since we're filling the
3300 // styleset from scratch anyway.
3301 for (StyleSheet
* sheet
: Reversed(mStyleSheets
)) {
3302 if (sheet
->IsApplicable()) {
3303 mStyleSet
->AddDocStyleSheet(*sheet
);
3307 EnumerateUniqueAdoptedStyleSheetsBackToFront([&](StyleSheet
& aSheet
) {
3308 if (aSheet
.IsApplicable()) {
3309 mStyleSet
->AddDocStyleSheet(aSheet
);
3313 nsStyleSheetService
* sheetService
= nsStyleSheetService::GetInstance();
3314 for (StyleSheet
* sheet
: *sheetService
->AuthorStyleSheets()) {
3315 mStyleSet
->AppendStyleSheet(*sheet
);
3318 AppendSheetsToStyleSet(mStyleSet
.get(), mAdditionalSheets
[eAgentSheet
]);
3319 AppendSheetsToStyleSet(mStyleSet
.get(), mAdditionalSheets
[eUserSheet
]);
3320 AppendSheetsToStyleSet(mStyleSet
.get(), mAdditionalSheets
[eAuthorSheet
]);
3323 void Document::CompatibilityModeChanged() {
3324 MOZ_ASSERT(IsHTMLOrXHTML());
3325 CSSLoader()->SetCompatibilityMode(mCompatMode
);
3326 mStyleSet
->CompatibilityModeChanged();
3327 if (PresShell
* presShell
= GetPresShell()) {
3328 // Selectors may have become case-sensitive / case-insensitive, the stylist
3329 // has already performed the relevant invalidation.
3330 presShell
->EnsureStyleFlush();
3332 if (!mStyleSetFilled
) {
3333 MOZ_ASSERT(!mQuirkSheetAdded
);
3336 if (mQuirkSheetAdded
== NeedsQuirksSheet()) {
3339 auto* cache
= GlobalStyleSheetCache::Singleton();
3340 StyleSheet
* sheet
= cache
->QuirkSheet();
3341 if (mQuirkSheetAdded
) {
3342 mStyleSet
->RemoveStyleSheet(*sheet
);
3344 mStyleSet
->AppendStyleSheet(*sheet
);
3346 mQuirkSheetAdded
= !mQuirkSheetAdded
;
3347 ApplicableStylesChanged();
3350 void Document::SetCompatibilityMode(nsCompatibility aMode
) {
3351 NS_ASSERTION(IsHTMLDocument() || aMode
== eCompatibility_FullStandards
,
3352 "Bad compat mode for XHTML document!");
3354 if (mCompatMode
== aMode
) {
3357 mCompatMode
= aMode
;
3358 CompatibilityModeChanged();
3359 // Trigger recomputation of the nsViewportInfo the next time it's queried.
3360 mViewportType
= Unknown
;
3363 static void WarnIfSandboxIneffective(nsIDocShell
* aDocShell
,
3364 uint32_t aSandboxFlags
,
3365 nsIChannel
* aChannel
) {
3366 // If the document permits allow-top-navigation and
3367 // allow-top-navigation-by-user-activation this will permit all top
3369 if (aSandboxFlags
!= SANDBOXED_NONE
&&
3370 !(aSandboxFlags
& SANDBOXED_TOPLEVEL_NAVIGATION
) &&
3371 !(aSandboxFlags
& SANDBOXED_TOPLEVEL_NAVIGATION_USER_ACTIVATION
)) {
3372 nsContentUtils::ReportToConsole(
3373 nsIScriptError::warningFlag
, "Iframe Sandbox"_ns
,
3374 aDocShell
->GetDocument(), nsContentUtils::eSECURITY_PROPERTIES
,
3375 "BothAllowTopNavigationAndUserActivationPresent");
3377 // If the document is sandboxed (via the HTML5 iframe sandbox
3378 // attribute) and both the allow-scripts and allow-same-origin
3379 // keywords are supplied, the sandboxed document can call into its
3380 // parent document and remove its sandboxing entirely - we print a
3381 // warning to the web console in this case.
3382 if (aSandboxFlags
& SANDBOXED_NAVIGATION
&&
3383 !(aSandboxFlags
& SANDBOXED_SCRIPTS
) &&
3384 !(aSandboxFlags
& SANDBOXED_ORIGIN
)) {
3385 RefPtr
<BrowsingContext
> bc
= aDocShell
->GetBrowsingContext();
3386 MOZ_ASSERT(bc
->IsInProcess());
3388 RefPtr
<BrowsingContext
> parentBC
= bc
->GetParent();
3389 if (!parentBC
|| !parentBC
->IsInProcess()) {
3390 // If parent document is not in process, then by construction it
3391 // cannot be same origin.
3395 // Don't warn if our parent is not the top-level document.
3396 if (!parentBC
->IsTopContent()) {
3400 nsCOMPtr
<nsIDocShell
> parentDocShell
= parentBC
->GetDocShell();
3401 MOZ_ASSERT(parentDocShell
);
3403 nsCOMPtr
<nsIChannel
> parentChannel
;
3404 parentDocShell
->GetCurrentDocumentChannel(getter_AddRefs(parentChannel
));
3405 if (!parentChannel
) {
3408 nsresult rv
= nsContentUtils::CheckSameOrigin(aChannel
, parentChannel
);
3409 if (NS_FAILED(rv
)) {
3413 nsCOMPtr
<Document
> parentDocument
= parentDocShell
->GetDocument();
3414 nsCOMPtr
<nsIURI
> iframeUri
;
3415 parentChannel
->GetURI(getter_AddRefs(iframeUri
));
3416 nsContentUtils::ReportToConsole(nsIScriptError::warningFlag
,
3417 "Iframe Sandbox"_ns
, parentDocument
,
3418 nsContentUtils::eSECURITY_PROPERTIES
,
3419 "BothAllowScriptsAndSameOriginPresent",
3420 nsTArray
<nsString
>(), iframeUri
);
3424 bool Document::IsSynthesized() {
3425 nsCOMPtr
<nsILoadInfo
> loadInfo
= mChannel
? mChannel
->LoadInfo() : nullptr;
3426 return loadInfo
&& loadInfo
->GetServiceWorkerTaintingSynthesized();
3430 bool Document::IsCallerChromeOrAddon(JSContext
* aCx
, JSObject
* aObject
) {
3431 nsIPrincipal
* principal
= nsContentUtils::SubjectPrincipal(aCx
);
3432 return principal
&& (principal
->IsSystemPrincipal() ||
3433 principal
->GetIsAddonOrExpandedAddonPrincipal());
3436 nsresult
Document::StartDocumentLoad(const char* aCommand
, nsIChannel
* aChannel
,
3437 nsILoadGroup
* aLoadGroup
,
3438 nsISupports
* aContainer
,
3439 nsIStreamListener
** aDocListener
,
3441 if (MOZ_LOG_TEST(gDocumentLeakPRLog
, LogLevel::Debug
)) {
3442 nsCOMPtr
<nsIURI
> uri
;
3443 aChannel
->GetURI(getter_AddRefs(uri
));
3444 MOZ_LOG(gDocumentLeakPRLog
, LogLevel::Debug
,
3445 ("DOCUMENT %p StartDocumentLoad %s", this,
3446 uri
? uri
->GetSpecOrDefault().get() : ""));
3449 MOZ_ASSERT(GetReadyStateEnum() == Document::READYSTATE_UNINITIALIZED
,
3451 SetReadyStateInternal(READYSTATE_LOADING
);
3453 if (nsCRT::strcmp(kLoadAsData
, aCommand
) == 0) {
3454 mLoadedAsData
= true;
3455 SetLoadedAsData(true, /* aConsiderForMemoryReporting */ true);
3456 // We need to disable script & style loading in this case.
3457 // We leave them disabled even in EndLoad(), and let anyone
3458 // who puts the document on display to worry about enabling.
3460 // Do not load/process scripts when loading as data
3461 ScriptLoader()->SetEnabled(false);
3464 CSSLoader()->SetEnabled(
3465 false); // Do not load/process styles when loading as data
3466 } else if (nsCRT::strcmp("external-resource", aCommand
) == 0) {
3467 // Allow CSS, but not scripts
3468 ScriptLoader()->SetEnabled(false);
3471 mMayStartLayout
= false;
3472 MOZ_ASSERT(!mReadyForIdle
,
3473 "We should never hit DOMContentLoaded before this point");
3476 Reset(aChannel
, aLoadGroup
);
3479 nsAutoCString contentType
;
3480 nsCOMPtr
<nsIPropertyBag2
> bag
= do_QueryInterface(aChannel
);
3481 if ((bag
&& NS_SUCCEEDED(bag
->GetPropertyAsACString(u
"contentType"_ns
,
3483 NS_SUCCEEDED(aChannel
->GetContentType(contentType
))) {
3484 // XXX this is only necessary for viewsource:
3485 nsACString::const_iterator start
, end
, semicolon
;
3486 contentType
.BeginReading(start
);
3487 contentType
.EndReading(end
);
3489 FindCharInReadable(';', semicolon
, end
);
3490 SetContentType(Substring(start
, semicolon
));
3493 RetrieveRelevantHeaders(aChannel
);
3495 mChannel
= aChannel
;
3496 RecomputeResistFingerprinting();
3497 nsCOMPtr
<nsIInputStreamChannel
> inStrmChan
= do_QueryInterface(mChannel
);
3499 bool isSrcdocChannel
;
3500 inStrmChan
->GetIsSrcdocChannel(&isSrcdocChannel
);
3501 if (isSrcdocChannel
) {
3502 mIsSrcdocDocument
= true;
3507 nsLoadFlags loadFlags
;
3508 mChannel
->GetLoadFlags(&loadFlags
);
3509 bool isDocument
= false;
3510 mChannel
->GetIsDocument(&isDocument
);
3511 if (loadFlags
& nsIRequest::LOAD_DOCUMENT_NEEDS_COOKIE
&& isDocument
&&
3512 IsSynthesized() && XRE_IsContentProcess()) {
3513 ContentChild::UpdateCookieStatus(mChannel
);
3516 // Store the security info for future use.
3517 mChannel
->GetSecurityInfo(getter_AddRefs(mSecurityInfo
));
3520 // If this document is being loaded by a docshell, copy its sandbox flags
3521 // to the document, and store the fullscreen enabled flag. These are
3522 // immutable after being set here.
3523 nsCOMPtr
<nsIDocShell
> docShell
= do_QueryInterface(aContainer
);
3525 // If this is an error page, don't inherit sandbox flags
3526 nsCOMPtr
<nsILoadInfo
> loadInfo
= aChannel
->LoadInfo();
3527 if (docShell
&& !loadInfo
->GetLoadErrorPage()) {
3528 mSandboxFlags
= loadInfo
->GetSandboxFlags();
3529 WarnIfSandboxIneffective(docShell
, mSandboxFlags
, GetChannel());
3532 // Set the opener policy for the top level content document.
3533 nsCOMPtr
<nsIHttpChannelInternal
> httpChan
= do_QueryInterface(mChannel
);
3534 nsILoadInfo::CrossOriginOpenerPolicy policy
=
3535 nsILoadInfo::OPENER_POLICY_UNSAFE_NONE
;
3536 if (IsTopLevelContentDocument() && httpChan
&&
3537 NS_SUCCEEDED(httpChan
->GetCrossOriginOpenerPolicy(&policy
)) && docShell
&&
3538 docShell
->GetBrowsingContext()) {
3539 // Setting the opener policy on a discarded context has no effect.
3540 Unused
<< docShell
->GetBrowsingContext()->SetOpenerPolicy(policy
);
3543 // The CSP directives upgrade-insecure-requests as well as
3544 // block-all-mixed-content not only apply to the toplevel document,
3545 // but also to nested documents. The loadInfo of a subdocument
3546 // load already holds the correct flag, so let's just set it here
3547 // on the document. Please note that we set the appropriate preload
3548 // bits just for the sake of completeness here, because the preloader
3549 // does not reach into subdocuments.
3550 mUpgradeInsecureRequests
= loadInfo
->GetUpgradeInsecureRequests();
3551 mUpgradeInsecurePreloads
= mUpgradeInsecureRequests
;
3552 mBlockAllMixedContent
= loadInfo
->GetBlockAllMixedContent();
3553 mBlockAllMixedContentPreloads
= mBlockAllMixedContent
;
3555 // HTTPS-Only Mode flags
3556 // The HTTPS_ONLY_EXEMPT flag of the HTTPS-Only state gets propagated to all
3557 // sub-resources and sub-documents.
3558 mHttpsOnlyStatus
= loadInfo
->GetHttpsOnlyStatus();
3560 nsresult rv
= InitReferrerInfo(aChannel
);
3561 NS_ENSURE_SUCCESS(rv
, rv
);
3563 rv
= InitCOEP(aChannel
);
3564 NS_ENSURE_SUCCESS(rv
, rv
);
3566 // Check CSP navigate-to
3567 // We need to enforce the CSP of the document that initiated the load,
3568 // which is the CSP to inherit.
3569 nsCOMPtr
<nsIContentSecurityPolicy
> cspToInherit
= loadInfo
->GetCspToInherit();
3571 bool allowsNavigateTo
= false;
3572 rv
= cspToInherit
->GetAllowsNavigateTo(
3573 mDocumentURI
, loadInfo
->GetIsFormSubmission(),
3574 !loadInfo
->RedirectChain().IsEmpty(), /* aWasRedirected */
3575 true, /* aEnforceWhitelist */
3577 NS_ENSURE_SUCCESS(rv
, rv
);
3579 if (!allowsNavigateTo
) {
3580 aChannel
->Cancel(NS_ERROR_CSP_NAVIGATE_TO_VIOLATION
);
3585 rv
= InitCSP(aChannel
);
3586 NS_ENSURE_SUCCESS(rv
, rv
);
3588 // Initialize FeaturePolicy
3589 rv
= InitFeaturePolicy(aChannel
);
3590 NS_ENSURE_SUCCESS(rv
, rv
);
3592 rv
= loadInfo
->GetCookieJarSettings(getter_AddRefs(mCookieJarSettings
));
3593 NS_ENSURE_SUCCESS(rv
, rv
);
3595 // Generally XFO and CSP frame-ancestors is handled within
3596 // DocumentLoadListener. However, the DocumentLoadListener can not handle
3597 // object and embed. Until then we have to enforce it here (See Bug 1646899).
3598 nsContentPolicyType internalContentType
=
3599 loadInfo
->InternalContentPolicyType();
3600 if (internalContentType
== nsIContentPolicy::TYPE_INTERNAL_OBJECT
||
3601 internalContentType
== nsIContentPolicy::TYPE_INTERNAL_EMBED
) {
3602 nsContentSecurityUtils::PerformCSPFrameAncestorAndXFOCheck(aChannel
);
3605 aChannel
->GetStatus(&status
);
3606 if (status
== NS_ERROR_XFO_VIOLATION
) {
3607 // stop! ERROR page!
3608 // But before we have to reset the principal of the document
3609 // because the onload() event fires before the error page
3610 // is displayed and we do not want the enclosing document
3611 // to access the contentDocument.
3612 RefPtr
<NullPrincipal
> nullPrincipal
=
3613 NullPrincipal::CreateWithInheritedAttributes(NodePrincipal());
3614 // Before calling SetPrincipals() we should ensure that mFontFaceSet
3615 // and also GetInnerWindow() is still null at this point, before
3616 // we can fix Bug 1614735: Evaluate calls to SetPrincipal
3617 // within Document.cpp
3618 MOZ_ASSERT(!mFontFaceSet
&& !GetInnerWindow());
3619 SetPrincipals(nullPrincipal
, nullPrincipal
);
3626 void Document::SetLoadedAsData(bool aLoadedAsData
,
3627 bool aConsiderForMemoryReporting
) {
3628 mLoadedAsData
= aLoadedAsData
;
3629 if (aConsiderForMemoryReporting
) {
3630 nsIGlobalObject
* global
= GetScopeObject();
3632 if (nsPIDOMWindowInner
* window
= global
->AsInnerWindow()) {
3633 nsGlobalWindowInner::Cast(window
)
3634 ->RegisterDataDocumentForMemoryReporting(this);
3640 nsIContentSecurityPolicy
* Document::GetCsp() const { return mCSP
; }
3642 void Document::SetCsp(nsIContentSecurityPolicy
* aCSP
) { mCSP
= aCSP
; }
3644 nsIContentSecurityPolicy
* Document::GetPreloadCsp() const {
3648 void Document::SetPreloadCsp(nsIContentSecurityPolicy
* aPreloadCSP
) {
3649 mPreloadCSP
= aPreloadCSP
;
3652 void Document::GetCspJSON(nsString
& aJSON
) {
3656 dom::CSPPolicies jsonPolicies
;
3657 jsonPolicies
.ToJSON(aJSON
);
3660 mCSP
->ToJSON(aJSON
);
3663 void Document::SendToConsole(nsCOMArray
<nsISecurityConsoleMessage
>& aMessages
) {
3664 for (uint32_t i
= 0; i
< aMessages
.Length(); ++i
) {
3665 nsAutoString messageTag
;
3666 aMessages
[i
]->GetTag(messageTag
);
3668 nsAutoString category
;
3669 aMessages
[i
]->GetCategory(category
);
3671 nsContentUtils::ReportToConsole(nsIScriptError::warningFlag
,
3672 NS_ConvertUTF16toUTF8(category
), this,
3673 nsContentUtils::eSECURITY_PROPERTIES
,
3674 NS_ConvertUTF16toUTF8(messageTag
).get());
3678 void Document::ApplySettingsFromCSP(bool aSpeculative
) {
3679 nsresult rv
= NS_OK
;
3680 if (!aSpeculative
) {
3681 // 1) apply settings from regular CSP
3683 // Set up 'block-all-mixed-content' if not already inherited
3684 // from the parent context or set by any other CSP.
3685 if (!mBlockAllMixedContent
) {
3687 rv
= mCSP
->GetBlockAllMixedContent(&block
);
3688 NS_ENSURE_SUCCESS_VOID(rv
);
3689 mBlockAllMixedContent
= block
;
3691 if (!mBlockAllMixedContentPreloads
) {
3692 mBlockAllMixedContentPreloads
= mBlockAllMixedContent
;
3695 // Set up 'upgrade-insecure-requests' if not already inherited
3696 // from the parent context or set by any other CSP.
3697 if (!mUpgradeInsecureRequests
) {
3698 bool upgrade
= false;
3699 rv
= mCSP
->GetUpgradeInsecureRequests(&upgrade
);
3700 NS_ENSURE_SUCCESS_VOID(rv
);
3701 mUpgradeInsecureRequests
= upgrade
;
3703 if (!mUpgradeInsecurePreloads
) {
3704 mUpgradeInsecurePreloads
= mUpgradeInsecureRequests
;
3706 // Update csp settings in the parent process
3707 if (auto* wgc
= GetWindowGlobalChild()) {
3708 wgc
->SendUpdateDocumentCspSettings(mBlockAllMixedContent
,
3709 mUpgradeInsecureRequests
);
3715 // 2) apply settings from speculative csp
3717 if (!mBlockAllMixedContentPreloads
) {
3719 rv
= mPreloadCSP
->GetBlockAllMixedContent(&block
);
3720 NS_ENSURE_SUCCESS_VOID(rv
);
3721 mBlockAllMixedContent
= block
;
3723 if (!mUpgradeInsecurePreloads
) {
3724 bool upgrade
= false;
3725 rv
= mPreloadCSP
->GetUpgradeInsecureRequests(&upgrade
);
3726 NS_ENSURE_SUCCESS_VOID(rv
);
3727 mUpgradeInsecurePreloads
= upgrade
;
3732 nsresult
Document::InitCSP(nsIChannel
* aChannel
) {
3733 MOZ_ASSERT(!mScriptGlobalObject
,
3734 "CSP must be initialized before mScriptGlobalObject is set!");
3736 // If this is a data document - no need to set CSP.
3737 if (mLoadedAsData
) {
3741 // If this is an image, no need to set a CSP. Otherwise SVG images
3742 // served with a CSP might block internally applied inline styles.
3743 nsCOMPtr
<nsILoadInfo
> loadInfo
= aChannel
->LoadInfo();
3744 if (loadInfo
->GetExternalContentPolicyType() ==
3745 ExtContentPolicy::TYPE_IMAGE
||
3746 loadInfo
->GetExternalContentPolicyType() ==
3747 ExtContentPolicy::TYPE_IMAGESET
) {
3751 MOZ_ASSERT(!mCSP
, "where did mCSP get set if not here?");
3753 // If there is a CSP that needs to be inherited from whatever
3754 // global is considered the client of the document fetch then
3755 // we query it here from the loadinfo in case the newly created
3756 // document needs to inherit the CSP. See:
3757 // https://w3c.github.io/webappsec-csp/#initialize-document-csp
3758 bool inheritedCSP
= CSP_ShouldResponseInheritCSP(aChannel
);
3760 mCSP
= loadInfo
->GetCspToInherit();
3763 // If there is no CSP to inherit, then we create a new CSP here so
3764 // that history entries always have the right reference in case a
3765 // Meta CSP gets dynamically added after the history entry has
3766 // already been created.
3768 mCSP
= new nsCSPContext();
3771 // Always overwrite the requesting context of the CSP so that any new
3772 // 'self' keyword added to an inherited CSP translates correctly.
3773 nsresult rv
= mCSP
->SetRequestContextWithDocument(this);
3774 if (NS_WARN_IF(NS_FAILED(rv
))) {
3778 nsAutoCString tCspHeaderValue
, tCspROHeaderValue
;
3780 nsCOMPtr
<nsIHttpChannel
> httpChannel
;
3781 rv
= GetHttpChannelHelper(aChannel
, getter_AddRefs(httpChannel
));
3782 if (NS_WARN_IF(NS_FAILED(rv
))) {
3787 Unused
<< httpChannel
->GetResponseHeader("content-security-policy"_ns
,
3790 Unused
<< httpChannel
->GetResponseHeader(
3791 "content-security-policy-report-only"_ns
, tCspROHeaderValue
);
3793 NS_ConvertASCIItoUTF16
cspHeaderValue(tCspHeaderValue
);
3794 NS_ConvertASCIItoUTF16
cspROHeaderValue(tCspROHeaderValue
);
3796 // Check if this is a document from a WebExtension.
3797 nsCOMPtr
<nsIPrincipal
> principal
= NodePrincipal();
3798 auto addonPolicy
= BasePrincipal::Cast(principal
)->AddonPolicy();
3800 // If there's no CSP to apply, go ahead and return early
3801 if (!inheritedCSP
&& !addonPolicy
&& cspHeaderValue
.IsEmpty() &&
3802 cspROHeaderValue
.IsEmpty()) {
3803 if (MOZ_LOG_TEST(gCspPRLog
, LogLevel::Debug
)) {
3804 nsCOMPtr
<nsIURI
> chanURI
;
3805 aChannel
->GetURI(getter_AddRefs(chanURI
));
3806 nsAutoCString aspec
;
3807 chanURI
->GetAsciiSpec(aspec
);
3808 MOZ_LOG(gCspPRLog
, LogLevel::Debug
,
3809 ("no CSP for document, %s", aspec
.get()));
3815 MOZ_LOG(gCspPRLog
, LogLevel::Debug
,
3816 ("Document is an add-on or CSP header specified %p", this));
3818 // ----- if the doc is an addon, apply its CSP.
3820 mCSP
->AppendPolicy(addonPolicy
->BaseCSP(), false, false);
3822 mCSP
->AppendPolicy(addonPolicy
->ExtensionPageCSP(), false, false);
3823 // Bug 1548468: Move CSP off ExpandedPrincipal
3824 // Currently the LoadInfo holds the source of truth for every resource load
3825 // because LoadInfo::GetCsp() queries the CSP from an ExpandedPrincipal
3826 // (and not from the Client) if the load was triggered by an extension.
3827 auto* basePrin
= BasePrincipal::Cast(principal
);
3828 if (basePrin
->Is
<ExpandedPrincipal
>()) {
3829 basePrin
->As
<ExpandedPrincipal
>()->SetCsp(mCSP
);
3833 // ----- if there's a full-strength CSP header, apply it.
3834 if (!cspHeaderValue
.IsEmpty()) {
3835 mHasCSPDeliveredThroughHeader
= true;
3836 rv
= CSP_AppendCSPFromHeader(mCSP
, cspHeaderValue
, false);
3837 NS_ENSURE_SUCCESS(rv
, rv
);
3840 // ----- if there's a report-only CSP header, apply it.
3841 if (!cspROHeaderValue
.IsEmpty()) {
3842 rv
= CSP_AppendCSPFromHeader(mCSP
, cspROHeaderValue
, true);
3843 NS_ENSURE_SUCCESS(rv
, rv
);
3846 // ----- Enforce sandbox policy if supplied in CSP header
3847 // The document may already have some sandbox flags set (e.g. if the document
3848 // is an iframe with the sandbox attribute set). If we have a CSP sandbox
3849 // directive, intersect the CSP sandbox flags with the existing flags. This
3850 // corresponds to the _least_ permissive policy.
3851 uint32_t cspSandboxFlags
= SANDBOXED_NONE
;
3852 rv
= mCSP
->GetCSPSandboxFlags(&cspSandboxFlags
);
3853 NS_ENSURE_SUCCESS(rv
, rv
);
3855 // Probably the iframe sandbox attribute already caused the creation of a
3856 // new NullPrincipal. Only create a new NullPrincipal if CSP requires so
3857 // and no one has been created yet.
3858 bool needNewNullPrincipal
= (cspSandboxFlags
& SANDBOXED_ORIGIN
) &&
3859 !(mSandboxFlags
& SANDBOXED_ORIGIN
);
3861 mSandboxFlags
|= cspSandboxFlags
;
3863 if (needNewNullPrincipal
) {
3864 principal
= NullPrincipal::CreateWithInheritedAttributes(principal
);
3865 // Skip setting the content blocking allowlist principal to NullPrincipal.
3866 // The principal is only used to enable/disable trackingprotection via
3867 // permission and can be shared with the top level sandboxed site.
3869 SetPrincipals(principal
, principal
);
3872 ApplySettingsFromCSP(false);
3876 static Document
* GetInProcessParentDocumentFrom(BrowsingContext
* aContext
) {
3877 BrowsingContext
* parentContext
= aContext
->GetParent();
3878 if (!parentContext
) {
3882 WindowContext
* windowContext
= parentContext
->GetCurrentWindowContext();
3883 if (!windowContext
) {
3887 return windowContext
->GetDocument();
3890 already_AddRefed
<dom::FeaturePolicy
> Document::GetParentFeaturePolicy() {
3891 BrowsingContext
* browsingContext
= GetBrowsingContext();
3892 if (!browsingContext
) {
3895 if (!browsingContext
->IsContentSubframe()) {
3899 HTMLIFrameElement
* iframe
=
3900 HTMLIFrameElement::FromNodeOrNull(browsingContext
->GetEmbedderElement());
3902 return do_AddRef(iframe
->FeaturePolicy());
3905 if (XRE_IsParentProcess()) {
3906 return do_AddRef(browsingContext
->Canonical()->GetContainerFeaturePolicy());
3909 if (Document
* parentDocument
=
3910 GetInProcessParentDocumentFrom(browsingContext
)) {
3911 return do_AddRef(parentDocument
->FeaturePolicy());
3914 WindowContext
* windowContext
= browsingContext
->GetCurrentWindowContext();
3915 if (!windowContext
) {
3919 WindowGlobalChild
* child
= windowContext
->GetWindowGlobalChild();
3924 return do_AddRef(child
->GetContainerFeaturePolicy());
3927 void Document::InitFeaturePolicy() {
3928 MOZ_ASSERT(mFeaturePolicy
, "we should have FeaturePolicy created");
3930 mFeaturePolicy
->ResetDeclaredPolicy();
3932 mFeaturePolicy
->SetDefaultOrigin(NodePrincipal());
3934 RefPtr
<mozilla::dom::FeaturePolicy
> parentPolicy
= GetParentFeaturePolicy();
3936 // Let's inherit the policy from the parent HTMLIFrameElement if it exists.
3937 mFeaturePolicy
->InheritPolicy(parentPolicy
);
3938 mFeaturePolicy
->SetSrcOrigin(parentPolicy
->GetSrcOrigin());
3942 nsresult
Document::InitFeaturePolicy(nsIChannel
* aChannel
) {
3943 InitFeaturePolicy();
3945 // We don't want to parse the http Feature-Policy header if this pref is off.
3946 if (!StaticPrefs::dom_security_featurePolicy_header_enabled()) {
3950 nsCOMPtr
<nsIHttpChannel
> httpChannel
;
3951 nsresult rv
= GetHttpChannelHelper(aChannel
, getter_AddRefs(httpChannel
));
3952 if (NS_WARN_IF(NS_FAILED(rv
))) {
3960 // query the policy from the header
3961 nsAutoCString value
;
3962 rv
= httpChannel
->GetResponseHeader("Feature-Policy"_ns
, value
);
3963 if (NS_SUCCEEDED(rv
)) {
3964 mFeaturePolicy
->SetDeclaredPolicy(this, NS_ConvertUTF8toUTF16(value
),
3965 NodePrincipal(), nullptr);
3971 void Document::EnsureNotEnteringAndExitFullscreen() {
3972 Document::ClearPendingFullscreenRequests(this);
3973 if (GetFullscreenElement()) {
3974 Document::AsyncExitFullscreen(this);
3978 void Document::SetReferrerInfo(nsIReferrerInfo
* aReferrerInfo
) {
3979 mReferrerInfo
= aReferrerInfo
;
3980 mCachedReferrerInfoForInternalCSSAndSVGResources
= nullptr;
3981 mCachedURLData
= nullptr;
3984 nsresult
Document::InitReferrerInfo(nsIChannel
* aChannel
) {
3985 MOZ_ASSERT(mReferrerInfo
);
3986 MOZ_ASSERT(mPreloadReferrerInfo
);
3988 if (ReferrerInfo::ShouldResponseInheritReferrerInfo(aChannel
)) {
3989 // The channel is loading `about:srcdoc`. Srcdoc loads should respond with
3990 // their parent's ReferrerInfo when asked for their ReferrerInfo, unless
3991 // they have an opaque origin.
3992 // https://w3c.github.io/webappsec-referrer-policy/#determine-requests-referrer
3993 if (BrowsingContext
* bc
= GetBrowsingContext()) {
3994 // At this point the document is not fully created and mParentDocument has
3995 // not been set yet,
3996 Document
* parentDoc
= bc
->GetEmbedderElement()
3997 ? bc
->GetEmbedderElement()->OwnerDoc()
4000 SetReferrerInfo(parentDoc
->GetReferrerInfo());
4001 mPreloadReferrerInfo
= mReferrerInfo
;
4005 MOZ_ASSERT(bc
->IsInProcess() || NodePrincipal()->GetIsNullPrincipal(),
4006 "srcdoc without null principal as toplevel!");
4010 nsCOMPtr
<nsIHttpChannel
> httpChannel
;
4011 nsresult rv
= GetHttpChannelHelper(aChannel
, getter_AddRefs(httpChannel
));
4012 if (NS_WARN_IF(NS_FAILED(rv
))) {
4020 if (nsCOMPtr
<nsIReferrerInfo
> referrerInfo
= httpChannel
->GetReferrerInfo()) {
4021 SetReferrerInfo(referrerInfo
);
4024 // Override policy if we get one from Referrerr-Policy header
4025 mozilla::dom::ReferrerPolicy policy
=
4026 nsContentUtils::GetReferrerPolicyFromChannel(aChannel
);
4027 nsCOMPtr
<nsIReferrerInfo
> clone
=
4028 static_cast<dom::ReferrerInfo
*>(mReferrerInfo
.get())
4029 ->CloneWithNewPolicy(policy
);
4030 SetReferrerInfo(clone
);
4031 mPreloadReferrerInfo
= mReferrerInfo
;
4035 nsresult
Document::InitCOEP(nsIChannel
* aChannel
) {
4036 nsCOMPtr
<nsIHttpChannel
> httpChannel
;
4037 nsresult rv
= GetHttpChannelHelper(aChannel
, getter_AddRefs(httpChannel
));
4038 if (NS_FAILED(rv
)) {
4042 nsCOMPtr
<nsIHttpChannelInternal
> intChannel
= do_QueryInterface(httpChannel
);
4048 nsILoadInfo::CrossOriginEmbedderPolicy policy
=
4049 nsILoadInfo::EMBEDDER_POLICY_NULL
;
4050 if (NS_SUCCEEDED(intChannel
->GetResponseEmbedderPolicy(
4051 mTrials
.IsEnabled(OriginTrial::CoepCredentialless
), &policy
))) {
4052 mEmbedderPolicy
= Some(policy
);
4058 void Document::StopDocumentLoad() {
4060 mParserAborted
= true;
4061 mParser
->Terminate();
4065 void Document::SetDocumentURI(nsIURI
* aURI
) {
4066 nsCOMPtr
<nsIURI
> oldBase
= GetDocBaseURI();
4067 mDocumentURI
= aURI
;
4068 nsIURI
* newBase
= GetDocBaseURI();
4070 mChromeRulesEnabled
= URLExtraData::ChromeRulesEnabled(aURI
);
4072 bool equalBases
= false;
4073 // Changing just the ref of a URI does not change how relative URIs would
4074 // resolve wrt to it, so we can treat the bases as equal as long as they're
4075 // equal ignoring the ref.
4076 if (oldBase
&& newBase
) {
4077 oldBase
->EqualsExceptRef(newBase
, &equalBases
);
4079 equalBases
= !oldBase
&& !newBase
;
4082 // If this is the first time we're setting the document's URI, set the
4083 // document's original URI.
4084 if (!mOriginalURI
) mOriginalURI
= mDocumentURI
;
4086 // If changing the document's URI changed the base URI of the document, we
4087 // need to refresh the hrefs of all the links on the page.
4089 mCachedURLData
= nullptr;
4093 // Recalculate our base domain
4094 mBaseDomain
.Truncate();
4095 ThirdPartyUtil
* thirdPartyUtil
= ThirdPartyUtil::GetInstance();
4096 if (thirdPartyUtil
) {
4097 Unused
<< thirdPartyUtil
->GetBaseDomain(mDocumentURI
, mBaseDomain
);
4100 // Tell our WindowGlobalParent that the document's URI has been changed.
4101 if (WindowGlobalChild
* wgc
= GetWindowGlobalChild()) {
4102 wgc
->SetDocumentURI(mDocumentURI
);
4106 static void GetFormattedTimeString(PRTime aTime
,
4107 nsAString
& aFormattedTimeString
) {
4108 PRExplodedTime prtime
;
4109 PR_ExplodeTime(aTime
, PR_LocalTimeParameters
, &prtime
);
4110 // "MM/DD/YYYY hh:mm:ss"
4111 char formatedTime
[24];
4112 if (SprintfLiteral(formatedTime
, "%02d/%02d/%04d %02d:%02d:%02d",
4113 prtime
.tm_month
+ 1, prtime
.tm_mday
, int(prtime
.tm_year
),
4114 prtime
.tm_hour
, prtime
.tm_min
, prtime
.tm_sec
)) {
4115 CopyASCIItoUTF16(nsDependentCString(formatedTime
), aFormattedTimeString
);
4117 // If we for whatever reason failed to find the last modified time
4118 // (or even the current time), fall back to what NS4.x returned.
4119 aFormattedTimeString
.AssignLiteral(u
"01/01/1970 00:00:00");
4123 void Document::GetLastModified(nsAString
& aLastModified
) const {
4124 if (!mLastModified
.IsEmpty()) {
4125 aLastModified
.Assign(mLastModified
);
4127 GetFormattedTimeString(PR_Now(), aLastModified
);
4131 static void IncrementExpandoGeneration(Document
& aDoc
) {
4132 ++aDoc
.mExpandoAndGeneration
.generation
;
4135 void Document::AddToNameTable(Element
* aElement
, nsAtom
* aName
) {
4137 nsGenericHTMLElement::ShouldExposeNameAsHTMLDocumentProperty(aElement
),
4138 "Only put elements that need to be exposed as document['name'] in "
4139 "the named table.");
4141 IdentifierMapEntry
* entry
= mIdentifierMap
.PutEntry(aName
);
4143 // Null for out-of-memory
4145 if (!entry
->HasNameElement() &&
4146 !entry
->HasIdElementExposedAsHTMLDocumentProperty()) {
4147 IncrementExpandoGeneration(*this);
4149 entry
->AddNameElement(this, aElement
);
4153 void Document::RemoveFromNameTable(Element
* aElement
, nsAtom
* aName
) {
4154 // Speed up document teardown
4155 if (mIdentifierMap
.Count() == 0) return;
4157 IdentifierMapEntry
* entry
= mIdentifierMap
.GetEntry(aName
);
4158 if (!entry
) // Could be false if the element was anonymous, hence never added
4161 entry
->RemoveNameElement(aElement
);
4162 if (!entry
->HasNameElement() &&
4163 !entry
->HasIdElementExposedAsHTMLDocumentProperty()) {
4164 IncrementExpandoGeneration(*this);
4168 void Document::AddToIdTable(Element
* aElement
, nsAtom
* aId
) {
4169 IdentifierMapEntry
* entry
= mIdentifierMap
.PutEntry(aId
);
4171 if (entry
) { /* True except on OOM */
4172 if (nsGenericHTMLElement::ShouldExposeIdAsHTMLDocumentProperty(aElement
) &&
4173 !entry
->HasNameElement() &&
4174 !entry
->HasIdElementExposedAsHTMLDocumentProperty()) {
4175 IncrementExpandoGeneration(*this);
4177 entry
->AddIdElement(aElement
);
4181 void Document::RemoveFromIdTable(Element
* aElement
, nsAtom
* aId
) {
4182 NS_ASSERTION(aId
, "huhwhatnow?");
4184 // Speed up document teardown
4185 if (mIdentifierMap
.Count() == 0) {
4189 IdentifierMapEntry
* entry
= mIdentifierMap
.GetEntry(aId
);
4190 if (!entry
) // Can be null for XML elements with changing ids.
4193 entry
->RemoveIdElement(aElement
);
4194 if (nsGenericHTMLElement::ShouldExposeIdAsHTMLDocumentProperty(aElement
) &&
4195 !entry
->HasNameElement() &&
4196 !entry
->HasIdElementExposedAsHTMLDocumentProperty()) {
4197 IncrementExpandoGeneration(*this);
4199 if (entry
->IsEmpty()) {
4200 mIdentifierMap
.RemoveEntry(entry
);
4204 void Document::UpdateReferrerInfoFromMeta(const nsAString
& aMetaReferrer
,
4206 ReferrerPolicyEnum policy
=
4207 ReferrerInfo::ReferrerPolicyFromMetaString(aMetaReferrer
);
4208 // The empty string "" corresponds to no referrer policy, causing a fallback
4209 // to a referrer policy defined elsewhere.
4210 if (policy
== ReferrerPolicy::_empty
) {
4214 MOZ_ASSERT(mReferrerInfo
);
4215 MOZ_ASSERT(mPreloadReferrerInfo
);
4218 mPreloadReferrerInfo
=
4219 static_cast<mozilla::dom::ReferrerInfo
*>((mPreloadReferrerInfo
).get())
4220 ->CloneWithNewPolicy(policy
);
4222 nsCOMPtr
<nsIReferrerInfo
> clone
=
4223 static_cast<mozilla::dom::ReferrerInfo
*>((mReferrerInfo
).get())
4224 ->CloneWithNewPolicy(policy
);
4225 SetReferrerInfo(clone
);
4229 void Document::SetPrincipals(nsIPrincipal
* aNewPrincipal
,
4230 nsIPrincipal
* aNewPartitionedPrincipal
) {
4231 MOZ_ASSERT(!!aNewPrincipal
== !!aNewPartitionedPrincipal
);
4232 if (aNewPrincipal
&& mAllowDNSPrefetch
&&
4233 StaticPrefs::network_dns_disablePrefetchFromHTTPS()) {
4234 if (aNewPrincipal
->SchemeIs("https")) {
4235 mAllowDNSPrefetch
= false;
4239 mCSSLoader
->DeregisterFromSheetCache();
4241 mNodeInfoManager
->SetDocumentPrincipal(aNewPrincipal
);
4242 mPartitionedPrincipal
= aNewPartitionedPrincipal
;
4244 mCachedURLData
= nullptr;
4246 mCSSLoader
->RegisterInSheetCache();
4249 // Validate that the docgroup is set correctly by calling its getter and
4250 // triggering its sanity check.
4252 // If we're setting the principal to null, we don't want to perform the check,
4253 // as the document is entering an intermediate state where it does not have a
4254 // principal. It will be given another real principal shortly which we will
4255 // check. It's not unsafe to have a document which has a null principal in the
4256 // same docgroup as another document, so this should not be a problem.
4257 if (aNewPrincipal
) {
4264 void Document::AssertDocGroupMatchesKey() const {
4265 // Sanity check that we have an up-to-date and accurate docgroup
4266 // We only check if the principal when we can get the browsing context.
4268 // Note that we can be invoked during cycle collection, so we need to handle
4269 // the browsingcontext being partially unlinked - normally you shouldn't
4270 // null-check `Group()` as it shouldn't return nullptr.
4271 if (!GetBrowsingContext() || !GetBrowsingContext()->Group()) {
4275 if (mDocGroup
&& mDocGroup
->GetBrowsingContextGroup()) {
4276 MOZ_ASSERT(mDocGroup
->GetBrowsingContextGroup() ==
4277 GetBrowsingContext()->Group());
4279 // GetKey() can fail, e.g. after the TLD service has shut down.
4280 nsAutoCString docGroupKey
;
4281 nsresult rv
= mozilla::dom::DocGroup::GetKey(
4283 GetBrowsingContext()->Group()->IsPotentiallyCrossOriginIsolated(),
4285 if (NS_SUCCEEDED(rv
)) {
4286 MOZ_ASSERT(mDocGroup
->MatchesKey(docGroupKey
));
4292 nsresult
Document::Dispatch(TaskCategory aCategory
,
4293 already_AddRefed
<nsIRunnable
>&& aRunnable
) {
4294 // Note that this method may be called off the main thread.
4296 return mDocGroup
->Dispatch(aCategory
, std::move(aRunnable
));
4298 return DispatcherTrait::Dispatch(aCategory
, std::move(aRunnable
));
4301 nsISerialEventTarget
* Document::EventTargetFor(TaskCategory aCategory
) const {
4303 return mDocGroup
->EventTargetFor(aCategory
);
4305 return DispatcherTrait::EventTargetFor(aCategory
);
4308 AbstractThread
* Document::AbstractMainThreadFor(
4309 mozilla::TaskCategory aCategory
) {
4310 MOZ_ASSERT(NS_IsMainThread());
4312 return mDocGroup
->AbstractMainThreadFor(aCategory
);
4314 return DispatcherTrait::AbstractMainThreadFor(aCategory
);
4317 void Document::NoteScriptTrackingStatus(const nsACString
& aURL
,
4320 mTrackingScripts
.Insert(aURL
);
4322 MOZ_ASSERT(!mTrackingScripts
.Contains(aURL
));
4326 bool Document::IsScriptTracking(JSContext
* aCx
) const {
4327 JS::AutoFilename filename
;
4329 uint32_t column
= 0;
4330 if (!JS::DescribeScriptedCaller(aCx
, &filename
, &line
, &column
)) {
4333 return mTrackingScripts
.Contains(nsDependentCString(filename
.get()));
4336 void Document::GetContentType(nsAString
& aContentType
) {
4337 CopyUTF8toUTF16(GetContentTypeInternal(), aContentType
);
4340 void Document::SetContentType(const nsACString
& aContentType
) {
4341 if (!IsHTMLOrXHTML() && mDefaultElementType
== kNameSpaceID_None
&&
4342 aContentType
.EqualsLiteral("application/xhtml+xml")) {
4343 mDefaultElementType
= kNameSpaceID_XHTML
;
4346 mCachedEncoder
= nullptr;
4347 mContentType
= aContentType
;
4350 bool Document::GetAllowPlugins() {
4351 // First, we ask our docshell if it allows plugins.
4352 auto* browsingContext
= GetBrowsingContext();
4354 if (browsingContext
) {
4355 if (!browsingContext
->GetAllowPlugins()) {
4359 // If the docshell allows plugins, we check whether
4360 // we are sandboxed and plugins should not be allowed.
4361 if (mSandboxFlags
& SANDBOXED_PLUGINS
) {
4369 bool Document::HasPendingInitialTranslation() {
4370 return mDocumentL10n
&& mDocumentL10n
->GetState() != DocumentL10nState::Ready
;
4373 bool Document::HasPendingL10nMutations() const {
4374 return mDocumentL10n
&& mDocumentL10n
->HasPendingMutations();
4377 bool Document::DocumentSupportsL10n(JSContext
* aCx
, JSObject
* aObject
) {
4378 JS::Rooted
<JSObject
*> object(aCx
, aObject
);
4379 nsCOMPtr
<nsIPrincipal
> callerPrincipal
=
4380 nsContentUtils::SubjectPrincipal(aCx
);
4381 nsGlobalWindowInner
* win
= xpc::WindowOrNull(object
);
4382 bool allowed
= false;
4383 callerPrincipal
->IsL10nAllowed(win
? win
->GetDocumentURI() : nullptr,
4388 void Document::LocalizationLinkAdded(Element
* aLinkElement
) {
4389 if (!AllowsL10n()) {
4394 aLinkElement
->GetAttr(kNameSpaceID_None
, nsGkAtoms::href
, href
);
4396 if (!mDocumentL10n
) {
4397 Element
* elem
= GetDocumentElement();
4398 MOZ_DIAGNOSTIC_ASSERT(elem
);
4400 bool isSync
= elem
->HasAttr(nsGkAtoms::datal10nsync
);
4401 mDocumentL10n
= DocumentL10n::Create(this, isSync
);
4402 if (NS_WARN_IF(!mDocumentL10n
)) {
4407 mDocumentL10n
->AddResourceId(NS_ConvertUTF16toUTF8(href
));
4409 if (mReadyState
>= READYSTATE_INTERACTIVE
) {
4410 nsContentUtils::AddScriptRunner(NewRunnableMethod(
4411 "DocumentL10n::TriggerInitialTranslation()", mDocumentL10n
,
4412 &DocumentL10n::TriggerInitialTranslation
));
4414 if (!mDocumentL10n
->mBlockingLayout
) {
4415 // Our initial translation is going to block layout start. Make sure
4416 // we don't fire the load event until after that stops happening and
4417 // layout has a chance to start.
4419 mDocumentL10n
->mBlockingLayout
= true;
4424 void Document::LocalizationLinkRemoved(Element
* aLinkElement
) {
4425 if (!AllowsL10n()) {
4429 if (mDocumentL10n
) {
4431 aLinkElement
->GetAttr(kNameSpaceID_None
, nsGkAtoms::href
, href
);
4432 uint32_t remaining
=
4433 mDocumentL10n
->RemoveResourceId(NS_ConvertUTF16toUTF8(href
));
4434 if (remaining
== 0) {
4435 if (mDocumentL10n
->mBlockingLayout
) {
4436 mDocumentL10n
->mBlockingLayout
= false;
4437 UnblockOnload(/* aFireSync = */ false);
4439 mDocumentL10n
= nullptr;
4445 * This method should be called once the end of the l10n
4446 * resource container has been parsed.
4448 * In XUL this is the end of the first </linkset>,
4449 * In XHTML/HTML this is the end of </head>.
4451 * This milestone is used to allow for batch
4452 * localization context I/O and building done
4453 * once when all resources in the document have been
4456 void Document::OnL10nResourceContainerParsed() {
4457 // XXX: This is a scaffolding for where we might inject prefetch
4461 void Document::OnParsingCompleted() {
4462 // Let's call it again, in case the resource
4463 // container has not been closed, and only
4464 // now we're closing the document.
4465 OnL10nResourceContainerParsed();
4467 if (mDocumentL10n
) {
4468 RefPtr
<DocumentL10n
> l10n
= mDocumentL10n
;
4469 l10n
->TriggerInitialTranslation();
4473 void Document::InitialTranslationCompleted(bool aL10nCached
) {
4474 if (mDocumentL10n
&& mDocumentL10n
->mBlockingLayout
) {
4475 // This means we blocked the load event in LocalizationLinkAdded. It's
4476 // important that the load blocker removal here be async, because our caller
4477 // will notify the content sink after us, and we want the content sync's
4478 // work to happen before the load event fires.
4479 mDocumentL10n
->mBlockingLayout
= false;
4480 UnblockOnload(/* aFireSync = */ false);
4483 mL10nProtoElements
.Clear();
4485 nsXULPrototypeDocument
* proto
= GetPrototype();
4487 proto
->SetIsL10nCached(aL10nCached
);
4491 bool Document::AllowsL10n() const {
4492 if (IsStaticDocument()) {
4493 // We don't allow l10n on static documents, because the nodes are already
4494 // cloned translated, and static docs don't get parsed so we never
4495 // TriggerInitialTranslation, etc, so a load blocker would keep hanging
4499 bool allowed
= false;
4500 NodePrincipal()->IsL10nAllowed(GetDocumentURI(), &allowed
);
4504 bool Document::IsWebAnimationsEnabled(JSContext
* aCx
, JSObject
* /*unused*/) {
4505 MOZ_ASSERT(NS_IsMainThread());
4507 return nsContentUtils::IsSystemCaller(aCx
) ||
4508 StaticPrefs::dom_animations_api_core_enabled();
4511 bool Document::IsWebAnimationsEnabled(CallerType aCallerType
) {
4512 MOZ_ASSERT(NS_IsMainThread());
4514 return aCallerType
== dom::CallerType::System
||
4515 StaticPrefs::dom_animations_api_core_enabled();
4518 bool Document::IsWebAnimationsGetAnimationsEnabled(JSContext
* aCx
,
4519 JSObject
* /*unused*/
4521 MOZ_ASSERT(NS_IsMainThread());
4523 return nsContentUtils::IsSystemCaller(aCx
) ||
4524 StaticPrefs::dom_animations_api_getAnimations_enabled();
4527 bool Document::AreWebAnimationsImplicitKeyframesEnabled(JSContext
* aCx
,
4528 JSObject
* /*unused*/
4530 MOZ_ASSERT(NS_IsMainThread());
4532 return nsContentUtils::IsSystemCaller(aCx
) ||
4533 StaticPrefs::dom_animations_api_implicit_keyframes_enabled();
4536 bool Document::AreWebAnimationsTimelinesEnabled(JSContext
* aCx
,
4537 JSObject
* /*unused*/
4539 MOZ_ASSERT(NS_IsMainThread());
4541 return nsContentUtils::IsSystemCaller(aCx
) ||
4542 StaticPrefs::dom_animations_api_timelines_enabled();
4545 DocumentTimeline
* Document::Timeline() {
4546 if (!mDocumentTimeline
) {
4547 mDocumentTimeline
= new DocumentTimeline(this, TimeDuration(0));
4550 return mDocumentTimeline
;
4553 SVGSVGElement
* Document::GetSVGRootElement() const {
4554 Element
* root
= GetRootElement();
4555 if (!root
|| !root
->IsSVGElement(nsGkAtoms::svg
)) {
4558 return static_cast<SVGSVGElement
*>(root
);
4561 /* Return true if the document is in the focused top-level window, and is an
4562 * ancestor of the focused DOMWindow. */
4563 bool Document::HasFocus(ErrorResult
& rv
) const {
4564 nsFocusManager
* fm
= nsFocusManager::GetFocusManager();
4566 rv
.Throw(NS_ERROR_NOT_AVAILABLE
);
4570 BrowsingContext
* bc
= GetBrowsingContext();
4575 if (!fm
->IsInActiveWindow(bc
)) {
4579 return fm
->IsSameOrAncestor(bc
, fm
->GetFocusedBrowsingContext());
4582 bool Document::ThisDocumentHasFocus() const {
4583 nsFocusManager
* fm
= nsFocusManager::GetFocusManager();
4584 return fm
&& fm
->GetFocusedWindow() &&
4585 fm
->GetFocusedWindow()->GetExtantDoc() == this;
4588 void Document::GetDesignMode(nsAString
& aDesignMode
) {
4589 if (IsInDesignMode()) {
4590 aDesignMode
.AssignLiteral("on");
4592 aDesignMode
.AssignLiteral("off");
4596 void Document::SetDesignMode(const nsAString
& aDesignMode
,
4597 nsIPrincipal
& aSubjectPrincipal
, ErrorResult
& rv
) {
4598 SetDesignMode(aDesignMode
, Some(&aSubjectPrincipal
), rv
);
4601 static void NotifyEditableStateChange(Document
& aDoc
) {
4602 #ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
4605 for (nsIContent
* node
= aDoc
.GetNextNode(&aDoc
); node
;
4606 node
= node
->GetNextNode(&aDoc
)) {
4607 if (auto* element
= Element::FromNode(node
)) {
4608 element
->UpdateState(true);
4611 MOZ_DIAGNOSTIC_ASSERT(!g
.Mutated(0));
4614 void Document::SetDesignMode(const nsAString
& aDesignMode
,
4615 const Maybe
<nsIPrincipal
*>& aSubjectPrincipal
,
4617 if (aSubjectPrincipal
.isSome() &&
4618 !aSubjectPrincipal
.value()->Subsumes(NodePrincipal())) {
4619 rv
.Throw(NS_ERROR_DOM_PROP_ACCESS_DENIED
);
4622 const bool editableMode
= IsInDesignMode();
4623 if (aDesignMode
.LowerCaseEqualsASCII(editableMode
? "off" : "on")) {
4624 SetEditableFlag(!editableMode
);
4625 // Changing the NODE_IS_EDITABLE flags on document changes the intrinsic
4626 // state of all descendant elements of it. Update that now.
4627 NotifyEditableStateChange(*this);
4628 rv
= EditingStateChanged();
4632 nsCommandManager
* Document::GetMidasCommandManager() {
4633 // check if we have it cached
4634 if (mMidasCommandManager
) {
4635 return mMidasCommandManager
;
4638 nsPIDOMWindowOuter
* window
= GetWindow();
4643 nsIDocShell
* docshell
= window
->GetDocShell();
4648 mMidasCommandManager
= docshell
->GetCommandManager();
4649 return mMidasCommandManager
;
4653 void Document::EnsureInitializeInternalCommandDataHashtable() {
4654 if (sInternalCommandDataHashtable
) {
4657 using CommandOnTextEditor
= InternalCommandData::CommandOnTextEditor
;
4658 sInternalCommandDataHashtable
= new InternalCommandDataHashtable();
4660 sInternalCommandDataHashtable
->InsertOrUpdate(
4662 InternalCommandData(
4664 Command::FormatBold
,
4665 ExecCommandParam::Ignore
,
4666 StyleUpdatingCommand::GetInstance
,
4667 CommandOnTextEditor::Disabled
));
4668 sInternalCommandDataHashtable
->InsertOrUpdate(
4670 InternalCommandData(
4672 Command::FormatItalic
,
4673 ExecCommandParam::Ignore
,
4674 StyleUpdatingCommand::GetInstance
,
4675 CommandOnTextEditor::Disabled
));
4676 sInternalCommandDataHashtable
->InsertOrUpdate(
4678 InternalCommandData(
4680 Command::FormatUnderline
,
4681 ExecCommandParam::Ignore
,
4682 StyleUpdatingCommand::GetInstance
,
4683 CommandOnTextEditor::Disabled
));
4684 sInternalCommandDataHashtable
->InsertOrUpdate(
4685 u
"strikethrough"_ns
,
4686 InternalCommandData(
4687 "cmd_strikethrough",
4688 Command::FormatStrikeThrough
,
4689 ExecCommandParam::Ignore
,
4690 StyleUpdatingCommand::GetInstance
,
4691 CommandOnTextEditor::Disabled
));
4692 sInternalCommandDataHashtable
->InsertOrUpdate(
4694 InternalCommandData(
4696 Command::FormatSubscript
,
4697 ExecCommandParam::Ignore
,
4698 StyleUpdatingCommand::GetInstance
,
4699 CommandOnTextEditor::Disabled
));
4700 sInternalCommandDataHashtable
->InsertOrUpdate(
4702 InternalCommandData(
4704 Command::FormatSuperscript
,
4705 ExecCommandParam::Ignore
,
4706 StyleUpdatingCommand::GetInstance
,
4707 CommandOnTextEditor::Disabled
));
4708 sInternalCommandDataHashtable
->InsertOrUpdate(
4710 InternalCommandData(
4713 ExecCommandParam::Ignore
,
4714 CutCommand::GetInstance
,
4715 CommandOnTextEditor::Enabled
));
4716 sInternalCommandDataHashtable
->InsertOrUpdate(
4718 InternalCommandData(
4721 ExecCommandParam::Ignore
,
4722 CopyCommand::GetInstance
,
4723 CommandOnTextEditor::Enabled
));
4724 sInternalCommandDataHashtable
->InsertOrUpdate(
4726 InternalCommandData(
4729 ExecCommandParam::Ignore
,
4730 PasteCommand::GetInstance
,
4731 CommandOnTextEditor::Enabled
));
4732 sInternalCommandDataHashtable
->InsertOrUpdate(
4734 InternalCommandData(
4735 "cmd_deleteCharBackward",
4736 Command::DeleteCharBackward
,
4737 ExecCommandParam::Ignore
,
4738 DeleteCommand::GetInstance
,
4739 CommandOnTextEditor::Enabled
));
4740 sInternalCommandDataHashtable
->InsertOrUpdate(
4741 u
"forwarddelete"_ns
,
4742 InternalCommandData(
4743 "cmd_deleteCharForward",
4744 Command::DeleteCharForward
,
4745 ExecCommandParam::Ignore
,
4746 DeleteCommand::GetInstance
,
4747 CommandOnTextEditor::Enabled
));
4748 sInternalCommandDataHashtable
->InsertOrUpdate(
4750 InternalCommandData(
4753 ExecCommandParam::Ignore
,
4754 SelectAllCommand::GetInstance
,
4755 CommandOnTextEditor::Enabled
));
4756 sInternalCommandDataHashtable
->InsertOrUpdate(
4758 InternalCommandData(
4760 Command::HistoryUndo
,
4761 ExecCommandParam::Ignore
,
4762 UndoCommand::GetInstance
,
4763 CommandOnTextEditor::Enabled
));
4764 sInternalCommandDataHashtable
->InsertOrUpdate(
4766 InternalCommandData(
4768 Command::HistoryRedo
,
4769 ExecCommandParam::Ignore
,
4770 RedoCommand::GetInstance
,
4771 CommandOnTextEditor::Enabled
));
4772 sInternalCommandDataHashtable
->InsertOrUpdate(
4774 InternalCommandData("cmd_indent",
4775 Command::FormatIndent
,
4776 ExecCommandParam::Ignore
,
4777 IndentCommand::GetInstance
,
4778 CommandOnTextEditor::Disabled
));
4779 sInternalCommandDataHashtable
->InsertOrUpdate(
4781 InternalCommandData(
4783 Command::FormatOutdent
,
4784 ExecCommandParam::Ignore
,
4785 OutdentCommand::GetInstance
,
4786 CommandOnTextEditor::Disabled
));
4787 sInternalCommandDataHashtable
->InsertOrUpdate(
4789 InternalCommandData(
4791 Command::FormatBackColor
,
4792 ExecCommandParam::String
,
4793 HighlightColorStateCommand::GetInstance
,
4794 CommandOnTextEditor::Disabled
));
4795 sInternalCommandDataHashtable
->InsertOrUpdate(
4797 InternalCommandData(
4799 Command::FormatBackColor
,
4800 ExecCommandParam::String
,
4801 HighlightColorStateCommand::GetInstance
,
4802 CommandOnTextEditor::Disabled
));
4803 sInternalCommandDataHashtable
->InsertOrUpdate(
4805 InternalCommandData(
4807 Command::FormatFontColor
,
4808 ExecCommandParam::String
,
4809 FontColorStateCommand::GetInstance
,
4810 CommandOnTextEditor::Disabled
));
4811 sInternalCommandDataHashtable
->InsertOrUpdate(
4813 InternalCommandData(
4815 Command::FormatFontName
,
4816 ExecCommandParam::String
,
4817 FontFaceStateCommand::GetInstance
,
4818 CommandOnTextEditor::Disabled
));
4819 sInternalCommandDataHashtable
->InsertOrUpdate(
4821 InternalCommandData(
4823 Command::FormatFontSize
,
4824 ExecCommandParam::String
,
4825 FontSizeStateCommand::GetInstance
,
4826 CommandOnTextEditor::Disabled
));
4827 sInternalCommandDataHashtable
->InsertOrUpdate(
4828 u
"inserthorizontalrule"_ns
,
4829 InternalCommandData(
4831 Command::InsertHorizontalRule
,
4832 ExecCommandParam::Ignore
,
4833 InsertTagCommand::GetInstance
,
4834 CommandOnTextEditor::Disabled
));
4835 sInternalCommandDataHashtable
->InsertOrUpdate(
4837 InternalCommandData(
4838 "cmd_insertLinkNoUI",
4839 Command::InsertLink
,
4840 ExecCommandParam::String
,
4841 InsertTagCommand::GetInstance
,
4842 CommandOnTextEditor::Disabled
));
4843 sInternalCommandDataHashtable
->InsertOrUpdate(
4845 InternalCommandData(
4846 "cmd_insertImageNoUI",
4847 Command::InsertImage
,
4848 ExecCommandParam::String
,
4849 InsertTagCommand::GetInstance
,
4850 CommandOnTextEditor::Disabled
));
4851 sInternalCommandDataHashtable
->InsertOrUpdate(
4853 InternalCommandData(
4855 Command::InsertHTML
,
4856 ExecCommandParam::String
,
4857 InsertHTMLCommand::GetInstance
,
4858 // TODO: Chromium inserts text content of the document fragment
4859 // created from the param.
4860 // https://source.chromium.org/chromium/chromium/src/+/master:third_party/blink/renderer/core/editing/commands/insert_commands.cc;l=105;drc=a4708b724062f17824815b896c3aaa43825128f8
4861 CommandOnTextEditor::Disabled
));
4862 sInternalCommandDataHashtable
->InsertOrUpdate(
4864 InternalCommandData(
4866 Command::InsertText
,
4867 ExecCommandParam::String
,
4868 InsertPlaintextCommand::GetInstance
,
4869 CommandOnTextEditor::Enabled
));
4870 sInternalCommandDataHashtable
->InsertOrUpdate(
4872 InternalCommandData(
4874 Command::FormatJustifyLeft
,
4875 ExecCommandParam::Ignore
, // Will be set to "left"
4876 AlignCommand::GetInstance
,
4877 CommandOnTextEditor::Disabled
));
4878 sInternalCommandDataHashtable
->InsertOrUpdate(
4880 InternalCommandData(
4882 Command::FormatJustifyRight
,
4883 ExecCommandParam::Ignore
, // Will be set to "right"
4884 AlignCommand::GetInstance
,
4885 CommandOnTextEditor::Disabled
));
4886 sInternalCommandDataHashtable
->InsertOrUpdate(
4887 u
"justifycenter"_ns
,
4888 InternalCommandData(
4890 Command::FormatJustifyCenter
,
4891 ExecCommandParam::Ignore
, // Will be set to "center"
4892 AlignCommand::GetInstance
,
4893 CommandOnTextEditor::Disabled
));
4894 sInternalCommandDataHashtable
->InsertOrUpdate(
4896 InternalCommandData(
4898 Command::FormatJustifyFull
,
4899 ExecCommandParam::Ignore
, // Will be set to "justify"
4900 AlignCommand::GetInstance
,
4901 CommandOnTextEditor::Disabled
));
4902 sInternalCommandDataHashtable
->InsertOrUpdate(
4904 InternalCommandData(
4906 Command::FormatRemove
,
4907 ExecCommandParam::Ignore
,
4908 RemoveStylesCommand::GetInstance
,
4909 CommandOnTextEditor::Disabled
));
4910 sInternalCommandDataHashtable
->InsertOrUpdate(
4912 InternalCommandData(
4914 Command::FormatRemoveLink
,
4915 ExecCommandParam::Ignore
,
4916 StyleUpdatingCommand::GetInstance
,
4917 CommandOnTextEditor::Disabled
));
4918 sInternalCommandDataHashtable
->InsertOrUpdate(
4919 u
"insertorderedlist"_ns
,
4920 InternalCommandData(
4922 Command::InsertOrderedList
,
4923 ExecCommandParam::Ignore
,
4924 ListCommand::GetInstance
,
4925 CommandOnTextEditor::Disabled
));
4926 sInternalCommandDataHashtable
->InsertOrUpdate(
4927 u
"insertunorderedlist"_ns
,
4928 InternalCommandData(
4930 Command::InsertUnorderedList
,
4931 ExecCommandParam::Ignore
,
4932 ListCommand::GetInstance
,
4933 CommandOnTextEditor::Disabled
));
4934 sInternalCommandDataHashtable
->InsertOrUpdate(
4935 u
"insertparagraph"_ns
,
4936 InternalCommandData(
4937 "cmd_insertParagraph",
4938 Command::InsertParagraph
,
4939 ExecCommandParam::Ignore
,
4940 InsertParagraphCommand::GetInstance
,
4941 CommandOnTextEditor::Enabled
));
4942 sInternalCommandDataHashtable
->InsertOrUpdate(
4943 u
"insertlinebreak"_ns
,
4944 InternalCommandData(
4945 "cmd_insertLineBreak",
4946 Command::InsertLineBreak
,
4947 ExecCommandParam::Ignore
,
4948 InsertLineBreakCommand::GetInstance
,
4949 CommandOnTextEditor::Enabled
));
4950 sInternalCommandDataHashtable
->InsertOrUpdate(
4952 InternalCommandData(
4953 "cmd_paragraphState",
4954 Command::FormatBlock
,
4955 ExecCommandParam::String
,
4956 ParagraphStateCommand::GetInstance
,
4957 CommandOnTextEditor::Disabled
));
4958 sInternalCommandDataHashtable
->InsertOrUpdate(
4960 InternalCommandData(
4961 "cmd_setDocumentUseCSS",
4962 Command::SetDocumentUseCSS
,
4963 ExecCommandParam::Boolean
,
4964 SetDocumentStateCommand::GetInstance
,
4965 CommandOnTextEditor::FallThrough
));
4966 sInternalCommandDataHashtable
->InsertOrUpdate(
4967 u
"usecss"_ns
, // Legacy command
4968 InternalCommandData(
4969 "cmd_setDocumentUseCSS",
4970 Command::SetDocumentUseCSS
,
4971 ExecCommandParam::InvertedBoolean
,
4972 SetDocumentStateCommand::GetInstance
,
4973 CommandOnTextEditor::FallThrough
));
4974 sInternalCommandDataHashtable
->InsertOrUpdate(
4975 u
"contentReadOnly"_ns
,
4976 InternalCommandData(
4977 "cmd_setDocumentReadOnly",
4978 Command::SetDocumentReadOnly
,
4979 ExecCommandParam::Boolean
,
4980 SetDocumentStateCommand::GetInstance
,
4981 CommandOnTextEditor::Enabled
));
4982 sInternalCommandDataHashtable
->InsertOrUpdate(
4983 u
"insertBrOnReturn"_ns
,
4984 InternalCommandData(
4985 "cmd_insertBrOnReturn",
4986 Command::SetDocumentInsertBROnEnterKeyPress
,
4987 ExecCommandParam::Boolean
,
4988 SetDocumentStateCommand::GetInstance
,
4989 CommandOnTextEditor::FallThrough
));
4990 sInternalCommandDataHashtable
->InsertOrUpdate(
4991 u
"defaultParagraphSeparator"_ns
,
4992 InternalCommandData(
4993 "cmd_defaultParagraphSeparator",
4994 Command::SetDocumentDefaultParagraphSeparator
,
4995 ExecCommandParam::String
,
4996 SetDocumentStateCommand::GetInstance
,
4997 CommandOnTextEditor::FallThrough
));
4998 sInternalCommandDataHashtable
->InsertOrUpdate(
4999 u
"enableObjectResizing"_ns
,
5000 InternalCommandData(
5001 "cmd_enableObjectResizing",
5002 Command::ToggleObjectResizers
,
5003 ExecCommandParam::Boolean
,
5004 SetDocumentStateCommand::GetInstance
,
5005 CommandOnTextEditor::FallThrough
));
5006 sInternalCommandDataHashtable
->InsertOrUpdate(
5007 u
"enableInlineTableEditing"_ns
,
5008 InternalCommandData(
5009 "cmd_enableInlineTableEditing",
5010 Command::ToggleInlineTableEditor
,
5011 ExecCommandParam::Boolean
,
5012 SetDocumentStateCommand::GetInstance
,
5013 CommandOnTextEditor::FallThrough
));
5014 sInternalCommandDataHashtable
->InsertOrUpdate(
5015 u
"enableAbsolutePositionEditing"_ns
,
5016 InternalCommandData(
5017 "cmd_enableAbsolutePositionEditing",
5018 Command::ToggleAbsolutePositionEditor
,
5019 ExecCommandParam::Boolean
,
5020 SetDocumentStateCommand::GetInstance
,
5021 CommandOnTextEditor::FallThrough
));
5022 sInternalCommandDataHashtable
->InsertOrUpdate(
5023 u
"enableCompatibleJoinSplitDirection"_ns
,
5024 InternalCommandData("cmd_enableCompatibleJoinSplitNodeDirection",
5025 Command::EnableCompatibleJoinSplitNodeDirection
,
5026 ExecCommandParam::Boolean
,
5027 SetDocumentStateCommand::GetInstance
,
5028 CommandOnTextEditor::FallThrough
));
5030 // with empty string
5031 sInternalCommandDataHashtable
->InsertOrUpdate(
5033 InternalCommandData(
5036 ExecCommandParam::Ignore
,
5038 CommandOnTextEditor::Disabled
)); // Not implemented yet.
5039 // REQUIRED SPECIAL REVIEW special review
5040 sInternalCommandDataHashtable
->InsertOrUpdate(
5042 InternalCommandData(
5045 ExecCommandParam::Boolean
,
5047 CommandOnTextEditor::FallThrough
)); // Not implemented yet.
5048 // REQUIRED SPECIAL REVIEW special review
5049 sInternalCommandDataHashtable
->InsertOrUpdate(
5051 InternalCommandData(
5054 ExecCommandParam::Boolean
,
5056 CommandOnTextEditor::FallThrough
)); // Not implemented yet.
5061 Document::InternalCommandData
Document::ConvertToInternalCommand(
5062 const nsAString
& aHTMLCommandName
, const nsAString
& aValue
/* = u""_ns */,
5063 nsAString
* aAdjustedValue
/* = nullptr */) {
5064 MOZ_ASSERT(!aAdjustedValue
|| aAdjustedValue
->IsEmpty());
5065 EnsureInitializeInternalCommandDataHashtable();
5066 InternalCommandData commandData
;
5067 if (!sInternalCommandDataHashtable
->Get(aHTMLCommandName
, &commandData
)) {
5068 return InternalCommandData();
5070 // Ignore if the command is disabled by a corresponding pref due to Gecko
5072 switch (commandData
.mCommand
) {
5073 case Command::SetDocumentReadOnly
:
5074 if (!StaticPrefs::dom_document_edit_command_contentReadOnly_enabled() &&
5075 aHTMLCommandName
.LowerCaseEqualsLiteral("contentreadonly")) {
5076 return InternalCommandData();
5079 case Command::SetDocumentInsertBROnEnterKeyPress
:
5080 MOZ_DIAGNOSTIC_ASSERT(
5081 aHTMLCommandName
.LowerCaseEqualsLiteral("insertbronreturn"));
5082 if (!StaticPrefs::dom_document_edit_command_insertBrOnReturn_enabled()) {
5083 return InternalCommandData();
5089 if (!aAdjustedValue
) {
5090 // No further work to do
5093 switch (commandData
.mExecCommandParam
) {
5094 case ExecCommandParam::Ignore
:
5095 // Just have to copy it, no checking
5096 switch (commandData
.mCommand
) {
5097 case Command::FormatJustifyLeft
:
5098 aAdjustedValue
->AssignLiteral("left");
5100 case Command::FormatJustifyRight
:
5101 aAdjustedValue
->AssignLiteral("right");
5103 case Command::FormatJustifyCenter
:
5104 aAdjustedValue
->AssignLiteral("center");
5106 case Command::FormatJustifyFull
:
5107 aAdjustedValue
->AssignLiteral("justify");
5110 MOZ_ASSERT(EditorCommand::GetParamType(commandData
.mCommand
) ==
5111 EditorCommandParamType::None
);
5116 case ExecCommandParam::Boolean
:
5117 MOZ_ASSERT(!!(EditorCommand::GetParamType(commandData
.mCommand
) &
5118 EditorCommandParamType::Bool
));
5119 // If this is a boolean value and it's not explicitly false (e.g. no
5120 // value). We default to "true" (see bug 301490).
5121 if (!aValue
.LowerCaseEqualsLiteral("false")) {
5122 aAdjustedValue
->AssignLiteral("true");
5124 aAdjustedValue
->AssignLiteral("false");
5128 case ExecCommandParam::InvertedBoolean
:
5129 MOZ_ASSERT(!!(EditorCommand::GetParamType(commandData
.mCommand
) &
5130 EditorCommandParamType::Bool
));
5131 // For old backwards commands we invert the check.
5132 if (aValue
.LowerCaseEqualsLiteral("false")) {
5133 aAdjustedValue
->AssignLiteral("true");
5135 aAdjustedValue
->AssignLiteral("false");
5139 case ExecCommandParam::String
:
5141 EditorCommand::GetParamType(commandData
.mCommand
) &
5142 (EditorCommandParamType::String
| EditorCommandParamType::CString
)));
5143 switch (commandData
.mCommand
) {
5144 case Command::FormatBlock
: {
5145 const char16_t
* start
= aValue
.BeginReading();
5146 const char16_t
* end
= aValue
.EndReading();
5147 if (start
!= end
&& *start
== '<' && *(end
- 1) == '>') {
5151 // XXX Should we reorder this array with actual usage?
5152 static const nsStaticAtom
* kFormattableBlockTags
[] = {
5155 nsGkAtoms::blockquote
,
5170 nsAutoString
value(nsDependentSubstring(start
, end
));
5172 const nsStaticAtom
* valueAtom
= NS_GetStaticAtom(value
);
5173 for (const nsStaticAtom
* kTag
: kFormattableBlockTags
) {
5174 if (valueAtom
== kTag
) {
5175 kTag
->ToString(*aAdjustedValue
);
5179 return InternalCommandData();
5181 case Command::FormatFontSize
: {
5182 // Per editing spec as of April 23, 2012, we need to reject the value
5183 // if it's not a valid floating-point number surrounded by optional
5184 // whitespace. Otherwise, we parse it as a legacy font size. For
5185 // now, we just parse as a legacy font size regardless (matching
5186 // WebKit) -- bug 747879.
5187 int32_t size
= nsContentUtils::ParseLegacyFontSize(aValue
);
5189 return InternalCommandData();
5191 MOZ_ASSERT(aAdjustedValue
->IsEmpty());
5192 aAdjustedValue
->AppendInt(size
);
5195 case Command::InsertImage
:
5196 case Command::InsertLink
:
5197 if (aValue
.IsEmpty()) {
5198 // Invalid value, return false
5199 return InternalCommandData();
5201 aAdjustedValue
->Assign(aValue
);
5203 case Command::SetDocumentDefaultParagraphSeparator
:
5204 if (!aValue
.LowerCaseEqualsLiteral("div") &&
5205 !aValue
.LowerCaseEqualsLiteral("p") &&
5206 !aValue
.LowerCaseEqualsLiteral("br")) {
5208 return InternalCommandData();
5210 aAdjustedValue
->Assign(aValue
);
5213 aAdjustedValue
->Assign(aValue
);
5218 MOZ_ASSERT_UNREACHABLE("New ExecCommandParam value hasn't been handled");
5219 return InternalCommandData();
5223 Document::AutoEditorCommandTarget::AutoEditorCommandTarget(
5224 Document
& aDocument
, const InternalCommandData
& aCommandData
)
5225 : mCommandData(aCommandData
) {
5226 // We'll retrieve an editor with current DOM tree and layout information.
5227 // However, JS may have already hidden or remove exposed root content of
5228 // the editor. Therefore, we need the latest layout information here.
5229 aDocument
.FlushPendingNotifications(FlushType::Layout
);
5230 if (!aDocument
.GetPresShell() || aDocument
.GetPresShell()->IsDestroying()) {
5235 if (nsPresContext
* presContext
= aDocument
.GetPresContext()) {
5236 // Consider context of command handling which is automatically resolved
5237 // by order of controllers in `nsCommandManager::GetControllerForCommand()`.
5239 // 1. TextEditor if there is an active element and it has TextEditor like
5240 // <input type="text"> or <textarea>.
5241 // 2. HTMLEditor for the document, if there is.
5242 // 3. Retarget to the DocShell or nsCommandManager as what we've done.
5243 if (aCommandData
.IsCutOrCopyCommand()) {
5244 // Note that we used to use DocShell to handle `cut` and `copy` command
5245 // for dispatching corresponding events for making possible web apps to
5246 // implement their own editor without editable elements but supports
5247 // standard shortcut keys, etc. In this case, we prefer to use active
5248 // element's editor to keep same behavior.
5249 mActiveEditor
= nsContentUtils::GetActiveEditor(presContext
);
5251 mActiveEditor
= nsContentUtils::GetActiveEditor(presContext
);
5252 mHTMLEditor
= nsContentUtils::GetHTMLEditor(presContext
);
5253 if (!mActiveEditor
) {
5254 mActiveEditor
= mHTMLEditor
;
5259 // Then, retrieve editor command class instance which should handle it
5260 // and can handle it now.
5261 if (!mActiveEditor
) {
5262 // If the command is available without editor, we should redirect the
5263 // command to focused descendant with DocShell.
5264 if (aCommandData
.IsAvailableOnlyWhenEditable()) {
5271 // Otherwise, we should use EditorCommand instance (which is singleton
5272 // instance) when it's enabled.
5273 mEditorCommand
= aCommandData
.mGetEditorCommandFunc
5274 ? aCommandData
.mGetEditorCommandFunc()
5276 if (!mEditorCommand
) {
5278 mActiveEditor
= nullptr;
5279 mHTMLEditor
= nullptr;
5283 if (IsCommandEnabled()) {
5287 // If the EditorCommand instance is disabled, we should do nothing if
5288 // the command requires an editor.
5289 if (aCommandData
.IsAvailableOnlyWhenEditable()) {
5290 // Do nothing if editor specific commands is disabled (bug 760052).
5295 // Otherwise, we should redirect it to focused descendant with DocShell.
5296 mEditorCommand
= nullptr;
5297 mActiveEditor
= nullptr;
5298 mHTMLEditor
= nullptr;
5301 EditorBase
* Document::AutoEditorCommandTarget::GetTargetEditor() const {
5302 using CommandOnTextEditor
= InternalCommandData::CommandOnTextEditor
;
5303 switch (mCommandData
.mCommandOnTextEditor
) {
5304 case CommandOnTextEditor::Enabled
:
5305 return mActiveEditor
;
5306 case CommandOnTextEditor::Disabled
:
5307 return mActiveEditor
&& mActiveEditor
->IsTextEditor()
5309 : mActiveEditor
.get();
5310 case CommandOnTextEditor::FallThrough
:
5316 bool Document::AutoEditorCommandTarget::IsEditable(Document
* aDocument
) const {
5317 if (RefPtr
<Document
> doc
= aDocument
->GetInProcessParentDocument()) {
5318 // Make sure frames are up to date, since that can affect whether
5320 doc
->FlushPendingNotifications(FlushType::Frames
);
5322 EditorBase
* targetEditor
= GetTargetEditor();
5323 if (targetEditor
&& targetEditor
->IsTextEditor()) {
5324 // FYI: When `disabled` attribute is set, `TextEditor` treats it as
5326 return !targetEditor
->IsReadonly();
5328 return aDocument
->IsEditingOn();
5331 bool Document::AutoEditorCommandTarget::IsCommandEnabled() const {
5332 EditorBase
* targetEditor
= GetTargetEditor();
5333 if (!targetEditor
) {
5336 MOZ_ASSERT(targetEditor
== mActiveEditor
|| targetEditor
== mHTMLEditor
);
5337 return MOZ_KnownLive(mEditorCommand
)
5338 ->IsCommandEnabled(mCommandData
.mCommand
, MOZ_KnownLive(targetEditor
));
5341 nsresult
Document::AutoEditorCommandTarget::DoCommand(
5342 nsIPrincipal
* aPrincipal
) const {
5343 MOZ_ASSERT(!DoNothing());
5344 MOZ_ASSERT(mEditorCommand
);
5345 EditorBase
* targetEditor
= GetTargetEditor();
5346 if (!targetEditor
) {
5347 return NS_SUCCESS_DOM_NO_OPERATION
;
5349 MOZ_ASSERT(targetEditor
== mActiveEditor
|| targetEditor
== mHTMLEditor
);
5350 return MOZ_KnownLive(mEditorCommand
)
5351 ->DoCommand(mCommandData
.mCommand
, MOZ_KnownLive(*targetEditor
),
5355 template <typename ParamType
>
5356 nsresult
Document::AutoEditorCommandTarget::DoCommandParam(
5357 const ParamType
& aParam
, nsIPrincipal
* aPrincipal
) const {
5358 MOZ_ASSERT(!DoNothing());
5359 MOZ_ASSERT(mEditorCommand
);
5360 EditorBase
* targetEditor
= GetTargetEditor();
5361 if (!targetEditor
) {
5362 return NS_SUCCESS_DOM_NO_OPERATION
;
5364 MOZ_ASSERT(targetEditor
== mActiveEditor
|| targetEditor
== mHTMLEditor
);
5365 return MOZ_KnownLive(mEditorCommand
)
5366 ->DoCommandParam(mCommandData
.mCommand
, aParam
,
5367 MOZ_KnownLive(*targetEditor
), aPrincipal
);
5370 nsresult
Document::AutoEditorCommandTarget::GetCommandStateParams(
5371 nsCommandParams
& aParams
) const {
5372 MOZ_ASSERT(mEditorCommand
);
5373 EditorBase
* targetEditor
= GetTargetEditor();
5374 if (!targetEditor
) {
5377 MOZ_ASSERT(targetEditor
== mActiveEditor
|| targetEditor
== mHTMLEditor
);
5378 return MOZ_KnownLive(mEditorCommand
)
5379 ->GetCommandStateParams(mCommandData
.mCommand
, MOZ_KnownLive(aParams
),
5380 MOZ_KnownLive(targetEditor
), nullptr);
5383 bool Document::ExecCommand(const nsAString
& aHTMLCommandName
, bool aShowUI
,
5384 const nsAString
& aValue
,
5385 nsIPrincipal
& aSubjectPrincipal
, ErrorResult
& aRv
) {
5386 // Only allow on HTML documents.
5387 if (!IsHTMLOrXHTML()) {
5388 aRv
.ThrowInvalidStateError(
5389 "execCommand is only supported on HTML documents");
5392 // Otherwise, don't throw exception for compatibility with Chrome.
5394 // if they are requesting UI from us, let's fail since we have no UI
5399 // If we're running an execCommand, we should just return false.
5400 // https://github.com/w3c/editing/issues/200#issuecomment-575241816
5401 if (!StaticPrefs::dom_document_exec_command_nested_calls_allowed() &&
5402 mIsRunningExecCommand
) {
5406 // for optional parameters see dom/src/base/nsHistory.cpp: HistoryImpl::Go()
5407 // this might add some ugly JS dependencies?
5409 nsAutoString adjustedValue
;
5410 InternalCommandData commandData
=
5411 ConvertToInternalCommand(aHTMLCommandName
, aValue
, &adjustedValue
);
5412 switch (commandData
.mCommand
) {
5413 case Command::DoNothing
:
5415 case Command::SetDocumentReadOnly
:
5416 SetUseCounter(eUseCounter_custom_DocumentExecCommandContentReadOnly
);
5418 case Command::EnableCompatibleJoinSplitNodeDirection
:
5419 // We don't allow to take the legacy behavior back if the new one is
5420 // enabled by default.
5422 editor_join_split_direction_compatible_with_the_other_browsers() &&
5423 !adjustedValue
.EqualsLiteral("true") &&
5424 !aSubjectPrincipal
.IsSystemPrincipal()) {
5432 // Do security check first.
5433 if (commandData
.IsCutOrCopyCommand()) {
5434 if (!nsContentUtils::IsCutCopyAllowed(this, aSubjectPrincipal
)) {
5435 // We have rejected the event due to it not being performed in an
5436 // input-driven context therefore, we report the error to the console.
5437 nsContentUtils::ReportToConsole(nsIScriptError::warningFlag
, "DOM"_ns
,
5438 this, nsContentUtils::eDOM_PROPERTIES
,
5439 "ExecCommandCutCopyDeniedNotInputDriven");
5442 } else if (commandData
.IsPasteCommand()) {
5443 if (!nsContentUtils::PrincipalHasPermission(aSubjectPrincipal
,
5444 nsGkAtoms::clipboardRead
)) {
5449 AutoRunningExecCommandMarker
markRunningExecCommand(*this);
5451 // Next, consider context of command handling which is automatically resolved
5452 // by order of controllers in `nsCommandManager::GetControllerForCommand()`.
5453 AutoEditorCommandTarget
editCommandTarget(*this, commandData
);
5454 if (commandData
.IsAvailableOnlyWhenEditable() &&
5455 !editCommandTarget
.IsEditable(this)) {
5459 if (editCommandTarget
.DoNothing()) {
5463 // If we cannot use EditorCommand instance directly, we need to handle the
5464 // command with traditional path (i.e., with DocShell or nsCommandManager).
5465 if (!editCommandTarget
.IsEditor()) {
5466 MOZ_ASSERT(!commandData
.IsAvailableOnlyWhenEditable());
5468 // Special case clipboard write commands like Command::Cut and
5469 // Command::Copy. For such commands, we need the behaviour from
5470 // nsWindowRoot::GetControllers() which is to look at the focused element,
5471 // and defer to a focused textbox's controller. The code past taken by
5472 // other commands in ExecCommand() always uses the window directly, rather
5473 // than deferring to the textbox, which is desireable for most editor
5474 // commands, but not these commands (as those should allow copying out of
5475 // embedded editors). This behaviour is invoked if we call DoCommand()
5476 // directly on the docShell.
5477 // XXX This means that we allow web app to pick up selected content in
5478 // descendant document and write it into the clipboard when a
5479 // descendant document has focus. However, Chromium does not allow
5480 // this and this seems that it's not good behavior from point of view
5481 // of security. We should treat this issue in another bug.
5482 if (commandData
.IsCutOrCopyCommand()) {
5483 nsCOMPtr
<nsIDocShell
> docShell(mDocumentContainer
);
5487 nsresult rv
= docShell
->DoCommand(commandData
.mXULCommandName
);
5488 if (rv
== NS_SUCCESS_DOM_NO_OPERATION
) {
5491 return NS_SUCCEEDED(rv
);
5494 // Otherwise (currently, only clipboard read commands like Command::Paste),
5495 // we don't need to redirect the command to focused subdocument.
5496 // Therefore, we should handle it with nsCommandManager as used to be.
5497 // It may dispatch only preceding event of editing on non-editable element
5498 // to make web apps possible to handle standard shortcut key, etc in
5499 // their own editor.
5500 RefPtr
<nsCommandManager
> commandManager
= GetMidasCommandManager();
5501 if (!commandManager
) {
5505 nsCOMPtr
<nsPIDOMWindowOuter
> window
= GetWindow();
5510 // Return false for disabled commands (bug 760052)
5511 if (!commandManager
->IsCommandEnabled(
5512 nsDependentCString(commandData
.mXULCommandName
), window
)) {
5516 MOZ_ASSERT(commandData
.IsPasteCommand() ||
5517 commandData
.mCommand
== Command::SelectAll
);
5519 commandManager
->DoCommand(commandData
.mXULCommandName
, nullptr, window
);
5520 return NS_SUCCEEDED(rv
) && rv
!= NS_SUCCESS_DOM_NO_OPERATION
;
5523 // Now, our target is fixed to the editor. So, we can use EditorCommand
5524 // in EditorCommandTarget directly.
5526 EditorCommandParamType paramType
=
5527 EditorCommand::GetParamType(commandData
.mCommand
);
5529 // If we don't have meaningful parameter or the EditorCommand does not
5530 // require additional parameter, we can use `DoCommand()`.
5531 if (adjustedValue
.IsEmpty() || paramType
== EditorCommandParamType::None
) {
5532 MOZ_ASSERT(!(paramType
& EditorCommandParamType::Bool
));
5533 nsresult rv
= editCommandTarget
.DoCommand(&aSubjectPrincipal
);
5534 return NS_SUCCEEDED(rv
) && rv
!= NS_SUCCESS_DOM_NO_OPERATION
;
5537 // If the EditorCommand requires `bool` parameter, `adjustedValue` must be
5538 // "true" or "false" here. So, we can use `DoCommandParam()` which takes
5540 if (!!(paramType
& EditorCommandParamType::Bool
)) {
5541 MOZ_ASSERT(adjustedValue
.EqualsLiteral("true") ||
5542 adjustedValue
.EqualsLiteral("false"));
5543 nsresult rv
= editCommandTarget
.DoCommandParam(
5544 Some(adjustedValue
.EqualsLiteral("true")), &aSubjectPrincipal
);
5545 return NS_SUCCEEDED(rv
) && rv
!= NS_SUCCESS_DOM_NO_OPERATION
;
5548 // Now, the EditorCommand requires `nsAString` or `nsACString` parameter
5549 // in this case. However, `paramType` may contain both `String` and
5550 // `CString` but in such case, we should use `DoCommandParam()` which
5551 // takes `nsAString`. So, we should check whether `paramType` contains
5552 // `String` or not first.
5553 if (!!(paramType
& EditorCommandParamType::String
)) {
5554 MOZ_ASSERT(!adjustedValue
.IsVoid());
5556 editCommandTarget
.DoCommandParam(adjustedValue
, &aSubjectPrincipal
);
5557 return NS_SUCCEEDED(rv
) && rv
!= NS_SUCCESS_DOM_NO_OPERATION
;
5560 // Finally, `paramType` should have `CString`. We should use
5561 // `DoCommandParam()` which takes `nsACString`.
5562 if (!!(paramType
& EditorCommandParamType::CString
)) {
5563 NS_ConvertUTF16toUTF8
utf8Value(adjustedValue
);
5564 MOZ_ASSERT(!utf8Value
.IsVoid());
5566 editCommandTarget
.DoCommandParam(utf8Value
, &aSubjectPrincipal
);
5567 return NS_SUCCEEDED(rv
) && rv
!= NS_SUCCESS_DOM_NO_OPERATION
;
5570 MOZ_ASSERT_UNREACHABLE(
5571 "Not yet implemented to handle new EditorCommandParamType");
5575 bool Document::QueryCommandEnabled(const nsAString
& aHTMLCommandName
,
5576 nsIPrincipal
& aSubjectPrincipal
,
5578 // Only allow on HTML documents.
5579 if (!IsHTMLOrXHTML()) {
5580 aRv
.ThrowInvalidStateError(
5581 "queryCommandEnabled is only supported on HTML documents");
5584 // Otherwise, don't throw exception for compatibility with Chrome.
5586 InternalCommandData commandData
= ConvertToInternalCommand(aHTMLCommandName
);
5587 switch (commandData
.mCommand
) {
5588 case Command::DoNothing
:
5590 case Command::SetDocumentReadOnly
:
5592 eUseCounter_custom_DocumentQueryCommandSupportedOrEnabledContentReadOnly
);
5594 case Command::SetDocumentInsertBROnEnterKeyPress
:
5596 eUseCounter_custom_DocumentQueryCommandSupportedOrEnabledInsertBrOnReturn
);
5602 // cut & copy are always allowed
5603 if (commandData
.IsCutOrCopyCommand()) {
5604 return nsContentUtils::IsCutCopyAllowed(this, aSubjectPrincipal
);
5607 // Report false for restricted commands
5608 if (commandData
.IsPasteCommand() && !aSubjectPrincipal
.IsSystemPrincipal()) {
5612 AutoEditorCommandTarget
editCommandTarget(*this, commandData
);
5613 if (commandData
.IsAvailableOnlyWhenEditable() &&
5614 !editCommandTarget
.IsEditable(this)) {
5618 if (editCommandTarget
.IsEditor()) {
5619 return editCommandTarget
.IsCommandEnabled();
5622 // get command manager and dispatch command to our window if it's acceptable
5623 RefPtr
<nsCommandManager
> commandManager
= GetMidasCommandManager();
5624 if (!commandManager
) {
5628 nsPIDOMWindowOuter
* window
= GetWindow();
5633 return commandManager
->IsCommandEnabled(
5634 nsDependentCString(commandData
.mXULCommandName
), window
);
5637 bool Document::QueryCommandIndeterm(const nsAString
& aHTMLCommandName
,
5639 // Only allow on HTML documents.
5640 if (!IsHTMLOrXHTML()) {
5641 aRv
.ThrowInvalidStateError(
5642 "queryCommandIndeterm is only supported on HTML documents");
5645 // Otherwise, don't throw exception for compatibility with Chrome.
5647 InternalCommandData commandData
= ConvertToInternalCommand(aHTMLCommandName
);
5648 if (commandData
.mCommand
== Command::DoNothing
) {
5652 AutoEditorCommandTarget
editCommandTarget(*this, commandData
);
5653 if (commandData
.IsAvailableOnlyWhenEditable() &&
5654 !editCommandTarget
.IsEditable(this)) {
5657 RefPtr
<nsCommandParams
> params
= new nsCommandParams();
5658 if (editCommandTarget
.IsEditor()) {
5659 if (NS_FAILED(editCommandTarget
.GetCommandStateParams(*params
))) {
5663 // get command manager and dispatch command to our window if it's acceptable
5664 RefPtr
<nsCommandManager
> commandManager
= GetMidasCommandManager();
5665 if (!commandManager
) {
5669 nsPIDOMWindowOuter
* window
= GetWindow();
5674 if (NS_FAILED(commandManager
->GetCommandState(commandData
.mXULCommandName
,
5680 // If command does not have a state_mixed value, this call fails and sets
5681 // retval to false. This is fine -- we want to return false in that case
5682 // anyway (bug 738385), so we just don't throw regardless.
5683 return params
->GetBool("state_mixed");
5686 bool Document::QueryCommandState(const nsAString
& aHTMLCommandName
,
5688 // Only allow on HTML documents.
5689 if (!IsHTMLOrXHTML()) {
5690 aRv
.ThrowInvalidStateError(
5691 "queryCommandState is only supported on HTML documents");
5694 // Otherwise, don't throw exception for compatibility with Chrome.
5696 InternalCommandData commandData
= ConvertToInternalCommand(aHTMLCommandName
);
5697 switch (commandData
.mCommand
) {
5698 case Command::DoNothing
:
5700 case Command::SetDocumentReadOnly
:
5702 eUseCounter_custom_DocumentQueryCommandStateOrValueContentReadOnly
);
5704 case Command::SetDocumentInsertBROnEnterKeyPress
:
5706 eUseCounter_custom_DocumentQueryCommandStateOrValueInsertBrOnReturn
);
5712 if (aHTMLCommandName
.LowerCaseEqualsLiteral("usecss")) {
5713 // Per spec, state is supported for styleWithCSS but not useCSS, so we just
5714 // return false always.
5718 AutoEditorCommandTarget
editCommandTarget(*this, commandData
);
5719 if (commandData
.IsAvailableOnlyWhenEditable() &&
5720 !editCommandTarget
.IsEditable(this)) {
5723 RefPtr
<nsCommandParams
> params
= new nsCommandParams();
5724 if (editCommandTarget
.IsEditor()) {
5725 if (NS_FAILED(editCommandTarget
.GetCommandStateParams(*params
))) {
5729 // get command manager and dispatch command to our window if it's acceptable
5730 RefPtr
<nsCommandManager
> commandManager
= GetMidasCommandManager();
5731 if (!commandManager
) {
5735 nsPIDOMWindowOuter
* window
= GetWindow();
5740 if (NS_FAILED(commandManager
->GetCommandState(commandData
.mXULCommandName
,
5746 // handle alignment as a special case (possibly other commands too?)
5747 // Alignment is special because the external api is individual
5748 // commands but internally we use cmd_align with different
5749 // parameters. When getting the state of this command, we need to
5750 // return the boolean for this particular alignment rather than the
5751 // string of 'which alignment is this?'
5752 switch (commandData
.mCommand
) {
5753 case Command::FormatJustifyLeft
: {
5754 nsAutoCString currentValue
;
5755 nsresult rv
= params
->GetCString("state_attribute", currentValue
);
5756 if (NS_FAILED(rv
)) {
5759 return currentValue
.EqualsLiteral("left");
5761 case Command::FormatJustifyRight
: {
5762 nsAutoCString currentValue
;
5763 nsresult rv
= params
->GetCString("state_attribute", currentValue
);
5764 if (NS_FAILED(rv
)) {
5767 return currentValue
.EqualsLiteral("right");
5769 case Command::FormatJustifyCenter
: {
5770 nsAutoCString currentValue
;
5771 nsresult rv
= params
->GetCString("state_attribute", currentValue
);
5772 if (NS_FAILED(rv
)) {
5775 return currentValue
.EqualsLiteral("center");
5777 case Command::FormatJustifyFull
: {
5778 nsAutoCString currentValue
;
5779 nsresult rv
= params
->GetCString("state_attribute", currentValue
);
5780 if (NS_FAILED(rv
)) {
5783 return currentValue
.EqualsLiteral("justify");
5789 // If command does not have a state_all value, this call fails and sets
5790 // retval to false. This is fine -- we want to return false in that case
5791 // anyway (bug 738385), so we just succeed and return false regardless.
5792 return params
->GetBool("state_all");
5795 bool Document::QueryCommandSupported(const nsAString
& aHTMLCommandName
,
5796 CallerType aCallerType
, ErrorResult
& aRv
) {
5797 // Only allow on HTML documents.
5798 if (!IsHTMLOrXHTML()) {
5799 aRv
.ThrowInvalidStateError(
5800 "queryCommandSupported is only supported on HTML documents");
5803 // Otherwise, don't throw exception for compatibility with Chrome.
5805 InternalCommandData commandData
= ConvertToInternalCommand(aHTMLCommandName
);
5806 switch (commandData
.mCommand
) {
5807 case Command::DoNothing
:
5809 case Command::SetDocumentReadOnly
:
5811 eUseCounter_custom_DocumentQueryCommandSupportedOrEnabledContentReadOnly
);
5813 case Command::SetDocumentInsertBROnEnterKeyPress
:
5815 eUseCounter_custom_DocumentQueryCommandSupportedOrEnabledInsertBrOnReturn
);
5821 // Gecko technically supports all the clipboard commands including
5822 // cut/copy/paste, but non-privileged content will be unable to call
5823 // paste, and depending on the pref "dom.allow_cut_copy", cut and copy
5824 // may also be disallowed to be called from non-privileged content.
5825 // For that reason, we report the support status of corresponding
5826 // command accordingly.
5827 if (aCallerType
!= CallerType::System
) {
5828 if (commandData
.IsPasteCommand()) {
5831 if (commandData
.IsCutOrCopyCommand() &&
5832 !StaticPrefs::dom_allow_cut_copy()) {
5833 // XXXbz should we worry about correctly reporting "true" in the
5834 // "restricted, but we're an addon with clipboardWrite permissions" case?
5835 // See also nsContentUtils::IsCutCopyAllowed.
5840 // aHTMLCommandName is supported if it can be converted to a Midas command
5844 void Document::QueryCommandValue(const nsAString
& aHTMLCommandName
,
5845 nsAString
& aValue
, ErrorResult
& aRv
) {
5848 // Only allow on HTML documents.
5849 if (!IsHTMLOrXHTML()) {
5850 aRv
.ThrowInvalidStateError(
5851 "queryCommandValue is only supported on HTML documents");
5854 // Otherwise, don't throw exception for compatibility with Chrome.
5856 InternalCommandData commandData
= ConvertToInternalCommand(aHTMLCommandName
);
5857 switch (commandData
.mCommand
) {
5858 case Command::DoNothing
:
5859 // Return empty string
5861 case Command::SetDocumentReadOnly
:
5863 eUseCounter_custom_DocumentQueryCommandStateOrValueContentReadOnly
);
5865 case Command::SetDocumentInsertBROnEnterKeyPress
:
5867 eUseCounter_custom_DocumentQueryCommandStateOrValueInsertBrOnReturn
);
5873 AutoEditorCommandTarget
editCommandTarget(*this, commandData
);
5874 if (commandData
.IsAvailableOnlyWhenEditable() &&
5875 !editCommandTarget
.IsEditable(this)) {
5878 RefPtr
<nsCommandParams
> params
= new nsCommandParams();
5879 if (editCommandTarget
.IsEditor()) {
5880 if (NS_FAILED(params
->SetCString("state_attribute", ""_ns
))) {
5884 if (NS_FAILED(editCommandTarget
.GetCommandStateParams(*params
))) {
5888 // get command manager and dispatch command to our window if it's acceptable
5889 RefPtr
<nsCommandManager
> commandManager
= GetMidasCommandManager();
5890 if (!commandManager
) {
5894 nsCOMPtr
<nsPIDOMWindowOuter
> window
= GetWindow();
5899 if (NS_FAILED(params
->SetCString("state_attribute", ""_ns
))) {
5903 if (NS_FAILED(commandManager
->GetCommandState(commandData
.mXULCommandName
,
5909 // If command does not have a state_attribute value, this call fails, and
5910 // aValue will wind up being the empty string. This is fine -- we want to
5911 // return "" in that case anyway (bug 738385), so we just return NS_OK
5913 nsAutoCString result
;
5914 params
->GetCString("state_attribute", result
);
5915 CopyUTF8toUTF16(result
, aValue
);
5918 void Document::MaybeEditingStateChanged() {
5919 if (!mPendingMaybeEditingStateChanged
&& mMayStartLayout
&&
5920 mUpdateNestLevel
== 0 && (mContentEditableCount
> 0) != IsEditingOn()) {
5921 if (nsContentUtils::IsSafeToRunScript()) {
5922 EditingStateChanged();
5923 } else if (!mInDestructor
) {
5924 nsContentUtils::AddScriptRunner(
5925 NewRunnableMethod("Document::MaybeEditingStateChanged", this,
5926 &Document::MaybeEditingStateChanged
));
5931 void Document::NotifyFetchOrXHRSuccess() {
5932 if (mShouldNotifyFetchSuccess
) {
5933 nsContentUtils::DispatchEventOnlyToChrome(
5934 this, ToSupports(this), u
"DOMDocFetchSuccess"_ns
, CanBubble::eNo
,
5935 Cancelable::eNo
, /* DefaultAction */ nullptr);
5939 void Document::SetNotifyFetchSuccess(bool aShouldNotify
) {
5940 mShouldNotifyFetchSuccess
= aShouldNotify
;
5943 void Document::SetNotifyFormOrPasswordRemoved(bool aShouldNotify
) {
5944 mShouldNotifyFormOrPasswordRemoved
= aShouldNotify
;
5947 void Document::TearingDownEditor() {
5948 if (IsEditingOn()) {
5949 mEditingState
= EditingState::eTearingDown
;
5950 if (IsHTMLOrXHTML()) {
5951 RemoveContentEditableStyleSheets();
5956 nsresult
Document::TurnEditingOff() {
5957 NS_ASSERTION(mEditingState
!= EditingState::eOff
, "Editing is already off.");
5959 nsPIDOMWindowOuter
* window
= GetWindow();
5961 return NS_ERROR_FAILURE
;
5964 nsIDocShell
* docshell
= window
->GetDocShell();
5966 return NS_ERROR_FAILURE
;
5969 bool isBeingDestroyed
= false;
5970 docshell
->IsBeingDestroyed(&isBeingDestroyed
);
5971 if (isBeingDestroyed
) {
5972 return NS_ERROR_FAILURE
;
5975 nsCOMPtr
<nsIEditingSession
> editSession
;
5976 nsresult rv
= docshell
->GetEditingSession(getter_AddRefs(editSession
));
5977 NS_ENSURE_SUCCESS(rv
, rv
);
5980 rv
= editSession
->TearDownEditorOnWindow(window
);
5981 NS_ENSURE_SUCCESS(rv
, rv
);
5983 mEditingState
= EditingState::eOff
;
5985 // Editor resets selection since it is being destroyed. But if focus is
5986 // still into editable control, we have to initialize selection again.
5987 if (nsFocusManager
* fm
= nsFocusManager::GetFocusManager()) {
5988 if (RefPtr
<TextControlElement
> textControlElement
=
5989 TextControlElement::FromNodeOrNull(fm
->GetFocusedElement())) {
5990 if (RefPtr
<TextEditor
> textEditor
= textControlElement
->GetTextEditor()) {
5991 textEditor
->ReinitializeSelection(*textControlElement
);
5999 static bool HasPresShell(nsPIDOMWindowOuter
* aWindow
) {
6000 nsIDocShell
* docShell
= aWindow
->GetDocShell();
6004 return docShell
->GetPresShell() != nullptr;
6007 HTMLEditor
* Document::GetHTMLEditor() const {
6008 nsPIDOMWindowOuter
* window
= GetWindow();
6013 nsIDocShell
* docshell
= window
->GetDocShell();
6018 return docshell
->GetHTMLEditor();
6021 nsresult
Document::EditingStateChanged() {
6022 if (mRemovedFromDocShell
) {
6026 if (mEditingState
== EditingState::eSettingUp
||
6027 mEditingState
== EditingState::eTearingDown
) {
6028 // XXX We shouldn't recurse
6032 const bool designMode
= IsInDesignMode();
6033 EditingState newState
=
6034 designMode
? EditingState::eDesignMode
6035 : (mContentEditableCount
> 0 ? EditingState::eContentEditable
6036 : EditingState::eOff
);
6037 if (mEditingState
== newState
) {
6038 // No changes in editing mode.
6042 const bool thisDocumentHasFocus
= ThisDocumentHasFocus();
6043 if (newState
== EditingState::eOff
) {
6044 // Editing is being turned off.
6045 nsAutoScriptBlocker scriptBlocker
;
6046 RefPtr
<HTMLEditor
> htmlEditor
= GetHTMLEditor();
6047 NotifyEditableStateChange(*this);
6048 nsresult rv
= TurnEditingOff();
6049 // If this document has focus and the editing state of this document
6050 // becomes "off", it means that HTMLEditor won't handle any inputs nor
6051 // modify the DOM tree. However, HTMLEditor may not receive `blur`
6052 // event for this state change since this may occur without focus change.
6053 // Therefore, let's notify HTMLEditor of this editing state change.
6054 // Note that even if focusedElement is an editable text control element,
6055 // it becomes not editable from HTMLEditor point of view since text
6056 // control elements are manged by TextEditor.
6057 RefPtr
<Element
> focusedElement
=
6058 nsFocusManager::GetFocusManager()
6059 ? nsFocusManager::GetFocusManager()->GetFocusedElement()
6061 DebugOnly
<nsresult
> rvIgnored
=
6062 HTMLEditor::FocusedElementOrDocumentBecomesNotEditable(
6063 htmlEditor
, *this, focusedElement
);
6064 NS_WARNING_ASSERTION(
6065 NS_SUCCEEDED(rvIgnored
),
6066 "HTMLEditor::FocusedElementOrDocumentBecomesNotEditable() failed, but "
6071 // Flush out style changes on our _parent_ document, if any, so that
6072 // our check for a presshell won't get stale information.
6073 if (mParentDocument
) {
6074 mParentDocument
->FlushPendingNotifications(FlushType::Style
);
6077 // get editing session, make sure this is a strong reference so the
6078 // window can't get deleted during the rest of this call.
6079 const nsCOMPtr
<nsPIDOMWindowOuter
> window
= GetWindow();
6081 return NS_ERROR_FAILURE
;
6084 nsIDocShell
* docshell
= window
->GetDocShell();
6086 return NS_ERROR_FAILURE
;
6089 // FlushPendingNotifications might destroy our docshell.
6090 bool isBeingDestroyed
= false;
6091 docshell
->IsBeingDestroyed(&isBeingDestroyed
);
6092 if (isBeingDestroyed
) {
6093 return NS_ERROR_FAILURE
;
6096 nsCOMPtr
<nsIEditingSession
> editSession
;
6097 nsresult rv
= docshell
->GetEditingSession(getter_AddRefs(editSession
));
6098 NS_ENSURE_SUCCESS(rv
, rv
);
6100 RefPtr
<HTMLEditor
> htmlEditor
= editSession
->GetHTMLEditorForWindow(window
);
6102 // We might already have an editor if it was set up for mail, let's see
6103 // if this is actually the case.
6105 htmlEditor
->GetFlags(&flags
);
6106 if (flags
& nsIEditor::eEditorMailMask
) {
6107 // We already have a mail editor, then we should not attempt to create
6113 if (!HasPresShell(window
)) {
6114 // We should not make the window editable or setup its editor.
6115 // It's probably style=display:none.
6119 bool makeWindowEditable
= mEditingState
== EditingState::eOff
;
6120 bool spellRecheckAll
= false;
6121 bool putOffToRemoveScriptBlockerUntilModifyingEditingState
= false;
6122 htmlEditor
= nullptr;
6125 EditingState oldState
= mEditingState
;
6126 nsAutoEditingState
push(this, EditingState::eSettingUp
);
6128 RefPtr
<PresShell
> presShell
= GetPresShell();
6129 NS_ENSURE_TRUE(presShell
, NS_ERROR_FAILURE
);
6131 // If we're entering the design mode from non-editable state, put the
6132 // selection at the beginning of the document for compatibility reasons.
6133 bool collapseSelectionAtBeginningOfDocument
=
6134 designMode
&& oldState
== EditingState::eOff
;
6135 // However, mEditingState may be eOff even if there is some
6136 // `contenteditable` area and selection has been initialized for it because
6137 // mEditingState for `contenteditable` may have been scheduled to modify
6138 // when safe. In such case, we should not reinitialize selection.
6139 if (collapseSelectionAtBeginningOfDocument
&& mContentEditableCount
) {
6140 Selection
* selection
=
6141 presShell
->GetSelection(nsISelectionController::SELECTION_NORMAL
);
6142 NS_WARNING_ASSERTION(selection
, "Why don't we have Selection?");
6143 if (selection
&& selection
->RangeCount()) {
6144 // Perhaps, we don't need to check whether the selection is in
6145 // an editing host or not because all contents will be editable
6146 // in designMode. (And we don't want to make this code so complicated
6147 // because of legacy API.)
6148 collapseSelectionAtBeginningOfDocument
= false;
6152 MOZ_ASSERT(mStyleSetFilled
);
6154 // Before making this window editable, we need to modify UA style sheet
6155 // because new style may change whether focused element will be focusable
6157 if (IsHTMLOrXHTML()) {
6158 AddContentEditableStyleSheetsToStyleSet(designMode
);
6162 // designMode is being turned on (overrides contentEditable).
6163 spellRecheckAll
= oldState
== EditingState::eContentEditable
;
6166 // Adjust focused element with new style but blur event shouldn't be fired
6167 // until mEditingState is modified with newState.
6168 nsAutoScriptBlocker scriptBlocker
;
6170 nsCOMPtr
<nsPIDOMWindowOuter
> focusedWindow
;
6171 nsIContent
* focusedContent
= nsFocusManager::GetFocusedDescendant(
6172 window
, nsFocusManager::eOnlyCurrentWindow
,
6173 getter_AddRefs(focusedWindow
));
6174 if (focusedContent
) {
6175 nsIFrame
* focusedFrame
= focusedContent
->GetPrimaryFrame();
6176 bool clearFocus
= focusedFrame
? !focusedFrame
->IsFocusable()
6177 : !focusedContent
->IsFocusable();
6179 if (RefPtr
<nsFocusManager
> fm
= nsFocusManager::GetFocusManager()) {
6180 fm
->ClearFocus(window
);
6181 // If we need to dispatch blur event, we should put off after
6182 // modifying mEditingState since blur event handler may change
6183 // designMode state again.
6184 putOffToRemoveScriptBlockerUntilModifyingEditingState
= true;
6190 if (makeWindowEditable
) {
6191 // Editing is being turned on (through designMode or contentEditable)
6193 // XXX This can cause flushing which can change the editing state, so make
6194 // sure to avoid recursing.
6195 rv
= editSession
->MakeWindowEditable(window
, "html", false, false, true);
6196 NS_ENSURE_SUCCESS(rv
, rv
);
6199 // XXX Need to call TearDownEditorOnWindow for all failures.
6200 htmlEditor
= docshell
->GetHTMLEditor();
6202 // Return NS_OK even though we've failed to create an editor here. This
6203 // is so that the setter of designMode on non-HTML documents does not
6205 // This is OK to do because in nsEditingSession::SetupEditorOnWindow() we
6206 // would detect that we can't support the mimetype if appropriate and
6207 // would fall onto the eEditorErrorCantEditMimeType path.
6211 if (collapseSelectionAtBeginningOfDocument
) {
6212 htmlEditor
->BeginningOfDocument();
6215 if (putOffToRemoveScriptBlockerUntilModifyingEditingState
) {
6216 nsContentUtils::AddScriptBlocker();
6220 mEditingState
= newState
;
6221 if (putOffToRemoveScriptBlockerUntilModifyingEditingState
) {
6222 nsContentUtils::RemoveScriptBlocker();
6223 // If mEditingState is overwritten by another call and already disabled
6224 // the editing, we shouldn't keep making window editable.
6225 if (mEditingState
== EditingState::eOff
) {
6230 if (makeWindowEditable
) {
6231 // TODO: We should do this earlier in this method.
6232 // Previously, we called `ExecCommand` with `insertBrOnReturn` command
6233 // whose argument is false here. Then, if it returns error, we
6234 // stopped making it editable. However, after bug 1697078 fixed,
6235 // `ExecCommand` returns error only when the document is not XHTML's
6236 // nor HTML's. Therefore, we use same error handling for now.
6237 if (MOZ_UNLIKELY(NS_WARN_IF(!IsHTMLOrXHTML()))) {
6238 // Editor setup failed. Editing is not on after all.
6239 // XXX Should we reset the editable flag on nodes?
6240 editSession
->TearDownEditorOnWindow(window
);
6241 mEditingState
= EditingState::eOff
;
6242 return NS_ERROR_DOM_INVALID_STATE_ERR
;
6244 // Set the editor to not insert <br> elements on return when in <p> elements
6246 htmlEditor
->SetReturnInParagraphCreatesNewParagraph(true);
6249 // Resync the editor's spellcheck state.
6250 if (spellRecheckAll
) {
6251 nsCOMPtr
<nsISelectionController
> selectionController
=
6252 htmlEditor
->GetSelectionController();
6253 if (NS_WARN_IF(!selectionController
)) {
6254 return NS_ERROR_FAILURE
;
6257 RefPtr
<Selection
> spellCheckSelection
= selectionController
->GetSelection(
6258 nsISelectionController::SELECTION_SPELLCHECK
);
6259 if (spellCheckSelection
) {
6260 spellCheckSelection
->RemoveAllRanges(IgnoreErrors());
6263 htmlEditor
->SyncRealTimeSpell();
6265 MaybeDispatchCheckKeyPressEventModelEvent();
6267 // If this document keeps having focus and the HTMLEditor is in the design
6268 // mode, it may not receive `focus` event for this editing state change since
6269 // this may occur without a focus change. Therefore, let's notify HTMLEditor
6270 // of this editing state change.
6271 if (thisDocumentHasFocus
&& htmlEditor
->IsInDesignMode() &&
6272 ThisDocumentHasFocus()) {
6273 DebugOnly
<nsresult
> rvIgnored
=
6274 htmlEditor
->FocusedElementOrDocumentBecomesEditable(*this, nullptr);
6275 NS_WARNING_ASSERTION(NS_SUCCEEDED(rvIgnored
),
6276 "HTMLEditor::FocusedElementOrDocumentBecomesEditable()"
6277 " failed, but ignored");
6283 // Helper class, used below in ChangeContentEditableCount().
6284 class DeferredContentEditableCountChangeEvent
: public Runnable
{
6286 DeferredContentEditableCountChangeEvent(Document
* aDoc
, Element
* aElement
)
6287 : mozilla::Runnable("DeferredContentEditableCountChangeEvent"),
6289 mElement(aElement
) {}
6291 MOZ_CAN_RUN_SCRIPT_BOUNDARY NS_IMETHOD
Run() override
{
6292 if (mElement
&& mElement
->OwnerDoc() == mDoc
) {
6293 RefPtr
<Document
> doc
= std::move(mDoc
);
6294 RefPtr
<Element
> element
= std::move(mElement
);
6295 doc
->DeferredContentEditableCountChange(element
);
6301 RefPtr
<Document
> mDoc
;
6302 RefPtr
<Element
> mElement
;
6305 void Document::ChangeContentEditableCount(Element
* aElement
, int32_t aChange
) {
6306 NS_ASSERTION(int32_t(mContentEditableCount
) + aChange
>= 0,
6307 "Trying to decrement too much.");
6309 mContentEditableCount
+= aChange
;
6311 nsContentUtils::AddScriptRunner(
6312 new DeferredContentEditableCountChangeEvent(this, aElement
));
6315 void Document::DeferredContentEditableCountChange(Element
* aElement
) {
6316 const RefPtr
<nsFocusManager
> fm
= nsFocusManager::GetFocusManager();
6317 const bool elementHasFocus
=
6318 aElement
&& fm
&& fm
->GetFocusedElement() == aElement
;
6319 if (elementHasFocus
) {
6320 MOZ_ASSERT(nsContentUtils::IsSafeToRunScript());
6321 // When contenteditable of aElement is changed and HTMLEditor works with it
6322 // or needs to start working with it, HTMLEditor may not receive `focus`
6323 // event nor `blur` event because this may occur without a focus change.
6324 // Therefore, we need to notify HTMLEditor of this contenteditable attribute
6326 RefPtr
<HTMLEditor
> htmlEditor
= GetHTMLEditor();
6327 if (aElement
->HasFlag(NODE_IS_EDITABLE
)) {
6329 DebugOnly
<nsresult
> rvIgnored
=
6330 htmlEditor
->FocusedElementOrDocumentBecomesEditable(*this,
6332 NS_WARNING_ASSERTION(
6333 NS_SUCCEEDED(rvIgnored
),
6334 "HTMLEditor::FocusedElementOrDocumentBecomesEditable() failed, but "
6338 DebugOnly
<nsresult
> rvIgnored
=
6339 HTMLEditor::FocusedElementOrDocumentBecomesNotEditable(
6340 htmlEditor
, *this, aElement
);
6341 NS_WARNING_ASSERTION(
6342 NS_SUCCEEDED(rvIgnored
),
6343 "HTMLEditor::FocusedElementOrDocumentBecomesNotEditable() failed, "
6349 (mUpdateNestLevel
> 0 && (mContentEditableCount
> 0) != IsEditingOn())) {
6353 EditingState oldState
= mEditingState
;
6355 nsresult rv
= EditingStateChanged();
6356 NS_ENSURE_SUCCESS_VOID(rv
);
6358 if (oldState
== mEditingState
&&
6359 mEditingState
== EditingState::eContentEditable
) {
6360 // We just changed the contentEditable state of a node, we need to reset
6361 // the spellchecking state of that node.
6363 if (RefPtr
<HTMLEditor
> htmlEditor
= GetHTMLEditor()) {
6364 nsCOMPtr
<nsIInlineSpellChecker
> spellChecker
;
6365 rv
= htmlEditor
->GetInlineSpellChecker(false,
6366 getter_AddRefs(spellChecker
));
6367 NS_ENSURE_SUCCESS_VOID(rv
);
6370 aElement
->InclusiveDescendantMayNeedSpellchecking(htmlEditor
)) {
6371 RefPtr
<nsRange
> range
= nsRange::Create(aElement
);
6372 IgnoredErrorResult res
;
6373 range
->SelectNode(*aElement
, res
);
6375 // The node might be detached from the document at this point,
6376 // which would cause this call to fail. In this case, we can
6377 // safely ignore the contenteditable count change.
6381 rv
= spellChecker
->SpellCheckRange(range
);
6382 NS_ENSURE_SUCCESS_VOID(rv
);
6388 // aElement causes creating new HTMLEditor and the element had and keep
6389 // having focus, the HTMLEditor won't receive `focus` event. Therefore, we
6390 // need to notify HTMLEditor of it becomes editable.
6391 if (elementHasFocus
&& aElement
->HasFlag(NODE_IS_EDITABLE
) &&
6392 fm
->GetFocusedElement() == aElement
) {
6393 if (RefPtr
<HTMLEditor
> htmlEditor
= GetHTMLEditor()) {
6394 DebugOnly
<nsresult
> rvIgnored
=
6395 htmlEditor
->FocusedElementOrDocumentBecomesEditable(*this, aElement
);
6396 NS_WARNING_ASSERTION(
6397 NS_SUCCEEDED(rvIgnored
),
6398 "HTMLEditor::FocusedElementOrDocumentBecomesEditable() failed, but "
6404 void Document::MaybeDispatchCheckKeyPressEventModelEvent() {
6405 // Currently, we need to check only when we're becoming editable for
6407 if (mEditingState
!= EditingState::eContentEditable
) {
6411 if (mHasBeenEditable
) {
6414 mHasBeenEditable
= true;
6416 // Dispatch "CheckKeyPressEventModel" event. That is handled only by
6417 // KeyPressEventModelCheckerChild. Then, it calls SetKeyPressEventModel()
6418 // with proper keypress event for the active web app.
6419 WidgetEvent
checkEvent(true, eUnidentifiedEvent
);
6420 checkEvent
.mSpecifiedEventType
= nsGkAtoms::onCheckKeyPressEventModel
;
6421 checkEvent
.mFlags
.mCancelable
= false;
6422 checkEvent
.mFlags
.mBubbles
= false;
6423 checkEvent
.mFlags
.mOnlySystemGroupDispatch
= true;
6424 // Post the event rather than dispatching it synchronously because we need
6425 // a call of SetKeyPressEventModel() before first key input. Therefore, we
6426 // can avoid paying unnecessary runtime cost for most web apps.
6427 (new AsyncEventDispatcher(this, checkEvent
))->PostDOMEvent();
6430 void Document::SetKeyPressEventModel(uint16_t aKeyPressEventModel
) {
6431 PresShell
* presShell
= GetPresShell();
6435 presShell
->SetKeyPressEventModel(aKeyPressEventModel
);
6438 TimeStamp
Document::LastFocusTime() const { return mLastFocusTime
; }
6440 void Document::SetLastFocusTime(const TimeStamp
& aFocusTime
) {
6441 MOZ_DIAGNOSTIC_ASSERT(!aFocusTime
.IsNull());
6442 MOZ_DIAGNOSTIC_ASSERT(mLastFocusTime
.IsNull() ||
6443 aFocusTime
>= mLastFocusTime
);
6444 mLastFocusTime
= aFocusTime
;
6447 void Document::GetReferrer(nsAString
& aReferrer
) const {
6448 aReferrer
.Truncate();
6449 if (!mReferrerInfo
) {
6453 nsCOMPtr
<nsIURI
> referrer
= mReferrerInfo
->GetComputedReferrer();
6459 nsresult rv
= URLDecorationStripper::StripTrackingIdentifiers(referrer
, uri
);
6460 if (NS_WARN_IF(NS_FAILED(rv
))) {
6464 CopyUTF8toUTF16(uri
, aReferrer
);
6467 void Document::GetCookie(nsAString
& aCookie
, ErrorResult
& aRv
) {
6468 aCookie
.Truncate(); // clear current cookie in case service fails;
6469 // no cookie isn't an error condition.
6471 if (mDisableCookieAccess
) {
6475 // If the document's sandboxed origin flag is set, then reading cookies
6477 if (mSandboxFlags
& SANDBOXED_ORIGIN
) {
6478 aRv
.ThrowSecurityError(
6479 "Forbidden in a sandboxed document without the 'allow-same-origin' "
6484 StorageAccess storageAccess
= CookieAllowedForDocument(this);
6485 if (storageAccess
== StorageAccess::eDeny
) {
6489 if (ShouldPartitionStorage(storageAccess
) &&
6490 !StoragePartitioningEnabled(storageAccess
, CookieJarSettings())) {
6494 // If the document is a cookie-averse Document... return the empty string.
6495 if (IsCookieAverse()) {
6499 // not having a cookie service isn't an error
6500 nsCOMPtr
<nsICookieService
> service
=
6501 do_GetService(NS_COOKIESERVICE_CONTRACTID
);
6503 nsAutoCString cookie
;
6504 service
->GetCookieStringFromDocument(this, cookie
);
6505 // CopyUTF8toUTF16 doesn't handle error
6506 // because it assumes that the input is valid.
6507 UTF_8_ENCODING
->DecodeWithoutBOMHandling(cookie
, aCookie
);
6511 void Document::SetCookie(const nsAString
& aCookie
, ErrorResult
& aRv
) {
6512 if (mDisableCookieAccess
) {
6516 // If the document's sandboxed origin flag is set, then setting cookies
6518 if (mSandboxFlags
& SANDBOXED_ORIGIN
) {
6519 aRv
.ThrowSecurityError(
6520 "Forbidden in a sandboxed document without the 'allow-same-origin' "
6525 StorageAccess storageAccess
= CookieAllowedForDocument(this);
6526 if (storageAccess
== StorageAccess::eDeny
) {
6530 if (ShouldPartitionStorage(storageAccess
) &&
6531 !StoragePartitioningEnabled(storageAccess
, CookieJarSettings())) {
6535 // If the document is a cookie-averse Document... do nothing.
6536 if (IsCookieAverse()) {
6540 if (!mDocumentURI
) {
6544 // not having a cookie service isn't an error
6545 nsCOMPtr
<nsICookieService
> service
=
6546 do_GetService(NS_COOKIESERVICE_CONTRACTID
);
6551 NS_ConvertUTF16toUTF8
cookie(aCookie
);
6552 nsresult rv
= service
->SetCookieStringFromDocument(this, cookie
);
6554 // No warning messages here.
6555 if (NS_FAILED(rv
)) {
6559 nsCOMPtr
<nsIObserverService
> observerService
=
6560 mozilla::services::GetObserverService();
6561 if (observerService
) {
6562 observerService
->NotifyObservers(ToSupports(this), "document-set-cookie",
6563 nsString(aCookie
).get());
6567 ReferrerPolicy
Document::GetReferrerPolicy() const {
6568 return mReferrerInfo
? mReferrerInfo
->ReferrerPolicy()
6569 : ReferrerPolicy::_empty
;
6572 void Document::GetAlinkColor(nsAString
& aAlinkColor
) {
6573 aAlinkColor
.Truncate();
6575 HTMLBodyElement
* body
= GetBodyElement();
6577 body
->GetALink(aAlinkColor
);
6581 void Document::SetAlinkColor(const nsAString
& aAlinkColor
) {
6582 HTMLBodyElement
* body
= GetBodyElement();
6584 body
->SetALink(aAlinkColor
);
6588 void Document::GetLinkColor(nsAString
& aLinkColor
) {
6589 aLinkColor
.Truncate();
6591 HTMLBodyElement
* body
= GetBodyElement();
6593 body
->GetLink(aLinkColor
);
6597 void Document::SetLinkColor(const nsAString
& aLinkColor
) {
6598 HTMLBodyElement
* body
= GetBodyElement();
6600 body
->SetLink(aLinkColor
);
6604 void Document::GetVlinkColor(nsAString
& aVlinkColor
) {
6605 aVlinkColor
.Truncate();
6607 HTMLBodyElement
* body
= GetBodyElement();
6609 body
->GetVLink(aVlinkColor
);
6613 void Document::SetVlinkColor(const nsAString
& aVlinkColor
) {
6614 HTMLBodyElement
* body
= GetBodyElement();
6616 body
->SetVLink(aVlinkColor
);
6620 void Document::GetBgColor(nsAString
& aBgColor
) {
6621 aBgColor
.Truncate();
6623 HTMLBodyElement
* body
= GetBodyElement();
6625 body
->GetBgColor(aBgColor
);
6629 void Document::SetBgColor(const nsAString
& aBgColor
) {
6630 HTMLBodyElement
* body
= GetBodyElement();
6632 body
->SetBgColor(aBgColor
);
6636 void Document::GetFgColor(nsAString
& aFgColor
) {
6637 aFgColor
.Truncate();
6639 HTMLBodyElement
* body
= GetBodyElement();
6641 body
->GetText(aFgColor
);
6645 void Document::SetFgColor(const nsAString
& aFgColor
) {
6646 HTMLBodyElement
* body
= GetBodyElement();
6648 body
->SetText(aFgColor
);
6652 void Document::CaptureEvents() {
6653 WarnOnceAbout(DeprecatedOperations::eUseOfCaptureEvents
);
6656 void Document::ReleaseEvents() {
6657 WarnOnceAbout(DeprecatedOperations::eUseOfReleaseEvents
);
6660 HTMLAllCollection
* Document::All() {
6662 mAll
= new HTMLAllCollection(this);
6667 nsresult
Document::GetSrcdocData(nsAString
& aSrcdocData
) {
6668 if (mIsSrcdocDocument
) {
6669 nsCOMPtr
<nsIInputStreamChannel
> inStrmChan
= do_QueryInterface(mChannel
);
6671 return inStrmChan
->GetSrcdocData(aSrcdocData
);
6674 aSrcdocData
= VoidString();
6678 Nullable
<WindowProxyHolder
> Document::GetDefaultView() const {
6679 nsPIDOMWindowOuter
* win
= GetWindow();
6683 return WindowProxyHolder(win
->GetBrowsingContext());
6686 nsIContent
* Document::GetUnretargetedFocusedContent(
6687 IncludeChromeOnly aIncludeChromeOnly
) const {
6688 nsCOMPtr
<nsPIDOMWindowOuter
> window
= GetWindow();
6692 nsCOMPtr
<nsPIDOMWindowOuter
> focusedWindow
;
6693 nsIContent
* focusedContent
= nsFocusManager::GetFocusedDescendant(
6694 window
, nsFocusManager::eOnlyCurrentWindow
,
6695 getter_AddRefs(focusedWindow
));
6696 if (!focusedContent
) {
6699 // be safe and make sure the element is from this document
6700 if (focusedContent
->OwnerDoc() != this) {
6703 if (focusedContent
->ChromeOnlyAccess() &&
6704 aIncludeChromeOnly
== IncludeChromeOnly::No
) {
6705 return focusedContent
->FindFirstNonChromeOnlyAccessContent();
6707 return focusedContent
;
6710 Element
* Document::GetActiveElement() {
6711 // Get the focused element.
6712 Element
* focusedElement
= GetRetargetedFocusedElement();
6713 if (focusedElement
) {
6714 return focusedElement
;
6717 // No focused element anywhere in this document. Try to get the BODY.
6718 if (IsHTMLOrXHTML()) {
6719 Element
* bodyElement
= AsHTMLDocument()->GetBody();
6723 // Special case to handle the transition to XHTML from XUL documents
6724 // where there currently isn't a body element, but we need to match the
6725 // XUL behavior. This should be removed when bug 1540278 is resolved.
6726 if (nsContentUtils::IsChromeDoc(this)) {
6727 Element
* docElement
= GetDocumentElement();
6728 if (docElement
&& docElement
->IsXULElement()) {
6732 // Because of IE compatibility, return null when html document doesn't have
6737 // If we couldn't get a BODY, return the root element.
6738 return GetDocumentElement();
6741 Element
* Document::GetCurrentScript() {
6742 nsCOMPtr
<Element
> el(do_QueryInterface(ScriptLoader()->GetCurrentScript()));
6746 void Document::ReleaseCapture() const {
6747 // only release the capture if the caller can access it. This prevents a
6748 // page from stopping a scrollbar grab for example.
6749 nsCOMPtr
<nsINode
> node
= PresShell::GetCapturingContent();
6750 if (node
&& nsContentUtils::CanCallerAccess(node
)) {
6751 PresShell::ReleaseCapturingContent();
6755 nsIURI
* Document::GetBaseURI(bool aTryUseXHRDocBaseURI
) const {
6756 if (aTryUseXHRDocBaseURI
&& mChromeXHRDocBaseURI
) {
6757 return mChromeXHRDocBaseURI
;
6760 return GetDocBaseURI();
6763 void Document::SetBaseURI(nsIURI
* aURI
) {
6764 if (!aURI
&& !mDocumentBaseURI
) {
6768 // Don't do anything if the URI wasn't actually changed.
6769 if (aURI
&& mDocumentBaseURI
) {
6770 bool equalBases
= false;
6771 mDocumentBaseURI
->Equals(aURI
, &equalBases
);
6777 mDocumentBaseURI
= aURI
;
6778 mCachedURLData
= nullptr;
6782 Result
<OwningNonNull
<nsIURI
>, nsresult
> Document::ResolveWithBaseURI(
6783 const nsAString
& aURI
) {
6784 RefPtr
<nsIURI
> resolvedURI
;
6786 NS_NewURI(getter_AddRefs(resolvedURI
), aURI
, nullptr, GetDocBaseURI()));
6787 return OwningNonNull
<nsIURI
>(std::move(resolvedURI
));
6790 nsIReferrerInfo
* Document::ReferrerInfoForInternalCSSAndSVGResources() {
6791 if (!mCachedReferrerInfoForInternalCSSAndSVGResources
) {
6792 mCachedReferrerInfoForInternalCSSAndSVGResources
=
6793 ReferrerInfo::CreateForInternalCSSAndSVGResources(this);
6795 return mCachedReferrerInfoForInternalCSSAndSVGResources
;
6798 URLExtraData
* Document::DefaultStyleAttrURLData() {
6799 MOZ_ASSERT(NS_IsMainThread());
6800 if (!mCachedURLData
) {
6801 mCachedURLData
= new URLExtraData(
6802 GetDocBaseURI(), ReferrerInfoForInternalCSSAndSVGResources(),
6805 return mCachedURLData
;
6808 void Document::SetDocumentCharacterSet(NotNull
<const Encoding
*> aEncoding
) {
6809 if (mCharacterSet
!= aEncoding
) {
6810 mCharacterSet
= aEncoding
;
6811 mEncodingMenuDisabled
= aEncoding
== UTF_8_ENCODING
;
6812 RecomputeLanguageFromCharset();
6814 if (nsPresContext
* context
= GetPresContext()) {
6815 context
->DocumentCharSetChanged(aEncoding
);
6820 void Document::GetSandboxFlagsAsString(nsAString
& aFlags
) {
6821 nsContentUtils::SandboxFlagsToString(mSandboxFlags
, aFlags
);
6824 void Document::GetHeaderData(nsAtom
* aHeaderField
, nsAString
& aData
) const {
6826 const HeaderData
* data
= mHeaderData
.get();
6828 if (data
->mField
== aHeaderField
) {
6829 aData
= data
->mData
;
6832 data
= data
->mNext
.get();
6836 void Document::SetHeaderData(nsAtom
* aHeaderField
, const nsAString
& aData
) {
6837 if (!aHeaderField
) {
6838 NS_ERROR("null headerField");
6843 if (!aData
.IsEmpty()) { // don't bother storing empty string
6844 mHeaderData
= MakeUnique
<HeaderData
>(aHeaderField
, aData
);
6847 HeaderData
* data
= mHeaderData
.get();
6848 UniquePtr
<HeaderData
>* lastPtr
= &mHeaderData
;
6850 do { // look for existing and replace
6851 if (data
->mField
== aHeaderField
) {
6852 if (!aData
.IsEmpty()) {
6853 data
->mData
.Assign(aData
);
6854 } else { // don't store empty string
6855 // Note that data->mNext is moved to a temporary before the old value
6856 // of *lastPtr is deleted.
6857 *lastPtr
= std::move(data
->mNext
);
6863 lastPtr
= &data
->mNext
;
6864 data
= lastPtr
->get();
6867 if (!aData
.IsEmpty() && !found
) {
6868 // didn't find, append
6869 *lastPtr
= MakeUnique
<HeaderData
>(aHeaderField
, aData
);
6873 if (aHeaderField
== nsGkAtoms::headerContentLanguage
) {
6874 CopyUTF16toUTF8(aData
, mContentLanguage
);
6875 mMayNeedFontPrefsUpdate
= true;
6876 if (auto* presContext
= GetPresContext()) {
6877 presContext
->ContentLanguageChanged();
6881 if (aHeaderField
== nsGkAtoms::origin_trial
) {
6882 mTrials
.UpdateFromToken(aData
, NodePrincipal());
6883 if (mTrials
.IsEnabled(OriginTrial::CoepCredentialless
)) {
6886 // If we still don't have a WindowContext, WindowContext::OnNewDocument
6887 // will take care of this.
6888 if (WindowContext
* ctx
= GetWindowContext()) {
6889 if (mEmbedderPolicy
) {
6890 Unused
<< ctx
->SetEmbedderPolicy(mEmbedderPolicy
.value());
6896 if (aHeaderField
== nsGkAtoms::headerDefaultStyle
) {
6897 SetPreferredStyleSheetSet(aData
);
6900 if (aHeaderField
== nsGkAtoms::refresh
&& !IsStaticDocument()) {
6901 // We get into this code before we have a script global yet, so get to our
6902 // container via mDocumentContainer.
6903 if (mDocumentContainer
) {
6904 // Note: using mDocumentURI instead of mBaseURI here, for consistency
6905 // (used to just use the current URI of our webnavigation, but that
6906 // should really be the same thing). Note that this code can run
6907 // before the current URI of the webnavigation has been updated, so we
6908 // can't assert equality here.
6909 mDocumentContainer
->SetupRefreshURIFromHeader(this, aData
);
6913 if (aHeaderField
== nsGkAtoms::headerDNSPrefetchControl
&&
6914 mAllowDNSPrefetch
) {
6915 // Chromium treats any value other than 'on' (case insensitive) as 'off'.
6916 mAllowDNSPrefetch
= aData
.IsEmpty() || aData
.LowerCaseEqualsLiteral("on");
6919 if (aHeaderField
== nsGkAtoms::handheldFriendly
) {
6920 mViewportType
= Unknown
;
6924 void Document::SetEarlyHints(
6925 nsTArray
<net::EarlyHintConnectArgs
>&& aEarlyHints
) {
6926 mEarlyHints
= std::move(aEarlyHints
);
6929 void Document::TryChannelCharset(nsIChannel
* aChannel
, int32_t& aCharsetSource
,
6930 NotNull
<const Encoding
*>& aEncoding
,
6931 nsHtml5TreeOpExecutor
* aExecutor
) {
6933 nsAutoCString charsetVal
;
6934 nsresult rv
= aChannel
->GetContentCharset(charsetVal
);
6935 if (NS_SUCCEEDED(rv
)) {
6936 const Encoding
* preferred
= Encoding::ForLabel(charsetVal
);
6938 if (aExecutor
&& preferred
== REPLACEMENT_ENCODING
) {
6939 aExecutor
->ComplainAboutBogusProtocolCharset(this, false);
6941 aEncoding
= WrapNotNull(preferred
);
6942 aCharsetSource
= kCharsetFromChannel
;
6944 } else if (aExecutor
&& !charsetVal
.IsEmpty()) {
6945 aExecutor
->ComplainAboutBogusProtocolCharset(this, true);
6951 static inline void AssertNoStaleServoDataIn(nsINode
& aSubtreeRoot
) {
6953 for (nsINode
* node
: ShadowIncludingTreeIterator(aSubtreeRoot
)) {
6954 const Element
* element
= Element::FromNode(node
);
6958 MOZ_ASSERT(!element
->HasServoData());
6963 already_AddRefed
<PresShell
> Document::CreatePresShell(
6964 nsPresContext
* aContext
, nsViewManager
* aViewManager
) {
6965 MOZ_DIAGNOSTIC_ASSERT(!mPresShell
, "We have a presshell already!");
6967 NS_ENSURE_FALSE(GetBFCacheEntry(), nullptr);
6969 AssertNoStaleServoDataIn(*this);
6971 RefPtr
<PresShell
> presShell
= new PresShell(this);
6972 // Note: we don't hold a ref to the shell (it holds a ref to us)
6973 mPresShell
= presShell
;
6975 if (!mStyleSetFilled
) {
6979 presShell
->Init(aContext
, aViewManager
);
6980 if (RefPtr
<class HighlightRegistry
> highlightRegistry
= mHighlightRegistry
) {
6981 highlightRegistry
->AddHighlightSelectionsToFrameSelection();
6983 // Gaining a shell causes changes in how media queries are evaluated, so
6985 aContext
->MediaFeatureValuesChanged(
6986 {MediaFeatureChange::kAllChanges
},
6987 MediaFeatureChangePropagation::JustThisDocument
);
6989 // Make sure to never paint if we belong to an invisible DocShell.
6990 nsCOMPtr
<nsIDocShell
> docShell(mDocumentContainer
);
6991 if (docShell
&& docShell
->IsInvisible()) {
6992 presShell
->SetNeverPainting(true);
6995 MOZ_LOG(gDocumentLeakPRLog
, LogLevel::Debug
,
6996 ("DOCUMENT %p with PressShell %p and DocShell %p", this,
6997 presShell
.get(), docShell
.get()));
6999 mExternalResourceMap
.ShowViewers();
7001 UpdateFrameRequestCallbackSchedulingState();
7003 if (mDocumentL10n
) {
7004 // In case we already accumulated mutations,
7005 // we'll trigger the refresh driver now.
7006 mDocumentL10n
->OnCreatePresShell();
7009 if (HasAutoFocusCandidates()) {
7010 ScheduleFlushAutoFocusCandidates();
7012 // Now that we have a shell, we might have @font-face rules (the presence of a
7013 // shell may change which rules apply to us). We don't need to do anything
7014 // like EnsureStyleFlush or such, there's nothing to update yet and when stuff
7015 // is ready to update we'll flush the font set.
7016 MarkUserFontSetDirty();
7018 // Take the author style disabled state from the top browsing cvontext.
7019 // (PageStyleChild.sys.mjs ensures this is up to date.)
7020 if (BrowsingContext
* bc
= GetBrowsingContext()) {
7021 presShell
->SetAuthorStyleDisabled(bc
->Top()->AuthorStyleDisabledDefault());
7024 return presShell
.forget();
7027 void Document::UpdateFrameRequestCallbackSchedulingState(
7028 PresShell
* aOldPresShell
) {
7029 // If this condition changes to depend on some other variable, make sure to
7030 // call UpdateFrameRequestCallbackSchedulingState() calls to the places where
7031 // that variable can change. Also consider if you should change
7032 // WouldScheduleFrameRequestCallbacks() instead of adding more stuff to this
7034 bool shouldBeScheduled
=
7035 WouldScheduleFrameRequestCallbacks() && !mFrameRequestManager
.IsEmpty();
7036 if (shouldBeScheduled
== mFrameRequestCallbacksScheduled
) {
7041 PresShell
* presShell
= aOldPresShell
? aOldPresShell
: mPresShell
;
7042 MOZ_RELEASE_ASSERT(presShell
);
7044 nsRefreshDriver
* rd
= presShell
->GetPresContext()->RefreshDriver();
7045 if (shouldBeScheduled
) {
7046 rd
->ScheduleFrameRequestCallbacks(this);
7048 rd
->RevokeFrameRequestCallbacks(this);
7051 mFrameRequestCallbacksScheduled
= shouldBeScheduled
;
7054 void Document::TakeFrameRequestCallbacks(nsTArray
<FrameRequest
>& aCallbacks
) {
7055 MOZ_ASSERT(aCallbacks
.IsEmpty());
7056 mFrameRequestManager
.Take(aCallbacks
);
7057 // No need to manually remove ourselves from the refresh driver; it will
7058 // handle that part. But we do have to update our state.
7059 mFrameRequestCallbacksScheduled
= false;
7062 bool Document::ShouldThrottleFrameRequests() const {
7063 if (mStaticCloneCount
> 0) {
7064 // Even if we're not visible, a static clone may be, so run at full speed.
7069 // We're not visible (probably in a background tab or the bf cache).
7074 // Can't do anything smarter. We don't run frame requests in documents
7075 // without a pres shell anyways.
7079 if (!mPresShell
->IsActive()) {
7080 // The pres shell is not active (we're an invisible OOP iframe or such), so
7085 if (mPresShell
->IsPaintingSuppressed()) {
7086 // Historically we have throttled frame requests until we've painted at
7087 // least once, so keep doing that.
7091 Element
* el
= GetEmbedderElement();
7093 // If we're not in-process, our refresh driver is throttled separately (via
7094 // PresShell::SetIsActive, so not much more we can do here.
7098 if (!StaticPrefs::layout_throttle_in_process_iframes()) {
7102 // Note that because we have to scroll this document into view at least once
7103 // to unthrottle it, we will drop one requestAnimationFrame frame when a
7104 // document that previously wasn't visible scrolls into view. This is
7105 // acceptable / unlikely to be human-perceivable, though we could improve on
7106 // it if needed by adding an intersection margin or something of that sort.
7107 const IntersectionInput input
= DOMIntersectionObserver::ComputeInput(
7108 *el
->OwnerDoc(), /* aRoot = */ nullptr, /* aMargin = */ nullptr);
7109 const IntersectionOutput output
=
7110 DOMIntersectionObserver::Intersect(input
, *el
);
7111 return !output
.Intersects();
7114 void Document::DeletePresShell() {
7115 mExternalResourceMap
.HideViewers();
7116 if (nsPresContext
* presContext
= mPresShell
->GetPresContext()) {
7117 presContext
->RefreshDriver()->CancelPendingFullscreenEvents(this);
7118 presContext
->RefreshDriver()->CancelFlushAutoFocus(this);
7121 // When our shell goes away, request that all our images be immediately
7122 // discarded, so we don't carry around decoded image data for a document we
7123 // no longer intend to paint.
7124 ImageTracker()->RequestDiscardAll();
7126 // Now that we no longer have a shell, we need to forget about any FontFace
7127 // objects for @font-face rules that came from the style set. There's no need
7128 // to call EnsureStyleFlush either, the shell is going away anyway, so there's
7130 MarkUserFontSetDirty();
7132 if (mResizeObserverController
) {
7133 mResizeObserverController
->ShellDetachedFromDocument();
7136 if (IsEditingOn()) {
7140 PresShell
* oldPresShell
= mPresShell
;
7141 mPresShell
= nullptr;
7142 UpdateFrameRequestCallbackSchedulingState(oldPresShell
);
7144 ClearStaleServoData();
7145 AssertNoStaleServoDataIn(*this);
7147 mStyleSet
->ShellDetachedFromDocument();
7148 mStyleSetFilled
= false;
7149 mQuirkSheetAdded
= false;
7150 mContentEditableSheetAdded
= false;
7151 mDesignModeSheetAdded
= false;
7154 void Document::DisallowBFCaching(uint32_t aStatus
) {
7155 NS_ASSERTION(!mBFCacheEntry
, "We're already in the bfcache!");
7156 if (!mBFCacheDisallowed
) {
7157 if (WindowGlobalChild
* wgc
= GetWindowGlobalChild()) {
7158 wgc
->SendUpdateBFCacheStatus(aStatus
, 0);
7161 mBFCacheDisallowed
= true;
7164 void Document::SetBFCacheEntry(nsIBFCacheEntry
* aEntry
) {
7165 MOZ_ASSERT(IsBFCachingAllowed() || !aEntry
, "You should have checked!");
7169 mPresShell
->StopObservingRefreshDriver();
7170 } else if (mBFCacheEntry
) {
7171 mPresShell
->StartObservingRefreshDriver();
7174 mBFCacheEntry
= aEntry
;
7177 bool Document::RemoveFromBFCacheSync() {
7178 bool removed
= false;
7179 if (nsCOMPtr
<nsIBFCacheEntry
> entry
= GetBFCacheEntry()) {
7180 entry
->RemoveFromBFCacheSync();
7182 } else if (!IsCurrentActiveDocument()) {
7183 // In the old bfcache implementation while the new page is loading, but
7184 // before nsIContentViewer.show() has been called, the previous page doesn't
7185 // yet have nsIBFCacheEntry. However, the previous page isn't the current
7186 // active document anymore.
7187 DisallowBFCaching();
7191 if (mozilla::SessionHistoryInParent() && XRE_IsContentProcess()) {
7192 if (BrowsingContext
* bc
= GetBrowsingContext()) {
7193 if (bc
->IsInBFCache()) {
7194 ContentChild
* cc
= ContentChild::GetSingleton();
7195 // IPC is asynchronous but the caller is supposed to check the return
7196 // value. The reason for 'Sync' in the method name is that the old
7197 // implementation may run scripts. There is Async variant in
7198 // the old session history implementation for the cases where
7199 // synchronous operation isn't safe.
7200 cc
->SendRemoveFromBFCache(bc
->Top());
7208 static void SubDocClearEntry(PLDHashTable
* table
, PLDHashEntryHdr
* entry
) {
7209 SubDocMapEntry
* e
= static_cast<SubDocMapEntry
*>(entry
);
7211 NS_RELEASE(e
->mKey
);
7212 if (e
->mSubDocument
) {
7213 e
->mSubDocument
->SetParentDocument(nullptr);
7214 NS_RELEASE(e
->mSubDocument
);
7218 static void SubDocInitEntry(PLDHashEntryHdr
* entry
, const void* key
) {
7220 const_cast<SubDocMapEntry
*>(static_cast<const SubDocMapEntry
*>(entry
));
7222 e
->mKey
= const_cast<Element
*>(static_cast<const Element
*>(key
));
7225 e
->mSubDocument
= nullptr;
7228 nsresult
Document::SetSubDocumentFor(Element
* aElement
, Document
* aSubDoc
) {
7229 NS_ENSURE_TRUE(aElement
, NS_ERROR_UNEXPECTED
);
7232 // aSubDoc is nullptr, remove the mapping
7234 if (mSubDocuments
) {
7235 mSubDocuments
->Remove(aElement
);
7238 if (!mSubDocuments
) {
7239 // Create a new hashtable
7241 static const PLDHashTableOps hash_table_ops
= {
7242 PLDHashTable::HashVoidPtrKeyStub
, PLDHashTable::MatchEntryStub
,
7243 PLDHashTable::MoveEntryStub
, SubDocClearEntry
, SubDocInitEntry
};
7245 mSubDocuments
= new PLDHashTable(&hash_table_ops
, sizeof(SubDocMapEntry
));
7248 // Add a mapping to the hash table
7250 static_cast<SubDocMapEntry
*>(mSubDocuments
->Add(aElement
, fallible
));
7253 return NS_ERROR_OUT_OF_MEMORY
;
7256 if (entry
->mSubDocument
) {
7257 entry
->mSubDocument
->SetParentDocument(nullptr);
7259 // Release the old sub document
7260 NS_RELEASE(entry
->mSubDocument
);
7263 entry
->mSubDocument
= aSubDoc
;
7264 NS_ADDREF(entry
->mSubDocument
);
7266 aSubDoc
->SetParentDocument(this);
7272 Document
* Document::GetSubDocumentFor(nsIContent
* aContent
) const {
7273 if (mSubDocuments
&& aContent
->IsElement()) {
7274 auto entry
= static_cast<SubDocMapEntry
*>(
7275 mSubDocuments
->Search(aContent
->AsElement()));
7278 return entry
->mSubDocument
;
7285 Element
* Document::GetEmbedderElement() const {
7286 // We check if we're the active document in our BrowsingContext
7287 // by comparing against its document, rather than checking if the
7288 // WindowContext is cached, since mWindow may be null when we're
7289 // called (such as in nsPresContext::Init).
7290 if (BrowsingContext
* bc
= GetBrowsingContext()) {
7291 return bc
->GetExtantDocument() == this ? bc
->GetEmbedderElement() : nullptr;
7297 Element
* Document::GetRootElement() const {
7298 return (mCachedRootElement
&& mCachedRootElement
->GetParentNode() == this)
7299 ? mCachedRootElement
7300 : GetRootElementInternal();
7303 Element
* Document::GetUnfocusedKeyEventTarget() { return GetRootElement(); }
7305 Element
* Document::GetRootElementInternal() const {
7306 // We invoke GetRootElement() immediately before the servo traversal, so we
7307 // should always have a cache hit from Servo.
7308 MOZ_ASSERT(NS_IsMainThread());
7310 // Loop backwards because any non-elements, such as doctypes and PIs
7311 // are likely to appear before the root element.
7312 for (nsIContent
* child
= GetLastChild(); child
;
7313 child
= child
->GetPreviousSibling()) {
7314 if (Element
* element
= Element::FromNode(child
)) {
7315 const_cast<Document
*>(this)->mCachedRootElement
= element
;
7320 const_cast<Document
*>(this)->mCachedRootElement
= nullptr;
7324 void Document::InsertChildBefore(nsIContent
* aKid
, nsIContent
* aBeforeThis
,
7325 bool aNotify
, ErrorResult
& aRv
) {
7326 if (aKid
->IsElement() && GetRootElement()) {
7327 NS_WARNING("Inserting root element when we already have one");
7328 aRv
.ThrowHierarchyRequestError("There is already a root element.");
7332 nsINode::InsertChildBefore(aKid
, aBeforeThis
, aNotify
, aRv
);
7335 void Document::RemoveChildNode(nsIContent
* aKid
, bool aNotify
) {
7336 Maybe
<mozAutoDocUpdate
> updateBatch
;
7337 if (aKid
->IsElement()) {
7338 updateBatch
.emplace(this, aNotify
);
7339 // Destroy the link map up front before we mess with the child list.
7340 DestroyElementMaps();
7343 // Preemptively clear mCachedRootElement, since we may be about to remove it
7344 // from our child list, and we don't want to return this maybe-obsolete value
7345 // from any GetRootElement() calls that happen inside of RemoveChildNode().
7346 // (NOTE: for this to be useful, RemoveChildNode() must NOT trigger any
7347 // GetRootElement() calls until after it's removed the child from mChildren.
7348 // Any call before that point would restore this soon-to-be-obsolete cached
7349 // answer, and our clearing here would be fruitless.)
7350 mCachedRootElement
= nullptr;
7351 nsINode::RemoveChildNode(aKid
, aNotify
);
7352 MOZ_ASSERT(mCachedRootElement
!= aKid
,
7353 "Stale pointer in mCachedRootElement, after we tried to clear it "
7354 "(maybe somebody called GetRootElement() too early?)");
7357 void Document::AddStyleSheetToStyleSets(StyleSheet
& aSheet
) {
7358 if (mStyleSetFilled
) {
7359 mStyleSet
->AddDocStyleSheet(aSheet
);
7360 ApplicableStylesChanged();
7364 void Document::RecordShadowStyleChange(ShadowRoot
& aShadowRoot
) {
7365 mStyleSet
->RecordShadowStyleChange(aShadowRoot
);
7366 ApplicableStylesChanged();
7369 void Document::ApplicableStylesChanged() {
7370 // TODO(emilio): if we decide to resolve style in display: none iframes, then
7371 // we need to always track style changes and remove the mStyleSetFilled.
7372 if (!mStyleSetFilled
) {
7376 MarkUserFontSetDirty();
7377 PresShell
* ps
= GetPresShell();
7382 ps
->EnsureStyleFlush();
7383 nsPresContext
* pc
= ps
->GetPresContext();
7388 pc
->MarkCounterStylesDirty();
7389 pc
->MarkFontFeatureValuesDirty();
7390 pc
->MarkFontPaletteValuesDirty();
7391 pc
->RestyleManager()->NextRestyleIsForCSSRuleChanges();
7394 void Document::RemoveStyleSheetFromStyleSets(StyleSheet
& aSheet
) {
7395 if (mStyleSetFilled
) {
7396 mStyleSet
->RemoveStyleSheet(aSheet
);
7397 ApplicableStylesChanged();
7401 void Document::InsertSheetAt(size_t aIndex
, StyleSheet
& aSheet
) {
7402 DocumentOrShadowRoot::InsertSheetAt(aIndex
, aSheet
);
7404 if (aSheet
.IsApplicable()) {
7405 AddStyleSheetToStyleSets(aSheet
);
7409 void Document::StyleSheetApplicableStateChanged(StyleSheet
& aSheet
) {
7410 const bool applicable
= aSheet
.IsApplicable();
7411 // If we're actually in the document style sheet list
7412 if (StyleOrderIndexOfSheet(aSheet
) >= 0) {
7414 AddStyleSheetToStyleSets(aSheet
);
7416 RemoveStyleSheetFromStyleSets(aSheet
);
7420 PostStyleSheetApplicableStateChangeEvent(aSheet
);
7422 if (!mSSApplicableStateNotificationPending
) {
7423 MOZ_RELEASE_ASSERT(NS_IsMainThread());
7424 nsCOMPtr
<nsIRunnable
> notification
= NewRunnableMethod(
7425 "Document::NotifyStyleSheetApplicableStateChanged", this,
7426 &Document::NotifyStyleSheetApplicableStateChanged
);
7427 mSSApplicableStateNotificationPending
=
7428 NS_SUCCEEDED(Dispatch(TaskCategory::Other
, notification
.forget()));
7432 void Document::PostStyleSheetApplicableStateChangeEvent(StyleSheet
& aSheet
) {
7433 if (!StyleSheetChangeEventsEnabled()) {
7437 StyleSheetApplicableStateChangeEventInit init
;
7438 init
.mBubbles
= true;
7439 init
.mCancelable
= true;
7440 init
.mStylesheet
= &aSheet
;
7441 init
.mApplicable
= aSheet
.IsApplicable();
7443 RefPtr
<StyleSheetApplicableStateChangeEvent
> event
=
7444 StyleSheetApplicableStateChangeEvent::Constructor(
7445 this, u
"StyleSheetApplicableStateChanged"_ns
, init
);
7446 event
->SetTrusted(true);
7447 event
->SetTarget(this);
7448 RefPtr
<AsyncEventDispatcher
> asyncDispatcher
=
7449 new AsyncEventDispatcher(this, event
);
7450 asyncDispatcher
->mOnlyChromeDispatch
= ChromeOnlyDispatch::eYes
;
7451 asyncDispatcher
->PostDOMEvent();
7454 void Document::NotifyStyleSheetApplicableStateChanged() {
7455 mSSApplicableStateNotificationPending
= false;
7456 nsCOMPtr
<nsIObserverService
> observerService
=
7457 mozilla::services::GetObserverService();
7458 if (observerService
) {
7459 observerService
->NotifyObservers(
7460 ToSupports(this), "style-sheet-applicable-state-changed", nullptr);
7464 static int32_t FindSheet(const nsTArray
<RefPtr
<StyleSheet
>>& aSheets
,
7465 nsIURI
* aSheetURI
) {
7466 for (int32_t i
= aSheets
.Length() - 1; i
>= 0; i
--) {
7468 nsIURI
* uri
= aSheets
[i
]->GetSheetURI();
7470 if (uri
&& NS_SUCCEEDED(uri
->Equals(aSheetURI
, &bEqual
)) && bEqual
)
7477 nsresult
Document::LoadAdditionalStyleSheet(additionalSheetType aType
,
7478 nsIURI
* aSheetURI
) {
7479 MOZ_ASSERT(aSheetURI
, "null arg");
7481 // Checking if we have loaded this one already.
7482 if (FindSheet(mAdditionalSheets
[aType
], aSheetURI
) >= 0)
7483 return NS_ERROR_INVALID_ARG
;
7485 // Loading the sheet sync.
7486 RefPtr
<css::Loader
> loader
= new css::Loader(GetDocGroup());
7488 css::SheetParsingMode parsingMode
;
7490 case Document::eAgentSheet
:
7491 parsingMode
= css::eAgentSheetFeatures
;
7494 case Document::eUserSheet
:
7495 parsingMode
= css::eUserSheetFeatures
;
7498 case Document::eAuthorSheet
:
7499 parsingMode
= css::eAuthorSheetFeatures
;
7503 MOZ_CRASH("impossible value for aType");
7506 auto result
= loader
->LoadSheetSync(aSheetURI
, parsingMode
,
7507 css::Loader::UseSystemPrincipal::Yes
);
7508 if (result
.isErr()) {
7509 return result
.unwrapErr();
7512 RefPtr
<StyleSheet
> sheet
= result
.unwrap();
7514 sheet
->SetAssociatedDocumentOrShadowRoot(this);
7515 MOZ_ASSERT(sheet
->IsApplicable());
7517 return AddAdditionalStyleSheet(aType
, sheet
);
7520 nsresult
Document::AddAdditionalStyleSheet(additionalSheetType aType
,
7521 StyleSheet
* aSheet
) {
7522 if (mAdditionalSheets
[aType
].Contains(aSheet
)) {
7523 return NS_ERROR_INVALID_ARG
;
7526 if (!aSheet
->IsApplicable()) {
7527 return NS_ERROR_INVALID_ARG
;
7530 mAdditionalSheets
[aType
].AppendElement(aSheet
);
7532 if (mStyleSetFilled
) {
7533 mStyleSet
->AppendStyleSheet(*aSheet
);
7534 ApplicableStylesChanged();
7539 void Document::RemoveAdditionalStyleSheet(additionalSheetType aType
,
7540 nsIURI
* aSheetURI
) {
7541 MOZ_ASSERT(aSheetURI
);
7543 nsTArray
<RefPtr
<StyleSheet
>>& sheets
= mAdditionalSheets
[aType
];
7545 int32_t i
= FindSheet(mAdditionalSheets
[aType
], aSheetURI
);
7547 RefPtr
<StyleSheet
> sheetRef
= std::move(sheets
[i
]);
7548 sheets
.RemoveElementAt(i
);
7550 if (!mIsGoingAway
) {
7551 MOZ_ASSERT(sheetRef
->IsApplicable());
7552 if (mStyleSetFilled
) {
7553 mStyleSet
->RemoveStyleSheet(*sheetRef
);
7554 ApplicableStylesChanged();
7557 sheetRef
->ClearAssociatedDocumentOrShadowRoot();
7561 nsIGlobalObject
* Document::GetScopeObject() const {
7562 nsCOMPtr
<nsIGlobalObject
> scope(do_QueryReferent(mScopeObject
));
7566 DocGroup
* Document::GetDocGroupOrCreate() {
7567 if (!mDocGroup
&& GetBrowsingContext()) {
7568 BrowsingContextGroup
* group
= GetBrowsingContext()->Group();
7571 nsAutoCString docGroupKey
;
7572 nsresult rv
= mozilla::dom::DocGroup::GetKey(
7573 NodePrincipal(), group
->IsPotentiallyCrossOriginIsolated(),
7575 if (NS_SUCCEEDED(rv
)) {
7576 mDocGroup
= group
->AddDocument(docGroupKey
, this);
7582 void Document::SetScopeObject(nsIGlobalObject
* aGlobal
) {
7583 mScopeObject
= do_GetWeakReference(aGlobal
);
7585 mHasHadScriptHandlingObject
= true;
7587 nsCOMPtr
<nsPIDOMWindowInner
> window
= do_QueryInterface(aGlobal
);
7591 BrowsingContextGroup
* browsingContextGroup
=
7592 window
->GetBrowsingContextGroup();
7594 // We should already have the principal, and now that we have been added
7595 // to a window, we should be able to join a DocGroup!
7596 nsAutoCString docGroupKey
;
7597 nsresult rv
= mozilla::dom::DocGroup::GetKey(
7599 browsingContextGroup
->IsPotentiallyCrossOriginIsolated(), docGroupKey
);
7601 if (NS_SUCCEEDED(rv
)) {
7602 MOZ_RELEASE_ASSERT(mDocGroup
->MatchesKey(docGroupKey
));
7604 MOZ_RELEASE_ASSERT(mDocGroup
->GetBrowsingContextGroup() ==
7605 browsingContextGroup
);
7607 mDocGroup
= browsingContextGroup
->AddDocument(docGroupKey
, this);
7609 MOZ_ASSERT(mDocGroup
);
7613 mNodeInfoManager
->GetArenaAllocator(),
7614 mNodeInfoManager
->GetArenaAllocator() == mDocGroup
->ArenaAllocator());
7618 bool Document::ContainsEMEContent() {
7619 nsPIDOMWindowInner
* win
= GetInnerWindow();
7620 // Note this case is different from checking just media elements in that
7621 // it covers when we've created MediaKeys but not associated them with a
7623 return win
&& win
->HasActiveMediaKeysInstance();
7626 bool Document::ContainsMSEContent() {
7627 bool containsMSE
= false;
7629 auto check
= [&containsMSE
](nsISupports
* aSupports
) {
7630 nsCOMPtr
<nsIContent
> content(do_QueryInterface(aSupports
));
7631 if (auto* mediaElem
= HTMLMediaElement::FromNodeOrNull(content
)) {
7632 RefPtr
<MediaSource
> ms
= mediaElem
->GetMozMediaSourceObject();
7639 EnumerateActivityObservers(check
);
7643 static void NotifyActivityChangedCallback(nsISupports
* aSupports
) {
7644 nsCOMPtr
<nsIContent
> content(do_QueryInterface(aSupports
));
7645 if (auto mediaElem
= HTMLMediaElement::FromNodeOrNull(content
)) {
7646 mediaElem
->NotifyOwnerDocumentActivityChanged();
7648 nsCOMPtr
<nsIObjectLoadingContent
> objectLoadingContent(
7649 do_QueryInterface(aSupports
));
7650 if (objectLoadingContent
) {
7651 nsObjectLoadingContent
* olc
=
7652 static_cast<nsObjectLoadingContent
*>(objectLoadingContent
.get());
7653 olc
->NotifyOwnerDocumentActivityChanged();
7655 nsCOMPtr
<nsIDocumentActivity
> objectDocumentActivity(
7656 do_QueryInterface(aSupports
));
7657 if (objectDocumentActivity
) {
7658 objectDocumentActivity
->NotifyOwnerDocumentActivityChanged();
7660 nsCOMPtr
<nsIImageLoadingContent
> imageLoadingContent(
7661 do_QueryInterface(aSupports
));
7662 if (imageLoadingContent
) {
7663 auto ilc
= static_cast<nsImageLoadingContent
*>(imageLoadingContent
.get());
7664 ilc
->NotifyOwnerDocumentActivityChanged();
7669 void Document::NotifyActivityChanged() {
7670 EnumerateActivityObservers(NotifyActivityChangedCallback
);
7673 bool Document::IsTopLevelWindowInactive() const {
7674 if (BrowsingContext
* bc
= GetBrowsingContext()) {
7675 return !bc
->GetIsActiveBrowserWindow();
7681 void Document::SetContainer(nsDocShell
* aContainer
) {
7683 mDocumentContainer
= aContainer
;
7685 mDocumentContainer
= WeakPtr
<nsDocShell
>();
7689 aContainer
&& aContainer
->GetBrowsingContext()->IsChrome();
7691 NotifyActivityChanged();
7693 // IsTopLevelWindowInactive depends on the docshell, so
7694 // update the cached value now that it's available.
7695 UpdateDocumentStates(DocumentState::WINDOW_INACTIVE
, false);
7700 BrowsingContext
* context
= aContainer
->GetBrowsingContext();
7701 MOZ_ASSERT_IF(context
&& mDocGroup
,
7702 context
->Group() == mDocGroup
->GetBrowsingContextGroup());
7703 if (context
&& context
->IsContent()) {
7704 SetIsTopLevelContentDocument(context
->IsTopContent());
7705 SetIsContentDocument(true);
7707 SetIsTopLevelContentDocument(false);
7708 SetIsContentDocument(false);
7712 nsISupports
* Document::GetContainer() const {
7713 return static_cast<nsIDocShell
*>(mDocumentContainer
);
7716 void Document::SetScriptGlobalObject(
7717 nsIScriptGlobalObject
* aScriptGlobalObject
) {
7718 MOZ_ASSERT(aScriptGlobalObject
|| !mAnimationController
||
7719 mAnimationController
->IsPausedByType(
7720 SMILTimeContainer::PAUSE_PAGEHIDE
|
7721 SMILTimeContainer::PAUSE_BEGIN
),
7722 "Clearing window pointer while animations are unpaused");
7724 if (mScriptGlobalObject
&& !aScriptGlobalObject
) {
7725 // We're detaching from the window. We need to grab a pointer to
7726 // our layout history state now.
7727 mLayoutHistoryState
= GetLayoutHistoryState();
7729 // Also make sure to remove our onload blocker now if we haven't done it yet
7730 if (mOnloadBlockCount
!= 0) {
7731 nsCOMPtr
<nsILoadGroup
> loadGroup
= GetDocumentLoadGroup();
7733 loadGroup
->RemoveRequest(mOnloadBlocker
, nullptr, NS_OK
);
7737 if (GetController().isSome()) {
7738 if (imgLoader
* loader
= nsContentUtils::GetImgLoaderForDocument(this)) {
7739 loader
->ClearCacheForControlledDocument(this);
7742 // We may become controlled again if this document comes back out
7743 // of bfcache. Clear our state to allow that to happen. Only
7744 // clear this flag if we are actually controlled, though, so pages
7745 // that were force reloaded don't become controlled when they
7746 // come out of bfcache.
7747 mMaybeServiceWorkerControlled
= false;
7750 if (GetWindowContext()) {
7751 // The document is about to lose its window, so this is a good time to
7752 // send our page use counters, while we still have access to our
7755 // (We also do this in nsGlobalWindowInner::FreeInnerObjects(), which
7756 // catches some cases of documents losing their window that don't
7758 SendPageUseCounters();
7762 // BlockOnload() might be called before mScriptGlobalObject is set.
7763 // We may need to add the blocker once mScriptGlobalObject is set.
7764 bool needOnloadBlocker
= !mScriptGlobalObject
&& aScriptGlobalObject
;
7766 mScriptGlobalObject
= aScriptGlobalObject
;
7768 if (needOnloadBlocker
) {
7769 EnsureOnloadBlocker();
7772 UpdateFrameRequestCallbackSchedulingState();
7774 if (aScriptGlobalObject
) {
7775 // Go back to using the docshell for the layout history state
7776 mLayoutHistoryState
= nullptr;
7777 SetScopeObject(aScriptGlobalObject
);
7778 mHasHadDefaultView
= true;
7780 if (mAllowDNSPrefetch
) {
7781 nsCOMPtr
<nsIDocShell
> docShell(mDocumentContainer
);
7784 nsCOMPtr
<nsIWebNavigation
> webNav
=
7785 do_GetInterface(aScriptGlobalObject
);
7786 NS_ASSERTION(SameCOMIdentity(webNav
, docShell
),
7787 "Unexpected container or script global?");
7789 bool allowDNSPrefetch
;
7790 docShell
->GetAllowDNSPrefetch(&allowDNSPrefetch
);
7791 mAllowDNSPrefetch
= allowDNSPrefetch
;
7795 // If we are set in a window that is already focused we should remember this
7796 // as the time the document gained focus.
7797 if (HasFocus(IgnoreErrors())) {
7798 SetLastFocusTime(TimeStamp::Now());
7802 // Remember the pointer to our window (or lack there of), to avoid
7803 // having to QI every time it's asked for.
7804 nsCOMPtr
<nsPIDOMWindowInner
> window
= do_QueryInterface(mScriptGlobalObject
);
7807 // Now that we know what our window is, we can flush the CSP errors to the
7808 // Web Console. We are flushing all messages that occurred and were stored in
7809 // the queue prior to this point.
7811 static_cast<nsCSPContext
*>(mCSP
.get())->flushConsoleMessages();
7814 nsCOMPtr
<nsIHttpChannelInternal
> internalChannel
=
7815 do_QueryInterface(GetChannel());
7816 if (internalChannel
) {
7817 nsCOMArray
<nsISecurityConsoleMessage
> messages
;
7818 DebugOnly
<nsresult
> rv
= internalChannel
->TakeAllSecurityMessages(messages
);
7819 MOZ_ASSERT(NS_SUCCEEDED(rv
));
7820 SendToConsole(messages
);
7823 // Set our visibility state, but do not fire the event. This is correct
7824 // because either we're coming out of bfcache (in which case IsVisible() will
7825 // still test false at this point and no state change will happen) or we're
7826 // doing the initial document load and don't want to fire the event for this
7829 // When the visibility is changed, notify it to observers.
7830 // Some observers need the notification, for example HTMLMediaElement uses
7831 // it to update internal media resource allocation.
7832 // When video is loaded via VideoDocument, HTMLMediaElement and MediaDecoder
7833 // creation are already done before Document::SetScriptGlobalObject() call.
7834 // MediaDecoder decides whether starting decoding is decided based on
7835 // document's visibility. When the MediaDecoder is created,
7836 // Document::SetScriptGlobalObject() is not yet called and document is
7837 // hidden state. Therefore the MediaDecoder decides that decoding is
7838 // not yet necessary. But soon after Document::SetScriptGlobalObject()
7839 // call, the document becomes not hidden. At the time, MediaDecoder needs
7840 // to know it and needs to start updating decoding.
7841 UpdateVisibilityState(DispatchVisibilityChange::No
);
7843 // The global in the template contents owner document should be the same.
7844 if (mTemplateContentsOwner
&& mTemplateContentsOwner
!= this) {
7845 mTemplateContentsOwner
->SetScriptGlobalObject(aScriptGlobalObject
);
7848 // Tell the script loader about the new global object.
7849 if (mScriptLoader
&& !IsTemplateContentsOwner()) {
7850 mScriptLoader
->SetGlobalObject(mScriptGlobalObject
);
7853 if (!mMaybeServiceWorkerControlled
&& mDocumentContainer
&&
7854 mScriptGlobalObject
&& GetChannel()) {
7855 // If we are shift-reloaded, don't associate with a ServiceWorker.
7856 if (mDocumentContainer
->IsForceReloading()) {
7857 NS_WARNING("Page was shift reloaded, skipping ServiceWorker control");
7861 mMaybeServiceWorkerControlled
= true;
7865 nsIScriptGlobalObject
* Document::GetScriptHandlingObjectInternal() const {
7866 MOZ_ASSERT(!mScriptGlobalObject
,
7867 "Do not call this when mScriptGlobalObject is set!");
7868 if (mHasHadDefaultView
) {
7872 nsCOMPtr
<nsIScriptGlobalObject
> scriptHandlingObject
=
7873 do_QueryReferent(mScopeObject
);
7874 nsCOMPtr
<nsPIDOMWindowInner
> win
= do_QueryInterface(scriptHandlingObject
);
7876 nsPIDOMWindowOuter
* outer
= win
->GetOuterWindow();
7877 if (!outer
|| outer
->GetCurrentInnerWindow() != win
) {
7878 NS_WARNING("Wrong inner/outer window combination!");
7882 return scriptHandlingObject
;
7884 void Document::SetScriptHandlingObject(nsIScriptGlobalObject
* aScriptObject
) {
7885 NS_ASSERTION(!mScriptGlobalObject
|| mScriptGlobalObject
== aScriptObject
,
7886 "Wrong script object!");
7887 if (aScriptObject
) {
7888 SetScopeObject(aScriptObject
);
7889 mHasHadDefaultView
= false;
7893 nsPIDOMWindowOuter
* Document::GetWindowInternal() const {
7894 MOZ_ASSERT(!mWindow
, "This should not be called when mWindow is not null!");
7895 // Let's use mScriptGlobalObject. Even if the document is already removed from
7896 // the docshell, the outer window might be still obtainable from the it.
7897 nsCOMPtr
<nsPIDOMWindowOuter
> win
;
7898 if (mRemovedFromDocShell
) {
7899 // The docshell returns the outer window we are done.
7900 nsCOMPtr
<nsIDocShell
> kungFuDeathGrip(mDocumentContainer
);
7901 if (kungFuDeathGrip
) {
7902 win
= kungFuDeathGrip
->GetWindow();
7905 if (nsCOMPtr
<nsPIDOMWindowInner
> inner
=
7906 do_QueryInterface(mScriptGlobalObject
)) {
7907 // mScriptGlobalObject is always the inner window, let's get the outer.
7908 win
= inner
->GetOuterWindow();
7915 bool Document::InternalAllowXULXBL() {
7916 if (nsContentUtils::AllowXULXBLForPrincipal(NodePrincipal())) {
7917 mAllowXULXBL
= eTriTrue
;
7921 mAllowXULXBL
= eTriFalse
;
7925 // Note: We don't hold a reference to the document observer; we assume
7926 // that it has a live reference to the document.
7927 void Document::AddObserver(nsIDocumentObserver
* aObserver
) {
7928 NS_ASSERTION(mObservers
.IndexOf(aObserver
) == nsTArray
<int>::NoIndex
,
7929 "Observer already in the list");
7930 mObservers
.AppendElement(aObserver
);
7931 AddMutationObserver(aObserver
);
7934 bool Document::RemoveObserver(nsIDocumentObserver
* aObserver
) {
7935 // If we're in the process of destroying the document (and we're
7936 // informing the observers of the destruction), don't remove the
7937 // observers from the list. This is not a big deal, since we
7938 // don't hold a live reference to the observers.
7939 if (!mInDestructor
) {
7940 RemoveMutationObserver(aObserver
);
7941 return mObservers
.RemoveElement(aObserver
);
7944 return mObservers
.Contains(aObserver
);
7947 void Document::BeginUpdate() {
7949 nsContentUtils::AddScriptBlocker();
7950 NS_DOCUMENT_NOTIFY_OBSERVERS(BeginUpdate
, (this));
7953 void Document::EndUpdate() {
7954 const bool reset
= !mPendingMaybeEditingStateChanged
;
7955 mPendingMaybeEditingStateChanged
= true;
7957 NS_DOCUMENT_NOTIFY_OBSERVERS(EndUpdate
, (this));
7961 nsContentUtils::RemoveScriptBlocker();
7963 if (mXULBroadcastManager
) {
7964 mXULBroadcastManager
->MaybeBroadcast();
7968 mPendingMaybeEditingStateChanged
= false;
7970 MaybeEditingStateChanged();
7973 void Document::BeginLoad() {
7974 if (IsEditingOn()) {
7975 // Reset() blows away all event listeners in the document, and our
7976 // editor relies heavily on those. Midas is turned on, to make it
7977 // work, re-initialize it to give it a chance to add its event
7981 EditingStateChanged();
7984 MOZ_ASSERT(!mDidCallBeginLoad
);
7985 mDidCallBeginLoad
= true;
7987 // Block onload here to prevent having to deal with blocking and
7988 // unblocking it while we know the document is loading.
7990 mDidFireDOMContentLoaded
= false;
7991 BlockDOMContentLoaded();
7993 if (mScriptLoader
) {
7994 mScriptLoader
->BeginDeferringScripts();
7997 NS_DOCUMENT_NOTIFY_OBSERVERS(BeginLoad
, (this));
8000 void Document::MozSetImageElement(const nsAString
& aImageElementId
,
8001 Element
* aElement
) {
8002 if (aImageElementId
.IsEmpty()) return;
8004 // Hold a script blocker while calling SetImageElement since that can call
8005 // out to id-observers
8006 nsAutoScriptBlocker scriptBlocker
;
8008 IdentifierMapEntry
* entry
= mIdentifierMap
.PutEntry(aImageElementId
);
8010 entry
->SetImageElement(aElement
);
8011 if (entry
->IsEmpty()) {
8012 mIdentifierMap
.RemoveEntry(entry
);
8017 void Document::DispatchContentLoadedEvents() {
8018 // If you add early returns from this method, make sure you're
8019 // calling UnblockOnload properly.
8021 // Unpin references to preloaded images
8022 mPreloadingImages
.Clear();
8024 // DOM manipulation after content loaded should not care if the element
8025 // came from the preloader.
8026 mPreloadedPreconnects
.Clear();
8029 mTiming
->NotifyDOMContentLoadedStart(Document::GetDocumentURI());
8032 // Dispatch observer notification to notify observers document is interactive.
8033 nsCOMPtr
<nsIObserverService
> os
= mozilla::services::GetObserverService();
8035 nsIPrincipal
* principal
= NodePrincipal();
8036 os
->NotifyObservers(ToSupports(this),
8037 principal
->IsSystemPrincipal()
8038 ? "chrome-document-interactive"
8039 : "content-document-interactive",
8043 // Fire a DOM event notifying listeners that this document has been
8044 // loaded (excluding images and other loads initiated by this
8046 nsContentUtils::DispatchTrustedEvent(this, ToSupports(this),
8047 u
"DOMContentLoaded"_ns
, CanBubble::eYes
,
8050 if (auto* const window
= GetInnerWindow()) {
8051 const RefPtr
<ServiceWorkerContainer
> serviceWorker
=
8052 window
->Navigator()->ServiceWorker();
8054 // This could cause queued messages from a service worker to get
8055 // dispatched on serviceWorker.
8056 serviceWorker
->StartMessages();
8059 if (MayStartLayout()) {
8060 MaybeResolveReadyForIdle();
8063 nsIDocShell
* docShell
= GetDocShell();
8065 if (TimelineConsumers::HasConsumer(docShell
)) {
8066 TimelineConsumers::AddMarkerForDocShell(
8068 MakeUnique
<DocLoadingTimelineMarker
>("document::DOMContentLoaded"));
8072 mTiming
->NotifyDOMContentLoadedEnd(Document::GetDocumentURI());
8075 // If this document is a [i]frame, fire a DOMFrameContentLoaded
8076 // event on all parent documents notifying that the HTML (excluding
8077 // other external files such as images and stylesheets) in a frame
8078 // has finished loading.
8080 // target_frame is the [i]frame element that will be used as the
8081 // target for the event. It's the [i]frame whose content is done
8083 nsCOMPtr
<Element
> target_frame
= GetEmbedderElement();
8085 if (target_frame
&& target_frame
->IsInComposedDoc()) {
8086 nsCOMPtr
<Document
> parent
= target_frame
->OwnerDoc();
8088 RefPtr
<Event
> event
;
8090 IgnoredErrorResult ignored
;
8091 event
= parent
->CreateEvent(u
"Events"_ns
, CallerType::System
, ignored
);
8095 event
->InitEvent(u
"DOMFrameContentLoaded"_ns
, true, true);
8097 event
->SetTarget(target_frame
);
8098 event
->SetTrusted(true);
8100 // To dispatch this event we must manually call
8101 // EventDispatcher::Dispatch() on the ancestor document since the
8102 // target is not in the same document, so the event would never reach
8103 // the ancestor document if we used the normal event
8104 // dispatching code.
8106 WidgetEvent
* innerEvent
= event
->WidgetEventPtr();
8108 nsEventStatus status
= nsEventStatus_eIgnore
;
8110 if (RefPtr
<nsPresContext
> context
= parent
->GetPresContext()) {
8111 // TODO: Bug 1506441
8112 EventDispatcher::Dispatch(MOZ_KnownLive(ToSupports(parent
)),
8113 context
, innerEvent
, event
, &status
);
8118 parent
= parent
->GetInProcessParentDocument();
8122 nsPIDOMWindowInner
* inner
= GetInnerWindow();
8124 inner
->NoteDOMContentLoaded();
8128 if (mMaybeServiceWorkerControlled
) {
8129 using mozilla::dom::ServiceWorkerManager
;
8130 RefPtr
<ServiceWorkerManager
> swm
= ServiceWorkerManager::GetInstance();
8132 Maybe
<ClientInfo
> clientInfo
= GetClientInfo();
8133 if (clientInfo
.isSome()) {
8134 swm
->MaybeCheckNavigationUpdate(clientInfo
.ref());
8139 if (mSetCompleteAfterDOMContentLoaded
) {
8140 SetReadyStateInternal(ReadyState::READYSTATE_COMPLETE
);
8141 mSetCompleteAfterDOMContentLoaded
= false;
8144 UnblockOnload(true);
8147 void Document::EndLoad() {
8148 bool turnOnEditing
=
8149 mParser
&& (IsInDesignMode() || mContentEditableCount
> 0);
8152 // only assert if nothing stopped the load on purpose
8153 if (!mParserAborted
) {
8154 nsContentSecurityUtils::AssertAboutPageHasCSP(this);
8158 // EndLoad may have been called without a matching call to BeginLoad, in the
8159 // case of a failed parse (for example, due to timeout). In such a case, we
8160 // still want to execute part of this code to do appropriate cleanup, but we
8161 // gate part of it because it is intended to match 1-for-1 with calls to
8162 // BeginLoad. We have an explicit flag bit for this purpose, since it's
8163 // complicated and error prone to derive this condition from other related
8164 // flags that can be manipulated outside of a BeginLoad/EndLoad pair.
8166 // Part 1: Code that always executes to cleanup end of parsing, whether
8167 // that parsing was successful or not.
8169 // Drop the ref to our parser, if any, but keep hold of the sink so that we
8170 // can flush it from FlushPendingNotifications as needed. We might have to
8171 // do that to get a StartLayout() to happen.
8173 mWeakSink
= do_GetWeakReference(mParser
->GetContentSink());
8177 // Update the attributes on the PerformanceNavigationTiming before notifying
8178 // the onload observers.
8179 if (nsPIDOMWindowInner
* window
= GetInnerWindow()) {
8180 if (RefPtr
<Performance
> performance
= window
->GetPerformance()) {
8181 performance
->UpdateNavigationTimingEntry();
8185 NS_DOCUMENT_NOTIFY_OBSERVERS(EndLoad
, (this));
8187 // Part 2: Code that only executes when this EndLoad matches a BeginLoad.
8189 if (!mDidCallBeginLoad
) {
8192 mDidCallBeginLoad
= false;
8194 UnblockDOMContentLoaded();
8196 if (turnOnEditing
) {
8197 EditingStateChanged();
8201 // This is a document that's not in a window. For example, this could be an
8202 // XMLHttpRequest responseXML document, or a document created via DOMParser
8203 // or DOMImplementation. We don't reach this code normally for such
8204 // documents (which is not obviously correct), but can reach it via
8205 // document.open()/document.close().
8207 // Such documents don't fire load events, but per spec should set their
8208 // readyState to "complete" when parsing and all loading of subresources is
8209 // done. Parsing is done now, and documents not in a window don't load
8210 // subresources, so just go ahead and mark ourselves as complete.
8211 SetReadyStateInternal(Document::READYSTATE_COMPLETE
,
8212 /* updateTimingInformation = */ false);
8214 // Reset mSkipLoadEventAfterClose just in case.
8215 mSkipLoadEventAfterClose
= false;
8219 void Document::UnblockDOMContentLoaded() {
8220 MOZ_ASSERT(mBlockDOMContentLoaded
);
8221 if (--mBlockDOMContentLoaded
!= 0 || mDidFireDOMContentLoaded
) {
8225 MOZ_LOG(gDocumentLeakPRLog
, LogLevel::Debug
,
8226 ("DOCUMENT %p UnblockDOMContentLoaded", this));
8228 mDidFireDOMContentLoaded
= true;
8229 if (PresShell
* presShell
= GetPresShell()) {
8230 presShell
->GetRefreshDriver()->NotifyDOMContentLoaded();
8233 MOZ_ASSERT(mReadyState
== READYSTATE_INTERACTIVE
);
8234 if (!mSynchronousDOMContentLoaded
) {
8235 MOZ_RELEASE_ASSERT(NS_IsMainThread());
8236 nsCOMPtr
<nsIRunnable
> ev
=
8237 NewRunnableMethod("Document::DispatchContentLoadedEvents", this,
8238 &Document::DispatchContentLoadedEvents
);
8239 Dispatch(TaskCategory::Other
, ev
.forget());
8241 DispatchContentLoadedEvents();
8245 void Document::ElementStateChanged(Element
* aElement
, ElementState aStateMask
) {
8246 MOZ_ASSERT(!nsContentUtils::IsSafeToRunScript(),
8247 "Someone forgot a scriptblocker");
8248 NS_DOCUMENT_NOTIFY_OBSERVERS(ElementStateChanged
,
8249 (this, aElement
, aStateMask
));
8252 void Document::RuleChanged(StyleSheet
& aSheet
, css::Rule
*,
8253 StyleRuleChangeKind
) {
8254 if (aSheet
.IsApplicable()) {
8255 ApplicableStylesChanged();
8259 void Document::RuleAdded(StyleSheet
& aSheet
, css::Rule
& aRule
) {
8260 if (aRule
.IsIncompleteImportRule()) {
8264 if (aSheet
.IsApplicable()) {
8265 ApplicableStylesChanged();
8269 void Document::ImportRuleLoaded(dom::CSSImportRule
& aRule
, StyleSheet
& aSheet
) {
8270 if (aSheet
.IsApplicable()) {
8271 ApplicableStylesChanged();
8275 void Document::RuleRemoved(StyleSheet
& aSheet
, css::Rule
& aRule
) {
8276 if (aSheet
.IsApplicable()) {
8277 ApplicableStylesChanged();
8281 static Element
* GetCustomContentContainer(PresShell
* aPresShell
) {
8282 if (!aPresShell
|| !aPresShell
->GetCanvasFrame()) {
8286 return aPresShell
->GetCanvasFrame()->GetCustomContentContainer();
8289 already_AddRefed
<AnonymousContent
> Document::InsertAnonymousContent(
8290 Element
& aElement
, bool aForce
, ErrorResult
& aRv
) {
8291 // Clone the node to avoid returning a direct reference.
8292 nsCOMPtr
<nsINode
> clone
= aElement
.CloneNode(true, aRv
);
8297 PresShell
* shell
= GetPresShell();
8298 if (aForce
&& !GetCustomContentContainer(shell
)) {
8299 FlushPendingNotifications(FlushType::Layout
);
8300 shell
= GetPresShell();
8303 nsAutoScriptBlocker scriptBlocker
;
8306 MakeRefPtr
<AnonymousContent
>(clone
.forget().downcast
<Element
>());
8308 mAnonymousContents
.AppendElement(anonContent
);
8310 if (Element
* container
= GetCustomContentContainer(shell
)) {
8311 container
->AppendChildTo(&anonContent
->ContentNode(), true, IgnoreErrors());
8312 shell
->GetCanvasFrame()->ShowCustomContentContainer();
8315 return anonContent
.forget();
8318 static void RemoveAnonContentFromCanvas(AnonymousContent
& aAnonContent
,
8319 PresShell
* aPresShell
) {
8320 RefPtr
<Element
> container
= GetCustomContentContainer(aPresShell
);
8324 container
->RemoveChild(aAnonContent
.ContentNode(), IgnoreErrors());
8327 void Document::RemoveAnonymousContent(AnonymousContent
& aContent
) {
8328 nsAutoScriptBlocker scriptBlocker
;
8330 auto index
= mAnonymousContents
.IndexOf(&aContent
);
8331 if (index
== mAnonymousContents
.NoIndex
) {
8335 mAnonymousContents
.RemoveElementAt(index
);
8336 RemoveAnonContentFromCanvas(aContent
, GetPresShell());
8338 if (mAnonymousContents
.IsEmpty() &&
8339 GetCustomContentContainer(GetPresShell())) {
8340 GetPresShell()->GetCanvasFrame()->HideCustomContentContainer();
8344 Element
* Document::GetAnonRootIfInAnonymousContentContainer(
8345 nsINode
* aNode
) const {
8346 if (!aNode
->IsInNativeAnonymousSubtree()) {
8350 PresShell
* presShell
= GetPresShell();
8351 if (!presShell
|| !presShell
->GetCanvasFrame()) {
8355 nsAutoScriptBlocker scriptBlocker
;
8356 nsCOMPtr
<Element
> customContainer
=
8357 presShell
->GetCanvasFrame()->GetCustomContentContainer();
8358 if (!customContainer
) {
8362 // An arbitrary number of elements can be inserted as children of the custom
8363 // container frame. We want the one that was added that contains aNode, so
8364 // we need to keep track of the last child separately using |child| here.
8365 nsINode
* child
= aNode
;
8366 nsINode
* parent
= aNode
->GetParentNode();
8367 while (parent
&& parent
->IsInNativeAnonymousSubtree()) {
8368 if (parent
== customContainer
) {
8369 return Element::FromNode(child
);
8372 parent
= child
->GetParentNode();
8377 Maybe
<ClientInfo
> Document::GetClientInfo() const {
8378 if (const Document
* orig
= GetOriginalDocument()) {
8379 if (Maybe
<ClientInfo
> info
= orig
->GetClientInfo()) {
8384 if (nsPIDOMWindowInner
* inner
= GetInnerWindow()) {
8385 return inner
->GetClientInfo();
8388 return Maybe
<ClientInfo
>();
8391 Maybe
<ClientState
> Document::GetClientState() const {
8392 if (const Document
* orig
= GetOriginalDocument()) {
8393 if (Maybe
<ClientState
> state
= orig
->GetClientState()) {
8398 if (nsPIDOMWindowInner
* inner
= GetInnerWindow()) {
8399 return inner
->GetClientState();
8402 return Maybe
<ClientState
>();
8405 Maybe
<ServiceWorkerDescriptor
> Document::GetController() const {
8406 if (const Document
* orig
= GetOriginalDocument()) {
8407 if (Maybe
<ServiceWorkerDescriptor
> controller
= orig
->GetController()) {
8412 if (nsPIDOMWindowInner
* inner
= GetInnerWindow()) {
8413 return inner
->GetController();
8416 return Maybe
<ServiceWorkerDescriptor
>();
8420 // Document interface
8422 DocumentType
* Document::GetDoctype() const {
8423 for (nsIContent
* child
= GetFirstChild(); child
;
8424 child
= child
->GetNextSibling()) {
8425 if (child
->NodeType() == DOCUMENT_TYPE_NODE
) {
8426 return static_cast<DocumentType
*>(child
);
8432 DOMImplementation
* Document::GetImplementation(ErrorResult
& rv
) {
8433 if (!mDOMImplementation
) {
8434 nsCOMPtr
<nsIURI
> uri
;
8435 NS_NewURI(getter_AddRefs(uri
), "about:blank");
8437 rv
.Throw(NS_ERROR_OUT_OF_MEMORY
);
8440 bool hasHadScriptObject
= true;
8441 nsIScriptGlobalObject
* scriptObject
=
8442 GetScriptHandlingObject(hasHadScriptObject
);
8443 if (!scriptObject
&& hasHadScriptObject
) {
8444 rv
.Throw(NS_ERROR_UNEXPECTED
);
8447 mDOMImplementation
= new DOMImplementation(
8448 this, scriptObject
? scriptObject
: GetScopeObject(), uri
, uri
);
8451 return mDOMImplementation
;
8454 bool IsLowercaseASCII(const nsAString
& aValue
) {
8455 int32_t len
= aValue
.Length();
8456 for (int32_t i
= 0; i
< len
; ++i
) {
8457 char16_t c
= aValue
[i
];
8458 if (!(0x0061 <= (c
) && ((c
) <= 0x007a))) {
8465 already_AddRefed
<Element
> Document::CreateElement(
8466 const nsAString
& aTagName
, const ElementCreationOptionsOrString
& aOptions
,
8468 rv
= nsContentUtils::CheckQName(aTagName
, false);
8473 bool needsLowercase
= IsHTMLDocument() && !IsLowercaseASCII(aTagName
);
8474 nsAutoString lcTagName
;
8475 if (needsLowercase
) {
8476 nsContentUtils::ASCIIToLower(aTagName
, lcTagName
);
8479 const nsString
* is
= nullptr;
8480 PseudoStyleType pseudoType
= PseudoStyleType::NotPseudo
;
8481 if (aOptions
.IsElementCreationOptions()) {
8482 const ElementCreationOptions
& options
=
8483 aOptions
.GetAsElementCreationOptions();
8485 if (options
.mIs
.WasPassed()) {
8486 is
= &options
.mIs
.Value();
8489 // Check 'pseudo' and throw an exception if it's not one allowed
8490 // with CSS_PSEUDO_ELEMENT_IS_JS_CREATED_NAC.
8491 if (options
.mPseudo
.WasPassed()) {
8492 Maybe
<PseudoStyleType
> type
=
8493 nsCSSPseudoElements::GetPseudoType(options
.mPseudo
.Value());
8494 if (!type
|| *type
== PseudoStyleType::NotPseudo
||
8495 !nsCSSPseudoElements::PseudoElementIsJSCreatedNAC(*type
)) {
8496 rv
.ThrowNotSupportedError("Invalid pseudo-element");
8503 RefPtr
<Element
> elem
= CreateElem(needsLowercase
? lcTagName
: aTagName
,
8504 nullptr, mDefaultElementType
, is
);
8506 if (pseudoType
!= PseudoStyleType::NotPseudo
) {
8507 elem
->SetPseudoElementType(pseudoType
);
8510 return elem
.forget();
8513 already_AddRefed
<Element
> Document::CreateElementNS(
8514 const nsAString
& aNamespaceURI
, const nsAString
& aQualifiedName
,
8515 const ElementCreationOptionsOrString
& aOptions
, ErrorResult
& rv
) {
8516 RefPtr
<mozilla::dom::NodeInfo
> nodeInfo
;
8517 rv
= nsContentUtils::GetNodeInfoFromQName(aNamespaceURI
, aQualifiedName
,
8518 mNodeInfoManager
, ELEMENT_NODE
,
8519 getter_AddRefs(nodeInfo
));
8524 const nsString
* is
= nullptr;
8525 if (aOptions
.IsElementCreationOptions()) {
8526 const ElementCreationOptions
& options
=
8527 aOptions
.GetAsElementCreationOptions();
8528 if (options
.mIs
.WasPassed()) {
8529 is
= &options
.mIs
.Value();
8533 nsCOMPtr
<Element
> element
;
8534 rv
= NS_NewElement(getter_AddRefs(element
), nodeInfo
.forget(),
8535 NOT_FROM_PARSER
, is
);
8540 return element
.forget();
8543 already_AddRefed
<Element
> Document::CreateXULElement(
8544 const nsAString
& aTagName
, const ElementCreationOptionsOrString
& aOptions
,
8546 aRv
= nsContentUtils::CheckQName(aTagName
, false);
8551 const nsString
* is
= nullptr;
8552 if (aOptions
.IsElementCreationOptions()) {
8553 const ElementCreationOptions
& options
=
8554 aOptions
.GetAsElementCreationOptions();
8555 if (options
.mIs
.WasPassed()) {
8556 is
= &options
.mIs
.Value();
8560 RefPtr
<Element
> elem
= CreateElem(aTagName
, nullptr, kNameSpaceID_XUL
, is
);
8562 aRv
.Throw(NS_ERROR_NOT_AVAILABLE
);
8565 return elem
.forget();
8568 already_AddRefed
<nsTextNode
> Document::CreateEmptyTextNode() const {
8569 RefPtr
<nsTextNode
> text
= new (mNodeInfoManager
) nsTextNode(mNodeInfoManager
);
8570 return text
.forget();
8573 already_AddRefed
<nsTextNode
> Document::CreateTextNode(
8574 const nsAString
& aData
) const {
8575 RefPtr
<nsTextNode
> text
= new (mNodeInfoManager
) nsTextNode(mNodeInfoManager
);
8576 // Don't notify; this node is still being created.
8577 text
->SetText(aData
, false);
8578 return text
.forget();
8581 already_AddRefed
<DocumentFragment
> Document::CreateDocumentFragment() const {
8582 RefPtr
<DocumentFragment
> frag
=
8583 new (mNodeInfoManager
) DocumentFragment(mNodeInfoManager
);
8584 return frag
.forget();
8587 // Unfortunately, bareword "Comment" is ambiguous with some Mac system headers.
8588 already_AddRefed
<dom::Comment
> Document::CreateComment(
8589 const nsAString
& aData
) const {
8590 RefPtr
<dom::Comment
> comment
=
8591 new (mNodeInfoManager
) dom::Comment(mNodeInfoManager
);
8593 // Don't notify; this node is still being created.
8594 comment
->SetText(aData
, false);
8595 return comment
.forget();
8598 already_AddRefed
<CDATASection
> Document::CreateCDATASection(
8599 const nsAString
& aData
, ErrorResult
& rv
) {
8600 if (IsHTMLDocument()) {
8601 rv
.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR
);
8605 if (FindInReadable(u
"]]>"_ns
, aData
)) {
8606 rv
.Throw(NS_ERROR_DOM_INVALID_CHARACTER_ERR
);
8610 RefPtr
<CDATASection
> cdata
=
8611 new (mNodeInfoManager
) CDATASection(mNodeInfoManager
);
8613 // Don't notify; this node is still being created.
8614 cdata
->SetText(aData
, false);
8616 return cdata
.forget();
8619 already_AddRefed
<ProcessingInstruction
> Document::CreateProcessingInstruction(
8620 const nsAString
& aTarget
, const nsAString
& aData
, ErrorResult
& rv
) const {
8621 nsresult res
= nsContentUtils::CheckQName(aTarget
, false);
8622 if (NS_FAILED(res
)) {
8627 if (FindInReadable(u
"?>"_ns
, aData
)) {
8628 rv
.Throw(NS_ERROR_DOM_INVALID_CHARACTER_ERR
);
8632 RefPtr
<ProcessingInstruction
> pi
=
8633 NS_NewXMLProcessingInstruction(mNodeInfoManager
, aTarget
, aData
);
8638 already_AddRefed
<Attr
> Document::CreateAttribute(const nsAString
& aName
,
8640 if (!mNodeInfoManager
) {
8641 rv
.Throw(NS_ERROR_NOT_INITIALIZED
);
8645 nsresult res
= nsContentUtils::CheckQName(aName
, false);
8646 if (NS_FAILED(res
)) {
8652 if (IsHTMLDocument()) {
8653 nsContentUtils::ASCIIToLower(aName
, name
);
8658 RefPtr
<mozilla::dom::NodeInfo
> nodeInfo
;
8659 res
= mNodeInfoManager
->GetNodeInfo(name
, nullptr, kNameSpaceID_None
,
8660 ATTRIBUTE_NODE
, getter_AddRefs(nodeInfo
));
8661 if (NS_FAILED(res
)) {
8666 RefPtr
<Attr
> attribute
=
8667 new (mNodeInfoManager
) Attr(nullptr, nodeInfo
.forget(), u
""_ns
);
8668 return attribute
.forget();
8671 already_AddRefed
<Attr
> Document::CreateAttributeNS(
8672 const nsAString
& aNamespaceURI
, const nsAString
& aQualifiedName
,
8674 RefPtr
<mozilla::dom::NodeInfo
> nodeInfo
;
8675 rv
= nsContentUtils::GetNodeInfoFromQName(aNamespaceURI
, aQualifiedName
,
8676 mNodeInfoManager
, ATTRIBUTE_NODE
,
8677 getter_AddRefs(nodeInfo
));
8682 RefPtr
<Attr
> attribute
=
8683 new (mNodeInfoManager
) Attr(nullptr, nodeInfo
.forget(), u
""_ns
);
8684 return attribute
.forget();
8687 void Document::ResolveScheduledSVGPresAttrs() {
8688 for (SVGElement
* svg
: mLazySVGPresElements
) {
8689 svg
->UpdateContentDeclarationBlock();
8691 mLazySVGPresElements
.Clear();
8694 already_AddRefed
<nsSimpleContentList
> Document::BlockedNodesByClassifier()
8696 RefPtr
<nsSimpleContentList
> list
= new nsSimpleContentList(nullptr);
8698 const nsTArray
<nsWeakPtr
> blockedNodes
= mBlockedNodesByClassifier
.Clone();
8700 for (unsigned long i
= 0; i
< blockedNodes
.Length(); i
++) {
8701 nsWeakPtr weakNode
= blockedNodes
[i
];
8702 nsCOMPtr
<nsIContent
> node
= do_QueryReferent(weakNode
);
8703 // Consider only nodes to which we have managed to get strong references.
8704 // Coping with nullptrs since it's expected for nodes to disappear when
8705 // nobody else is referring to them.
8707 list
->AppendElement(node
);
8711 return list
.forget();
8714 void Document::GetSelectedStyleSheetSet(nsAString
& aSheetSet
) {
8715 aSheetSet
.Truncate();
8717 // Look through our sheets, find the selected set title
8718 size_t count
= SheetCount();
8720 for (size_t index
= 0; index
< count
; index
++) {
8721 StyleSheet
* sheet
= SheetAt(index
);
8722 NS_ASSERTION(sheet
, "Null sheet in sheet list!");
8724 if (sheet
->Disabled()) {
8725 // Disabled sheets don't affect the currently selected set
8729 sheet
->GetTitle(title
);
8731 if (aSheetSet
.IsEmpty()) {
8733 } else if (!title
.IsEmpty() && !aSheetSet
.Equals(title
)) {
8734 // Sheets from multiple sets enabled; return null string, per spec.
8735 SetDOMStringToNull(aSheetSet
);
8741 void Document::SetSelectedStyleSheetSet(const nsAString
& aSheetSet
) {
8742 if (DOMStringIsNull(aSheetSet
)) {
8746 // Must update mLastStyleSheetSet before doing anything else with stylesheets
8748 mLastStyleSheetSet
= aSheetSet
;
8749 EnableStyleSheetsForSetInternal(aSheetSet
, true);
8752 void Document::SetPreferredStyleSheetSet(const nsAString
& aSheetSet
) {
8753 mPreferredStyleSheetSet
= aSheetSet
;
8754 // Only mess with our stylesheets if we don't have a lastStyleSheetSet, per
8756 if (DOMStringIsNull(mLastStyleSheetSet
)) {
8757 // Calling EnableStyleSheetsForSetInternal, not SetSelectedStyleSheetSet,
8758 // per spec. The idea here is that we're changing our preferred set and
8759 // that shouldn't change the value of lastStyleSheetSet. Also, we're
8760 // using the Internal version so we can update the CSSLoader and not have
8761 // to worry about null strings.
8762 EnableStyleSheetsForSetInternal(aSheetSet
, true);
8766 DOMStringList
* Document::StyleSheetSets() {
8767 if (!mStyleSheetSetList
) {
8768 mStyleSheetSetList
= new DOMStyleSheetSetList(this);
8770 return mStyleSheetSetList
;
8773 void Document::EnableStyleSheetsForSet(const nsAString
& aSheetSet
) {
8774 // Per spec, passing in null is a no-op.
8775 if (!DOMStringIsNull(aSheetSet
)) {
8776 // Note: must make sure to not change the CSSLoader's preferred sheet --
8777 // that value should be equal to either our lastStyleSheetSet (if that's
8778 // non-null) or to our preferredStyleSheetSet. And this method doesn't
8779 // change either of those.
8780 EnableStyleSheetsForSetInternal(aSheetSet
, false);
8784 void Document::EnableStyleSheetsForSetInternal(const nsAString
& aSheetSet
,
8785 bool aUpdateCSSLoader
) {
8786 size_t count
= SheetCount();
8788 for (size_t index
= 0; index
< count
; index
++) {
8789 StyleSheet
* sheet
= SheetAt(index
);
8790 NS_ASSERTION(sheet
, "Null sheet in sheet list!");
8792 sheet
->GetTitle(title
);
8793 if (!title
.IsEmpty()) {
8794 sheet
->SetEnabled(title
.Equals(aSheetSet
));
8797 if (aUpdateCSSLoader
) {
8798 CSSLoader()->DocumentStyleSheetSetChanged();
8800 if (mStyleSet
->StyleSheetsHaveChanged()) {
8801 ApplicableStylesChanged();
8805 void Document::GetCharacterSet(nsAString
& aCharacterSet
) const {
8806 nsAutoCString charset
;
8807 GetDocumentCharacterSet()->Name(charset
);
8808 CopyASCIItoUTF16(charset
, aCharacterSet
);
8811 already_AddRefed
<nsINode
> Document::ImportNode(nsINode
& aNode
, bool aDeep
,
8812 ErrorResult
& rv
) const {
8813 nsINode
* imported
= &aNode
;
8815 switch (imported
->NodeType()) {
8816 case DOCUMENT_NODE
: {
8819 case DOCUMENT_FRAGMENT_NODE
:
8820 case ATTRIBUTE_NODE
:
8822 case PROCESSING_INSTRUCTION_NODE
:
8824 case CDATA_SECTION_NODE
:
8826 case DOCUMENT_TYPE_NODE
: {
8827 return imported
->Clone(aDeep
, mNodeInfoManager
, rv
);
8830 NS_WARNING("Don't know how to clone this nodetype for importNode.");
8834 rv
.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR
);
8838 already_AddRefed
<nsRange
> Document::CreateRange(ErrorResult
& rv
) {
8839 return nsRange::Create(this, 0, this, 0, rv
);
8842 already_AddRefed
<NodeIterator
> Document::CreateNodeIterator(
8843 nsINode
& aRoot
, uint32_t aWhatToShow
, NodeFilter
* aFilter
,
8844 ErrorResult
& rv
) const {
8845 RefPtr
<NodeIterator
> iterator
=
8846 new NodeIterator(&aRoot
, aWhatToShow
, aFilter
);
8847 return iterator
.forget();
8850 already_AddRefed
<TreeWalker
> Document::CreateTreeWalker(nsINode
& aRoot
,
8851 uint32_t aWhatToShow
,
8852 NodeFilter
* aFilter
,
8853 ErrorResult
& rv
) const {
8854 RefPtr
<TreeWalker
> walker
= new TreeWalker(&aRoot
, aWhatToShow
, aFilter
);
8855 return walker
.forget();
8858 already_AddRefed
<Location
> Document::GetLocation() const {
8859 nsCOMPtr
<nsPIDOMWindowInner
> w
= do_QueryInterface(mScriptGlobalObject
);
8865 return do_AddRef(w
->Location());
8868 already_AddRefed
<nsIURI
> Document::GetDomainURI() {
8869 nsIPrincipal
* principal
= NodePrincipal();
8871 nsCOMPtr
<nsIURI
> uri
;
8872 principal
->GetDomain(getter_AddRefs(uri
));
8874 return uri
.forget();
8876 auto* basePrin
= BasePrincipal::Cast(principal
);
8877 basePrin
->GetURI(getter_AddRefs(uri
));
8878 return uri
.forget();
8881 void Document::GetDomain(nsAString
& aDomain
) {
8882 nsCOMPtr
<nsIURI
> uri
= GetDomainURI();
8889 nsAutoCString hostName
;
8890 nsresult rv
= nsContentUtils::GetHostOrIPv6WithBrackets(uri
, hostName
);
8891 if (NS_SUCCEEDED(rv
)) {
8892 CopyUTF8toUTF16(hostName
, aDomain
);
8894 // If we can't get the host from the URI (e.g. about:, javascript:,
8895 // etc), just return an empty string.
8900 void Document::SetDomain(const nsAString
& aDomain
, ErrorResult
& rv
) {
8901 if (!GetBrowsingContext()) {
8902 // If our browsing context is null; disallow setting domain
8903 rv
.Throw(NS_ERROR_DOM_SECURITY_ERR
);
8907 if (mSandboxFlags
& SANDBOXED_DOMAIN
) {
8908 // We're sandboxed; disallow setting domain
8909 rv
.Throw(NS_ERROR_DOM_SECURITY_ERR
);
8913 if (!FeaturePolicyUtils::IsFeatureAllowed(this, u
"document-domain"_ns
)) {
8914 rv
.Throw(NS_ERROR_DOM_SECURITY_ERR
);
8918 if (aDomain
.IsEmpty()) {
8919 rv
.Throw(NS_ERROR_DOM_SECURITY_ERR
);
8923 nsCOMPtr
<nsIURI
> uri
= GetDomainURI();
8925 rv
.Throw(NS_ERROR_FAILURE
);
8929 // Check new domain - must be a superdomain of the current host
8930 // For example, a page from foo.bar.com may set domain to bar.com,
8931 // but not to ar.com, baz.com, or fi.foo.bar.com.
8933 nsCOMPtr
<nsIURI
> newURI
= RegistrableDomainSuffixOfInternal(aDomain
, uri
);
8935 // Error: illegal domain
8936 rv
.Throw(NS_ERROR_DOM_SECURITY_ERR
);
8940 if (GetBrowsingContext()->Group()->IsPotentiallyCrossOriginIsolated()) {
8941 WarnOnceAbout(Document::eDocumentSetDomainNotAllowed
);
8945 MOZ_ALWAYS_SUCCEEDS(NodePrincipal()->SetDomain(newURI
));
8946 MOZ_ALWAYS_SUCCEEDS(PartitionedPrincipal()->SetDomain(newURI
));
8947 if (WindowGlobalChild
* wgc
= GetWindowGlobalChild()) {
8948 wgc
->SendSetDocumentDomain(newURI
);
8952 already_AddRefed
<nsIURI
> Document::CreateInheritingURIForHost(
8953 const nsACString
& aHostString
) {
8954 if (aHostString
.IsEmpty()) {
8959 nsCOMPtr
<nsIURI
> uri
= GetDomainURI();
8965 rv
= NS_MutateURI(uri
)
8967 .SetPort(-1) // we want to reset the port number if needed.
8968 .SetHostPort(aHostString
)
8970 if (NS_FAILED(rv
)) {
8974 return uri
.forget();
8977 already_AddRefed
<nsIURI
> Document::RegistrableDomainSuffixOfInternal(
8978 const nsAString
& aNewDomain
, nsIURI
* aOrigHost
) {
8979 if (NS_WARN_IF(!aOrigHost
)) {
8983 nsCOMPtr
<nsIURI
> newURI
=
8984 CreateInheritingURIForHost(NS_ConvertUTF16toUTF8(aNewDomain
));
8986 // Error: failed to parse input domain
8990 if (!IsValidDomain(aOrigHost
, newURI
)) {
8991 // Error: illegal domain
8995 nsAutoCString domain
;
8996 if (NS_FAILED(newURI
->GetAsciiHost(domain
))) {
9000 return CreateInheritingURIForHost(domain
);
9004 bool Document::IsValidDomain(nsIURI
* aOrigHost
, nsIURI
* aNewURI
) {
9005 // Check new domain - must be a superdomain of the current host
9006 // For example, a page from foo.bar.com may set domain to bar.com,
9007 // but not to ar.com, baz.com, or fi.foo.bar.com.
9008 nsAutoCString current
;
9009 nsAutoCString domain
;
9010 if (NS_FAILED(aOrigHost
->GetAsciiHost(current
))) {
9013 if (NS_FAILED(aNewURI
->GetAsciiHost(domain
))) {
9017 bool ok
= current
.Equals(domain
);
9018 if (current
.Length() > domain
.Length() && StringEndsWith(current
, domain
) &&
9019 current
.CharAt(current
.Length() - domain
.Length() - 1) == '.') {
9020 // We're golden if the new domain is the current page's base domain or a
9022 nsCOMPtr
<nsIEffectiveTLDService
> tldService
=
9023 do_GetService(NS_EFFECTIVETLDSERVICE_CONTRACTID
);
9028 nsAutoCString currentBaseDomain
;
9030 tldService
->GetBaseDomain(aOrigHost
, 0, currentBaseDomain
));
9031 NS_ASSERTION(StringEndsWith(domain
, currentBaseDomain
) ==
9032 (domain
.Length() >= currentBaseDomain
.Length()),
9033 "uh-oh! slight optimization wasn't valid somehow!");
9034 ok
= ok
&& domain
.Length() >= currentBaseDomain
.Length();
9040 Element
* Document::GetHtmlElement() const {
9041 Element
* rootElement
= GetRootElement();
9042 if (rootElement
&& rootElement
->IsHTMLElement(nsGkAtoms::html
))
9047 Element
* Document::GetHtmlChildElement(nsAtom
* aTag
) {
9048 Element
* html
= GetHtmlElement();
9049 if (!html
) return nullptr;
9051 // Look for the element with aTag inside html. This needs to run
9052 // forwards to find the first such element.
9053 for (nsIContent
* child
= html
->GetFirstChild(); child
;
9054 child
= child
->GetNextSibling()) {
9055 if (child
->IsHTMLElement(aTag
)) return child
->AsElement();
9060 nsGenericHTMLElement
* Document::GetBody() {
9061 Element
* html
= GetHtmlElement();
9066 for (nsIContent
* child
= html
->GetFirstChild(); child
;
9067 child
= child
->GetNextSibling()) {
9068 if (child
->IsHTMLElement(nsGkAtoms::body
) ||
9069 child
->IsHTMLElement(nsGkAtoms::frameset
)) {
9070 return static_cast<nsGenericHTMLElement
*>(child
);
9077 void Document::SetBody(nsGenericHTMLElement
* newBody
, ErrorResult
& rv
) {
9078 nsCOMPtr
<Element
> root
= GetRootElement();
9080 // The body element must be either a body tag or a frameset tag. And we must
9081 // have a root element to be able to add kids to it.
9083 !newBody
->IsAnyOfHTMLElements(nsGkAtoms::body
, nsGkAtoms::frameset
)) {
9084 rv
.ThrowHierarchyRequestError(
9085 "The new body must be either a body tag or frameset tag.");
9090 rv
.ThrowHierarchyRequestError("No root element.");
9094 // Use DOM methods so that we pass through the appropriate security checks.
9095 nsCOMPtr
<Element
> currentBody
= GetBody();
9097 root
->ReplaceChild(*newBody
, *currentBody
, rv
);
9099 root
->AppendChild(*newBody
, rv
);
9103 HTMLSharedElement
* Document::GetHead() {
9104 return static_cast<HTMLSharedElement
*>(GetHeadElement());
9107 Element
* Document::GetTitleElement() {
9108 // mMayHaveTitleElement will have been set to true if any HTML or SVG
9109 // <title> element has been bound to this document. So if it's false,
9110 // we know there is nothing to do here. This avoids us having to search
9111 // the whole DOM if someone calls document.title on a large document
9113 if (!mMayHaveTitleElement
) {
9117 Element
* root
= GetRootElement();
9118 if (root
&& root
->IsSVGElement(nsGkAtoms::svg
)) {
9119 // In SVG, the document's title must be a child
9120 for (nsIContent
* child
= root
->GetFirstChild(); child
;
9121 child
= child
->GetNextSibling()) {
9122 if (child
->IsSVGElement(nsGkAtoms::title
)) {
9123 return child
->AsElement();
9129 // We check the HTML namespace even for non-HTML documents, except SVG. This
9130 // matches the spec and the behavior of all tested browsers.
9131 for (nsINode
* node
= GetFirstChild(); node
; node
= node
->GetNextNode(this)) {
9132 if (node
->IsHTMLElement(nsGkAtoms::title
)) {
9133 return node
->AsElement();
9139 void Document::GetTitle(nsAString
& aTitle
) {
9142 Element
* rootElement
= GetRootElement();
9147 if (rootElement
->IsXULElement()) {
9148 rootElement
->GetAttr(nsGkAtoms::title
, aTitle
);
9149 } else if (Element
* title
= GetTitleElement()) {
9150 nsContentUtils::GetNodeTextContent(title
, false, aTitle
);
9155 aTitle
.CompressWhitespace();
9158 void Document::SetTitle(const nsAString
& aTitle
, ErrorResult
& aRv
) {
9159 Element
* rootElement
= GetRootElement();
9164 if (rootElement
->IsXULElement()) {
9166 rootElement
->SetAttr(kNameSpaceID_None
, nsGkAtoms::title
, aTitle
, true);
9170 Maybe
<mozAutoDocUpdate
> updateBatch
;
9171 nsCOMPtr
<Element
> title
= GetTitleElement();
9172 if (rootElement
->IsSVGElement(nsGkAtoms::svg
)) {
9174 // Batch updates so that mutation events don't change "the title
9175 // element" under us
9176 updateBatch
.emplace(this, true);
9177 RefPtr
<mozilla::dom::NodeInfo
> titleInfo
= mNodeInfoManager
->GetNodeInfo(
9178 nsGkAtoms::title
, nullptr, kNameSpaceID_SVG
, ELEMENT_NODE
);
9179 NS_NewSVGElement(getter_AddRefs(title
), titleInfo
.forget(),
9184 rootElement
->InsertChildBefore(title
, rootElement
->GetFirstChild(), true,
9187 } else if (rootElement
->IsHTMLElement()) {
9189 // Batch updates so that mutation events don't change "the title
9190 // element" under us
9191 updateBatch
.emplace(this, true);
9192 Element
* head
= GetHeadElement();
9197 RefPtr
<mozilla::dom::NodeInfo
> titleInfo
;
9198 titleInfo
= mNodeInfoManager
->GetNodeInfo(
9199 nsGkAtoms::title
, nullptr, kNameSpaceID_XHTML
, ELEMENT_NODE
);
9200 title
= NS_NewHTMLTitleElement(titleInfo
.forget());
9205 head
->AppendChildTo(title
, true, IgnoreErrors());
9211 aRv
= nsContentUtils::SetNodeTextContent(title
, aTitle
, false);
9214 void Document::NotifyPossibleTitleChange(bool aBoundTitleElement
) {
9215 NS_ASSERTION(!mInUnlinkOrDeletion
|| !aBoundTitleElement
,
9216 "Setting a title while unlinking or destroying the element?");
9217 if (mInUnlinkOrDeletion
) {
9221 if (aBoundTitleElement
) {
9222 mMayHaveTitleElement
= true;
9224 if (mPendingTitleChangeEvent
.IsPending()) {
9228 MOZ_RELEASE_ASSERT(NS_IsMainThread());
9229 RefPtr
<nsRunnableMethod
<Document
, void, false>> event
=
9230 NewNonOwningRunnableMethod("Document::DoNotifyPossibleTitleChange", this,
9231 &Document::DoNotifyPossibleTitleChange
);
9232 if (NS_WARN_IF(NS_FAILED(Dispatch(TaskCategory::Other
, do_AddRef(event
))))) {
9235 mPendingTitleChangeEvent
= std::move(event
);
9238 void Document::DoNotifyPossibleTitleChange() {
9239 if (!mPendingTitleChangeEvent
.IsPending()) {
9242 // Make sure the pending runnable method is cleared.
9243 mPendingTitleChangeEvent
.Revoke();
9244 mHaveFiredTitleChange
= true;
9249 if (RefPtr
<PresShell
> presShell
= GetPresShell()) {
9250 nsCOMPtr
<nsISupports
> container
=
9251 presShell
->GetPresContext()->GetContainerWeak();
9253 if (nsCOMPtr
<nsIBaseWindow
> docShellWin
= do_QueryInterface(container
)) {
9254 docShellWin
->SetTitle(title
);
9259 if (WindowGlobalChild
* child
= GetWindowGlobalChild()) {
9260 child
->SendUpdateDocumentTitle(title
);
9263 // Fire a DOM event for the title change.
9264 nsContentUtils::DispatchChromeEvent(this, ToSupports(this),
9265 u
"DOMTitleChanged"_ns
, CanBubble::eYes
,
9268 nsCOMPtr
<nsIObserverService
> obs
= mozilla::services::GetObserverService();
9270 obs
->NotifyObservers(ToSupports(this), "document-title-changed", nullptr);
9274 already_AddRefed
<MediaQueryList
> Document::MatchMedia(
9275 const nsACString
& aMediaQueryList
, CallerType aCallerType
) {
9276 RefPtr
<MediaQueryList
> result
=
9277 new MediaQueryList(this, aMediaQueryList
, aCallerType
);
9279 mDOMMediaQueryLists
.insertBack(result
);
9281 return result
.forget();
9284 void Document::SetMayStartLayout(bool aMayStartLayout
) {
9285 mMayStartLayout
= aMayStartLayout
;
9286 if (MayStartLayout()) {
9287 // Before starting layout, check whether we're a toplevel chrome
9288 // window. If we are, setup some state so that we don't have to restyle
9289 // the whole tree after StartLayout.
9290 if (nsCOMPtr
<nsIAppWindow
> win
= GetAppWindowIfToplevelChrome()) {
9291 // We're the chrome document!
9292 win
->BeforeStartLayout();
9294 ReadyState state
= GetReadyStateEnum();
9295 if (state
>= READYSTATE_INTERACTIVE
) {
9296 // DOMContentLoaded has fired already.
9297 MaybeResolveReadyForIdle();
9301 MaybeEditingStateChanged();
9304 nsresult
Document::InitializeFrameLoader(nsFrameLoader
* aLoader
) {
9305 mInitializableFrameLoaders
.RemoveElement(aLoader
);
9306 // Don't even try to initialize.
9307 if (mInDestructor
) {
9309 "Trying to initialize a frame loader while"
9310 "document is being deleted");
9311 return NS_ERROR_FAILURE
;
9314 mInitializableFrameLoaders
.AppendElement(aLoader
);
9315 if (!mFrameLoaderRunner
) {
9316 mFrameLoaderRunner
=
9317 NewRunnableMethod("Document::MaybeInitializeFinalizeFrameLoaders", this,
9318 &Document::MaybeInitializeFinalizeFrameLoaders
);
9319 NS_ENSURE_TRUE(mFrameLoaderRunner
, NS_ERROR_OUT_OF_MEMORY
);
9320 nsContentUtils::AddScriptRunner(mFrameLoaderRunner
);
9325 nsresult
Document::FinalizeFrameLoader(nsFrameLoader
* aLoader
,
9326 nsIRunnable
* aFinalizer
) {
9327 mInitializableFrameLoaders
.RemoveElement(aLoader
);
9328 if (mInDestructor
) {
9329 return NS_ERROR_FAILURE
;
9332 LogRunnable::LogDispatch(aFinalizer
);
9333 mFrameLoaderFinalizers
.AppendElement(aFinalizer
);
9334 if (!mFrameLoaderRunner
) {
9335 mFrameLoaderRunner
=
9336 NewRunnableMethod("Document::MaybeInitializeFinalizeFrameLoaders", this,
9337 &Document::MaybeInitializeFinalizeFrameLoaders
);
9338 NS_ENSURE_TRUE(mFrameLoaderRunner
, NS_ERROR_OUT_OF_MEMORY
);
9339 nsContentUtils::AddScriptRunner(mFrameLoaderRunner
);
9344 void Document::MaybeInitializeFinalizeFrameLoaders() {
9345 if (mDelayFrameLoaderInitialization
) {
9346 // This method will be recalled when !mDelayFrameLoaderInitialization.
9347 mFrameLoaderRunner
= nullptr;
9351 // We're not in an update, but it is not safe to run scripts, so
9352 // postpone frameloader initialization and finalization.
9353 if (!nsContentUtils::IsSafeToRunScript()) {
9354 if (!mInDestructor
&& !mFrameLoaderRunner
&&
9355 (mInitializableFrameLoaders
.Length() ||
9356 mFrameLoaderFinalizers
.Length())) {
9357 mFrameLoaderRunner
= NewRunnableMethod(
9358 "Document::MaybeInitializeFinalizeFrameLoaders", this,
9359 &Document::MaybeInitializeFinalizeFrameLoaders
);
9360 nsContentUtils::AddScriptRunner(mFrameLoaderRunner
);
9364 mFrameLoaderRunner
= nullptr;
9366 // Don't use a temporary array for mInitializableFrameLoaders, because
9367 // loading a frame may cause some other frameloader to be removed from the
9368 // array. But be careful to keep the loader alive when starting the load!
9369 while (mInitializableFrameLoaders
.Length()) {
9370 RefPtr
<nsFrameLoader
> loader
= mInitializableFrameLoaders
[0];
9371 mInitializableFrameLoaders
.RemoveElementAt(0);
9372 NS_ASSERTION(loader
, "null frameloader in the array?");
9373 loader
->ReallyStartLoading();
9376 uint32_t length
= mFrameLoaderFinalizers
.Length();
9378 nsTArray
<nsCOMPtr
<nsIRunnable
>> finalizers
=
9379 std::move(mFrameLoaderFinalizers
);
9380 for (uint32_t i
= 0; i
< length
; ++i
) {
9381 LogRunnable::Run
run(finalizers
[i
]);
9382 finalizers
[i
]->Run();
9387 void Document::TryCancelFrameLoaderInitialization(nsIDocShell
* aShell
) {
9388 uint32_t length
= mInitializableFrameLoaders
.Length();
9389 for (uint32_t i
= 0; i
< length
; ++i
) {
9390 if (mInitializableFrameLoaders
[i
]->GetExistingDocShell() == aShell
) {
9391 mInitializableFrameLoaders
.RemoveElementAt(i
);
9397 void Document::SetPrototypeDocument(nsXULPrototypeDocument
* aPrototype
) {
9398 mPrototypeDocument
= aPrototype
;
9399 mSynchronousDOMContentLoaded
= true;
9402 nsIPermissionDelegateHandler
* Document::PermDelegateHandler() {
9403 return GetPermissionDelegateHandler();
9406 Document
* Document::RequestExternalResource(
9407 nsIURI
* aURI
, nsIReferrerInfo
* aReferrerInfo
, nsINode
* aRequestingNode
,
9408 ExternalResourceLoad
** aPendingLoad
) {
9409 MOZ_ASSERT(aURI
, "Must have a URI");
9410 MOZ_ASSERT(aRequestingNode
, "Must have a node");
9411 MOZ_ASSERT(aReferrerInfo
, "Must have a referrerInfo");
9412 if (mDisplayDocument
) {
9413 return mDisplayDocument
->RequestExternalResource(
9414 aURI
, aReferrerInfo
, aRequestingNode
, aPendingLoad
);
9417 return mExternalResourceMap
.RequestResource(
9418 aURI
, aReferrerInfo
, aRequestingNode
, this, aPendingLoad
);
9421 void Document::EnumerateExternalResources(SubDocEnumFunc aCallback
) {
9422 mExternalResourceMap
.EnumerateResources(aCallback
);
9425 SMILAnimationController
* Document::GetAnimationController() {
9426 // We create the animation controller lazily because most documents won't want
9427 // one and only SVG documents and the like will call this
9428 if (mAnimationController
) return mAnimationController
;
9429 // Refuse to create an Animation Controller for data documents.
9430 if (mLoadedAsData
) return nullptr;
9432 mAnimationController
= new SMILAnimationController(this);
9434 // If there's a presContext then check the animation mode and pause if
9436 nsPresContext
* context
= GetPresContext();
9437 if (mAnimationController
&& context
&&
9438 context
->ImageAnimationMode() == imgIContainer::kDontAnimMode
) {
9439 mAnimationController
->Pause(SMILTimeContainer::PAUSE_USERPREF
);
9442 // If we're hidden (or being hidden), notify the newly-created animation
9443 // controller. (Skip this check for SVG-as-an-image documents, though,
9444 // because they don't get OnPageShow / OnPageHide calls).
9445 if (!mIsShowing
&& !mIsBeingUsedAsImage
) {
9446 mAnimationController
->OnPageHide();
9449 return mAnimationController
;
9452 PendingAnimationTracker
* Document::GetOrCreatePendingAnimationTracker() {
9453 if (!mPendingAnimationTracker
) {
9454 mPendingAnimationTracker
= new PendingAnimationTracker(this);
9457 return mPendingAnimationTracker
;
9460 ScrollTimelineAnimationTracker
*
9461 Document::GetOrCreateScrollTimelineAnimationTracker() {
9462 if (!mScrollTimelineAnimationTracker
) {
9463 mScrollTimelineAnimationTracker
= new ScrollTimelineAnimationTracker(this);
9466 return mScrollTimelineAnimationTracker
;
9470 * Retrieve the "direction" property of the document.
9474 void Document::GetDir(nsAString
& aDirection
) const {
9475 aDirection
.Truncate();
9476 Element
* rootElement
= GetHtmlElement();
9478 static_cast<nsGenericHTMLElement
*>(rootElement
)->GetDir(aDirection
);
9483 * Set the "direction" property of the document.
9487 void Document::SetDir(const nsAString
& aDirection
) {
9488 Element
* rootElement
= GetHtmlElement();
9490 rootElement
->SetAttr(kNameSpaceID_None
, nsGkAtoms::dir
, aDirection
, true);
9494 nsIHTMLCollection
* Document::Images() {
9496 mImages
= new nsContentList(this, kNameSpaceID_XHTML
, nsGkAtoms::img
,
9502 nsIHTMLCollection
* Document::Embeds() {
9504 mEmbeds
= new nsContentList(this, kNameSpaceID_XHTML
, nsGkAtoms::embed
,
9510 static bool MatchLinks(Element
* aElement
, int32_t aNamespaceID
, nsAtom
* aAtom
,
9512 return aElement
->IsAnyOfHTMLElements(nsGkAtoms::a
, nsGkAtoms::area
) &&
9513 aElement
->HasAttr(kNameSpaceID_None
, nsGkAtoms::href
);
9516 nsIHTMLCollection
* Document::Links() {
9518 mLinks
= new nsContentList(this, MatchLinks
, nullptr, nullptr);
9523 nsIHTMLCollection
* Document::Forms() {
9525 // Please keep this in sync with nsHTMLDocument::GetFormsAndFormControls.
9526 mForms
= new nsContentList(this, kNameSpaceID_XHTML
, nsGkAtoms::form
,
9533 nsIHTMLCollection
* Document::Scripts() {
9535 mScripts
= new nsContentList(this, kNameSpaceID_XHTML
, nsGkAtoms::script
,
9541 nsIHTMLCollection
* Document::Applets() {
9543 mApplets
= new nsEmptyContentList(this);
9548 static bool MatchAnchors(Element
* aElement
, int32_t aNamespaceID
, nsAtom
* aAtom
,
9550 return aElement
->IsHTMLElement(nsGkAtoms::a
) &&
9551 aElement
->HasAttr(kNameSpaceID_None
, nsGkAtoms::name
);
9554 nsIHTMLCollection
* Document::Anchors() {
9556 mAnchors
= new nsContentList(this, MatchAnchors
, nullptr, nullptr);
9561 mozilla::dom::Nullable
<mozilla::dom::WindowProxyHolder
> Document::Open(
9562 const nsAString
& aURL
, const nsAString
& aName
, const nsAString
& aFeatures
,
9564 MOZ_ASSERT(nsContentUtils::CanCallerAccess(this),
9565 "XOW should have caught this!");
9567 nsCOMPtr
<nsPIDOMWindowInner
> window
= GetInnerWindow();
9569 rv
.Throw(NS_ERROR_DOM_INVALID_ACCESS_ERR
);
9572 nsCOMPtr
<nsPIDOMWindowOuter
> outer
=
9573 nsPIDOMWindowOuter::GetFromCurrentInner(window
);
9575 rv
.Throw(NS_ERROR_NOT_INITIALIZED
);
9578 RefPtr
<nsGlobalWindowOuter
> win
= nsGlobalWindowOuter::Cast(outer
);
9579 RefPtr
<BrowsingContext
> newBC
;
9580 rv
= win
->OpenJS(aURL
, aName
, aFeatures
, getter_AddRefs(newBC
));
9584 return WindowProxyHolder(std::move(newBC
));
9587 Document
* Document::Open(const Optional
<nsAString
>& /* unused */,
9588 const Optional
<nsAString
>& /* unused */,
9589 ErrorResult
& aError
) {
9591 // <https://html.spec.whatwg.org/multipage/dynamic-markup-insertion.html#document-open-steps>
9593 MOZ_ASSERT(nsContentUtils::CanCallerAccess(this),
9594 "XOW should have caught this!");
9596 // Step 1 -- throw if we're an XML document.
9597 if (!IsHTMLDocument() || mDisableDocWrite
) {
9598 aError
.Throw(NS_ERROR_DOM_INVALID_STATE_ERR
);
9602 // Step 2 -- throw if dynamic markup insertion should throw.
9603 if (ShouldThrowOnDynamicMarkupInsertion()) {
9604 aError
.Throw(NS_ERROR_DOM_INVALID_STATE_ERR
);
9608 // Step 3 -- get the entry document, so we can use it for security checks.
9609 nsCOMPtr
<Document
> callerDoc
= GetEntryDocument();
9611 // If we're called from C++ or in some other way without an originating
9612 // document we can't do a document.open w/o changing the principal of the
9613 // document to something like about:blank (as that's the only sane thing to
9614 // do when we don't know the origin of this call), and since we can't
9615 // change the principals of a document for security reasons we'll have to
9616 // refuse to go ahead with this call.
9618 aError
.Throw(NS_ERROR_DOM_SECURITY_ERR
);
9622 // Step 4 -- make sure we're same-origin (not just same origin-domain) with
9623 // the entry document.
9624 if (!callerDoc
->NodePrincipal()->Equals(NodePrincipal())) {
9625 aError
.Throw(NS_ERROR_DOM_SECURITY_ERR
);
9629 // Step 5 -- if we have an active parser with a nonzero script nesting level,
9631 if ((mParser
&& mParser
->HasNonzeroScriptNestingLevel()) || mParserAborted
) {
9635 // Step 6 -- check for open() during unload. Per spec, this is just a check
9636 // of the ignore-opens-during-unload counter, but our unload event code
9637 // doesn't affect that counter yet (unlike pagehide and beforeunload, which
9638 // do), so we check for unload directly.
9639 if (ShouldIgnoreOpens()) {
9643 RefPtr
<nsDocShell
> shell(mDocumentContainer
);
9646 shell
->GetIsInUnload(&inUnload
);
9652 // document.open() inherits the CSP from the opening document.
9653 // Please create an actual copy of the CSP (do not share the same
9654 // reference) otherwise appending a new policy within the opened
9655 // document will be incorrectly propagated to the opening doc.
9656 nsCOMPtr
<nsIContentSecurityPolicy
> csp
= callerDoc
->GetCsp();
9658 RefPtr
<nsCSPContext
> cspToInherit
= new nsCSPContext();
9659 cspToInherit
->InitFromOther(static_cast<nsCSPContext
*>(csp
.get()));
9660 mCSP
= cspToInherit
;
9663 // At this point we know this is a valid-enough document.open() call
9664 // and not a no-op. Increment our use counter.
9665 SetUseCounter(eUseCounter_custom_DocumentOpen
);
9667 // Step 7 -- stop existing navigation of our browsing context (and all other
9668 // loads it's doing) if we're the active document of our browsing context.
9669 // Note that we do not want to stop anything if there is no existing
9671 if (shell
&& IsCurrentActiveDocument() &&
9672 shell
->GetIsAttemptingToNavigate()) {
9673 shell
->Stop(nsIWebNavigation::STOP_NETWORK
);
9675 // The Stop call may have cancelled the onload blocker request or
9676 // prevented it from getting added, so we need to make sure it gets added
9677 // to the document again otherwise the document could have a non-zero
9678 // onload block count without the onload blocker request being in the
9680 EnsureOnloadBlocker();
9683 // Step 8 -- clear event listeners out of our DOM tree
9684 for (nsINode
* node
: ShadowIncludingTreeIterator(*this)) {
9685 if (EventListenerManager
* elm
= node
->GetExistingListenerManager()) {
9686 elm
->RemoveAllListeners();
9690 // Step 9 -- clear event listeners from our window, if we have one.
9692 // Note that we explicitly want the inner window, and only if we're its
9693 // document. We want to do this (per spec) even when we're not the "active
9694 // document", so we can't go through GetWindow(), because it might forward to
9696 if (nsPIDOMWindowInner
* win
= GetInnerWindow()) {
9697 if (win
->GetExtantDoc() == this) {
9698 if (EventListenerManager
* elm
=
9699 nsGlobalWindowInner::Cast(win
)->GetExistingListenerManager()) {
9700 elm
->RemoveAllListeners();
9705 // If we have a parser that has a zero script nesting level, we need to
9706 // properly terminate it. We do that after we've removed all the event
9707 // listeners (so termination won't trigger event listeners if it does
9708 // something to the DOM), but before we remove all elements from the document
9709 // (so if termination does modify the DOM in some way we will just blow it
9710 // away immediately. See the similar code in WriteCommon that handles the
9711 // !IsInsertionPointDefined() case and should stay in sync with this code.
9713 MOZ_ASSERT(!mParser
->HasNonzeroScriptNestingLevel(),
9714 "Why didn't we take the early return?");
9715 // Make sure we don't re-enter.
9716 IgnoreOpensDuringUnload
ignoreOpenGuard(this);
9717 mParser
->Terminate();
9718 MOZ_RELEASE_ASSERT(!mParser
, "mParser should have been null'd out");
9721 // Step 10 -- remove all our DOM kids without firing any mutation events.
9723 // We want to ignore any recursive calls to Open() that happen while
9724 // disconnecting the node tree. The spec doesn't say to do this, but the
9725 // spec also doesn't envision unload events on subframes firing while we do
9726 // this, while all browsers fire them in practice. See
9727 // <https://github.com/whatwg/html/issues/4611>.
9728 IgnoreOpensDuringUnload
ignoreOpenGuard(this);
9729 DisconnectNodeTree();
9732 // Step 11 -- if we're the current document in our docshell, do the
9733 // equivalent of pushState() with the new URL we should have.
9734 if (shell
&& IsCurrentActiveDocument()) {
9735 nsCOMPtr
<nsIURI
> newURI
= callerDoc
->GetDocumentURI();
9736 if (callerDoc
!= this) {
9737 nsCOMPtr
<nsIURI
> noFragmentURI
;
9738 nsresult rv
= NS_GetURIWithoutRef(newURI
, getter_AddRefs(noFragmentURI
));
9739 if (NS_WARN_IF(NS_FAILED(rv
))) {
9743 newURI
= std::move(noFragmentURI
);
9746 // UpdateURLAndHistory might do various member-setting, so make sure we're
9747 // holding strong refs to all the refcounted args on the stack. We can
9748 // assume that our caller is holding on to "this" already.
9749 nsCOMPtr
<nsIURI
> currentURI
= GetDocumentURI();
9751 nsresult rv
= currentURI
->Equals(newURI
, &equalURIs
);
9752 if (NS_WARN_IF(NS_FAILED(rv
))) {
9756 nsCOMPtr
<nsIStructuredCloneContainer
> stateContainer(mStateObjectContainer
);
9757 rv
= shell
->UpdateURLAndHistory(this, newURI
, stateContainer
, u
""_ns
,
9758 /* aReplace = */ true, currentURI
,
9760 if (NS_WARN_IF(NS_FAILED(rv
))) {
9765 // And use the security info of the caller document as well, since
9766 // it's the thing providing our data.
9767 mSecurityInfo
= callerDoc
->GetSecurityInfo();
9769 // This is not mentioned in the spec, but I think that's a spec bug. See
9770 // <https://github.com/whatwg/html/issues/4299>. In any case, since our
9771 // URL may be changing away from about:blank here, we really want to unset
9772 // this flag no matter what, since only about:blank can be an initial
9774 SetIsInitialDocument(false);
9776 // And let our docloader know that it will need to track our load event.
9777 nsDocShell::Cast(shell
)->SetDocumentOpenedButNotLoaded();
9780 // Per spec nothing happens with our URI in other cases, though note
9781 // <https://github.com/whatwg/html/issues/4286>.
9783 // Note that we don't need to do anything here with base URIs per spec.
9784 // That said, this might be assuming that we implement
9785 // https://html.spec.whatwg.org/multipage/urls-and-fetching.html#fallback-base-url
9786 // correctly, which we don't right now for the about:blank case.
9788 // Step 12, but note <https://github.com/whatwg/html/issues/4292>.
9789 mSkipLoadEventAfterClose
= mLoadEventFiring
;
9791 // Preliminary to steps 13-16. Set our ready state to uninitialized before
9792 // we do anything else, so we can then proceed to later ready state levels.
9793 SetReadyStateInternal(READYSTATE_UNINITIALIZED
,
9794 /* updateTimingInformation = */ false);
9795 // Reset a flag that affects readyState behavior.
9796 mSetCompleteAfterDOMContentLoaded
= false;
9798 // Step 13 -- set our compat mode to standards.
9799 SetCompatibilityMode(eCompatibility_FullStandards
);
9801 // Step 14 -- create a new parser associated with document. This also does
9802 // step 16 implicitly.
9803 mParserAborted
= false;
9804 RefPtr
<nsHtml5Parser
> parser
= nsHtml5Module::NewHtml5Parser();
9806 parser
->Initialize(this, GetDocumentURI(), ToSupports(shell
), nullptr);
9807 nsresult rv
= parser
->StartExecutor();
9808 if (NS_WARN_IF(NS_FAILED(rv
))) {
9813 // Clear out our form control state, because the state of controls
9814 // in the pre-open() document should not affect the state of
9815 // controls that are now going to be written.
9816 mLayoutHistoryState
= nullptr;
9819 // Prepare the docshell and the document viewer for the impending
9820 // out-of-band document.write()
9821 shell
->PrepareForNewContentModel();
9823 nsCOMPtr
<nsIContentViewer
> cv
;
9824 shell
->GetContentViewer(getter_AddRefs(cv
));
9826 cv
->LoadStart(this);
9831 SetReadyStateInternal(Document::READYSTATE_LOADING
,
9832 /* updateTimingInformation = */ false);
9834 // Step 16 happened with step 14 above.
9840 void Document::Close(ErrorResult
& rv
) {
9841 if (!IsHTMLDocument()) {
9842 // No calling document.close() on XHTML!
9844 rv
.Throw(NS_ERROR_DOM_INVALID_STATE_ERR
);
9848 if (ShouldThrowOnDynamicMarkupInsertion()) {
9849 rv
.Throw(NS_ERROR_DOM_INVALID_STATE_ERR
);
9853 if (!mParser
|| !mParser
->IsScriptCreated()) {
9858 rv
= (static_cast<nsHtml5Parser
*>(mParser
.get()))
9859 ->Parse(u
""_ns
, nullptr, true);
9863 void Document::WriteCommon(const Sequence
<nsString
>& aText
,
9864 bool aNewlineTerminate
, mozilla::ErrorResult
& rv
) {
9865 // Fast path the common case
9866 if (aText
.Length() == 1) {
9867 WriteCommon(aText
[0], aNewlineTerminate
, rv
);
9869 // XXXbz it would be nice if we could pass all the strings to the parser
9870 // without having to do all this copying and then ask it to start
9873 for (size_t i
= 0; i
< aText
.Length(); ++i
) {
9874 text
.Append(aText
[i
]);
9876 WriteCommon(text
, aNewlineTerminate
, rv
);
9880 void Document::WriteCommon(const nsAString
& aText
, bool aNewlineTerminate
,
9884 // Assert that we do not use or accidentally introduce doc.write()
9885 // in system privileged context or in any of our about: pages.
9886 nsCOMPtr
<nsIPrincipal
> principal
= NodePrincipal();
9887 bool isAboutOrPrivContext
= principal
->IsSystemPrincipal();
9888 if (!isAboutOrPrivContext
) {
9889 if (principal
->SchemeIs("about")) {
9890 // about:blank inherits the security contetext and this assertion
9891 // is only meant for actual about: pages.
9893 principal
->GetHost(host
);
9894 isAboutOrPrivContext
= !host
.EqualsLiteral("blank");
9897 // Some automated tests use an empty string to kick off some parsing
9898 // mechansims, but they do not do any harm since they use an empty string.
9899 MOZ_ASSERT(!isAboutOrPrivContext
|| aText
.IsEmpty(),
9900 "do not use doc.write in privileged context!");
9904 mTooDeepWriteRecursion
=
9905 (mWriteLevel
> NS_MAX_DOCUMENT_WRITE_DEPTH
|| mTooDeepWriteRecursion
);
9906 if (NS_WARN_IF(mTooDeepWriteRecursion
)) {
9907 aRv
.Throw(NS_ERROR_UNEXPECTED
);
9911 if (!IsHTMLDocument() || mDisableDocWrite
) {
9912 // No calling document.write*() on XHTML!
9914 aRv
.Throw(NS_ERROR_DOM_INVALID_STATE_ERR
);
9918 if (ShouldThrowOnDynamicMarkupInsertion()) {
9919 aRv
.Throw(NS_ERROR_DOM_INVALID_STATE_ERR
);
9923 if (mParserAborted
) {
9924 // Hixie says aborting the parser doesn't undefine the insertion point.
9925 // However, since we null out mParser in that case, we track the
9926 // theoretically defined insertion point using mParserAborted.
9930 // Implement Step 4.1 of:
9931 // https://html.spec.whatwg.org/multipage/dynamic-markup-insertion.html#document-write-steps
9932 if (ShouldIgnoreOpens()) {
9936 void* key
= GenerateParserKey();
9937 if (mParser
&& !mParser
->IsInsertionPointDefined()) {
9938 if (mIgnoreDestructiveWritesCounter
) {
9939 // Instead of implying a call to document.open(), ignore the call.
9940 nsContentUtils::ReportToConsole(
9941 nsIScriptError::warningFlag
, "DOM Events"_ns
, this,
9942 nsContentUtils::eDOM_PROPERTIES
, "DocumentWriteIgnored");
9945 // The spec doesn't tell us to ignore opens from here, but we need to
9946 // ensure opens are ignored here. See similar code in Open() that handles
9947 // the case of an existing parser which is not currently running script and
9948 // should stay in sync with this code.
9949 IgnoreOpensDuringUnload
ignoreOpenGuard(this);
9950 mParser
->Terminate();
9951 MOZ_RELEASE_ASSERT(!mParser
, "mParser should have been null'd out");
9955 if (mIgnoreDestructiveWritesCounter
) {
9956 // Instead of implying a call to document.open(), ignore the call.
9957 nsContentUtils::ReportToConsole(
9958 nsIScriptError::warningFlag
, "DOM Events"_ns
, this,
9959 nsContentUtils::eDOM_PROPERTIES
, "DocumentWriteIgnored");
9965 // If Open() fails, or if it didn't create a parser (as it won't
9966 // if the user chose to not discard the current document through
9967 // onbeforeunload), don't write anything.
9968 if (aRv
.Failed() || !mParser
) {
9973 static constexpr auto new_line
= u
"\n"_ns
;
9977 // This could be done with less code, but for performance reasons it
9978 // makes sense to have the code for two separate Parse() calls here
9979 // since the concatenation of strings costs more than we like. And
9980 // why pay that price when we don't need to?
9981 if (aNewlineTerminate
) {
9982 aRv
= (static_cast<nsHtml5Parser
*>(mParser
.get()))
9983 ->Parse(aText
+ new_line
, key
, false);
9986 (static_cast<nsHtml5Parser
*>(mParser
.get()))->Parse(aText
, key
, false);
9991 mTooDeepWriteRecursion
= (mWriteLevel
!= 0 && mTooDeepWriteRecursion
);
9994 void Document::Write(const Sequence
<nsString
>& aText
, ErrorResult
& rv
) {
9995 WriteCommon(aText
, false, rv
);
9998 void Document::Writeln(const Sequence
<nsString
>& aText
, ErrorResult
& rv
) {
9999 WriteCommon(aText
, true, rv
);
10002 void* Document::GenerateParserKey(void) {
10003 if (!mScriptLoader
) {
10004 // If we don't have a script loader, then the parser probably isn't parsing
10005 // anything anyway, so just return null.
10009 // The script loader provides us with the currently executing script element,
10010 // which is guaranteed to be unique per script.
10011 nsIScriptElement
* script
= mScriptLoader
->GetCurrentParserInsertedScript();
10012 if (script
&& mParser
&& mParser
->IsScriptCreated()) {
10013 nsCOMPtr
<nsIParser
> creatorParser
= script
->GetCreatorParser();
10014 if (creatorParser
!= mParser
) {
10015 // Make scripts that aren't inserted by the active parser of this document
10016 // participate in the context of the script that document.open()ed
10025 bool Document::MatchNameAttribute(Element
* aElement
, int32_t aNamespaceID
,
10026 nsAtom
* aAtom
, void* aData
) {
10027 MOZ_ASSERT(aElement
, "Must have element to work with!");
10029 if (!aElement
->HasName()) {
10033 nsString
* elementName
= static_cast<nsString
*>(aData
);
10034 return aElement
->GetNameSpaceID() == kNameSpaceID_XHTML
&&
10035 aElement
->AttrValueIs(kNameSpaceID_None
, nsGkAtoms::name
, *elementName
,
10040 void* Document::UseExistingNameString(nsINode
* aRootNode
,
10041 const nsString
* aName
) {
10042 return const_cast<nsString
*>(aName
);
10045 nsresult
Document::GetDocumentURI(nsString
& aDocumentURI
) const {
10046 if (mDocumentURI
) {
10048 nsresult rv
= mDocumentURI
->GetSpec(uri
);
10049 NS_ENSURE_SUCCESS(rv
, rv
);
10051 CopyUTF8toUTF16(uri
, aDocumentURI
);
10053 aDocumentURI
.Truncate();
10060 nsresult
Document::GetURL(nsString
& aURL
) const { return GetDocumentURI(aURL
); }
10062 void Document::GetDocumentURIFromJS(nsString
& aDocumentURI
,
10063 CallerType aCallerType
,
10064 ErrorResult
& aRv
) const {
10065 if (!mChromeXHRDocURI
|| aCallerType
!= CallerType::System
) {
10066 aRv
= GetDocumentURI(aDocumentURI
);
10071 nsresult res
= mChromeXHRDocURI
->GetSpec(uri
);
10072 if (NS_FAILED(res
)) {
10076 CopyUTF8toUTF16(uri
, aDocumentURI
);
10079 nsIURI
* Document::GetDocumentURIObject() const {
10080 if (!mChromeXHRDocURI
) {
10081 return GetDocumentURI();
10084 return mChromeXHRDocURI
;
10087 void Document::GetCompatMode(nsString
& aCompatMode
) const {
10088 NS_ASSERTION(mCompatMode
== eCompatibility_NavQuirks
||
10089 mCompatMode
== eCompatibility_AlmostStandards
||
10090 mCompatMode
== eCompatibility_FullStandards
,
10091 "mCompatMode is neither quirks nor strict for this document");
10093 if (mCompatMode
== eCompatibility_NavQuirks
) {
10094 aCompatMode
.AssignLiteral("BackCompat");
10096 aCompatMode
.AssignLiteral("CSS1Compat");
10101 } // namespace mozilla
10103 void nsDOMAttributeMap::BlastSubtreeToPieces(nsINode
* aNode
) {
10104 if (Element
* element
= Element::FromNode(aNode
)) {
10105 if (const nsDOMAttributeMap
* map
= element
->GetAttributeMap()) {
10109 // Use an iterator to get an arbitrary attribute from the
10110 // cache. The iterator must be destroyed before any other
10111 // operations on mAttributeCache, to avoid hash table
10113 auto iter
= map
->mAttributeCache
.ConstIter();
10117 attr
= iter
.UserData();
10120 BlastSubtreeToPieces(attr
);
10122 mozilla::DebugOnly
<nsresult
> rv
=
10123 element
->UnsetAttr(attr
->NodeInfo()->NamespaceID(),
10124 attr
->NodeInfo()->NameAtom(), false);
10126 // XXX Should we abort here?
10127 NS_ASSERTION(NS_SUCCEEDED(rv
), "Uh-oh, UnsetAttr shouldn't fail!");
10131 if (mozilla::dom::ShadowRoot
* shadow
= element
->GetShadowRoot()) {
10132 BlastSubtreeToPieces(shadow
);
10133 element
->UnattachShadow();
10137 while (aNode
->HasChildren()) {
10138 nsIContent
* node
= aNode
->GetFirstChild();
10139 BlastSubtreeToPieces(node
);
10140 aNode
->RemoveChildNode(node
, false);
10144 namespace mozilla::dom
{
10146 nsINode
* Document::AdoptNode(nsINode
& aAdoptedNode
, ErrorResult
& rv
) {
10147 OwningNonNull
<nsINode
> adoptedNode
= aAdoptedNode
;
10149 // Scope firing mutation events so that we don't carry any state that
10152 if (nsCOMPtr
<nsINode
> parent
= adoptedNode
->GetParentNode()) {
10153 nsContentUtils::MaybeFireNodeRemoved(adoptedNode
, parent
);
10157 nsAutoScriptBlocker scriptBlocker
;
10159 switch (adoptedNode
->NodeType()) {
10160 case ATTRIBUTE_NODE
: {
10161 // Remove from ownerElement.
10162 OwningNonNull
<Attr
> adoptedAttr
= static_cast<Attr
&>(*adoptedNode
);
10164 nsCOMPtr
<Element
> ownerElement
= adoptedAttr
->GetOwnerElement();
10169 if (ownerElement
) {
10170 OwningNonNull
<Attr
> newAttr
=
10171 ownerElement
->RemoveAttributeNode(*adoptedAttr
, rv
);
10179 case DOCUMENT_FRAGMENT_NODE
: {
10180 if (adoptedNode
->IsShadowRoot()) {
10181 rv
.ThrowHierarchyRequestError("The adopted node is a shadow root.");
10187 case PROCESSING_INSTRUCTION_NODE
:
10189 case CDATA_SECTION_NODE
:
10191 case DOCUMENT_TYPE_NODE
: {
10192 // Don't allow adopting a node's anonymous subtree out from under it.
10193 if (adoptedNode
->IsRootOfNativeAnonymousSubtree()) {
10194 rv
.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR
);
10198 // We don't want to adopt an element into its own contentDocument or into
10199 // a descendant contentDocument, so we check if the frameElement of this
10200 // document or any of its parents is the adopted node or one of its
10202 RefPtr
<BrowsingContext
> bc
= GetBrowsingContext();
10204 nsCOMPtr
<nsINode
> node
= bc
->GetEmbedderElement();
10205 if (node
&& node
->IsInclusiveDescendantOf(adoptedNode
)) {
10206 rv
.ThrowHierarchyRequestError(
10207 "Trying to adopt a node into its own contentDocument or a "
10208 "descendant contentDocument.");
10212 if (XRE_IsParentProcess()) {
10213 bc
= bc
->Canonical()->GetParentCrossChromeBoundary();
10215 bc
= bc
->GetParent();
10219 // Remove from parent.
10220 nsCOMPtr
<nsINode
> parent
= adoptedNode
->GetParentNode();
10222 parent
->RemoveChildNode(adoptedNode
->AsContent(), true);
10224 MOZ_ASSERT(!adoptedNode
->IsInUncomposedDoc());
10229 case DOCUMENT_NODE
: {
10230 rv
.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR
);
10234 NS_WARNING("Don't know how to adopt this nodetype for adoptNode.");
10236 rv
.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR
);
10241 nsCOMPtr
<Document
> oldDocument
= adoptedNode
->OwnerDoc();
10242 bool sameDocument
= oldDocument
== this;
10245 JS::Rooted
<JSObject
*> newScope(cx
, nullptr);
10246 if (!sameDocument
) {
10247 newScope
= GetWrapper();
10248 if (!newScope
&& GetScopeObject() && GetScopeObject()->HasJSGlobal()) {
10249 // Make sure cx is in a semi-sane compartment before we call WrapNative.
10250 // It's kind of irrelevant, given that we're passing aAllowWrapping =
10251 // false, and documents should always insist on being wrapped in an
10252 // canonical scope. But we try to pass something sane anyway.
10253 JSObject
* globalObject
= GetScopeObject()->GetGlobalJSObject();
10254 JSAutoRealm
ar(cx
, globalObject
);
10255 JS::Rooted
<JS::Value
> v(cx
);
10256 rv
= nsContentUtils::WrapNative(cx
, ToSupports(this), this, &v
,
10257 /* aAllowWrapping = */ false);
10258 if (rv
.Failed()) return nullptr;
10259 newScope
= &v
.toObject();
10263 adoptedNode
->Adopt(sameDocument
? nullptr : mNodeInfoManager
, newScope
, rv
);
10265 // Disconnect all nodes from their parents, since some have the old document
10266 // as their ownerDocument and some have this as their ownerDocument.
10267 nsDOMAttributeMap::BlastSubtreeToPieces(adoptedNode
);
10271 MOZ_ASSERT(adoptedNode
->OwnerDoc() == this,
10272 "Should still be in the document we just got adopted into");
10274 return adoptedNode
;
10277 bool Document::UseWidthDeviceWidthFallbackViewport() const { return false; }
10279 static Maybe
<LayoutDeviceToScreenScale
> ParseScaleString(
10280 const nsString
& aScaleString
) {
10281 // https://drafts.csswg.org/css-device-adapt/#min-scale-max-scale
10282 if (aScaleString
.EqualsLiteral("device-width") ||
10283 aScaleString
.EqualsLiteral("device-height")) {
10284 return Some(LayoutDeviceToScreenScale(10.0f
));
10285 } else if (aScaleString
.EqualsLiteral("yes")) {
10286 return Some(LayoutDeviceToScreenScale(1.0f
));
10287 } else if (aScaleString
.EqualsLiteral("no")) {
10288 return Some(LayoutDeviceToScreenScale(ViewportMinScale()));
10289 } else if (aScaleString
.IsEmpty()) {
10293 nsresult scaleErrorCode
;
10294 float scale
= aScaleString
.ToFloatAllowTrailingChars(&scaleErrorCode
);
10295 if (NS_FAILED(scaleErrorCode
)) {
10296 return Some(LayoutDeviceToScreenScale(ViewportMinScale()));
10302 return Some(clamped(LayoutDeviceToScreenScale(scale
), ViewportMinScale(),
10303 ViewportMaxScale()));
10306 void Document::ParseScalesInViewportMetaData(
10307 const ViewportMetaData
& aViewportMetaData
) {
10308 Maybe
<LayoutDeviceToScreenScale
> scale
;
10310 scale
= ParseScaleString(aViewportMetaData
.mInitialScale
);
10311 mScaleFloat
= scale
.valueOr(LayoutDeviceToScreenScale(0.0f
));
10312 mValidScaleFloat
= scale
.isSome();
10314 scale
= ParseScaleString(aViewportMetaData
.mMaximumScale
);
10315 // Chrome uses '5' for the fallback value of maximum-scale, we might
10316 // consider matching it in future.
10317 // https://cs.chromium.org/chromium/src/third_party/blink/renderer/core/html/html_meta_element.cc?l=452&rcl=65ca4278b42d269ca738fc93ef7ae04a032afeb0
10318 mScaleMaxFloat
= scale
.valueOr(ViewportMaxScale());
10319 mValidMaxScale
= scale
.isSome();
10321 scale
= ParseScaleString(aViewportMetaData
.mMinimumScale
);
10322 mScaleMinFloat
= scale
.valueOr(ViewportMinScale());
10323 mValidMinScale
= scale
.isSome();
10325 // Resolve min-zoom and max-zoom values.
10326 // https://drafts.csswg.org/css-device-adapt/#constraining-min-max-zoom
10327 if (mValidMaxScale
&& mValidMinScale
) {
10328 mScaleMaxFloat
= std::max(mScaleMinFloat
, mScaleMaxFloat
);
10332 void Document::ParseWidthAndHeightInMetaViewport(const nsAString
& aWidthString
,
10333 const nsAString
& aHeightString
,
10334 bool aHasValidScale
) {
10335 // The width and height properties
10336 // https://drafts.csswg.org/css-device-adapt/#width-and-height-properties
10338 // The width and height viewport <META> properties are translated into width
10339 // and height descriptors, setting the min-width/min-height value to
10340 // extend-to-zoom and the max-width/max-height value to the length from the
10341 // viewport <META> property as follows:
10343 // 1. Non-negative number values are translated to pixel lengths, clamped to
10344 // the range: [1px, 10000px]
10345 // 2. Negative number values are dropped
10346 // 3. device-width and device-height translate to 100vw and 100vh respectively
10347 // 4. Other keywords and unknown values are also dropped
10348 mMinWidth
= nsViewportInfo::kAuto
;
10349 mMaxWidth
= nsViewportInfo::kAuto
;
10350 if (!aWidthString
.IsEmpty()) {
10351 mMinWidth
= nsViewportInfo::kExtendToZoom
;
10352 if (aWidthString
.EqualsLiteral("device-width")) {
10353 mMaxWidth
= nsViewportInfo::kDeviceSize
;
10355 nsresult widthErrorCode
;
10356 mMaxWidth
= aWidthString
.ToInteger(&widthErrorCode
);
10357 if (NS_FAILED(widthErrorCode
)) {
10358 mMaxWidth
= nsViewportInfo::kAuto
;
10359 } else if (mMaxWidth
>= 0.0f
) {
10360 mMaxWidth
= clamped(mMaxWidth
, CSSCoord(1.0f
), CSSCoord(10000.0f
));
10362 mMaxWidth
= nsViewportInfo::kAuto
;
10365 } else if (aHasValidScale
) {
10366 if (aHeightString
.IsEmpty()) {
10367 mMinWidth
= nsViewportInfo::kExtendToZoom
;
10368 mMaxWidth
= nsViewportInfo::kExtendToZoom
;
10370 } else if (aHeightString
.IsEmpty() && UseWidthDeviceWidthFallbackViewport()) {
10371 mMinWidth
= nsViewportInfo::kExtendToZoom
;
10372 mMaxWidth
= nsViewportInfo::kDeviceSize
;
10375 mMinHeight
= nsViewportInfo::kAuto
;
10376 mMaxHeight
= nsViewportInfo::kAuto
;
10377 if (!aHeightString
.IsEmpty()) {
10378 mMinHeight
= nsViewportInfo::kExtendToZoom
;
10379 if (aHeightString
.EqualsLiteral("device-height")) {
10380 mMaxHeight
= nsViewportInfo::kDeviceSize
;
10382 nsresult heightErrorCode
;
10383 mMaxHeight
= aHeightString
.ToInteger(&heightErrorCode
);
10384 if (NS_FAILED(heightErrorCode
)) {
10385 mMaxHeight
= nsViewportInfo::kAuto
;
10386 } else if (mMaxHeight
>= 0.0f
) {
10387 mMaxHeight
= clamped(mMaxHeight
, CSSCoord(1.0f
), CSSCoord(10000.0f
));
10389 mMaxHeight
= nsViewportInfo::kAuto
;
10395 nsViewportInfo
Document::GetViewportInfo(const ScreenIntSize
& aDisplaySize
) {
10396 MOZ_ASSERT(mPresShell
);
10398 // Compute the CSS-to-LayoutDevice pixel scale as the product of the
10399 // widget scale and the full zoom.
10400 nsPresContext
* context
= mPresShell
->GetPresContext();
10401 // When querying the full zoom, get it from the device context rather than
10402 // directly from the pres context, because the device context's value can
10403 // include an adjustment necessary to keep the number of app units per device
10404 // pixel an integer, and we want the adjusted value.
10405 float fullZoom
= context
? context
->DeviceContext()->GetFullZoom() : 1.0;
10406 fullZoom
= (fullZoom
== 0.0) ? 1.0 : fullZoom
;
10407 CSSToLayoutDeviceScale layoutDeviceScale
=
10408 context
? context
->CSSToDevPixelScale() : CSSToLayoutDeviceScale(1);
10410 CSSToScreenScale defaultScale
=
10411 layoutDeviceScale
* LayoutDeviceToScreenScale(1.0);
10413 // Special behaviour for desktop mode, provided we are not on an about: page,
10415 const bool fullscreen
= Fullscreen();
10416 auto* bc
= GetBrowsingContext();
10417 if (bc
&& bc
->ForceDesktopViewport() && !IsAboutPage() && !fullscreen
) {
10418 CSSCoord viewportWidth
=
10419 StaticPrefs::browser_viewport_desktopWidth() / fullZoom
;
10420 CSSToScreenScale
scaleToFit(aDisplaySize
.width
/ viewportWidth
);
10421 float aspectRatio
= (float)aDisplaySize
.height
/ aDisplaySize
.width
;
10422 CSSSize
viewportSize(viewportWidth
, viewportWidth
* aspectRatio
);
10423 ScreenIntSize fakeDesktopSize
= RoundedToInt(viewportSize
* scaleToFit
);
10424 return nsViewportInfo(fakeDesktopSize
, scaleToFit
,
10425 nsViewportInfo::ZoomFlag::AllowZoom
,
10426 nsViewportInfo::ZoomBehaviour::Mobile
,
10427 nsViewportInfo::AutoScaleFlag::AutoScale
);
10430 // We ignore viewport meta tage etc when in fullscreen, see bug 1696717.
10431 if (fullscreen
|| !nsLayoutUtils::ShouldHandleMetaViewport(this)) {
10432 return nsViewportInfo(aDisplaySize
, defaultScale
,
10433 nsLayoutUtils::AllowZoomingForDocument(this)
10434 ? nsViewportInfo::ZoomFlag::AllowZoom
10435 : nsViewportInfo::ZoomFlag::DisallowZoom
,
10436 StaticPrefs::apz_allow_zooming_out()
10437 ? nsViewportInfo::ZoomBehaviour::Mobile
10438 : nsViewportInfo::ZoomBehaviour::Desktop
);
10441 // In cases where the width of the CSS viewport is less than or equal to the
10442 // width of the display (i.e. width <= device-width) then we disable
10443 // double-tap-to-zoom behaviour. See bug 941995 for details.
10445 switch (mViewportType
) {
10446 case DisplayWidthHeight
:
10447 return nsViewportInfo(aDisplaySize
, defaultScale
,
10448 nsViewportInfo::ZoomFlag::AllowZoom
,
10449 nsViewportInfo::ZoomBehaviour::Mobile
);
10451 // We might early exit if the viewport is empty. Even if we don't,
10452 // at the end of this case we'll note that it was empty. Later, when
10453 // we're using the cached values, this will trigger alternate code paths.
10454 if (!mLastModifiedViewportMetaData
) {
10455 // If the docType specifies that we are on a site optimized for mobile,
10456 // then we want to return specially crafted defaults for the viewport
10458 if (RefPtr
<DocumentType
> docType
= GetDoctype()) {
10459 nsAutoString docId
;
10460 docType
->GetPublicId(docId
);
10461 if ((docId
.Find(u
"WAP") != -1) || (docId
.Find(u
"Mobile") != -1) ||
10462 (docId
.Find(u
"WML") != -1)) {
10463 // We're making an assumption that the docType can't change here
10464 mViewportType
= DisplayWidthHeight
;
10465 return nsViewportInfo(aDisplaySize
, defaultScale
,
10466 nsViewportInfo::ZoomFlag::AllowZoom
,
10467 nsViewportInfo::ZoomBehaviour::Mobile
);
10471 nsAutoString handheldFriendly
;
10472 GetHeaderData(nsGkAtoms::handheldFriendly
, handheldFriendly
);
10473 if (handheldFriendly
.EqualsLiteral("true")) {
10474 mViewportType
= DisplayWidthHeight
;
10475 return nsViewportInfo(aDisplaySize
, defaultScale
,
10476 nsViewportInfo::ZoomFlag::AllowZoom
,
10477 nsViewportInfo::ZoomBehaviour::Mobile
);
10481 ViewportMetaData metaData
= GetViewportMetaData();
10483 // Parse initial-scale, minimum-scale and maximum-scale.
10484 ParseScalesInViewportMetaData(metaData
);
10486 // Parse width and height properties
10487 // This function sets m{Min,Max}{Width,Height}.
10488 ParseWidthAndHeightInMetaViewport(metaData
.mWidth
, metaData
.mHeight
,
10492 if ((metaData
.mUserScalable
.EqualsLiteral("0")) ||
10493 (metaData
.mUserScalable
.EqualsLiteral("no")) ||
10494 (metaData
.mUserScalable
.EqualsLiteral("false"))) {
10495 mAllowZoom
= false;
10498 // Resolve viewport-fit value.
10499 // https://drafts.csswg.org/css-round-display/#viewport-fit-descriptor
10500 mViewportFit
= ViewportFitType::Auto
;
10501 if (!metaData
.mViewportFit
.IsEmpty()) {
10502 if (metaData
.mViewportFit
.EqualsLiteral("contain")) {
10503 mViewportFit
= ViewportFitType::Contain
;
10504 } else if (metaData
.mViewportFit
.EqualsLiteral("cover")) {
10505 mViewportFit
= ViewportFitType::Cover
;
10509 mWidthStrEmpty
= metaData
.mWidth
.IsEmpty();
10511 mViewportType
= Specified
;
10516 LayoutDeviceToScreenScale effectiveMinScale
= mScaleMinFloat
;
10517 LayoutDeviceToScreenScale effectiveMaxScale
= mScaleMaxFloat
;
10518 bool effectiveValidMaxScale
= mValidMaxScale
;
10520 nsViewportInfo::ZoomFlag effectiveZoomFlag
=
10521 mAllowZoom
? nsViewportInfo::ZoomFlag::AllowZoom
10522 : nsViewportInfo::ZoomFlag::DisallowZoom
;
10523 if (StaticPrefs::browser_ui_zoom_force_user_scalable()) {
10524 // If the pref to force user-scalable is enabled, we ignore the values
10525 // from the meta-viewport tag for these properties and just assume they
10526 // allow the page to be scalable. Note in particular that this code is
10527 // in the "Specified" branch of the enclosing switch statement, so that
10528 // calls to GetViewportInfo always use the latest value of the
10529 // browser_ui_zoom_force_user_scalable pref. Other codepaths that
10530 // return nsViewportInfo instances are all consistent with
10531 // browser_ui_zoom_force_user_scalable() already.
10532 effectiveMinScale
= ViewportMinScale();
10533 effectiveMaxScale
= ViewportMaxScale();
10534 effectiveValidMaxScale
= true;
10535 effectiveZoomFlag
= nsViewportInfo::ZoomFlag::AllowZoom
;
10538 // Returns extend-zoom value which is MIN(mScaleFloat, mScaleMaxFloat).
10539 auto ComputeExtendZoom
= [&]() -> float {
10540 if (mValidScaleFloat
&& effectiveValidMaxScale
) {
10541 return std::min(mScaleFloat
.scale
, effectiveMaxScale
.scale
);
10543 if (mValidScaleFloat
) {
10544 return mScaleFloat
.scale
;
10546 if (effectiveValidMaxScale
) {
10547 return effectiveMaxScale
.scale
;
10549 return nsViewportInfo::kAuto
;
10552 // Resolving 'extend-to-zoom'
10553 // https://drafts.csswg.org/css-device-adapt/#resolve-extend-to-zoom
10554 float extendZoom
= ComputeExtendZoom();
10556 CSSCoord minWidth
= mMinWidth
;
10557 CSSCoord maxWidth
= mMaxWidth
;
10558 CSSCoord minHeight
= mMinHeight
;
10559 CSSCoord maxHeight
= mMaxHeight
;
10561 // aDisplaySize is in screen pixels; convert them to CSS pixels for the
10562 // viewport size. We need to use this scaled size for any clamping of
10563 // width or height.
10564 CSSSize displaySize
= ScreenSize(aDisplaySize
) / defaultScale
;
10566 // Our min and max width and height values are mostly as specified by
10567 // the viewport declaration, but we make an exception for max width.
10568 // Max width, if auto, and if there's no initial-scale, will be set
10569 // to a default size. This is to support legacy site design with no
10570 // viewport declaration, and to do that using the same scheme as
10571 // Chrome does, in order to maintain web compatibility. Since the
10572 // default size has a complicated calculation, we fixup the maxWidth
10573 // value after setting it, above.
10574 if (maxWidth
== nsViewportInfo::kAuto
&& !mValidScaleFloat
) {
10575 if (bc
&& bc
->TouchEventsOverride() == TouchEventsOverride::Enabled
&&
10577 // If RDM and touch simulation are active, then use the simulated
10578 // screen width to accommodate for cases where the screen width is
10579 // larger than the desktop viewport default.
10580 maxWidth
= nsViewportInfo::Max(
10581 displaySize
.width
, StaticPrefs::browser_viewport_desktopWidth());
10583 maxWidth
= StaticPrefs::browser_viewport_desktopWidth();
10585 // Divide by fullZoom to stretch CSS pixel size of viewport in order
10586 // to keep device pixel size unchanged after full zoom applied.
10588 maxWidth
/= fullZoom
;
10590 // We set minWidth to ExtendToZoom, which will cause our later width
10591 // calculation to expand to maxWidth, if scale restrictions allow it.
10592 minWidth
= nsViewportInfo::kExtendToZoom
;
10595 // Resolve device-width and device-height first.
10596 if (maxWidth
== nsViewportInfo::kDeviceSize
) {
10597 maxWidth
= displaySize
.width
;
10599 if (maxHeight
== nsViewportInfo::kDeviceSize
) {
10600 maxHeight
= displaySize
.height
;
10602 if (extendZoom
== nsViewportInfo::kAuto
) {
10603 if (maxWidth
== nsViewportInfo::kExtendToZoom
) {
10604 maxWidth
= nsViewportInfo::kAuto
;
10606 if (maxHeight
== nsViewportInfo::kExtendToZoom
) {
10607 maxHeight
= nsViewportInfo::kAuto
;
10609 if (minWidth
== nsViewportInfo::kExtendToZoom
) {
10610 minWidth
= maxWidth
;
10612 if (minHeight
== nsViewportInfo::kExtendToZoom
) {
10613 minHeight
= maxHeight
;
10616 CSSSize extendSize
= displaySize
/ extendZoom
;
10617 if (maxWidth
== nsViewportInfo::kExtendToZoom
) {
10618 maxWidth
= extendSize
.width
;
10620 if (maxHeight
== nsViewportInfo::kExtendToZoom
) {
10621 maxHeight
= extendSize
.height
;
10623 if (minWidth
== nsViewportInfo::kExtendToZoom
) {
10624 minWidth
= nsViewportInfo::Max(extendSize
.width
, maxWidth
);
10626 if (minHeight
== nsViewportInfo::kExtendToZoom
) {
10627 minHeight
= nsViewportInfo::Max(extendSize
.height
, maxHeight
);
10631 // Resolve initial width and height from min/max descriptors
10632 // https://drafts.csswg.org/css-device-adapt/#resolve-initial-width-height
10633 CSSCoord width
= nsViewportInfo::kAuto
;
10634 if (minWidth
!= nsViewportInfo::kAuto
||
10635 maxWidth
!= nsViewportInfo::kAuto
) {
10636 width
= nsViewportInfo::Max(
10637 minWidth
, nsViewportInfo::Min(maxWidth
, displaySize
.width
));
10639 CSSCoord height
= nsViewportInfo::kAuto
;
10640 if (minHeight
!= nsViewportInfo::kAuto
||
10641 maxHeight
!= nsViewportInfo::kAuto
) {
10642 height
= nsViewportInfo::Max(
10643 minHeight
, nsViewportInfo::Min(maxHeight
, displaySize
.height
));
10646 // Resolve width value
10647 // https://drafts.csswg.org/css-device-adapt/#resolve-width
10648 if (width
== nsViewportInfo::kAuto
) {
10649 if (height
== nsViewportInfo::kAuto
|| aDisplaySize
.height
== 0) {
10650 width
= displaySize
.width
;
10652 width
= height
* aDisplaySize
.width
/ aDisplaySize
.height
;
10656 // Resolve height value
10657 // https://drafts.csswg.org/css-device-adapt/#resolve-height
10658 if (height
== nsViewportInfo::kAuto
) {
10659 if (aDisplaySize
.width
== 0) {
10660 height
= displaySize
.height
;
10662 height
= width
* aDisplaySize
.height
/ aDisplaySize
.width
;
10665 MOZ_ASSERT(width
!= nsViewportInfo::kAuto
&&
10666 height
!= nsViewportInfo::kAuto
);
10668 CSSSize
size(width
, height
);
10670 CSSToScreenScale scaleFloat
= mScaleFloat
* layoutDeviceScale
;
10671 CSSToScreenScale scaleMinFloat
= effectiveMinScale
* layoutDeviceScale
;
10672 CSSToScreenScale scaleMaxFloat
= effectiveMaxScale
* layoutDeviceScale
;
10674 nsViewportInfo::AutoSizeFlag sizeFlag
=
10675 nsViewportInfo::AutoSizeFlag::FixedSize
;
10676 if (mMaxWidth
== nsViewportInfo::kDeviceSize
||
10677 (mWidthStrEmpty
&& (mMaxHeight
== nsViewportInfo::kDeviceSize
||
10678 mScaleFloat
.scale
== 1.0f
)) ||
10679 (!mWidthStrEmpty
&& mMaxWidth
== nsViewportInfo::kAuto
&&
10681 sizeFlag
= nsViewportInfo::AutoSizeFlag::AutoSize
;
10684 // FIXME: Resolving width and height should be done above 'Resolve width
10685 // value' and 'Resolve height value'.
10686 if (sizeFlag
== nsViewportInfo::AutoSizeFlag::AutoSize
) {
10687 size
= displaySize
;
10690 // The purpose of clamping the viewport width to a minimum size is to
10691 // prevent page authors from setting it to a ridiculously small value.
10692 // If the page is actually being rendered in a very small area (as might
10693 // happen in e.g. Android 8's picture-in-picture mode), we don't want to
10694 // prevent the viewport from taking on that size.
10695 CSSSize effectiveMinSize
= Min(CSSSize(kViewportMinSize
), displaySize
);
10697 size
.width
= clamped(size
.width
, effectiveMinSize
.width
,
10698 float(kViewportMaxSize
.width
));
10700 // Also recalculate the default zoom, if it wasn't specified in the
10701 // metadata, and the width is specified.
10702 if (!mValidScaleFloat
&& !mWidthStrEmpty
) {
10703 CSSToScreenScale
bestFitScale(float(aDisplaySize
.width
) / size
.width
);
10704 scaleFloat
= (scaleFloat
> bestFitScale
) ? scaleFloat
: bestFitScale
;
10707 size
.height
= clamped(size
.height
, effectiveMinSize
.height
,
10708 float(kViewportMaxSize
.height
));
10710 // In cases of user-scalable=no, if we have a positive scale, clamp it to
10711 // min and max, and then use the clamped value for the scale, the min, and
10712 // the max. If we don't have a positive scale, assert that we are setting
10713 // the auto scale flag.
10714 if (effectiveZoomFlag
== nsViewportInfo::ZoomFlag::DisallowZoom
&&
10715 scaleFloat
> CSSToScreenScale(0.0f
)) {
10716 scaleFloat
= scaleMinFloat
= scaleMaxFloat
=
10717 clamped(scaleFloat
, scaleMinFloat
, scaleMaxFloat
);
10720 scaleFloat
> CSSToScreenScale(0.0f
) || !mValidScaleFloat
,
10721 "If we don't have a positive scale, we should be using auto scale.");
10723 // We need to perform a conversion, but only if the initial or maximum
10724 // scale were set explicitly by the user.
10725 if (mValidScaleFloat
&& scaleFloat
>= scaleMinFloat
&&
10726 scaleFloat
<= scaleMaxFloat
) {
10727 CSSSize displaySize
= ScreenSize(aDisplaySize
) / scaleFloat
;
10728 size
.width
= std::max(size
.width
, displaySize
.width
);
10729 size
.height
= std::max(size
.height
, displaySize
.height
);
10730 } else if (effectiveValidMaxScale
) {
10731 CSSSize displaySize
= ScreenSize(aDisplaySize
) / scaleMaxFloat
;
10732 size
.width
= std::max(size
.width
, displaySize
.width
);
10733 size
.height
= std::max(size
.height
, displaySize
.height
);
10736 return nsViewportInfo(
10737 scaleFloat
, scaleMinFloat
, scaleMaxFloat
, size
, sizeFlag
,
10738 mValidScaleFloat
? nsViewportInfo::AutoScaleFlag::FixedScale
10739 : nsViewportInfo::AutoScaleFlag::AutoScale
,
10740 effectiveZoomFlag
, mViewportFit
);
10744 ViewportMetaData
Document::GetViewportMetaData() const {
10745 return mLastModifiedViewportMetaData
? *mLastModifiedViewportMetaData
10746 : ViewportMetaData();
10749 void Document::SetMetaViewportData(UniquePtr
<ViewportMetaData
> aData
) {
10750 mLastModifiedViewportMetaData
= std::move(aData
);
10751 // Trigger recomputation of the nsViewportInfo the next time it's queried.
10752 mViewportType
= Unknown
;
10754 AsyncEventDispatcher::RunDOMEventWhenSafe(
10755 *this, u
"DOMMetaViewportFitChanged"_ns
, CanBubble::eYes
,
10756 ChromeOnlyDispatch::eYes
);
10759 EventListenerManager
* Document::GetOrCreateListenerManager() {
10760 if (!mListenerManager
) {
10762 new EventListenerManager(static_cast<EventTarget
*>(this));
10763 SetFlags(NODE_HAS_LISTENERMANAGER
);
10766 return mListenerManager
;
10769 EventListenerManager
* Document::GetExistingListenerManager() const {
10770 return mListenerManager
;
10773 void Document::GetEventTargetParent(EventChainPreVisitor
& aVisitor
) {
10774 aVisitor
.mCanHandle
= true;
10775 // FIXME! This is a hack to make middle mouse paste working also in Editor.
10777 aVisitor
.mForceContentDispatch
= true;
10779 // Load events must not propagate to |window| object, see bug 335251.
10780 if (aVisitor
.mEvent
->mMessage
!= eLoad
) {
10781 nsGlobalWindowOuter
* window
= nsGlobalWindowOuter::Cast(GetWindow());
10782 aVisitor
.SetParentTarget(
10783 window
? window
->GetTargetForEventTargetChain() : nullptr, false);
10787 already_AddRefed
<Event
> Document::CreateEvent(const nsAString
& aEventType
,
10788 CallerType aCallerType
,
10789 ErrorResult
& rv
) const {
10790 nsPresContext
* presContext
= GetPresContext();
10792 // Create event even without presContext.
10794 EventDispatcher::CreateEvent(const_cast<Document
*>(this), presContext
,
10795 nullptr, aEventType
, aCallerType
);
10797 rv
.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR
);
10800 WidgetEvent
* e
= ev
->WidgetEventPtr();
10801 e
->mFlags
.mBubbles
= false;
10802 e
->mFlags
.mCancelable
= false;
10803 return ev
.forget();
10806 void Document::FlushPendingNotifications(FlushType aType
) {
10807 mozilla::ChangesToFlush
flush(aType
, aType
>= FlushType::Style
);
10808 FlushPendingNotifications(flush
);
10811 void Document::FlushPendingNotifications(mozilla::ChangesToFlush aFlush
) {
10812 FlushType flushType
= aFlush
.mFlushType
;
10814 RefPtr
<Document
> documentOnStack
= this;
10816 // We need to flush the sink for non-HTML documents (because the XML
10817 // parser still does insertion with deferred notifications). We
10818 // also need to flush the sink if this is a layout-related flush, to
10819 // make sure that layout is started as needed. But we can skip that
10820 // part if we have no presshell or if it's already done an initial
10822 if ((!IsHTMLDocument() || (flushType
> FlushType::ContentAndNotify
&&
10823 mPresShell
&& !mPresShell
->DidInitialize())) &&
10824 (mParser
|| mWeakSink
)) {
10825 nsCOMPtr
<nsIContentSink
> sink
;
10827 sink
= mParser
->GetContentSink();
10829 sink
= do_QueryReferent(mWeakSink
);
10831 mWeakSink
= nullptr;
10834 // Determine if it is safe to flush the sink notifications
10835 // by determining if it safe to flush all the presshells.
10836 if (sink
&& (flushType
== FlushType::Content
|| IsSafeToFlush())) {
10837 sink
->FlushPendingNotifications(flushType
);
10841 // Should we be flushing pending binding constructors in here?
10843 if (flushType
<= FlushType::ContentAndNotify
) {
10844 // Nothing to do here
10848 // If we have a parent we must flush the parent too to ensure that our
10849 // container is reflowed if its size was changed.
10851 // We do it only if the subdocument and the parent can observe each other
10852 // synchronously (that is, if we're not cross-origin), to avoid work that is
10853 // not observable, and if the parent document has finished loading all its
10854 // render-blocking stylesheets and may start laying out the document, to avoid
10855 // unnecessary flashes of unstyled content on the parent document. Note that
10856 // this last bit means that size-dependent media queries in this document may
10857 // produce incorrect results temporarily.
10859 // But if it's not safe to flush ourselves, then don't flush the parent, since
10860 // that can cause things like resizes of our frame's widget, which we can't
10861 // handle while flushing is unsafe.
10862 if (StyleOrLayoutObservablyDependsOnParentDocumentLayout() &&
10863 mParentDocument
->MayStartLayout() && IsSafeToFlush()) {
10864 ChangesToFlush parentFlush
= aFlush
;
10865 if (flushType
>= FlushType::Style
) {
10866 // Since media queries mean that a size change of our container can affect
10867 // style, we need to promote a style flush on ourself to a layout flush on
10868 // our parent, since we need our container to be the correct size to
10869 // determine the correct style.
10870 parentFlush
.mFlushType
= std::max(FlushType::Layout
, flushType
);
10872 mParentDocument
->FlushPendingNotifications(parentFlush
);
10875 if (RefPtr
<PresShell
> presShell
= GetPresShell()) {
10876 presShell
->FlushPendingNotifications(aFlush
);
10880 void Document::FlushExternalResources(FlushType aType
) {
10882 aType
>= FlushType::Style
,
10883 "should only need to flush for style or higher in external resources");
10884 if (GetDisplayDocument()) {
10888 auto flush
= [aType
](Document
& aDoc
) {
10889 aDoc
.FlushPendingNotifications(aType
);
10890 return CallState::Continue
;
10893 EnumerateExternalResources(flush
);
10896 void Document::SetXMLDeclaration(const char16_t
* aVersion
,
10897 const char16_t
* aEncoding
,
10898 const int32_t aStandalone
) {
10899 if (!aVersion
|| *aVersion
== '\0') {
10900 mXMLDeclarationBits
= 0;
10904 mXMLDeclarationBits
= XML_DECLARATION_BITS_DECLARATION_EXISTS
;
10906 if (aEncoding
&& *aEncoding
!= '\0') {
10907 mXMLDeclarationBits
|= XML_DECLARATION_BITS_ENCODING_EXISTS
;
10910 if (aStandalone
== 1) {
10911 mXMLDeclarationBits
|= XML_DECLARATION_BITS_STANDALONE_EXISTS
|
10912 XML_DECLARATION_BITS_STANDALONE_YES
;
10913 } else if (aStandalone
== 0) {
10914 mXMLDeclarationBits
|= XML_DECLARATION_BITS_STANDALONE_EXISTS
;
10918 void Document::GetXMLDeclaration(nsAString
& aVersion
, nsAString
& aEncoding
,
10919 nsAString
& aStandalone
) {
10920 aVersion
.Truncate();
10921 aEncoding
.Truncate();
10922 aStandalone
.Truncate();
10924 if (!(mXMLDeclarationBits
& XML_DECLARATION_BITS_DECLARATION_EXISTS
)) {
10928 // always until we start supporting 1.1 etc.
10929 aVersion
.AssignLiteral("1.0");
10931 if (mXMLDeclarationBits
& XML_DECLARATION_BITS_ENCODING_EXISTS
) {
10932 // This is what we have stored, not necessarily what was written
10934 GetCharacterSet(aEncoding
);
10937 if (mXMLDeclarationBits
& XML_DECLARATION_BITS_STANDALONE_EXISTS
) {
10938 if (mXMLDeclarationBits
& XML_DECLARATION_BITS_STANDALONE_YES
) {
10939 aStandalone
.AssignLiteral("yes");
10941 aStandalone
.AssignLiteral("no");
10946 void Document::AddColorSchemeMeta(HTMLMetaElement
& aMeta
) {
10947 mColorSchemeMetaTags
.Insert(aMeta
);
10948 RecomputeColorScheme();
10951 void Document::RemoveColorSchemeMeta(HTMLMetaElement
& aMeta
) {
10952 mColorSchemeMetaTags
.RemoveElement(aMeta
);
10953 RecomputeColorScheme();
10956 void Document::RecomputeColorScheme() {
10957 if (!StaticPrefs::layout_css_color_scheme_enabled()) {
10960 auto oldColorScheme
= mColorSchemeBits
;
10961 mColorSchemeBits
= 0;
10962 const nsTArray
<HTMLMetaElement
*>& elements
= mColorSchemeMetaTags
;
10963 for (const HTMLMetaElement
* el
: elements
) {
10964 nsAutoString content
;
10965 if (!el
->GetAttr(nsGkAtoms::content
, content
)) {
10969 NS_ConvertUTF16toUTF8
contentU8(content
);
10970 if (Servo_ColorScheme_Parse(&contentU8
, &mColorSchemeBits
)) {
10975 if (mColorSchemeBits
== oldColorScheme
) {
10979 if (nsPresContext
* pc
= GetPresContext()) {
10980 // This affects system colors, which are inherited, so we need to recascade.
10981 pc
->RebuildAllStyleData(nsChangeHint(0), RestyleHint::RecascadeSubtree());
10985 bool Document::IsScriptEnabled() const {
10986 // If this document is sandboxed without 'allow-scripts'
10987 // script is not enabled
10988 if (HasScriptsBlockedBySandbox()) {
10992 nsCOMPtr
<nsIScriptGlobalObject
> globalObject
=
10993 do_QueryInterface(GetInnerWindow());
10994 if (!globalObject
|| !globalObject
->HasJSGlobal()) {
10998 return xpc::Scriptability::Get(globalObject
->GetGlobalJSObjectPreserveColor())
11002 void Document::RetrieveRelevantHeaders(nsIChannel
* aChannel
) {
11003 PRTime modDate
= 0;
11006 nsCOMPtr
<nsIHttpChannel
> httpChannel
;
11007 rv
= GetHttpChannelHelper(aChannel
, getter_AddRefs(httpChannel
));
11008 if (NS_WARN_IF(NS_FAILED(rv
))) {
11014 rv
= httpChannel
->GetResponseHeader("last-modified"_ns
, tmp
);
11016 if (NS_SUCCEEDED(rv
)) {
11018 PRStatus st
= PR_ParseTimeString(tmp
.get(), true, &time
);
11019 if (st
== PR_SUCCESS
) {
11024 static const char* const headers
[] = {
11025 "default-style", "content-style-type", "content-language",
11026 "content-disposition", "refresh", "x-dns-prefetch-control",
11027 "x-frame-options", "origin-trial",
11028 // add more http headers if you need
11029 // XXXbz don't add content-location support without reading bug
11030 // 238654 and its dependencies/dups first.
11033 nsAutoCString headerVal
;
11034 const char* const* name
= headers
;
11036 rv
= httpChannel
->GetResponseHeader(nsDependentCString(*name
), headerVal
);
11037 if (NS_SUCCEEDED(rv
) && !headerVal
.IsEmpty()) {
11038 RefPtr
<nsAtom
> key
= NS_Atomize(*name
);
11039 SetHeaderData(key
, NS_ConvertASCIItoUTF16(headerVal
));
11044 nsCOMPtr
<nsIFileChannel
> fileChannel
= do_QueryInterface(aChannel
);
11046 nsCOMPtr
<nsIFile
> file
;
11047 fileChannel
->GetFile(getter_AddRefs(file
));
11050 rv
= file
->GetLastModifiedTime(&msecs
);
11052 if (NS_SUCCEEDED(rv
)) {
11053 modDate
= msecs
* int64_t(PR_USEC_PER_MSEC
);
11057 nsAutoCString contentDisp
;
11058 rv
= aChannel
->GetContentDispositionHeader(contentDisp
);
11059 if (NS_SUCCEEDED(rv
)) {
11060 SetHeaderData(nsGkAtoms::headerContentDisposition
,
11061 NS_ConvertASCIItoUTF16(contentDisp
));
11066 mLastModified
.Truncate();
11067 if (modDate
!= 0) {
11068 GetFormattedTimeString(modDate
, mLastModified
);
11072 void Document::ProcessMETATag(HTMLMetaElement
* aMetaElement
) {
11073 // set any HTTP-EQUIV data into document's header data as well as url
11074 nsAutoString header
;
11075 aMetaElement
->GetAttr(nsGkAtoms::httpEquiv
, header
);
11076 if (!header
.IsEmpty()) {
11077 // Ignore META REFRESH when document is sandboxed from automatic features.
11078 nsContentUtils::ASCIIToLower(header
);
11079 if (nsGkAtoms::refresh
->Equals(header
) &&
11080 (GetSandboxFlags() & SANDBOXED_AUTOMATIC_FEATURES
)) {
11084 nsAutoString result
;
11085 aMetaElement
->GetAttr(nsGkAtoms::content
, result
);
11086 if (!result
.IsEmpty()) {
11087 RefPtr
<nsAtom
> fieldAtom(NS_Atomize(header
));
11088 SetHeaderData(fieldAtom
, result
);
11092 if (aMetaElement
->AttrValueIs(kNameSpaceID_None
, nsGkAtoms::name
,
11093 nsGkAtoms::handheldFriendly
, eIgnoreCase
)) {
11094 nsAutoString result
;
11095 aMetaElement
->GetAttr(kNameSpaceID_None
, nsGkAtoms::content
, result
);
11096 if (!result
.IsEmpty()) {
11097 nsContentUtils::ASCIIToLower(result
);
11098 SetHeaderData(nsGkAtoms::handheldFriendly
, result
);
11103 already_AddRefed
<Element
> Document::CreateElem(const nsAString
& aName
,
11105 int32_t aNamespaceID
,
11106 const nsAString
* aIs
) {
11108 nsAutoString qName
;
11110 aPrefix
->ToString(qName
);
11113 qName
.Append(aName
);
11115 // Note: "a:b:c" is a valid name in non-namespaces XML, and
11116 // Document::CreateElement can call us with such a name and no prefix,
11117 // which would cause an error if we just used true here.
11118 bool nsAware
= aPrefix
!= nullptr || aNamespaceID
!= GetDefaultNamespaceID();
11119 NS_ASSERTION(NS_SUCCEEDED(nsContentUtils::CheckQName(qName
, nsAware
)),
11120 "Don't pass invalid prefixes to Document::CreateElem, "
11124 RefPtr
<mozilla::dom::NodeInfo
> nodeInfo
;
11125 mNodeInfoManager
->GetNodeInfo(aName
, aPrefix
, aNamespaceID
, ELEMENT_NODE
,
11126 getter_AddRefs(nodeInfo
));
11127 NS_ENSURE_TRUE(nodeInfo
, nullptr);
11129 nsCOMPtr
<Element
> element
;
11130 nsresult rv
= NS_NewElement(getter_AddRefs(element
), nodeInfo
.forget(),
11131 NOT_FROM_PARSER
, aIs
);
11132 return NS_SUCCEEDED(rv
) ? element
.forget() : nullptr;
11135 bool Document::IsSafeToFlush() const {
11136 PresShell
* presShell
= GetPresShell();
11140 return presShell
->IsSafeToFlush();
11143 void Document::Sanitize() {
11144 // Sanitize the document by resetting all (current and former) password fields
11145 // and any form fields with autocomplete=off to their default values. We do
11146 // this now, instead of when the presentation is restored, to offer some
11147 // protection in case there is ever an exploit that allows a cached document
11148 // to be accessed from a different document.
11150 // First locate all input elements, regardless of whether they are
11151 // in a form, and reset the password and autocomplete=off elements.
11153 RefPtr
<nsContentList
> nodes
= GetElementsByTagName(u
"input"_ns
);
11155 nsAutoString value
;
11157 uint32_t length
= nodes
->Length(true);
11158 for (uint32_t i
= 0; i
< length
; ++i
) {
11159 NS_ASSERTION(nodes
->Item(i
), "null item in node list!");
11161 RefPtr
<HTMLInputElement
> input
=
11162 HTMLInputElement::FromNodeOrNull(nodes
->Item(i
));
11163 if (!input
) continue;
11165 input
->GetAttr(nsGkAtoms::autocomplete
, value
);
11166 if (value
.LowerCaseEqualsLiteral("off") || input
->HasBeenTypePassword()) {
11171 // Now locate all _form_ elements that have autocomplete=off and reset them
11172 nodes
= GetElementsByTagName(u
"form"_ns
);
11174 length
= nodes
->Length(true);
11175 for (uint32_t i
= 0; i
< length
; ++i
) {
11176 // Reset() may change the list dynamically.
11177 RefPtr
<HTMLFormElement
> form
=
11178 HTMLFormElement::FromNodeOrNull(nodes
->Item(i
));
11179 if (!form
) continue;
11181 form
->GetAttr(kNameSpaceID_None
, nsGkAtoms::autocomplete
, value
);
11182 if (value
.LowerCaseEqualsLiteral("off")) form
->Reset();
11186 void Document::EnumerateSubDocuments(SubDocEnumFunc aCallback
) {
11187 if (!mSubDocuments
) {
11191 // PLDHashTable::Iterator can't handle modifications while iterating so we
11192 // copy all entries to an array first before calling any callbacks.
11193 AutoTArray
<RefPtr
<Document
>, 8> subdocs
;
11194 for (auto iter
= mSubDocuments
->Iter(); !iter
.Done(); iter
.Next()) {
11195 auto entry
= static_cast<SubDocMapEntry
*>(iter
.Get());
11196 if (Document
* subdoc
= entry
->mSubDocument
) {
11197 subdocs
.AppendElement(subdoc
);
11200 for (auto& subdoc
: subdocs
) {
11201 if (aCallback(*subdoc
) == CallState::Stop
) {
11207 void Document::CollectDescendantDocuments(
11208 nsTArray
<RefPtr
<Document
>>& aDescendants
, nsDocTestFunc aCallback
) const {
11209 if (!mSubDocuments
) {
11213 for (auto iter
= mSubDocuments
->Iter(); !iter
.Done(); iter
.Next()) {
11214 auto entry
= static_cast<SubDocMapEntry
*>(iter
.Get());
11215 const Document
* subdoc
= entry
->mSubDocument
;
11217 if (aCallback(subdoc
)) {
11218 aDescendants
.AppendElement(entry
->mSubDocument
);
11220 subdoc
->CollectDescendantDocuments(aDescendants
, aCallback
);
11225 bool Document::CanSavePresentation(nsIRequest
* aNewRequest
,
11226 uint32_t& aBFCacheCombo
,
11227 bool aIncludeSubdocuments
,
11228 bool aAllowUnloadListeners
) {
11231 if (!IsBFCachingAllowed()) {
11232 aBFCacheCombo
|= BFCacheStatus::NOT_ALLOWED
;
11237 if (MOZ_UNLIKELY(MOZ_LOG_TEST(gPageCacheLog
, LogLevel::Verbose
))) {
11238 if (mDocumentURI
) {
11239 mDocumentURI
->GetSpec(uri
);
11243 if (EventHandlingSuppressed()) {
11244 MOZ_LOG(gPageCacheLog
, mozilla::LogLevel::Verbose
,
11245 ("Save of %s blocked on event handling suppression", uri
.get()));
11246 aBFCacheCombo
|= BFCacheStatus::EVENT_HANDLING_SUPPRESSED
;
11250 // Do not allow suspended windows to be placed in the
11251 // bfcache. This method is also used to verify a document
11252 // coming out of the bfcache is ok to restore, though. So
11253 // we only want to block suspend windows that aren't also
11255 nsPIDOMWindowInner
* win
= GetInnerWindow();
11256 if (win
&& win
->IsSuspended() && !win
->IsFrozen()) {
11257 MOZ_LOG(gPageCacheLog
, mozilla::LogLevel::Verbose
,
11258 ("Save of %s blocked on suspended Window", uri
.get()));
11259 aBFCacheCombo
|= BFCacheStatus::SUSPENDED
;
11263 nsCOMPtr
<nsIChannel
> channel
= do_QueryInterface(aNewRequest
);
11264 bool thirdParty
= false;
11265 // Currently some other mobile browsers seem to bfcache only cross-domain
11266 // pages, but bfcache those also when there are unload event listeners, so
11267 // this is trying to match that behavior as much as possible.
11268 bool allowUnloadListeners
=
11269 aAllowUnloadListeners
&&
11270 StaticPrefs::docshell_shistory_bfcache_allow_unload_listeners() &&
11271 (!channel
|| (NS_SUCCEEDED(NodePrincipal()->IsThirdPartyChannel(
11272 channel
, &thirdParty
)) &&
11275 // Check our event listener manager for unload/beforeunload listeners.
11276 nsCOMPtr
<EventTarget
> piTarget
= do_QueryInterface(mScriptGlobalObject
);
11277 if (!allowUnloadListeners
&& piTarget
) {
11278 EventListenerManager
* manager
= piTarget
->GetExistingListenerManager();
11280 if (manager
->HasUnloadListeners()) {
11281 MOZ_LOG(gPageCacheLog
, mozilla::LogLevel::Verbose
,
11282 ("Save of %s blocked due to unload handlers", uri
.get()));
11283 aBFCacheCombo
|= BFCacheStatus::UNLOAD_LISTENER
;
11286 if (manager
->HasBeforeUnloadListeners()) {
11287 if (!mozilla::SessionHistoryInParent() ||
11289 docshell_shistory_bfcache_ship_allow_beforeunload_listeners()) {
11291 gPageCacheLog
, mozilla::LogLevel::Verbose
,
11292 ("Save of %s blocked due to beforeUnload handlers", uri
.get()));
11293 aBFCacheCombo
|= BFCacheStatus::BEFOREUNLOAD_LISTENER
;
11300 // Check if we have pending network requests
11301 nsCOMPtr
<nsILoadGroup
> loadGroup
= GetDocumentLoadGroup();
11303 nsCOMPtr
<nsISimpleEnumerator
> requests
;
11304 loadGroup
->GetRequests(getter_AddRefs(requests
));
11306 bool hasMore
= false;
11308 // We want to bail out if we have any requests other than aNewRequest (or
11309 // in the case when aNewRequest is a part of a multipart response the base
11310 // channel the multipart response is coming in on).
11311 nsCOMPtr
<nsIChannel
> baseChannel
;
11312 nsCOMPtr
<nsIMultiPartChannel
> part(do_QueryInterface(aNewRequest
));
11314 part
->GetBaseChannel(getter_AddRefs(baseChannel
));
11317 while (NS_SUCCEEDED(requests
->HasMoreElements(&hasMore
)) && hasMore
) {
11318 nsCOMPtr
<nsISupports
> elem
;
11319 requests
->GetNext(getter_AddRefs(elem
));
11321 nsCOMPtr
<nsIRequest
> request
= do_QueryInterface(elem
);
11322 if (request
&& request
!= aNewRequest
&& request
!= baseChannel
) {
11323 // Favicon loads don't need to block caching.
11324 nsCOMPtr
<nsIChannel
> channel
= do_QueryInterface(request
);
11326 nsCOMPtr
<nsILoadInfo
> li
= channel
->LoadInfo();
11327 if (li
->InternalContentPolicyType() ==
11328 nsIContentPolicy::TYPE_INTERNAL_IMAGE_FAVICON
) {
11333 if (MOZ_UNLIKELY(MOZ_LOG_TEST(gPageCacheLog
, LogLevel::Verbose
))) {
11334 nsAutoCString requestName
;
11335 request
->GetName(requestName
);
11336 MOZ_LOG(gPageCacheLog
, LogLevel::Verbose
,
11337 ("Save of %s blocked because document has request %s",
11338 uri
.get(), requestName
.get()));
11340 aBFCacheCombo
|= BFCacheStatus::REQUEST
;
11346 // Check if we have active GetUserMedia use
11347 if (MediaManager::Exists() && win
&&
11348 MediaManager::Get()->IsWindowStillActive(win
->WindowID())) {
11349 MOZ_LOG(gPageCacheLog
, mozilla::LogLevel::Verbose
,
11350 ("Save of %s blocked due to GetUserMedia", uri
.get()));
11351 aBFCacheCombo
|= BFCacheStatus::ACTIVE_GET_USER_MEDIA
;
11356 // Check if we have active PeerConnections
11357 if (win
&& win
->HasActivePeerConnections()) {
11358 MOZ_LOG(gPageCacheLog
, mozilla::LogLevel::Verbose
,
11359 ("Save of %s blocked due to PeerConnection", uri
.get()));
11360 aBFCacheCombo
|= BFCacheStatus::ACTIVE_PEER_CONNECTION
;
11363 #endif // MOZ_WEBRTC
11365 // Don't save presentations for documents containing EME content, so that
11366 // CDMs reliably shutdown upon user navigation.
11367 if (ContainsEMEContent()) {
11368 aBFCacheCombo
|= BFCacheStatus::CONTAINS_EME_CONTENT
;
11372 // Don't save presentations for documents containing MSE content, to
11373 // reduce memory usage.
11374 if (ContainsMSEContent()) {
11375 MOZ_LOG(gPageCacheLog
, mozilla::LogLevel::Verbose
,
11376 ("Save of %s blocked due to MSE use", uri
.get()));
11377 aBFCacheCombo
|= BFCacheStatus::CONTAINS_MSE_CONTENT
;
11381 if (aIncludeSubdocuments
&& mSubDocuments
) {
11382 for (auto iter
= mSubDocuments
->Iter(); !iter
.Done(); iter
.Next()) {
11383 auto entry
= static_cast<SubDocMapEntry
*>(iter
.Get());
11384 Document
* subdoc
= entry
->mSubDocument
;
11386 uint32_t subDocBFCacheCombo
= 0;
11387 // The aIgnoreRequest we were passed is only for us, so don't pass it on.
11389 subdoc
? subdoc
->CanSavePresentation(nullptr, subDocBFCacheCombo
,
11390 true, allowUnloadListeners
)
11393 MOZ_LOG(gPageCacheLog
, mozilla::LogLevel::Verbose
,
11394 ("Save of %s blocked due to subdocument blocked", uri
.get()));
11395 aBFCacheCombo
|= subDocBFCacheCombo
;
11401 if (!mozilla::BFCacheInParent()) {
11402 // BFCache is currently not compatible with remote subframes (bug 1609324)
11403 if (RefPtr
<BrowsingContext
> browsingContext
= GetBrowsingContext()) {
11404 for (auto& child
: browsingContext
->Children()) {
11405 if (!child
->IsInProcess()) {
11406 aBFCacheCombo
|= BFCacheStatus::CONTAINS_REMOTE_SUBFRAMES
;
11415 auto* globalWindow
= nsGlobalWindowInner::Cast(win
);
11416 #ifdef MOZ_WEBSPEECH
11417 if (globalWindow
->HasActiveSpeechSynthesis()) {
11418 MOZ_LOG(gPageCacheLog
, mozilla::LogLevel::Verbose
,
11419 ("Save of %s blocked due to Speech use", uri
.get()));
11420 aBFCacheCombo
|= BFCacheStatus::HAS_ACTIVE_SPEECH_SYNTHESIS
;
11424 if (globalWindow
->HasUsedVR()) {
11425 MOZ_LOG(gPageCacheLog
, mozilla::LogLevel::Verbose
,
11426 ("Save of %s blocked due to having used VR", uri
.get()));
11427 aBFCacheCombo
|= BFCacheStatus::HAS_USED_VR
;
11431 if (win
->HasActiveLocks()) {
11433 gPageCacheLog
, mozilla::LogLevel::Verbose
,
11434 ("Save of %s blocked due to having active lock requests", uri
.get()));
11435 aBFCacheCombo
|= BFCacheStatus::ACTIVE_LOCK
;
11439 if (win
->HasActiveWebTransports()) {
11440 MOZ_LOG(gPageCacheLog
, mozilla::LogLevel::Verbose
,
11441 ("Save of %s blocked due to WebTransport", uri
.get()));
11442 aBFCacheCombo
|= BFCacheStatus::ACTIVE_WEBTRANSPORT
;
11450 void Document::Destroy() {
11451 // The ContentViewer wants to release the document now. So, tell our content
11452 // to drop any references to the document so that it can be destroyed.
11453 if (mIsGoingAway
) {
11457 ReportDocumentUseCounters();
11458 SetDevToolsWatchingDOMMutations(false);
11460 mIsGoingAway
= true;
11462 ScriptLoader()->Destroy();
11463 SetScriptGlobalObject(nullptr);
11464 RemovedFromDocShell();
11466 bool oldVal
= mInUnlinkOrDeletion
;
11467 mInUnlinkOrDeletion
= true;
11470 uint32_t oldChildCount
= GetChildCount();
11473 for (nsIContent
* child
= GetFirstChild(); child
;
11474 child
= child
->GetNextSibling()) {
11475 child
->DestroyContent();
11476 MOZ_ASSERT(child
->GetParentNode() == this);
11478 MOZ_ASSERT(oldChildCount
== GetChildCount());
11479 MOZ_ASSERT(!mSubDocuments
|| mSubDocuments
->EntryCount() == 0);
11481 mInUnlinkOrDeletion
= oldVal
;
11483 mLayoutHistoryState
= nullptr;
11485 if (mOriginalDocument
) {
11486 mOriginalDocument
->mLatestStaticClone
= nullptr;
11489 if (IsStaticDocument()) {
11490 RemoveProperty(nsGkAtoms::printisfocuseddoc
);
11491 RemoveProperty(nsGkAtoms::printselectionranges
);
11494 // Shut down our external resource map. We might not need this for
11495 // leak-fixing if we fix nsDocumentViewer to do cycle-collection, but
11496 // tearing down all those frame trees right now is the right thing to do.
11497 mExternalResourceMap
.Shutdown();
11499 // Manually break cycles via promise's global object pointer.
11500 mReadyForIdle
= nullptr;
11501 mOrientationPendingPromise
= nullptr;
11503 // To break cycles.
11504 mPreloadService
.ClearAllPreloads();
11506 if (mDocumentL10n
) {
11507 mDocumentL10n
->Destroy();
11511 void Document::RemovedFromDocShell() {
11512 mEditingState
= EditingState::eOff
;
11514 if (mRemovedFromDocShell
) return;
11516 mRemovedFromDocShell
= true;
11517 NotifyActivityChanged();
11519 for (nsIContent
* child
= GetFirstChild(); child
;
11520 child
= child
->GetNextSibling()) {
11521 child
->SaveSubtreeState();
11524 nsIDocShell
* docShell
= GetDocShell();
11526 docShell
->SynchronizeLayoutHistoryState();
11530 already_AddRefed
<nsILayoutHistoryState
> Document::GetLayoutHistoryState()
11532 nsCOMPtr
<nsILayoutHistoryState
> state
;
11533 if (!mScriptGlobalObject
) {
11534 state
= mLayoutHistoryState
;
11536 nsCOMPtr
<nsIDocShell
> docShell(mDocumentContainer
);
11538 docShell
->GetLayoutHistoryState(getter_AddRefs(state
));
11542 return state
.forget();
11545 void Document::EnsureOnloadBlocker() {
11546 // If mScriptGlobalObject is null, we shouldn't be messing with the loadgroup
11547 // -- it's not ours.
11548 if (mOnloadBlockCount
!= 0 && mScriptGlobalObject
) {
11549 nsCOMPtr
<nsILoadGroup
> loadGroup
= GetDocumentLoadGroup();
11551 // Check first to see if mOnloadBlocker is in the loadgroup.
11552 nsCOMPtr
<nsISimpleEnumerator
> requests
;
11553 loadGroup
->GetRequests(getter_AddRefs(requests
));
11555 bool hasMore
= false;
11556 while (NS_SUCCEEDED(requests
->HasMoreElements(&hasMore
)) && hasMore
) {
11557 nsCOMPtr
<nsISupports
> elem
;
11558 requests
->GetNext(getter_AddRefs(elem
));
11559 nsCOMPtr
<nsIRequest
> request
= do_QueryInterface(elem
);
11560 if (request
&& request
== mOnloadBlocker
) {
11565 // Not in the loadgroup, so add it.
11566 loadGroup
->AddRequest(mOnloadBlocker
, nullptr);
11571 void Document::BlockOnload() {
11572 if (mDisplayDocument
) {
11573 mDisplayDocument
->BlockOnload();
11577 // If mScriptGlobalObject is null, we shouldn't be messing with the loadgroup
11578 // -- it's not ours.
11579 if (mOnloadBlockCount
== 0 && mScriptGlobalObject
) {
11580 if (nsCOMPtr
<nsILoadGroup
> loadGroup
= GetDocumentLoadGroup()) {
11581 loadGroup
->AddRequest(mOnloadBlocker
, nullptr);
11584 ++mOnloadBlockCount
;
11587 void Document::UnblockOnload(bool aFireSync
) {
11588 if (mDisplayDocument
) {
11589 mDisplayDocument
->UnblockOnload(aFireSync
);
11593 --mOnloadBlockCount
;
11595 if (mOnloadBlockCount
== 0) {
11596 if (mScriptGlobalObject
) {
11597 // Only manipulate the loadgroup in this case, because if
11598 // mScriptGlobalObject is null, it's not ours.
11600 // Increment mOnloadBlockCount, since DoUnblockOnload will decrement it
11601 ++mOnloadBlockCount
;
11604 PostUnblockOnloadEvent();
11606 } else if (mIsBeingUsedAsImage
) {
11607 // To correctly unblock onload for a document that contains an SVG
11608 // image, we need to know when all of the SVG document's resources are
11609 // done loading, in a way comparable to |window.onload|. We fire this
11610 // event to indicate that the SVG should be considered fully loaded.
11611 // Because scripting is disabled on SVG-as-image documents, this event
11612 // is not accessible to content authors. (See bug 837315.)
11613 RefPtr
<AsyncEventDispatcher
> asyncDispatcher
=
11614 new AsyncEventDispatcher(this, u
"MozSVGAsImageDocumentLoad"_ns
,
11615 CanBubble::eNo
, ChromeOnlyDispatch::eNo
);
11616 asyncDispatcher
->PostDOMEvent();
11621 class nsUnblockOnloadEvent
: public Runnable
{
11623 explicit nsUnblockOnloadEvent(Document
* aDoc
)
11624 : mozilla::Runnable("nsUnblockOnloadEvent"), mDoc(aDoc
) {}
11625 NS_IMETHOD
Run() override
{
11626 mDoc
->DoUnblockOnload();
11631 RefPtr
<Document
> mDoc
;
11634 void Document::PostUnblockOnloadEvent() {
11635 MOZ_RELEASE_ASSERT(NS_IsMainThread());
11636 nsCOMPtr
<nsIRunnable
> evt
= new nsUnblockOnloadEvent(this);
11637 nsresult rv
= Dispatch(TaskCategory::Other
, evt
.forget());
11638 if (NS_SUCCEEDED(rv
)) {
11639 // Stabilize block count so we don't post more events while this one is up
11640 ++mOnloadBlockCount
;
11642 NS_WARNING("failed to dispatch nsUnblockOnloadEvent");
11646 void Document::DoUnblockOnload() {
11647 MOZ_ASSERT(!mDisplayDocument
, "Shouldn't get here for resource document");
11648 MOZ_ASSERT(mOnloadBlockCount
!= 0,
11649 "Shouldn't have a count of zero here, since we stabilized in "
11650 "PostUnblockOnloadEvent");
11652 --mOnloadBlockCount
;
11654 if (mOnloadBlockCount
!= 0) {
11655 // We blocked again after the last unblock. Nothing to do here. We'll
11656 // post a new event when we unblock again.
11660 // If mScriptGlobalObject is null, we shouldn't be messing with the loadgroup
11661 // -- it's not ours.
11662 if (mScriptGlobalObject
) {
11663 if (nsCOMPtr
<nsILoadGroup
> loadGroup
= GetDocumentLoadGroup()) {
11664 loadGroup
->RemoveRequest(mOnloadBlocker
, nullptr, NS_OK
);
11669 nsIContent
* Document::GetContentInThisDocument(nsIFrame
* aFrame
) const {
11670 for (nsIFrame
* f
= aFrame
; f
;
11671 f
= nsLayoutUtils::GetParentOrPlaceholderForCrossDoc(f
)) {
11672 nsIContent
* content
= f
->GetContent();
11677 if (content
->OwnerDoc() == this) {
11680 // We must be in a subdocument so jump directly to the root frame.
11681 // GetParentOrPlaceholderForCrossDoc gets called immediately to jump up to
11682 // the containing document.
11683 f
= f
->PresContext()->GetPresShell()->GetRootFrame();
11689 void Document::DispatchPageTransition(EventTarget
* aDispatchTarget
,
11690 const nsAString
& aType
, bool aInFrameSwap
,
11691 bool aPersisted
, bool aOnlySystemGroup
) {
11692 if (!aDispatchTarget
) {
11696 PageTransitionEventInit init
;
11697 init
.mBubbles
= true;
11698 init
.mCancelable
= true;
11699 init
.mPersisted
= aPersisted
;
11700 init
.mInFrameSwap
= aInFrameSwap
;
11702 RefPtr
<PageTransitionEvent
> event
=
11703 PageTransitionEvent::Constructor(this, aType
, init
);
11705 event
->SetTrusted(true);
11706 event
->SetTarget(this);
11707 if (aOnlySystemGroup
) {
11708 event
->WidgetEventPtr()->mFlags
.mOnlySystemGroupDispatchInContent
= true;
11710 EventDispatcher::DispatchDOMEvent(aDispatchTarget
, nullptr, event
, nullptr,
11714 void Document::OnPageShow(bool aPersisted
, EventTarget
* aDispatchStartTarget
,
11715 bool aOnlySystemGroup
) {
11716 if (MOZ_LOG_TEST(gSHIPBFCacheLog
, LogLevel::Debug
)) {
11718 if (GetDocumentURI()) {
11719 uri
= GetDocumentURI()->GetSpecOrDefault();
11721 MOZ_LOG(gSHIPBFCacheLog
, LogLevel::Debug
,
11722 ("Document::OnPageShow [%s] persisted=%i", uri
.get(), aPersisted
));
11725 const bool inFrameLoaderSwap
= !!aDispatchStartTarget
;
11726 MOZ_DIAGNOSTIC_ASSERT(
11727 inFrameLoaderSwap
==
11728 (mDocumentContainer
&& mDocumentContainer
->InFrameSwap()));
11730 Element
* root
= GetRootElement();
11731 if (aPersisted
&& root
) {
11732 // Send out notifications that our <link> elements are attached.
11733 RefPtr
<nsContentList
> links
=
11734 NS_GetContentList(root
, kNameSpaceID_XHTML
, u
"link"_ns
);
11736 uint32_t linkCount
= links
->Length(true);
11737 for (uint32_t i
= 0; i
< linkCount
; ++i
) {
11738 static_cast<HTMLLinkElement
*>(links
->Item(i
, false))->LinkAdded();
11743 if (!inFrameLoaderSwap
) {
11745 ImageTracker()->SetAnimatingState(true);
11748 // Set mIsShowing before firing events, in case those event handlers
11753 UpdateVisibilityState();
11756 NotifyActivityChanged();
11758 auto notifyExternal
= [aPersisted
](Document
& aExternalResource
) {
11759 aExternalResource
.OnPageShow(aPersisted
, nullptr);
11760 return CallState::Continue
;
11762 EnumerateExternalResources(notifyExternal
);
11764 if (mAnimationController
) {
11765 mAnimationController
->OnPageShow();
11768 if (!mIsBeingUsedAsImage
) {
11769 // Dispatch observer notification to notify observers page is shown.
11770 nsCOMPtr
<nsIObserverService
> os
= mozilla::services::GetObserverService();
11772 nsIPrincipal
* principal
= NodePrincipal();
11773 os
->NotifyObservers(ToSupports(this),
11774 principal
->IsSystemPrincipal() ? "chrome-page-shown"
11775 : "content-page-shown",
11779 nsCOMPtr
<EventTarget
> target
= aDispatchStartTarget
;
11781 target
= do_QueryInterface(GetWindow());
11783 DispatchPageTransition(target
, u
"pageshow"_ns
, inFrameLoaderSwap
,
11784 aPersisted
, aOnlySystemGroup
);
11788 static void DispatchFullscreenChange(Document
& aDocument
, nsINode
* aTarget
) {
11789 if (nsPresContext
* presContext
= aDocument
.GetPresContext()) {
11790 auto pendingEvent
= MakeUnique
<PendingFullscreenEvent
>(
11791 FullscreenEventType::Change
, &aDocument
, aTarget
);
11792 presContext
->RefreshDriver()->ScheduleFullscreenEvent(
11793 std::move(pendingEvent
));
11797 void Document::OnPageHide(bool aPersisted
, EventTarget
* aDispatchStartTarget
,
11798 bool aOnlySystemGroup
) {
11799 if (MOZ_LOG_TEST(gSHIPBFCacheLog
, LogLevel::Debug
)) {
11801 if (GetDocumentURI()) {
11802 uri
= GetDocumentURI()->GetSpecOrDefault();
11804 MOZ_LOG(gSHIPBFCacheLog
, LogLevel::Debug
,
11805 ("Document::OnPageHide %s persisted=%i", uri
.get(), aPersisted
));
11808 const bool inFrameLoaderSwap
= !!aDispatchStartTarget
;
11809 MOZ_DIAGNOSTIC_ASSERT(
11810 inFrameLoaderSwap
==
11811 (mDocumentContainer
&& mDocumentContainer
->InFrameSwap()));
11813 // Send out notifications that our <link> elements are detached,
11814 // but only if this is not a full unload.
11815 Element
* root
= GetRootElement();
11816 if (aPersisted
&& root
) {
11817 RefPtr
<nsContentList
> links
=
11818 NS_GetContentList(root
, kNameSpaceID_XHTML
, u
"link"_ns
);
11820 uint32_t linkCount
= links
->Length(true);
11821 for (uint32_t i
= 0; i
< linkCount
; ++i
) {
11822 static_cast<HTMLLinkElement
*>(links
->Item(i
, false))->LinkRemoved();
11826 if (mAnimationController
) {
11827 mAnimationController
->OnPageHide();
11830 if (!inFrameLoaderSwap
) {
11832 // We do not stop the animations (bug 1024343) when the page is refreshing
11833 // while being dragged out.
11834 ImageTracker()->SetAnimatingState(false);
11837 // Set mIsShowing before firing events, in case those event handlers
11839 mIsShowing
= false;
11845 if (!mIsBeingUsedAsImage
) {
11846 // Dispatch observer notification to notify observers page is hidden.
11847 nsCOMPtr
<nsIObserverService
> os
= mozilla::services::GetObserverService();
11849 nsIPrincipal
* principal
= NodePrincipal();
11850 os
->NotifyObservers(ToSupports(this),
11851 principal
->IsSystemPrincipal()
11852 ? "chrome-page-hidden"
11853 : "content-page-hidden",
11857 // Now send out a PageHide event.
11858 nsCOMPtr
<EventTarget
> target
= aDispatchStartTarget
;
11860 target
= do_QueryInterface(GetWindow());
11863 PageUnloadingEventTimeStamp
timeStamp(this);
11864 DispatchPageTransition(target
, u
"pagehide"_ns
, inFrameLoaderSwap
,
11865 aPersisted
, aOnlySystemGroup
);
11869 if (!inFrameLoaderSwap
) {
11870 UpdateVisibilityState();
11873 auto notifyExternal
= [aPersisted
](Document
& aExternalResource
) {
11874 aExternalResource
.OnPageHide(aPersisted
, nullptr);
11875 return CallState::Continue
;
11877 EnumerateExternalResources(notifyExternal
);
11878 NotifyActivityChanged();
11880 ClearPendingFullscreenRequests(this);
11881 if (Fullscreen()) {
11882 // If this document was fullscreen, we should exit fullscreen in this
11883 // doctree branch. This ensures that if the user navigates while in
11884 // fullscreen mode we don't leave its still visible ancestor documents
11885 // in fullscreen mode. So exit fullscreen in the document's fullscreen
11886 // root document, as this will exit fullscreen in all the root's
11887 // descendant documents. Note that documents are removed from the
11888 // doctree by the time OnPageHide() is called, so we must store a
11889 // reference to the root (in Document::mFullscreenRoot) since we can't
11890 // just traverse the doctree to get the root.
11891 Document::ExitFullscreenInDocTree(this);
11893 // Since the document is removed from the doctree before OnPageHide() is
11894 // called, ExitFullscreen() can't traverse from the root down to *this*
11895 // document, so we must manually call CleanupFullscreenState() below too.
11896 // Note that CleanupFullscreenState() clears Document::mFullscreenRoot,
11897 // so we *must* call it after ExitFullscreen(), not before.
11898 // OnPageHide() is called in every hidden (i.e. descendant) document,
11899 // so calling CleanupFullscreenState() here will ensure all hidden
11900 // documents have their fullscreen state reset.
11901 CleanupFullscreenState();
11903 // The fullscreenchange event is to be queued in the refresh driver,
11904 // however a hidden page wouldn't trigger that again, so it makes no
11905 // sense to dispatch such event here.
11909 void Document::WillDispatchMutationEvent(nsINode
* aTarget
) {
11911 mSubtreeModifiedDepth
!= 0 || mSubtreeModifiedTargets
.Count() == 0,
11912 "mSubtreeModifiedTargets not cleared after dispatching?");
11913 ++mSubtreeModifiedDepth
;
11915 // MayDispatchMutationEvent is often called just before this method,
11916 // so it has already appended the node to mSubtreeModifiedTargets.
11917 int32_t count
= mSubtreeModifiedTargets
.Count();
11918 if (!count
|| mSubtreeModifiedTargets
[count
- 1] != aTarget
) {
11919 mSubtreeModifiedTargets
.AppendObject(aTarget
);
11924 void Document::MutationEventDispatched(nsINode
* aTarget
) {
11925 if (--mSubtreeModifiedDepth
) {
11929 int32_t count
= mSubtreeModifiedTargets
.Count();
11934 nsPIDOMWindowInner
* window
= GetInnerWindow();
11936 !window
->HasMutationListeners(NS_EVENT_BITS_MUTATION_SUBTREEMODIFIED
)) {
11937 mSubtreeModifiedTargets
.Clear();
11941 nsCOMArray
<nsINode
> realTargets
;
11942 for (nsINode
* possibleTarget
: mSubtreeModifiedTargets
) {
11943 if (possibleTarget
->ChromeOnlyAccess()) {
11947 nsINode
* commonAncestor
= nullptr;
11948 int32_t realTargetCount
= realTargets
.Count();
11949 for (int32_t j
= 0; j
< realTargetCount
; ++j
) {
11950 commonAncestor
= nsContentUtils::GetClosestCommonInclusiveAncestor(
11951 possibleTarget
, realTargets
[j
]);
11952 if (commonAncestor
) {
11953 realTargets
.ReplaceObjectAt(commonAncestor
, j
);
11957 if (!commonAncestor
) {
11958 realTargets
.AppendObject(possibleTarget
);
11962 mSubtreeModifiedTargets
.Clear();
11964 for (const nsCOMPtr
<nsINode
>& target
: realTargets
) {
11965 InternalMutationEvent
mutation(true, eLegacySubtreeModified
);
11966 // MOZ_KnownLive due to bug 1620312
11967 AsyncEventDispatcher::RunDOMEventWhenSafe(MOZ_KnownLive(*target
), mutation
);
11971 void Document::DestroyElementMaps() {
11973 mStyledLinksCleared
= true;
11975 mStyledLinks
.Clear();
11976 // Notify ID change listeners before clearing the identifier map.
11977 for (auto iter
= mIdentifierMap
.Iter(); !iter
.Done(); iter
.Next()) {
11978 iter
.Get()->ClearAndNotify();
11980 mIdentifierMap
.Clear();
11981 mComposedShadowRoots
.Clear();
11982 mResponsiveContent
.Clear();
11983 IncrementExpandoGeneration(*this);
11986 void Document::RefreshLinkHrefs() {
11987 // Get a list of all links we know about. We will reset them, which will
11988 // remove them from the document, so we need a copy of what is in the
11990 const LinkArray linksToNotify
= ToArray(mStyledLinks
);
11992 // Reset all of our styled links.
11993 nsAutoScriptBlocker scriptBlocker
;
11994 for (LinkArray::size_type i
= 0; i
< linksToNotify
.Length(); i
++) {
11995 linksToNotify
[i
]->ResetLinkState(true, linksToNotify
[i
]->ElementHasHref());
11999 nsresult
Document::CloneDocHelper(Document
* clone
) const {
12000 clone
->mIsStaticDocument
= mCreatingStaticClone
;
12003 nsresult rv
= clone
->Init();
12004 NS_ENSURE_SUCCESS(rv
, rv
);
12006 if (mCreatingStaticClone
) {
12007 if (mOriginalDocument
) {
12008 clone
->mOriginalDocument
= mOriginalDocument
;
12010 clone
->mOriginalDocument
= const_cast<Document
*>(this);
12012 clone
->mOriginalDocument
->mLatestStaticClone
= clone
;
12013 clone
->mOriginalDocument
->mStaticCloneCount
++;
12015 nsCOMPtr
<nsILoadGroup
> loadGroup
;
12017 // |mDocumentContainer| is the container of the document that is being
12018 // created and not the original container. See CreateStaticClone function().
12019 nsCOMPtr
<nsIDocumentLoader
> docLoader(mDocumentContainer
);
12021 docLoader
->GetLoadGroup(getter_AddRefs(loadGroup
));
12023 nsCOMPtr
<nsIChannel
> channel
= GetChannel();
12024 nsCOMPtr
<nsIURI
> uri
;
12026 NS_GetFinalChannelURI(channel
, getter_AddRefs(uri
));
12028 uri
= Document::GetDocumentURI();
12030 clone
->mChannel
= channel
;
12031 clone
->mShouldResistFingerprinting
= mShouldResistFingerprinting
;
12033 clone
->ResetToURI(uri
, loadGroup
, NodePrincipal(), mPartitionedPrincipal
);
12036 clone
->mIsSrcdocDocument
= mIsSrcdocDocument
;
12037 clone
->SetContainer(mDocumentContainer
);
12039 // Setup the navigation time. This will be needed by any animations in the
12040 // document, even if they are only paused.
12041 MOZ_ASSERT(!clone
->GetNavigationTiming(),
12042 "Navigation time was already set?");
12044 RefPtr
<nsDOMNavigationTiming
> timing
=
12045 mTiming
->CloneNavigationTime(nsDocShell::Cast(clone
->GetDocShell()));
12046 clone
->SetNavigationTiming(timing
);
12048 clone
->SetCsp(mCSP
);
12051 // Now ensure that our clone has the same URI, base URI, and principal as us.
12052 // We do this after the mCreatingStaticClone block above, because that block
12053 // can set the base URI to an incorrect value in cases when base URI
12054 // information came from the channel. So we override explicitly, and do it
12055 // for all these properties, in case ResetToURI messes with any of the rest of
12057 clone
->SetDocumentURI(Document::GetDocumentURI());
12058 clone
->SetChromeXHRDocURI(mChromeXHRDocURI
);
12059 clone
->SetPrincipals(NodePrincipal(), mPartitionedPrincipal
);
12060 clone
->mActiveStoragePrincipal
= mActiveStoragePrincipal
;
12061 clone
->mActiveCookiePrincipal
= mActiveCookiePrincipal
;
12062 // NOTE(emilio): Intentionally setting this to the GetDocBaseURI rather than
12063 // just mDocumentBaseURI, so that srcdoc iframes get the right base URI even
12064 // when printed standalone via window.print() (where there won't be a parent
12065 // document to grab the URI from).
12066 clone
->mDocumentBaseURI
= GetDocBaseURI();
12067 clone
->SetChromeXHRDocBaseURI(mChromeXHRDocBaseURI
);
12068 clone
->mReferrerInfo
=
12069 static_cast<dom::ReferrerInfo
*>(mReferrerInfo
.get())->Clone();
12070 clone
->mPreloadReferrerInfo
= clone
->mReferrerInfo
;
12072 bool hasHadScriptObject
= true;
12073 nsIScriptGlobalObject
* scriptObject
=
12074 GetScriptHandlingObject(hasHadScriptObject
);
12075 NS_ENSURE_STATE(scriptObject
|| !hasHadScriptObject
);
12076 if (mCreatingStaticClone
) {
12077 // If we're doing a static clone (print, print preview), then we're going to
12078 // be setting a scope object after the clone. It's better to set it only
12079 // once, so we don't do that here. However, we do want to act as if there is
12080 // a script handling object. So we set mHasHadScriptHandlingObject.
12081 clone
->mHasHadScriptHandlingObject
= true;
12082 } else if (scriptObject
) {
12083 clone
->SetScriptHandlingObject(scriptObject
);
12085 clone
->SetScopeObject(GetScopeObject());
12087 // Make the clone a data document
12088 clone
->SetLoadedAsData(
12090 /* aConsiderForMemoryReporting */ !mCreatingStaticClone
);
12094 // State from Document
12095 clone
->mCharacterSet
= mCharacterSet
;
12096 clone
->mCharacterSetSource
= mCharacterSetSource
;
12097 clone
->SetCompatibilityMode(mCompatMode
);
12098 clone
->mBidiOptions
= mBidiOptions
;
12099 clone
->mContentLanguage
= mContentLanguage
;
12100 clone
->SetContentType(GetContentTypeInternal());
12101 clone
->mSecurityInfo
= mSecurityInfo
;
12103 // State from Document
12104 clone
->mType
= mType
;
12105 clone
->mXMLDeclarationBits
= mXMLDeclarationBits
;
12106 clone
->mBaseTarget
= mBaseTarget
;
12111 void Document::NotifyLoading(bool aNewParentIsLoading
,
12112 const ReadyState
& aCurrentState
,
12113 ReadyState aNewState
) {
12114 // Mirror the top-level loading state down to all subdocuments
12115 bool was_loading
= mAncestorIsLoading
||
12116 aCurrentState
== READYSTATE_LOADING
||
12117 aCurrentState
== READYSTATE_INTERACTIVE
;
12118 bool is_loading
= aNewParentIsLoading
|| aNewState
== READYSTATE_LOADING
||
12119 aNewState
== READYSTATE_INTERACTIVE
; // new value for state
12120 bool set_load_state
= was_loading
!= is_loading
;
12123 gTimeoutDeferralLog
, mozilla::LogLevel::Debug
,
12124 ("NotifyLoading for doc %p: currentAncestor: %d, newParent: %d, "
12125 "currentState %d newState: %d, was_loading: %d, is_loading: %d, "
12126 "set_load_state: %d",
12127 (void*)this, mAncestorIsLoading
, aNewParentIsLoading
, (int)aCurrentState
,
12128 (int)aNewState
, was_loading
, is_loading
, set_load_state
));
12130 mAncestorIsLoading
= aNewParentIsLoading
;
12131 if (set_load_state
&& StaticPrefs::dom_timeout_defer_during_load()) {
12132 // Tell our innerwindow (and thus TimeoutManager)
12133 nsPIDOMWindowInner
* inner
= GetInnerWindow();
12135 inner
->SetActiveLoadingState(is_loading
);
12137 BrowsingContext
* context
= GetBrowsingContext();
12139 // Don't use PreOrderWalk to mirror this down; go down one level as a
12140 // time so we can set mAncestorIsLoading and take into account the
12141 // readystates of the subdocument. In the child process it will call
12142 // NotifyLoading() to notify the innerwindow/TimeoutManager, and then
12143 // iterate it's children
12144 for (auto& child
: context
->Children()) {
12145 MOZ_LOG(gTimeoutDeferralLog
, mozilla::LogLevel::Debug
,
12146 ("bc: %p SetAncestorLoading(%d)", (void*)child
, is_loading
));
12147 // Setting ancestor loading on a discarded browsing context has no
12149 Unused
<< child
->SetAncestorLoading(is_loading
);
12155 void Document::SetReadyStateInternal(ReadyState aReadyState
,
12156 bool aUpdateTimingInformation
) {
12157 if (aReadyState
== READYSTATE_UNINITIALIZED
) {
12158 // Transition back to uninitialized happens only to keep assertions happy
12159 // right before readyState transitions to something else. Make this
12160 // transition undetectable by Web content.
12161 mReadyState
= aReadyState
;
12165 if (IsTopLevelContentDocument()) {
12166 if (aReadyState
== READYSTATE_LOADING
) {
12167 AddToplevelLoadingDocument(this);
12168 } else if (aReadyState
== READYSTATE_COMPLETE
) {
12169 RemoveToplevelLoadingDocument(this);
12173 if (aUpdateTimingInformation
&& READYSTATE_LOADING
== aReadyState
) {
12174 mLoadingTimeStamp
= TimeStamp::Now();
12176 NotifyLoading(mAncestorIsLoading
, mReadyState
, aReadyState
);
12177 mReadyState
= aReadyState
;
12178 if (aUpdateTimingInformation
&& mTiming
) {
12179 switch (aReadyState
) {
12180 case READYSTATE_LOADING
:
12181 mTiming
->NotifyDOMLoading(GetDocumentURI());
12183 case READYSTATE_INTERACTIVE
:
12184 mTiming
->NotifyDOMInteractive(GetDocumentURI());
12186 case READYSTATE_COMPLETE
:
12187 mTiming
->NotifyDOMComplete(GetDocumentURI());
12190 MOZ_ASSERT_UNREACHABLE("Unexpected ReadyState value");
12194 // At the time of loading start, we don't have timing object, record time.
12196 if (READYSTATE_INTERACTIVE
== aReadyState
&&
12197 NodePrincipal()->IsSystemPrincipal()) {
12198 if (!mXULPersist
) {
12199 mXULPersist
= new XULPersist(this);
12200 mXULPersist
->Init();
12202 if (!mChromeObserver
) {
12203 mChromeObserver
= new ChromeObserver(this);
12204 mChromeObserver
->Init();
12208 if (aUpdateTimingInformation
) {
12209 RecordNavigationTiming(aReadyState
);
12212 AsyncEventDispatcher::RunDOMEventWhenSafe(
12213 *this, u
"readystatechange"_ns
, CanBubble::eNo
, ChromeOnlyDispatch::eNo
);
12216 void Document::GetReadyState(nsAString
& aReadyState
) const {
12217 switch (mReadyState
) {
12218 case READYSTATE_LOADING
:
12219 aReadyState
.AssignLiteral(u
"loading");
12221 case READYSTATE_INTERACTIVE
:
12222 aReadyState
.AssignLiteral(u
"interactive");
12224 case READYSTATE_COMPLETE
:
12225 aReadyState
.AssignLiteral(u
"complete");
12228 aReadyState
.AssignLiteral(u
"uninitialized");
12232 void Document::SuppressEventHandling(uint32_t aIncrease
) {
12233 mEventsSuppressed
+= aIncrease
;
12234 if (mEventsSuppressed
== aIncrease
) {
12235 if (WindowGlobalChild
* wgc
= GetWindowGlobalChild()) {
12236 wgc
->BlockBFCacheFor(BFCacheStatus::EVENT_HANDLING_SUPPRESSED
);
12239 UpdateFrameRequestCallbackSchedulingState();
12240 for (uint32_t i
= 0; i
< aIncrease
; ++i
) {
12241 ScriptLoader()->AddExecuteBlocker();
12244 auto suppressInSubDoc
= [aIncrease
](Document
& aSubDoc
) {
12245 aSubDoc
.SuppressEventHandling(aIncrease
);
12246 return CallState::Continue
;
12249 EnumerateSubDocuments(suppressInSubDoc
);
12252 void Document::NotifyAbortedLoad() {
12253 // If we still have outstanding work blocking DOMContentLoaded,
12254 // then don't try to change the readystate now, but wait until
12255 // they finish and then do so.
12256 if (mBlockDOMContentLoaded
> 0 && !mDidFireDOMContentLoaded
) {
12257 mSetCompleteAfterDOMContentLoaded
= true;
12261 // Otherwise we're fully done at this point, so set the
12262 // readystate to complete.
12263 if (GetReadyStateEnum() == Document::READYSTATE_INTERACTIVE
) {
12264 SetReadyStateInternal(Document::READYSTATE_COMPLETE
);
12268 MOZ_CAN_RUN_SCRIPT
static void FireOrClearDelayedEvents(
12269 nsTArray
<nsCOMPtr
<Document
>>&& aDocuments
, bool aFireEvents
) {
12270 RefPtr
<nsFocusManager
> fm
= nsFocusManager::GetFocusManager();
12271 if (MOZ_UNLIKELY(!fm
)) {
12275 nsTArray
<nsCOMPtr
<Document
>> documents
= std::move(aDocuments
);
12276 for (uint32_t i
= 0; i
< documents
.Length(); ++i
) {
12277 nsCOMPtr
<Document
> document
= std::move(documents
[i
]);
12278 // NB: Don't bother trying to fire delayed events on documents that were
12279 // closed before this event ran.
12280 if (!document
->EventHandlingSuppressed()) {
12281 fm
->FireDelayedEvents(document
);
12282 RefPtr
<PresShell
> presShell
= document
->GetPresShell();
12284 // Only fire events for active documents.
12285 bool fire
= aFireEvents
&& document
->GetInnerWindow() &&
12286 document
->GetInnerWindow()->IsCurrentInnerWindow();
12287 presShell
->FireOrClearDelayedEvents(fire
);
12289 document
->FireOrClearPostMessageEvents(aFireEvents
);
12294 void Document::PreloadPictureClosed() {
12295 MOZ_ASSERT(mPreloadPictureDepth
> 0);
12296 mPreloadPictureDepth
--;
12297 if (mPreloadPictureDepth
== 0) {
12298 mPreloadPictureFoundSource
.SetIsVoid(true);
12302 void Document::PreloadPictureImageSource(const nsAString
& aSrcsetAttr
,
12303 const nsAString
& aSizesAttr
,
12304 const nsAString
& aTypeAttr
,
12305 const nsAString
& aMediaAttr
) {
12306 // Nested pictures are not valid syntax, so while we'll eventually load them,
12307 // it's not worth tracking sources mixed between nesting levels to preload
12308 // them effectively.
12309 if (mPreloadPictureDepth
== 1 && mPreloadPictureFoundSource
.IsVoid()) {
12310 // <picture> selects the first matching source, so if this returns a URI we
12311 // needn't consider new sources until a new <picture> is encountered.
12312 bool found
= HTMLImageElement::SelectSourceForTagWithAttrs(
12313 this, true, VoidString(), aSrcsetAttr
, aSizesAttr
, aTypeAttr
,
12314 aMediaAttr
, mPreloadPictureFoundSource
);
12315 if (found
&& mPreloadPictureFoundSource
.IsVoid()) {
12316 // Found an empty source, which counts
12317 mPreloadPictureFoundSource
.SetIsVoid(false);
12322 already_AddRefed
<nsIURI
> Document::ResolvePreloadImage(
12323 nsIURI
* aBaseURI
, const nsAString
& aSrcAttr
, const nsAString
& aSrcsetAttr
,
12324 const nsAString
& aSizesAttr
, bool* aIsImgSet
) {
12325 nsString sourceURL
;
12327 if (mPreloadPictureDepth
== 1 && !mPreloadPictureFoundSource
.IsVoid()) {
12328 // We're in a <picture> element and found a URI from a source previous to
12329 // this image, use it.
12330 sourceURL
= mPreloadPictureFoundSource
;
12333 // Otherwise try to use this <img> as a source
12334 HTMLImageElement::SelectSourceForTagWithAttrs(
12335 this, false, aSrcAttr
, aSrcsetAttr
, aSizesAttr
, VoidString(),
12336 VoidString(), sourceURL
);
12337 isImgSet
= !aSrcsetAttr
.IsEmpty();
12340 // Empty sources are not loaded by <img> (i.e. not resolved to the baseURI)
12341 if (sourceURL
.IsEmpty()) {
12345 // Construct into URI using passed baseURI (the parser may know of base URI
12346 // changes that have not reached us)
12348 nsCOMPtr
<nsIURI
> uri
;
12349 rv
= nsContentUtils::NewURIWithDocumentCharset(getter_AddRefs(uri
), sourceURL
,
12351 if (NS_FAILED(rv
)) {
12355 *aIsImgSet
= isImgSet
;
12357 // We don't clear mPreloadPictureFoundSource because subsequent <img> tags in
12358 // this this <picture> share the same <sources> (though this is not valid per
12360 return uri
.forget();
12363 void Document::PreLoadImage(nsIURI
* aUri
, const nsAString
& aCrossOriginAttr
,
12364 ReferrerPolicyEnum aReferrerPolicy
, bool aIsImgSet
,
12365 bool aLinkPreload
, uint64_t aEarlyHintPreloaderId
) {
12366 nsLoadFlags loadFlags
= nsIRequest::LOAD_NORMAL
|
12367 nsIRequest::LOAD_RECORD_START_REQUEST_DELAY
|
12368 nsContentUtils::CORSModeToLoadImageFlags(
12369 Element::StringToCORSMode(aCrossOriginAttr
));
12371 nsContentPolicyType policyType
=
12372 aIsImgSet
? nsIContentPolicy::TYPE_IMAGESET
12373 : nsIContentPolicy::TYPE_INTERNAL_IMAGE_PRELOAD
;
12375 nsCOMPtr
<nsIReferrerInfo
> referrerInfo
=
12376 ReferrerInfo::CreateFromDocumentAndPolicyOverride(this, aReferrerPolicy
);
12378 RefPtr
<imgRequestProxy
> request
;
12380 nsLiteralString initiator
= aEarlyHintPreloaderId
12381 ? u
"early-hints"_ns
12382 : (aLinkPreload
? u
"link"_ns
: u
"img"_ns
);
12384 nsresult rv
= nsContentUtils::LoadImage(
12385 aUri
, static_cast<nsINode
*>(this), this, NodePrincipal(), 0, referrerInfo
,
12386 nullptr /* no observer */, loadFlags
, initiator
, getter_AddRefs(request
),
12387 policyType
, false /* urgent */, aLinkPreload
, aEarlyHintPreloaderId
);
12389 // Pin image-reference to avoid evicting it from the img-cache before
12390 // the "real" load occurs. Unpinned in DispatchContentLoadedEvents and
12392 if (!aLinkPreload
&& NS_SUCCEEDED(rv
)) {
12393 mPreloadingImages
.InsertOrUpdate(aUri
, std::move(request
));
12397 void Document::MaybePreLoadImage(nsIURI
* aUri
,
12398 const nsAString
& aCrossOriginAttr
,
12399 ReferrerPolicyEnum aReferrerPolicy
,
12400 bool aIsImgSet
, bool aLinkPreload
,
12401 const TimeStamp
& aInitTimestamp
) {
12402 const CORSMode corsMode
= dom::Element::StringToCORSMode(aCrossOriginAttr
);
12403 if (aLinkPreload
) {
12404 // Check if the image was already preloaded in this document to avoid
12405 // duplicate preloading.
12406 PreloadHashKey key
=
12407 PreloadHashKey::CreateAsImage(aUri
, NodePrincipal(), corsMode
);
12408 if (!mPreloadService
.PreloadExists(key
)) {
12409 PreLoadImage(aUri
, aCrossOriginAttr
, aReferrerPolicy
, aIsImgSet
,
12415 // Early exit if the img is already present in the img-cache
12416 // which indicates that the "real" load has already started and
12417 // that we shouldn't preload it.
12418 if (nsContentUtils::IsImageAvailable(aUri
, NodePrincipal(), corsMode
, this)) {
12422 #ifdef NIGHTLY_BUILD
12423 Telemetry::Accumulate(
12424 Telemetry::DOCUMENT_PRELOAD_IMAGE_ASYNCOPEN_DELAY
,
12425 static_cast<uint32_t>(
12426 (TimeStamp::Now() - aInitTimestamp
).ToMilliseconds()));
12429 // Image not in cache - trigger preload
12430 PreLoadImage(aUri
, aCrossOriginAttr
, aReferrerPolicy
, aIsImgSet
, aLinkPreload
,
12434 void Document::MaybePreconnect(nsIURI
* aOrigURI
, mozilla::CORSMode aCORSMode
) {
12435 NS_MutateURI
mutator(aOrigURI
);
12436 if (NS_FAILED(mutator
.GetStatus())) {
12440 // The URI created here is used in 2 contexts. One is nsISpeculativeConnect
12441 // which ignores the path and uses only the origin. The other is for the
12442 // document mPreloadedPreconnects de-duplication hash. Anonymous vs
12443 // non-Anonymous preconnects create different connections on the wire and
12444 // therefore should not be considred duplicates of each other and we
12445 // normalize the path before putting it in the hash to accomplish that.
12447 if (aCORSMode
== CORS_ANONYMOUS
) {
12448 mutator
.SetPathQueryRef("/anonymous"_ns
);
12450 mutator
.SetPathQueryRef("/"_ns
);
12453 nsCOMPtr
<nsIURI
> uri
;
12454 nsresult rv
= mutator
.Finalize(uri
);
12455 if (NS_FAILED(rv
)) {
12459 const bool existingEntryFound
=
12460 mPreloadedPreconnects
.WithEntryHandle(uri
, [](auto&& entry
) {
12464 entry
.Insert(true);
12467 if (existingEntryFound
) {
12471 nsCOMPtr
<nsISpeculativeConnect
> speculator(
12472 do_QueryInterface(nsContentUtils::GetIOService()));
12477 OriginAttributes oa
;
12478 StoragePrincipalHelper::GetOriginAttributesForNetworkState(this, oa
);
12479 speculator
->SpeculativeConnectWithOriginAttributesNative(
12480 uri
, std::move(oa
), nullptr, aCORSMode
== CORS_ANONYMOUS
);
12483 void Document::ForgetImagePreload(nsIURI
* aURI
) {
12484 // Checking count is faster than hashing the URI in the common
12485 // case of empty table.
12486 if (mPreloadingImages
.Count() != 0) {
12487 nsCOMPtr
<imgIRequest
> req
;
12488 mPreloadingImages
.Remove(aURI
, getter_AddRefs(req
));
12490 // Make sure to cancel the request so imagelib knows it's gone.
12491 req
->CancelAndForgetObserver(NS_BINDING_ABORTED
);
12496 void Document::UpdateDocumentStates(DocumentState aMaybeChangedStates
,
12498 const DocumentState oldStates
= mDocumentState
;
12499 if (aMaybeChangedStates
.HasAtLeastOneOfStates(
12500 DocumentState::ALL_LOCALEDIR_BITS
)) {
12501 mDocumentState
&= ~DocumentState::ALL_LOCALEDIR_BITS
;
12502 if (IsDocumentRightToLeft()) {
12503 mDocumentState
|= DocumentState::RTL_LOCALE
;
12505 mDocumentState
|= DocumentState::LTR_LOCALE
;
12509 if (aMaybeChangedStates
.HasAtLeastOneOfStates(DocumentState::LWTHEME
)) {
12510 if (ComputeDocumentLWTheme()) {
12511 mDocumentState
|= DocumentState::LWTHEME
;
12513 mDocumentState
&= ~DocumentState::LWTHEME
;
12517 if (aMaybeChangedStates
.HasState(DocumentState::WINDOW_INACTIVE
)) {
12518 if (IsTopLevelWindowInactive()) {
12519 mDocumentState
|= DocumentState::WINDOW_INACTIVE
;
12521 mDocumentState
&= ~DocumentState::WINDOW_INACTIVE
;
12525 const DocumentState changedStates
= oldStates
^ mDocumentState
;
12526 if (aNotify
&& !changedStates
.IsEmpty()) {
12527 if (PresShell
* ps
= GetObservingPresShell()) {
12528 ps
->DocumentStatesChanged(changedStates
);
12536 * Stub for LoadSheet(), since all we want is to get the sheet into
12537 * the CSSLoader's style cache
12539 class StubCSSLoaderObserver final
: public nsICSSLoaderObserver
{
12540 ~StubCSSLoaderObserver() = default;
12544 StyleSheetLoaded(StyleSheet
*, bool, nsresult
) override
{ return NS_OK
; }
12547 NS_IMPL_ISUPPORTS(StubCSSLoaderObserver
, nsICSSLoaderObserver
)
12551 SheetPreloadStatus
Document::PreloadStyle(
12552 nsIURI
* uri
, const Encoding
* aEncoding
, const nsAString
& aCrossOriginAttr
,
12553 const enum ReferrerPolicy aReferrerPolicy
, const nsAString
& aIntegrity
,
12554 css::StylePreloadKind aKind
, uint64_t aEarlyHintPreloaderId
) {
12555 MOZ_ASSERT(aKind
!= css::StylePreloadKind::None
);
12557 // The CSSLoader will retain this object after we return.
12558 nsCOMPtr
<nsICSSLoaderObserver
> obs
= new StubCSSLoaderObserver();
12560 nsCOMPtr
<nsIReferrerInfo
> referrerInfo
=
12561 ReferrerInfo::CreateFromDocumentAndPolicyOverride(this, aReferrerPolicy
);
12563 // Charset names are always ASCII.
12564 auto result
= CSSLoader()->LoadSheet(
12565 uri
, aKind
, aEncoding
, referrerInfo
, obs
, aEarlyHintPreloaderId
,
12566 Element::StringToCORSMode(aCrossOriginAttr
), aIntegrity
);
12567 if (result
.isErr()) {
12568 return SheetPreloadStatus::Errored
;
12570 RefPtr
<StyleSheet
> sheet
= result
.unwrap();
12571 if (sheet
->IsComplete()) {
12572 return SheetPreloadStatus::AlreadyComplete
;
12574 return SheetPreloadStatus::InProgress
;
12577 RefPtr
<StyleSheet
> Document::LoadChromeSheetSync(nsIURI
* uri
) {
12579 ->LoadSheetSync(uri
, css::eAuthorSheetFeatures
)
12580 .unwrapOr(nullptr);
12583 void Document::ResetDocumentDirection() {
12584 if (!nsContentUtils::IsChromeDoc(this)) {
12587 UpdateDocumentStates(DocumentState::ALL_LOCALEDIR_BITS
, true);
12590 bool Document::IsDocumentRightToLeft() {
12591 if (!nsContentUtils::IsChromeDoc(this)) {
12594 // setting the localedir attribute on the root element forces a
12595 // specific direction for the document.
12596 Element
* element
= GetRootElement();
12598 static Element::AttrValuesArray strings
[] = {nsGkAtoms::ltr
, nsGkAtoms::rtl
,
12600 switch (element
->FindAttrValueIn(kNameSpaceID_None
, nsGkAtoms::localedir
,
12601 strings
, eCaseMatters
)) {
12607 break; // otherwise, not a valid value, so fall through
12611 if (!mDocumentURI
->SchemeIs("chrome") && !mDocumentURI
->SchemeIs("about") &&
12612 !mDocumentURI
->SchemeIs("resource")) {
12616 return intl::LocaleService::GetInstance()->IsAppLocaleRTL();
12619 class nsDelayedEventDispatcher
: public Runnable
{
12621 explicit nsDelayedEventDispatcher(nsTArray
<nsCOMPtr
<Document
>>&& aDocuments
)
12622 : mozilla::Runnable("nsDelayedEventDispatcher"),
12623 mDocuments(std::move(aDocuments
)) {}
12624 virtual ~nsDelayedEventDispatcher() = default;
12626 // MOZ_CAN_RUN_SCRIPT_BOUNDARY until Runnable::Run is MOZ_CAN_RUN_SCRIPT. See
12628 MOZ_CAN_RUN_SCRIPT_BOUNDARY NS_IMETHOD
Run() override
{
12629 FireOrClearDelayedEvents(std::move(mDocuments
), true);
12634 nsTArray
<nsCOMPtr
<Document
>> mDocuments
;
12637 static void GetAndUnsuppressSubDocuments(
12638 Document
& aDocument
, nsTArray
<nsCOMPtr
<Document
>>& aDocuments
) {
12639 if (aDocument
.EventHandlingSuppressed() > 0) {
12640 aDocument
.DecreaseEventSuppression();
12641 aDocument
.ScriptLoader()->RemoveExecuteBlocker();
12643 aDocuments
.AppendElement(&aDocument
);
12644 auto recurse
= [&aDocuments
](Document
& aSubDoc
) {
12645 GetAndUnsuppressSubDocuments(aSubDoc
, aDocuments
);
12646 return CallState::Continue
;
12648 aDocument
.EnumerateSubDocuments(recurse
);
12651 void Document::UnsuppressEventHandlingAndFireEvents(bool aFireEvents
) {
12652 nsTArray
<nsCOMPtr
<Document
>> documents
;
12653 GetAndUnsuppressSubDocuments(*this, documents
);
12655 for (nsCOMPtr
<Document
>& doc
: documents
) {
12656 if (!doc
->EventHandlingSuppressed()) {
12657 if (WindowGlobalChild
* wgc
= doc
->GetWindowGlobalChild()) {
12658 wgc
->UnblockBFCacheFor(BFCacheStatus::EVENT_HANDLING_SUPPRESSED
);
12661 MOZ_ASSERT(NS_IsMainThread());
12662 nsTArray
<RefPtr
<net::ChannelEventQueue
>> queues
=
12663 std::move(doc
->mSuspendedQueues
);
12664 for (net::ChannelEventQueue
* queue
: queues
) {
12668 // If there have been any events driven by the refresh driver which were
12669 // delayed due to events being suppressed in this document, make sure
12670 // there is a refresh scheduled soon so the events will run.
12671 if (doc
->mHasDelayedRefreshEvent
) {
12672 doc
->mHasDelayedRefreshEvent
= false;
12674 if (doc
->mPresShell
) {
12675 nsRefreshDriver
* rd
=
12676 doc
->mPresShell
->GetPresContext()->RefreshDriver();
12677 rd
->RunDelayedEventsSoon();
12684 MOZ_RELEASE_ASSERT(NS_IsMainThread());
12685 nsCOMPtr
<nsIRunnable
> ded
=
12686 new nsDelayedEventDispatcher(std::move(documents
));
12687 Dispatch(TaskCategory::Other
, ded
.forget());
12689 FireOrClearDelayedEvents(std::move(documents
), false);
12693 bool Document::AreClipboardCommandsUnconditionallyEnabled() const {
12694 return IsHTMLOrXHTML() && !nsContentUtils::IsChromeDoc(this);
12697 void Document::AddSuspendedChannelEventQueue(net::ChannelEventQueue
* aQueue
) {
12698 MOZ_ASSERT(NS_IsMainThread());
12699 MOZ_ASSERT(EventHandlingSuppressed());
12700 mSuspendedQueues
.AppendElement(aQueue
);
12703 bool Document::SuspendPostMessageEvent(PostMessageEvent
* aEvent
) {
12704 MOZ_ASSERT(NS_IsMainThread());
12706 if (EventHandlingSuppressed() || !mSuspendedPostMessageEvents
.IsEmpty()) {
12707 mSuspendedPostMessageEvents
.AppendElement(aEvent
);
12713 void Document::FireOrClearPostMessageEvents(bool aFireEvents
) {
12714 nsTArray
<RefPtr
<PostMessageEvent
>> events
=
12715 std::move(mSuspendedPostMessageEvents
);
12718 for (PostMessageEvent
* event
: events
) {
12724 void Document::SetSuppressedEventListener(EventListener
* aListener
) {
12725 mSuppressedEventListener
= aListener
;
12726 auto setOnSubDocs
= [&](Document
& aDocument
) {
12727 aDocument
.SetSuppressedEventListener(aListener
);
12728 return CallState::Continue
;
12730 EnumerateSubDocuments(setOnSubDocs
);
12733 bool Document::IsActive() const {
12734 return mDocumentContainer
&& !mRemovedFromDocShell
&& GetBrowsingContext() &&
12735 !GetBrowsingContext()->IsInBFCache();
12738 nsISupports
* Document::GetCurrentContentSink() {
12739 return mParser
? mParser
->GetContentSink() : nullptr;
12742 Document
* Document::GetTemplateContentsOwner() {
12743 if (!mTemplateContentsOwner
) {
12744 bool hasHadScriptObject
= true;
12745 nsIScriptGlobalObject
* scriptObject
=
12746 GetScriptHandlingObject(hasHadScriptObject
);
12748 nsCOMPtr
<Document
> document
;
12749 nsresult rv
= NS_NewDOMDocument(
12750 getter_AddRefs(document
),
12751 u
""_ns
, // aNamespaceURI
12752 u
""_ns
, // aQualifiedName
12753 nullptr, // aDoctype
12754 Document::GetDocumentURI(), Document::GetDocBaseURI(), NodePrincipal(),
12755 true, // aLoadedAsData
12756 scriptObject
, // aEventObject
12757 IsHTMLDocument() ? DocumentFlavorHTML
: DocumentFlavorXML
);
12758 NS_ENSURE_SUCCESS(rv
, nullptr);
12760 mTemplateContentsOwner
= document
;
12761 NS_ENSURE_TRUE(mTemplateContentsOwner
, nullptr);
12763 if (!scriptObject
) {
12764 mTemplateContentsOwner
->SetScopeObject(GetScopeObject());
12767 mTemplateContentsOwner
->mHasHadScriptHandlingObject
= hasHadScriptObject
;
12769 // Set |mTemplateContentsOwner| as the template contents owner of itself so
12770 // that it is the template contents owner of nested template elements.
12771 mTemplateContentsOwner
->mTemplateContentsOwner
= mTemplateContentsOwner
;
12774 MOZ_ASSERT(mTemplateContentsOwner
->IsTemplateContentsOwner());
12775 return mTemplateContentsOwner
;
12778 // https://html.spec.whatwg.org/#the-autofocus-attribute
12779 void Document::ElementWithAutoFocusInserted(Element
* aAutoFocusCandidate
) {
12780 BrowsingContext
* bc
= GetBrowsingContext();
12785 // If target is not fully active, then return.
12786 if (!IsCurrentActiveDocument()) {
12790 // If target's active sandboxing flag set has the sandboxed automatic features
12791 // browsing context flag, then return.
12792 if (GetSandboxFlags() & SANDBOXED_AUTOMATIC_FEATURES
) {
12796 // For each ancestorBC of target's browsing context's ancestor browsing
12797 // contexts: if ancestorBC's active document's origin is not same origin with
12798 // target's origin, then return.
12800 BrowsingContext
* parent
= bc
->GetParent();
12804 // AncestorBC is not the same site
12805 if (!parent
->IsInProcess()) {
12809 Document
* currentDocument
= bc
->GetDocument();
12810 if (!currentDocument
) {
12814 Document
* parentDocument
= parent
->GetDocument();
12815 if (!parentDocument
) {
12820 if (!currentDocument
->NodePrincipal()->Equals(
12821 parentDocument
->NodePrincipal())) {
12827 MOZ_ASSERT(bc
->IsTop());
12829 Document
* topDocument
= bc
->GetDocument();
12830 MOZ_ASSERT(topDocument
);
12831 topDocument
->AppendAutoFocusCandidateToTopDocument(aAutoFocusCandidate
);
12834 void Document::ScheduleFlushAutoFocusCandidates() {
12835 MOZ_ASSERT(mPresShell
&& mPresShell
->DidInitialize());
12836 MOZ_ASSERT(GetBrowsingContext()->IsTop());
12837 if (nsRefreshDriver
* rd
= mPresShell
->GetRefreshDriver()) {
12838 rd
->ScheduleAutoFocusFlush(this);
12842 void Document::AppendAutoFocusCandidateToTopDocument(
12843 Element
* aAutoFocusCandidate
) {
12844 MOZ_ASSERT(GetBrowsingContext()->IsTop());
12845 if (mAutoFocusFired
) {
12849 if (!HasAutoFocusCandidates()) {
12850 // PresShell may be initialized later
12851 if (mPresShell
&& mPresShell
->DidInitialize()) {
12852 ScheduleFlushAutoFocusCandidates();
12856 nsWeakPtr element
= do_GetWeakReference(aAutoFocusCandidate
);
12857 mAutoFocusCandidates
.RemoveElement(element
);
12858 mAutoFocusCandidates
.AppendElement(element
);
12861 void Document::SetAutoFocusFired() {
12862 mAutoFocusCandidates
.Clear();
12863 mAutoFocusFired
= true;
12866 // https://html.spec.whatwg.org/#flush-autofocus-candidates
12867 void Document::FlushAutoFocusCandidates() {
12868 MOZ_ASSERT(GetBrowsingContext()->IsTop());
12869 if (mAutoFocusFired
) {
12877 MOZ_ASSERT(HasAutoFocusCandidates());
12878 MOZ_ASSERT(mPresShell
->DidInitialize());
12880 nsCOMPtr
<nsPIDOMWindowOuter
> topWindow
= GetWindow();
12881 // We should be the top document
12888 // Trying to find the top window (equivalent to window.top).
12889 nsCOMPtr
<nsPIDOMWindowOuter
> top
= topWindow
->GetInProcessTop();
12890 MOZ_ASSERT(topWindow
== top
);
12894 // Don't steal the focus from the user
12895 if (topWindow
->GetFocusedElement()) {
12896 SetAutoFocusFired();
12900 MOZ_ASSERT(mDocumentURI
);
12902 // GetRef never fails
12903 nsresult rv
= mDocumentURI
->GetRef(ref
);
12904 if (NS_SUCCEEDED(rv
) &&
12905 nsContentUtils::GetTargetElement(this, NS_ConvertUTF8toUTF16(ref
))) {
12906 SetAutoFocusFired();
12910 nsTObserverArray
<nsWeakPtr
>::ForwardIterator
iter(mAutoFocusCandidates
);
12911 while (iter
.HasMore()) {
12912 nsCOMPtr
<Element
> autoFocusElement
= do_QueryReferent(iter
.GetNext());
12913 if (!autoFocusElement
) {
12916 RefPtr
<Document
> autoFocusElementDoc
= autoFocusElement
->OwnerDoc();
12917 // Get the latest info about the frame and allow scripts
12918 // to run which might affect the focusability of this element.
12919 autoFocusElementDoc
->FlushPendingNotifications(FlushType::Frames
);
12921 // Above layout flush may cause the PresShell to disappear.
12926 // Re-get the element because the ownerDoc() might have changed
12927 autoFocusElementDoc
= autoFocusElement
->OwnerDoc();
12928 BrowsingContext
* bc
= autoFocusElementDoc
->GetBrowsingContext();
12933 // If doc is not fully active, then remove element from candidates, and
12935 if (!autoFocusElementDoc
->IsCurrentActiveDocument()) {
12940 nsCOMPtr
<nsIContentSink
> sink
=
12941 do_QueryInterface(autoFocusElementDoc
->GetCurrentContentSink());
12943 nsHtml5TreeOpExecutor
* executor
=
12944 static_cast<nsHtml5TreeOpExecutor
*>(sink
->AsExecutor());
12946 // This is a HTML5 document
12947 MOZ_ASSERT(autoFocusElementDoc
->IsHTMLDocument());
12948 // If doc's script-blocking style sheet counter is greater than 0, th
12950 if (executor
->WaitForPendingSheets()) {
12951 // In this case, element is the currently-best candidate, but doc is
12952 // not ready for autofocusing. We'll try again next time flush
12953 // autofocus candidates is called.
12954 ScheduleFlushAutoFocusCandidates();
12960 // The autofocus element could be moved to a different
12962 if (bc
->Top()->GetDocument() != this) {
12968 // Let inclusiveAncestorDocuments be a list consisting of doc, plus the
12969 // active documents of each of doc's browsing context's ancestor browsing
12971 // If any Document in inclusiveAncestorDocuments has non-null target
12972 // element, then continue.
12973 bool shouldFocus
= true;
12975 Document
* doc
= bc
->GetDocument();
12977 shouldFocus
= false;
12981 nsIURI
* uri
= doc
->GetDocumentURI();
12983 shouldFocus
= false;
12988 nsresult rv
= uri
->GetRef(ref
);
12989 // If there is an element in the document tree that has an ID equal to
12991 if (NS_SUCCEEDED(rv
) &&
12992 nsContentUtils::GetTargetElement(doc
, NS_ConvertUTF8toUTF16(ref
))) {
12993 shouldFocus
= false;
12996 bc
= bc
->GetParent();
12999 if (!shouldFocus
) {
13003 MOZ_ASSERT(topWindow
);
13004 if (TryAutoFocusCandidate(*autoFocusElement
)) {
13005 // We've successfully autofocused an element, don't
13006 // need to try to focus the rest.
13007 SetAutoFocusFired();
13012 if (HasAutoFocusCandidates()) {
13013 ScheduleFlushAutoFocusCandidates();
13017 bool Document::TryAutoFocusCandidate(Element
& aElement
) {
13018 const FocusOptions options
;
13019 if (RefPtr
<Element
> target
= nsFocusManager::GetTheFocusableArea(
13020 &aElement
, nsFocusManager::ProgrammaticFocusFlags(options
))) {
13021 target
->Focus(options
, CallerType::NonSystem
, IgnoreErrors());
13028 void Document::SetScrollToRef(nsIURI
* aDocumentURI
) {
13029 if (!aDocumentURI
) {
13035 // Since all URI's that pass through here aren't URL's we can't
13036 // rely on the nsIURI implementation for providing a way for
13037 // finding the 'ref' part of the URI, we'll haveto revert to
13038 // string routines for finding the data past '#'
13040 nsresult rv
= aDocumentURI
->GetSpec(ref
);
13041 if (NS_FAILED(rv
)) {
13042 Unused
<< aDocumentURI
->GetRef(mScrollToRef
);
13046 nsReadingIterator
<char> start
, end
;
13048 ref
.BeginReading(start
);
13049 ref
.EndReading(end
);
13051 if (FindCharInReadable('#', start
, end
)) {
13052 ++start
; // Skip over the '#'
13054 mScrollToRef
= Substring(start
, end
);
13058 void Document::ScrollToRef() {
13059 if (mScrolledToRefAlready
) {
13060 RefPtr
<PresShell
> presShell
= GetPresShell();
13062 presShell
->ScrollToAnchor();
13067 if (mScrollToRef
.IsEmpty()) {
13071 RefPtr
<PresShell
> presShell
= GetPresShell();
13073 nsresult rv
= NS_ERROR_FAILURE
;
13074 // We assume that the bytes are in UTF-8, as it says in the spec:
13075 // http://www.w3.org/TR/html4/appendix/notes.html#h-B.2.1
13076 NS_ConvertUTF8toUTF16
ref(mScrollToRef
);
13077 // Check an empty string which might be caused by the UTF-8 conversion
13078 if (!ref
.IsEmpty()) {
13079 // Note that GoToAnchor will handle flushing layout as needed.
13080 rv
= presShell
->GoToAnchor(ref
, mChangeScrollPosWhenScrollingToRef
);
13082 rv
= NS_ERROR_FAILURE
;
13085 if (NS_FAILED(rv
)) {
13086 nsAutoCString buff
;
13087 const bool unescaped
=
13088 NS_UnescapeURL(mScrollToRef
.BeginReading(), mScrollToRef
.Length(),
13089 /*aFlags =*/0, buff
);
13091 // This attempt is only necessary if characters were unescaped.
13093 NS_ConvertUTF8toUTF16
utf16Str(buff
);
13094 if (!utf16Str
.IsEmpty()) {
13095 rv
= presShell
->GoToAnchor(utf16Str
,
13096 mChangeScrollPosWhenScrollingToRef
);
13100 // If UTF-8 URI failed then try to assume the string as a
13101 // document's charset.
13102 if (NS_FAILED(rv
)) {
13103 const Encoding
* encoding
= GetDocumentCharacterSet();
13104 rv
= encoding
->DecodeWithoutBOMHandling(unescaped
? buff
: mScrollToRef
,
13106 if (NS_SUCCEEDED(rv
) && !ref
.IsEmpty()) {
13107 rv
= presShell
->GoToAnchor(ref
, mChangeScrollPosWhenScrollingToRef
);
13111 if (NS_SUCCEEDED(rv
)) {
13112 mScrolledToRefAlready
= true;
13117 void Document::RegisterActivityObserver(nsISupports
* aSupports
) {
13118 if (!mActivityObservers
) {
13119 mActivityObservers
= MakeUnique
<nsTHashSet
<nsISupports
*>>();
13121 mActivityObservers
->Insert(aSupports
);
13124 bool Document::UnregisterActivityObserver(nsISupports
* aSupports
) {
13125 if (!mActivityObservers
) {
13128 return mActivityObservers
->EnsureRemoved(aSupports
);
13131 void Document::EnumerateActivityObservers(
13132 ActivityObserverEnumerator aEnumerator
) {
13133 if (!mActivityObservers
) {
13137 const auto keyArray
=
13138 ToTArray
<nsTArray
<nsCOMPtr
<nsISupports
>>>(*mActivityObservers
);
13139 for (auto& observer
: keyArray
) {
13140 aEnumerator(observer
.get());
13144 void Document::RegisterPendingLinkUpdate(Link
* aLink
) {
13145 if (aLink
->HasPendingLinkUpdate()) {
13149 aLink
->SetHasPendingLinkUpdate();
13151 if (!mHasLinksToUpdateRunnable
&& !mFlushingPendingLinkUpdates
) {
13152 nsCOMPtr
<nsIRunnable
> event
=
13153 NewRunnableMethod("Document::FlushPendingLinkUpdates", this,
13154 &Document::FlushPendingLinkUpdates
);
13155 // Do this work in a second in the worst case.
13156 nsresult rv
= NS_DispatchToCurrentThreadQueue(event
.forget(), 1000,
13157 EventQueuePriority::Idle
);
13158 if (NS_FAILED(rv
)) {
13159 // If during shutdown posting a runnable doesn't succeed, we probably
13160 // don't need to update link states.
13163 mHasLinksToUpdateRunnable
= true;
13166 mLinksToUpdate
.InfallibleAppend(aLink
);
13169 void Document::FlushPendingLinkUpdates() {
13170 MOZ_DIAGNOSTIC_ASSERT(!mFlushingPendingLinkUpdates
);
13171 MOZ_ASSERT(mHasLinksToUpdateRunnable
);
13172 mHasLinksToUpdateRunnable
= false;
13174 auto restore
= MakeScopeExit([&] { mFlushingPendingLinkUpdates
= false; });
13175 mFlushingPendingLinkUpdates
= true;
13177 while (!mLinksToUpdate
.IsEmpty()) {
13178 LinksToUpdateList
links(std::move(mLinksToUpdate
));
13179 for (auto iter
= links
.Iter(); !iter
.Done(); iter
.Next()) {
13180 Link
* link
= iter
.Get();
13181 Element
* element
= link
->GetElement();
13182 if (element
->OwnerDoc() == this) {
13183 link
->ClearHasPendingLinkUpdate();
13184 if (element
->IsInComposedDoc()) {
13185 element
->UpdateLinkState(link
->LinkState());
13193 * Retrieves the node in a static-clone document that corresponds to aOrigNode,
13194 * which is a node in the original document from which aStaticClone was cloned.
13196 static nsINode
* GetCorrespondingNodeInDocument(const nsINode
* aOrigNode
,
13197 Document
& aStaticClone
) {
13198 MOZ_ASSERT(aOrigNode
);
13200 // Selections in anonymous subtrees aren't supported.
13201 if (NS_WARN_IF(aOrigNode
->IsInNativeAnonymousSubtree())) {
13205 // If the node is disconnected, this is a bug in the selection code, but it
13206 // can happen with shadow DOM so handle it.
13207 if (NS_WARN_IF(!aOrigNode
->IsInComposedDoc())) {
13211 AutoTArray
<Maybe
<uint32_t>, 32> indexArray
;
13212 const nsINode
* current
= aOrigNode
;
13213 while (const nsINode
* parent
= current
->GetParentNode()) {
13214 Maybe
<uint32_t> index
= parent
->ComputeIndexOf(current
);
13215 NS_ENSURE_TRUE(index
.isSome(), nullptr);
13216 indexArray
.AppendElement(std::move(index
));
13219 MOZ_ASSERT(current
->IsDocument() || current
->IsShadowRoot());
13220 nsINode
* correspondingNode
= [&]() -> nsINode
* {
13221 if (current
->IsDocument()) {
13222 return &aStaticClone
;
13224 const auto* shadow
= ShadowRoot::FromNode(*current
);
13228 nsINode
* correspondingHost
=
13229 GetCorrespondingNodeInDocument(shadow
->Host(), aStaticClone
);
13230 if (NS_WARN_IF(!correspondingHost
|| !correspondingHost
->IsElement())) {
13233 return correspondingHost
->AsElement()->GetShadowRoot();
13236 if (NS_WARN_IF(!correspondingNode
)) {
13239 for (const Maybe
<uint32_t>& index
: Reversed(indexArray
)) {
13240 correspondingNode
= correspondingNode
->GetChildAt_Deprecated(*index
);
13241 NS_ENSURE_TRUE(correspondingNode
, nullptr);
13243 return correspondingNode
;
13247 * Caches the selection ranges from the source document onto the static clone in
13248 * case the "Print Selection Only" functionality is invoked.
13250 * Note that we cannot use the selection obtained from GetOriginalDocument()
13251 * since that selection may have mutated after the print was invoked.
13253 * Note also that because nsRange objects point into a specific document's
13254 * nodes, we cannot reuse an array of nsRange objects across multiple static
13255 * clone documents. For that reason we cache a new array of ranges on each
13256 * static clone that we create.
13258 * TODO(emilio): This can be simplified once we don't re-clone from static
13261 * @param aSourceDoc the document from which we are caching selection ranges
13262 * @param aStaticClone the document that will hold the cache
13263 * @return true if a selection range was cached
13265 static void CachePrintSelectionRanges(const Document
& aSourceDoc
,
13266 Document
& aStaticClone
) {
13267 MOZ_ASSERT(aStaticClone
.IsStaticDocument());
13268 MOZ_ASSERT(!aStaticClone
.GetProperty(nsGkAtoms::printisfocuseddoc
));
13269 MOZ_ASSERT(!aStaticClone
.GetProperty(nsGkAtoms::printselectionranges
));
13271 bool sourceDocIsStatic
= aSourceDoc
.IsStaticDocument();
13273 // When the user opts to "Print Selection Only", the print code prefers any
13274 // selection in the static clone corresponding to the focused frame. If this
13275 // is that static clone, flag it for the printing code:
13276 const bool isFocusedDoc
= [&] {
13277 if (sourceDocIsStatic
) {
13278 return bool(aSourceDoc
.GetProperty(nsGkAtoms::printisfocuseddoc
));
13280 nsPIDOMWindowOuter
* window
= aSourceDoc
.GetWindow();
13284 nsCOMPtr
<nsPIDOMWindowOuter
> rootWindow
= window
->GetPrivateRoot();
13288 nsCOMPtr
<nsPIDOMWindowOuter
> focusedWindow
;
13289 nsFocusManager::GetFocusedDescendant(rootWindow
,
13290 nsFocusManager::eIncludeAllDescendants
,
13291 getter_AddRefs(focusedWindow
));
13292 return focusedWindow
&& focusedWindow
->GetExtantDoc() == &aSourceDoc
;
13294 if (isFocusedDoc
) {
13295 aStaticClone
.SetProperty(nsGkAtoms::printisfocuseddoc
,
13296 reinterpret_cast<void*>(true));
13299 const Selection
* origSelection
= nullptr;
13300 const nsTArray
<RefPtr
<nsRange
>>* origRanges
= nullptr;
13302 if (sourceDocIsStatic
) {
13303 origRanges
= static_cast<nsTArray
<RefPtr
<nsRange
>>*>(
13304 aSourceDoc
.GetProperty(nsGkAtoms::printselectionranges
));
13305 } else if (PresShell
* shell
= aSourceDoc
.GetPresShell()) {
13306 origSelection
= shell
->GetCurrentSelection(SelectionType::eNormal
);
13309 if (!origSelection
&& !origRanges
) {
13313 const uint32_t rangeCount
=
13314 sourceDocIsStatic
? origRanges
->Length() : origSelection
->RangeCount();
13315 auto printRanges
= MakeUnique
<nsTArray
<RefPtr
<nsRange
>>>(rangeCount
);
13317 for (const uint32_t i
: IntegerRange(rangeCount
)) {
13318 MOZ_ASSERT_IF(!sourceDocIsStatic
,
13319 origSelection
->RangeCount() == rangeCount
);
13320 const nsRange
* range
= sourceDocIsStatic
? origRanges
->ElementAt(i
).get()
13321 : origSelection
->GetRangeAt(i
);
13323 nsINode
* startContainer
= range
->GetStartContainer();
13324 nsINode
* endContainer
= range
->GetEndContainer();
13326 if (!startContainer
|| !endContainer
) {
13330 nsINode
* startNode
=
13331 GetCorrespondingNodeInDocument(startContainer
, aStaticClone
);
13333 GetCorrespondingNodeInDocument(endContainer
, aStaticClone
);
13335 if (NS_WARN_IF(!startNode
|| !endNode
)) {
13339 RefPtr
<nsRange
> clonedRange
=
13340 nsRange::Create(startNode
, range
->StartOffset(), endNode
,
13341 range
->EndOffset(), IgnoreErrors());
13342 if (clonedRange
&& !clonedRange
->Collapsed()) {
13343 printRanges
->AppendElement(std::move(clonedRange
));
13347 if (printRanges
->IsEmpty()) {
13351 aStaticClone
.SetProperty(nsGkAtoms::printselectionranges
,
13352 printRanges
.release(),
13353 nsINode::DeleteProperty
<nsTArray
<RefPtr
<nsRange
>>>);
13356 already_AddRefed
<Document
> Document::CreateStaticClone(
13357 nsIDocShell
* aCloneContainer
, nsIContentViewer
* aViewer
,
13358 nsIPrintSettings
* aPrintSettings
, bool* aOutHasInProcessPrintCallbacks
) {
13359 MOZ_ASSERT(!mCreatingStaticClone
);
13360 MOZ_ASSERT(!GetProperty(nsGkAtoms::adoptedsheetclones
));
13361 MOZ_DIAGNOSTIC_ASSERT(aViewer
);
13363 mCreatingStaticClone
= true;
13364 SetProperty(nsGkAtoms::adoptedsheetclones
, new AdoptedStyleSheetCloneCache(),
13365 nsINode::DeleteProperty
<AdoptedStyleSheetCloneCache
>);
13367 auto raii
= MakeScopeExit([&] {
13368 RemoveProperty(nsGkAtoms::adoptedsheetclones
);
13369 mCreatingStaticClone
= false;
13372 // Make document use different container during cloning.
13374 // FIXME(emilio): Why is this needed?
13375 RefPtr
<nsDocShell
> originalShell
= mDocumentContainer
.get();
13376 SetContainer(nsDocShell::Cast(aCloneContainer
));
13377 IgnoredErrorResult rv
;
13378 nsCOMPtr
<nsINode
> clonedNode
= CloneNode(true, rv
);
13379 SetContainer(originalShell
);
13384 nsCOMPtr
<Document
> clonedDoc
= do_QueryInterface(clonedNode
);
13389 size_t sheetsCount
= SheetCount();
13390 for (size_t i
= 0; i
< sheetsCount
; ++i
) {
13391 RefPtr
<StyleSheet
> sheet
= SheetAt(i
);
13393 if (sheet
->IsApplicable()) {
13394 RefPtr
<StyleSheet
> clonedSheet
= sheet
->Clone(nullptr, clonedDoc
);
13395 NS_WARNING_ASSERTION(clonedSheet
, "Cloning a stylesheet didn't work!");
13397 clonedDoc
->AddStyleSheet(clonedSheet
);
13402 clonedDoc
->CloneAdoptedSheetsFrom(*this);
13404 for (int t
= 0; t
< AdditionalSheetTypeCount
; ++t
) {
13405 auto& sheets
= mAdditionalSheets
[additionalSheetType(t
)];
13406 for (StyleSheet
* sheet
: sheets
) {
13407 if (sheet
->IsApplicable()) {
13408 RefPtr
<StyleSheet
> clonedSheet
= sheet
->Clone(nullptr, clonedDoc
);
13409 NS_WARNING_ASSERTION(clonedSheet
, "Cloning a stylesheet didn't work!");
13411 clonedDoc
->AddAdditionalStyleSheet(additionalSheetType(t
),
13418 // Font faces created with the JS API will not be reflected in the
13419 // stylesheets and need to be copied over to the cloned document.
13420 if (const FontFaceSet
* set
= GetFonts()) {
13421 set
->CopyNonRuleFacesTo(clonedDoc
->Fonts());
13424 clonedDoc
->mReferrerInfo
=
13425 static_cast<dom::ReferrerInfo
*>(mReferrerInfo
.get())->Clone();
13426 clonedDoc
->mPreloadReferrerInfo
= clonedDoc
->mReferrerInfo
;
13427 CachePrintSelectionRanges(*this, *clonedDoc
);
13429 // We're done with the clone, embed ourselves into the document viewer and
13430 // clone our children. The order here is pretty important, because our
13431 // document our document needs to have an owner global before we can create
13432 // the frame loaders for subdocuments.
13433 aViewer
->SetDocument(clonedDoc
);
13435 *aOutHasInProcessPrintCallbacks
|= clonedDoc
->HasPrintCallbacks();
13437 auto pendingClones
= std::move(clonedDoc
->mPendingFrameStaticClones
);
13438 for (const auto& clone
: pendingClones
) {
13439 RefPtr
<Element
> element
= do_QueryObject(clone
.mElement
);
13440 RefPtr
<nsFrameLoader
> frameLoader
=
13441 nsFrameLoader::Create(element
, /* aNetworkCreated */ false);
13443 if (NS_WARN_IF(!frameLoader
)) {
13447 clone
.mElement
->SetFrameLoader(frameLoader
);
13449 nsresult rv
= frameLoader
->FinishStaticClone(
13450 clone
.mStaticCloneOf
, aPrintSettings
, aOutHasInProcessPrintCallbacks
);
13451 Unused
<< NS_WARN_IF(NS_FAILED(rv
));
13454 return clonedDoc
.forget();
13457 void Document::UnlinkOriginalDocumentIfStatic() {
13458 if (IsStaticDocument() && mOriginalDocument
) {
13459 MOZ_ASSERT(mOriginalDocument
->mStaticCloneCount
> 0);
13460 mOriginalDocument
->mStaticCloneCount
--;
13461 mOriginalDocument
= nullptr;
13463 MOZ_ASSERT(!mOriginalDocument
);
13466 nsresult
Document::ScheduleFrameRequestCallback(FrameRequestCallback
& aCallback
,
13467 int32_t* aHandle
) {
13468 nsresult rv
= mFrameRequestManager
.Schedule(aCallback
, aHandle
);
13469 if (NS_FAILED(rv
)) {
13473 UpdateFrameRequestCallbackSchedulingState();
13477 void Document::CancelFrameRequestCallback(int32_t aHandle
) {
13478 if (mFrameRequestManager
.Cancel(aHandle
)) {
13479 UpdateFrameRequestCallbackSchedulingState();
13483 bool Document::IsCanceledFrameRequestCallback(int32_t aHandle
) const {
13484 return mFrameRequestManager
.IsCanceled(aHandle
);
13487 nsresult
Document::GetStateObject(JS::MutableHandle
<JS::Value
> aState
) {
13488 // Get the document's current state object. This is the object backing both
13489 // history.state and popStateEvent.state.
13491 // mStateObjectContainer may be null; this just means that there's no
13492 // current state object.
13494 if (!mCachedStateObjectValid
) {
13495 if (mStateObjectContainer
) {
13497 // Init with null is "OK" in the sense that it will just fail.
13498 if (!jsapi
.Init(GetScopeObject())) {
13499 return NS_ERROR_UNEXPECTED
;
13501 JS::Rooted
<JS::Value
> value(jsapi
.cx());
13503 mStateObjectContainer
->DeserializeToJsval(jsapi
.cx(), &value
);
13504 NS_ENSURE_SUCCESS(rv
, rv
);
13506 mCachedStateObject
= value
;
13507 if (!value
.isNullOrUndefined()) {
13508 mozilla::HoldJSObjects(this);
13511 mCachedStateObject
= JS::NullValue();
13513 mCachedStateObjectValid
= true;
13516 aState
.set(mCachedStateObject
);
13520 void Document::SetNavigationTiming(nsDOMNavigationTiming
* aTiming
) {
13522 if (!mLoadingTimeStamp
.IsNull() && mTiming
) {
13523 mTiming
->SetDOMLoadingTimeStamp(GetDocumentURI(), mLoadingTimeStamp
);
13526 // If there's already the DocumentTimeline instance, tell it since the
13527 // DocumentTimeline is based on both the navigation start time stamp and the
13528 // refresh driver timestamp.
13529 if (mDocumentTimeline
) {
13530 mDocumentTimeline
->UpdateLastRefreshDriverTime();
13534 nsContentList
* Document::ImageMapList() {
13536 mImageMaps
= new nsContentList(this, kNameSpaceID_XHTML
, nsGkAtoms::map
,
13543 #define DEPRECATED_OPERATION(_op) #_op "Warning",
13544 static const char* kDeprecationWarnings
[] = {
13545 #include "nsDeprecatedOperationList.h"
13547 #undef DEPRECATED_OPERATION
13549 #define DOCUMENT_WARNING(_op) #_op "Warning",
13550 static const char* kDocumentWarnings
[] = {
13551 #include "nsDocumentWarningList.h"
13553 #undef DOCUMENT_WARNING
13555 static UseCounter
OperationToUseCounter(DeprecatedOperations aOperation
) {
13556 switch (aOperation
) {
13557 #define DEPRECATED_OPERATION(_op) \
13558 case DeprecatedOperations::e##_op: \
13559 return eUseCounter_##_op;
13560 #include "nsDeprecatedOperationList.h"
13561 #undef DEPRECATED_OPERATION
13567 bool Document::HasWarnedAbout(DeprecatedOperations aOperation
) const {
13568 return mDeprecationWarnedAbout
[static_cast<size_t>(aOperation
)];
13571 void Document::WarnOnceAbout(
13572 DeprecatedOperations aOperation
, bool asError
/* = false */,
13573 const nsTArray
<nsString
>& aParams
/* = empty array */) const {
13574 MOZ_ASSERT(NS_IsMainThread());
13575 if (HasWarnedAbout(aOperation
)) {
13578 mDeprecationWarnedAbout
[static_cast<size_t>(aOperation
)] = true;
13579 // Don't count deprecated operations for about pages since those pages
13580 // are almost in our control, and we always need to remove uses there
13581 // before we remove the operation itself anyway.
13582 if (!IsAboutPage()) {
13583 const_cast<Document
*>(this)->SetUseCounter(
13584 OperationToUseCounter(aOperation
));
13587 asError
? nsIScriptError::errorFlag
: nsIScriptError::warningFlag
;
13588 nsContentUtils::ReportToConsole(
13589 flags
, "DOM Core"_ns
, this, nsContentUtils::eDOM_PROPERTIES
,
13590 kDeprecationWarnings
[static_cast<size_t>(aOperation
)], aParams
);
13593 bool Document::HasWarnedAbout(DocumentWarnings aWarning
) const {
13594 return mDocWarningWarnedAbout
[aWarning
];
13597 void Document::WarnOnceAbout(
13598 DocumentWarnings aWarning
, bool asError
/* = false */,
13599 const nsTArray
<nsString
>& aParams
/* = empty array */) const {
13600 MOZ_ASSERT(NS_IsMainThread());
13601 if (HasWarnedAbout(aWarning
)) {
13604 mDocWarningWarnedAbout
[aWarning
] = true;
13606 asError
? nsIScriptError::errorFlag
: nsIScriptError::warningFlag
;
13607 nsContentUtils::ReportToConsole(flags
, "DOM Core"_ns
, this,
13608 nsContentUtils::eDOM_PROPERTIES
,
13609 kDocumentWarnings
[aWarning
], aParams
);
13612 mozilla::dom::ImageTracker
* Document::ImageTracker() {
13613 if (!mImageTracker
) {
13614 mImageTracker
= new mozilla::dom::ImageTracker
;
13616 return mImageTracker
;
13619 void Document::ScheduleSVGUseElementShadowTreeUpdate(
13620 SVGUseElement
& aUseElement
) {
13621 MOZ_ASSERT(aUseElement
.IsInComposedDoc());
13623 if (MOZ_UNLIKELY(mIsStaticDocument
)) {
13624 // Printing doesn't deal well with dynamic DOM mutations.
13628 mSVGUseElementsNeedingShadowTreeUpdate
.Insert(&aUseElement
);
13630 if (PresShell
* presShell
= GetPresShell()) {
13631 presShell
->EnsureStyleFlush();
13635 void Document::DoUpdateSVGUseElementShadowTrees() {
13636 MOZ_ASSERT(!mSVGUseElementsNeedingShadowTreeUpdate
.IsEmpty());
13639 const auto useElementsToUpdate
= ToTArray
<nsTArray
<RefPtr
<SVGUseElement
>>>(
13640 mSVGUseElementsNeedingShadowTreeUpdate
);
13641 mSVGUseElementsNeedingShadowTreeUpdate
.Clear();
13643 for (const auto& useElement
: useElementsToUpdate
) {
13644 if (MOZ_UNLIKELY(!useElement
->IsInComposedDoc())) {
13645 // The element was in another <use> shadow tree which we processed
13646 // already and also needed an update, and is removed from the document
13647 // now, so nothing to do here.
13648 MOZ_ASSERT(useElementsToUpdate
.Length() > 1);
13651 useElement
->UpdateShadowTree();
13653 } while (!mSVGUseElementsNeedingShadowTreeUpdate
.IsEmpty());
13656 void Document::NotifyMediaFeatureValuesChanged() {
13657 for (RefPtr
<HTMLImageElement
> imageElement
: mResponsiveContent
) {
13658 imageElement
->MediaFeatureValuesChanged();
13662 already_AddRefed
<Touch
> Document::CreateTouch(
13663 nsGlobalWindowInner
* aView
, EventTarget
* aTarget
, int32_t aIdentifier
,
13664 int32_t aPageX
, int32_t aPageY
, int32_t aScreenX
, int32_t aScreenY
,
13665 int32_t aClientX
, int32_t aClientY
, int32_t aRadiusX
, int32_t aRadiusY
,
13666 float aRotationAngle
, float aForce
) {
13667 RefPtr
<Touch
> touch
=
13668 new Touch(aTarget
, aIdentifier
, aPageX
, aPageY
, aScreenX
, aScreenY
,
13669 aClientX
, aClientY
, aRadiusX
, aRadiusY
, aRotationAngle
, aForce
);
13670 return touch
.forget();
13673 already_AddRefed
<TouchList
> Document::CreateTouchList() {
13674 RefPtr
<TouchList
> retval
= new TouchList(ToSupports(this));
13675 return retval
.forget();
13678 already_AddRefed
<TouchList
> Document::CreateTouchList(
13679 Touch
& aTouch
, const Sequence
<OwningNonNull
<Touch
>>& aTouches
) {
13680 RefPtr
<TouchList
> retval
= new TouchList(ToSupports(this));
13681 retval
->Append(&aTouch
);
13682 for (uint32_t i
= 0; i
< aTouches
.Length(); ++i
) {
13683 retval
->Append(aTouches
[i
].get());
13685 return retval
.forget();
13688 already_AddRefed
<TouchList
> Document::CreateTouchList(
13689 const Sequence
<OwningNonNull
<Touch
>>& aTouches
) {
13690 RefPtr
<TouchList
> retval
= new TouchList(ToSupports(this));
13691 for (uint32_t i
= 0; i
< aTouches
.Length(); ++i
) {
13692 retval
->Append(aTouches
[i
].get());
13694 return retval
.forget();
13697 already_AddRefed
<nsDOMCaretPosition
> Document::CaretPositionFromPoint(
13698 float aX
, float aY
) {
13699 using FrameForPointOption
= nsLayoutUtils::FrameForPointOption
;
13701 nscoord x
= nsPresContext::CSSPixelsToAppUnits(aX
);
13702 nscoord y
= nsPresContext::CSSPixelsToAppUnits(aY
);
13705 FlushPendingNotifications(FlushType::Layout
);
13707 PresShell
* presShell
= GetPresShell();
13712 nsIFrame
* rootFrame
= presShell
->GetRootFrame();
13714 // XUL docs, unlike HTML, have no frame tree until everything's done loading
13719 nsIFrame
* ptFrame
= nsLayoutUtils::GetFrameForPoint(
13720 RelativeTo
{rootFrame
}, pt
,
13721 {{FrameForPointOption::IgnorePaintSuppression
,
13722 FrameForPointOption::IgnoreCrossDoc
}});
13727 // We require frame-relative coordinates for GetContentOffsetsFromPoint.
13728 nsPoint adjustedPoint
= pt
;
13729 if (nsLayoutUtils::TransformPoint(RelativeTo
{rootFrame
}, RelativeTo
{ptFrame
},
13731 nsLayoutUtils::TRANSFORM_SUCCEEDED
) {
13735 nsIFrame::ContentOffsets offsets
=
13736 ptFrame
->GetContentOffsetsFromPoint(adjustedPoint
);
13738 nsCOMPtr
<nsIContent
> node
= offsets
.content
;
13739 uint32_t offset
= offsets
.offset
;
13740 nsCOMPtr
<nsIContent
> anonNode
= node
;
13741 bool nodeIsAnonymous
= node
&& node
->IsInNativeAnonymousSubtree();
13742 if (nodeIsAnonymous
) {
13743 node
= ptFrame
->GetContent();
13744 nsIContent
* nonanon
= node
->FindFirstNonChromeOnlyAccessContent();
13745 HTMLTextAreaElement
* textArea
= HTMLTextAreaElement::FromNode(nonanon
);
13746 nsITextControlFrame
* textFrame
= do_QueryFrame(nonanon
->GetPrimaryFrame());
13748 // If the anonymous content node has a child, then we need to make sure
13749 // that we get the appropriate child, as otherwise the offset may not be
13750 // correct when we construct a range for it.
13751 nsCOMPtr
<nsIContent
> firstChild
= anonNode
->GetFirstChild();
13753 anonNode
= firstChild
;
13758 nsContentUtils::GetAdjustedOffsetInTextControl(ptFrame
, offset
);
13768 RefPtr
<nsDOMCaretPosition
> aCaretPos
= new nsDOMCaretPosition(node
, offset
);
13769 if (nodeIsAnonymous
) {
13770 aCaretPos
->SetAnonymousContentNode(anonNode
);
13772 return aCaretPos
.forget();
13775 bool Document::IsPotentiallyScrollable(HTMLBodyElement
* aBody
) {
13776 // We rely on correct frame information here, so need to flush frames.
13777 FlushPendingNotifications(FlushType::Frames
);
13779 // An element that is the HTML body element is potentially scrollable if all
13780 // of the following conditions are true:
13782 // The element has an associated CSS layout box.
13783 nsIFrame
* bodyFrame
= nsLayoutUtils::GetStyleFrame(aBody
);
13788 // The element's parent element's computed value of the overflow-x and
13789 // overflow-y properties are visible.
13790 MOZ_ASSERT(aBody
->GetParent() == aBody
->OwnerDoc()->GetRootElement());
13791 nsIFrame
* parentFrame
= nsLayoutUtils::GetStyleFrame(aBody
->GetParent());
13793 parentFrame
->StyleDisplay()->OverflowIsVisibleInBothAxis()) {
13797 // The element's computed value of the overflow-x or overflow-y properties is
13799 return !bodyFrame
->StyleDisplay()->OverflowIsVisibleInBothAxis();
13802 Element
* Document::GetScrollingElement() {
13803 // Keep this in sync with IsScrollingElement.
13804 if (GetCompatibilityMode() == eCompatibility_NavQuirks
) {
13805 RefPtr
<HTMLBodyElement
> body
= GetBodyElement();
13806 if (body
&& !IsPotentiallyScrollable(body
)) {
13813 return GetRootElement();
13816 bool Document::IsScrollingElement(Element
* aElement
) {
13817 // Keep this in sync with GetScrollingElement.
13818 MOZ_ASSERT(aElement
);
13820 if (GetCompatibilityMode() != eCompatibility_NavQuirks
) {
13821 return aElement
== GetRootElement();
13824 // In the common case when aElement != body, avoid refcounting.
13825 HTMLBodyElement
* body
= GetBodyElement();
13826 if (aElement
!= body
) {
13830 // Now we know body is non-null, since aElement is not null. It's the
13831 // scrolling element for the document if it itself is not potentially
13833 RefPtr
<HTMLBodyElement
> strongBody(body
);
13834 return !IsPotentiallyScrollable(strongBody
);
13837 class UnblockParsingPromiseHandler final
: public PromiseNativeHandler
{
13839 NS_DECL_CYCLE_COLLECTING_ISUPPORTS
13840 NS_DECL_CYCLE_COLLECTION_CLASS(UnblockParsingPromiseHandler
)
13842 explicit UnblockParsingPromiseHandler(Document
* aDocument
, Promise
* aPromise
,
13843 const BlockParsingOptions
& aOptions
)
13844 : mPromise(aPromise
) {
13845 nsCOMPtr
<nsIParser
> parser
= aDocument
->CreatorParserOrNull();
13847 (aOptions
.mBlockScriptCreated
|| !parser
->IsScriptCreated())) {
13848 parser
->BlockParser();
13849 mParser
= do_GetWeakReference(parser
);
13850 mDocument
= aDocument
;
13851 mDocument
->BlockOnload();
13852 mDocument
->BlockDOMContentLoaded();
13856 void ResolvedCallback(JSContext
* aCx
, JS::Handle
<JS::Value
> aValue
,
13857 ErrorResult
& aRv
) override
{
13858 MaybeUnblockParser();
13860 mPromise
->MaybeResolve(aValue
);
13863 void RejectedCallback(JSContext
* aCx
, JS::Handle
<JS::Value
> aValue
,
13864 ErrorResult
& aRv
) override
{
13865 MaybeUnblockParser();
13867 mPromise
->MaybeReject(aValue
);
13871 virtual ~UnblockParsingPromiseHandler() {
13872 // If we're being cleaned up by the cycle collector, our mDocument reference
13873 // may have been unlinked while our mParser weak reference is still alive.
13875 MaybeUnblockParser();
13880 void MaybeUnblockParser() {
13881 nsCOMPtr
<nsIParser
> parser
= do_QueryReferent(mParser
);
13883 MOZ_DIAGNOSTIC_ASSERT(mDocument
);
13884 nsCOMPtr
<nsIParser
> docParser
= mDocument
->CreatorParserOrNull();
13885 if (parser
== docParser
) {
13886 parser
->UnblockParser();
13887 parser
->ContinueInterruptedParsingAsync();
13891 // We blocked DOMContentLoaded and load events on this document. Unblock
13892 // them. Note that we want to do that no matter what's going on with the
13893 // parser state for this document. Maybe someone caused it to stop being
13894 // parsed, so CreatorParserOrNull() is returning null, but we still want
13895 // to unblock these.
13896 mDocument
->UnblockDOMContentLoaded();
13897 mDocument
->UnblockOnload(false);
13900 mDocument
= nullptr;
13904 RefPtr
<Promise
> mPromise
;
13905 RefPtr
<Document
> mDocument
;
13908 NS_IMPL_CYCLE_COLLECTION(UnblockParsingPromiseHandler
, mDocument
, mPromise
)
13910 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(UnblockParsingPromiseHandler
)
13911 NS_INTERFACE_MAP_ENTRY(nsISupports
)
13912 NS_INTERFACE_MAP_END
13914 NS_IMPL_CYCLE_COLLECTING_ADDREF(UnblockParsingPromiseHandler
)
13915 NS_IMPL_CYCLE_COLLECTING_RELEASE(UnblockParsingPromiseHandler
)
13917 already_AddRefed
<Promise
> Document::BlockParsing(
13918 Promise
& aPromise
, const BlockParsingOptions
& aOptions
, ErrorResult
& aRv
) {
13919 RefPtr
<Promise
> resultPromise
=
13920 Promise::Create(aPromise
.GetParentObject(), aRv
);
13921 if (aRv
.Failed()) {
13925 RefPtr
<PromiseNativeHandler
> promiseHandler
=
13926 new UnblockParsingPromiseHandler(this, resultPromise
, aOptions
);
13927 aPromise
.AppendNativeHandler(promiseHandler
);
13929 return resultPromise
.forget();
13932 already_AddRefed
<nsIURI
> Document::GetMozDocumentURIIfNotForErrorPages() {
13933 if (mFailedChannel
) {
13934 nsCOMPtr
<nsIURI
> failedURI
;
13935 if (NS_SUCCEEDED(mFailedChannel
->GetURI(getter_AddRefs(failedURI
)))) {
13936 return failedURI
.forget();
13940 nsCOMPtr
<nsIURI
> uri
= GetDocumentURIObject();
13945 return uri
.forget();
13948 Promise
* Document::GetDocumentReadyForIdle(ErrorResult
& aRv
) {
13949 if (mIsGoingAway
) {
13950 aRv
.Throw(NS_ERROR_NOT_AVAILABLE
);
13954 if (!mReadyForIdle
) {
13955 nsIGlobalObject
* global
= GetScopeObject();
13957 aRv
.Throw(NS_ERROR_NOT_AVAILABLE
);
13961 mReadyForIdle
= Promise::Create(global
, aRv
);
13962 if (aRv
.Failed()) {
13967 return mReadyForIdle
;
13970 void Document::MaybeResolveReadyForIdle() {
13971 IgnoredErrorResult rv
;
13972 Promise
* readyPromise
= GetDocumentReadyForIdle(rv
);
13973 if (readyPromise
) {
13974 readyPromise
->MaybeResolveWithUndefined();
13978 mozilla::dom::FeaturePolicy
* Document::FeaturePolicy() const {
13979 // The policy is created when the document is initialized. We _must_ have a
13980 // policy here even if the featurePolicy pref is off. If this assertion fails,
13981 // it means that ::FeaturePolicy() is called before ::StartDocumentLoad().
13982 MOZ_ASSERT(mFeaturePolicy
);
13983 return mFeaturePolicy
;
13986 nsIDOMXULCommandDispatcher
* Document::GetCommandDispatcher() {
13987 // Only chrome documents are allowed to use command dispatcher.
13988 if (!nsContentUtils::IsChromeDoc(this)) {
13991 if (!mCommandDispatcher
) {
13992 // Create our command dispatcher and hook it up.
13993 mCommandDispatcher
= new nsXULCommandDispatcher(this);
13995 return mCommandDispatcher
;
13998 void Document::InitializeXULBroadcastManager() {
13999 if (mXULBroadcastManager
) {
14002 mXULBroadcastManager
= new XULBroadcastManager(this);
14007 class DevToolsMutationObserver final
: public nsStubMutationObserver
{
14009 NS_DECL_NSIMUTATIONOBSERVER_ATTRIBUTECHANGED
14010 NS_DECL_NSIMUTATIONOBSERVER_CONTENTAPPENDED
14011 NS_DECL_NSIMUTATIONOBSERVER_CONTENTINSERTED
14013 // We handle this in nsContentUtils::MaybeFireNodeRemoved, since devtools
14014 // relies on the event firing _before_ the removal happens.
14015 // NS_DECL_NSIMUTATIONOBSERVER_CONTENTREMOVED
14017 // NOTE(emilio, bug 1694627): DevTools doesn't seem to deal with character
14018 // data changes right now (maybe intentionally?).
14019 // NS_DECL_NSIMUTATIONOBSERVER_CHARACTERDATACHANGED
14021 DevToolsMutationObserver() = default;
14024 void FireEvent(nsINode
* aTarget
, const nsAString
& aType
);
14026 ~DevToolsMutationObserver() = default;
14029 NS_IMPL_ISUPPORTS(DevToolsMutationObserver
, nsIMutationObserver
)
14031 void DevToolsMutationObserver::FireEvent(nsINode
* aTarget
,
14032 const nsAString
& aType
) {
14033 AsyncEventDispatcher::RunDOMEventWhenSafe(*aTarget
, aType
, CanBubble::eNo
,
14034 ChromeOnlyDispatch::eYes
,
14038 void DevToolsMutationObserver::AttributeChanged(Element
* aElement
,
14039 int32_t aNamespaceID
,
14040 nsAtom
* aAttribute
,
14042 const nsAttrValue
* aOldValue
) {
14043 FireEvent(aElement
, u
"devtoolsattrmodified"_ns
);
14046 void DevToolsMutationObserver::ContentAppended(nsIContent
* aFirstNewContent
) {
14047 for (nsIContent
* c
= aFirstNewContent
; c
; c
= c
->GetNextSibling()) {
14048 ContentInserted(c
);
14052 void DevToolsMutationObserver::ContentInserted(nsIContent
* aChild
) {
14053 FireEvent(aChild
, u
"devtoolschildinserted"_ns
);
14056 static StaticRefPtr
<DevToolsMutationObserver
> sDevToolsMutationObserver
;
14060 void Document::SetDevToolsWatchingDOMMutations(bool aValue
) {
14061 if (mDevToolsWatchingDOMMutations
== aValue
|| mIsGoingAway
) {
14064 mDevToolsWatchingDOMMutations
= aValue
;
14066 if (MOZ_UNLIKELY(!sDevToolsMutationObserver
)) {
14067 sDevToolsMutationObserver
= new DevToolsMutationObserver();
14068 ClearOnShutdown(&sDevToolsMutationObserver
);
14070 AddMutationObserver(sDevToolsMutationObserver
);
14071 } else if (sDevToolsMutationObserver
) {
14072 RemoveMutationObserver(sDevToolsMutationObserver
);
14076 void Document::MaybeWarnAboutZoom() {
14077 if (mHasWarnedAboutZoom
) {
14080 const bool usedZoom
= Servo_IsPropertyIdRecordedInUseCounter(
14081 mStyleUseCounters
.get(), eCSSProperty_zoom
);
14086 mHasWarnedAboutZoom
= true;
14087 nsContentUtils::ReportToConsole(nsIScriptError::warningFlag
, "Layout"_ns
,
14088 this, nsContentUtils::eLAYOUT_PROPERTIES
,
14089 "ZoomPropertyWarning");
14092 nsIHTMLCollection
* Document::Children() {
14093 if (!mChildrenCollection
) {
14094 mChildrenCollection
=
14095 new nsContentList(this, kNameSpaceID_Wildcard
, nsGkAtoms::_asterisk
,
14096 nsGkAtoms::_asterisk
, false);
14099 return mChildrenCollection
;
14102 uint32_t Document::ChildElementCount() { return Children()->Length(); }
14104 // Singleton class to manage the list of fullscreen documents which are the
14105 // root of a branch which contains fullscreen documents. We maintain this list
14106 // so that we can easily exit all windows from fullscreen when the user
14107 // presses the escape key.
14108 class FullscreenRoots
{
14110 // Adds the root of given document to the manager. Calling this method
14111 // with a document whose root is already contained has no effect.
14112 static void Add(Document
* aDoc
);
14114 // Iterates over every root in the root list, and calls aFunction, passing
14115 // each root once to aFunction. It is safe to call Add() and Remove() while
14116 // iterating over the list (i.e. in aFunction). Documents that are removed
14117 // from the manager during traversal are not traversed, and documents that
14118 // are added to the manager during traversal are also not traversed.
14119 static void ForEach(void (*aFunction
)(Document
* aDoc
));
14121 // Removes the root of a specific document from the manager.
14122 static void Remove(Document
* aDoc
);
14124 // Returns true if all roots added to the list have been removed.
14125 static bool IsEmpty();
14128 MOZ_COUNTED_DEFAULT_CTOR(FullscreenRoots
)
14129 MOZ_COUNTED_DTOR(FullscreenRoots
)
14131 enum : uint32_t { NotFound
= uint32_t(-1) };
14132 // Looks in mRoots for aRoot. Returns the index if found, otherwise NotFound.
14133 static uint32_t Find(Document
* aRoot
);
14135 // Returns true if aRoot is in the list of fullscreen roots.
14136 static bool Contains(Document
* aRoot
);
14138 // Singleton instance of the FullscreenRoots. This is instantiated when a
14139 // root is added, and it is deleted when the last root is removed.
14140 static FullscreenRoots
* sInstance
;
14142 // List of weak pointers to roots.
14143 nsTArray
<nsWeakPtr
> mRoots
;
14146 FullscreenRoots
* FullscreenRoots::sInstance
= nullptr;
14149 void FullscreenRoots::ForEach(void (*aFunction
)(Document
* aDoc
)) {
14153 // Create a copy of the roots array, and iterate over the copy. This is so
14154 // that if an element is removed from mRoots we don't mess up our iteration.
14155 nsTArray
<nsWeakPtr
> roots(sInstance
->mRoots
.Clone());
14156 // Call aFunction on all entries.
14157 for (uint32_t i
= 0; i
< roots
.Length(); i
++) {
14158 nsCOMPtr
<Document
> root
= do_QueryReferent(roots
[i
]);
14159 // Check that the root isn't in the manager. This is so that new additions
14160 // while we were running don't get traversed.
14161 if (root
&& FullscreenRoots::Contains(root
)) {
14168 bool FullscreenRoots::Contains(Document
* aRoot
) {
14169 return FullscreenRoots::Find(aRoot
) != NotFound
;
14173 void FullscreenRoots::Add(Document
* aDoc
) {
14174 nsCOMPtr
<Document
> root
=
14175 nsContentUtils::GetInProcessSubtreeRootDocument(aDoc
);
14176 if (!FullscreenRoots::Contains(root
)) {
14178 sInstance
= new FullscreenRoots();
14180 sInstance
->mRoots
.AppendElement(do_GetWeakReference(root
));
14185 uint32_t FullscreenRoots::Find(Document
* aRoot
) {
14189 nsTArray
<nsWeakPtr
>& roots
= sInstance
->mRoots
;
14190 for (uint32_t i
= 0; i
< roots
.Length(); i
++) {
14191 nsCOMPtr
<Document
> otherRoot(do_QueryReferent(roots
[i
]));
14192 if (otherRoot
== aRoot
) {
14200 void FullscreenRoots::Remove(Document
* aDoc
) {
14201 nsCOMPtr
<Document
> root
=
14202 nsContentUtils::GetInProcessSubtreeRootDocument(aDoc
);
14203 uint32_t index
= Find(root
);
14204 NS_ASSERTION(index
!= NotFound
,
14205 "Should only try to remove roots which are still added!");
14206 if (index
== NotFound
|| !sInstance
) {
14209 sInstance
->mRoots
.RemoveElementAt(index
);
14210 if (sInstance
->mRoots
.IsEmpty()) {
14212 sInstance
= nullptr;
14217 bool FullscreenRoots::IsEmpty() { return !sInstance
; }
14219 // Any fullscreen change waiting for the widget to finish transition
14220 // is queued here. This is declared static instead of a member of
14221 // Document because in the majority of time, there would be at most
14222 // one document requesting or exiting fullscreen. We shouldn't waste
14223 // the space to hold for it in every document.
14224 class PendingFullscreenChangeList
{
14226 PendingFullscreenChangeList() = delete;
14228 template <typename T
>
14229 static void Add(UniquePtr
<T
> aChange
) {
14230 sList
.insertBack(aChange
.release());
14233 static const FullscreenChange
* GetLast() { return sList
.getLast(); }
14235 enum IteratorOption
{
14236 // When we are committing fullscreen changes or preparing for
14237 // that, we generally want to iterate all requests in the same
14238 // window with eDocumentsWithSameRoot option.
14239 eDocumentsWithSameRoot
,
14240 // If we are removing a document from the tree, we would only
14241 // want to remove the requests from the given document and its
14242 // descendants. For that case, use eInclusiveDescendants.
14243 eInclusiveDescendants
14246 template <typename T
>
14249 explicit Iterator(Document
* aDoc
, IteratorOption aOption
)
14250 : mCurrent(PendingFullscreenChangeList::sList
.getFirst()) {
14252 if (aDoc
->GetBrowsingContext()) {
14253 mRootBCForIteration
= aDoc
->GetBrowsingContext();
14254 if (aOption
== eDocumentsWithSameRoot
) {
14255 RefPtr
<BrowsingContext
> bc
=
14256 GetParentIgnoreChromeBoundary(mRootBCForIteration
);
14258 mRootBCForIteration
= bc
;
14259 bc
= GetParentIgnoreChromeBoundary(mRootBCForIteration
);
14267 UniquePtr
<T
> TakeAndNext() {
14268 auto thisChange
= TakeAndNextInternal();
14272 bool AtEnd() const { return mCurrent
== nullptr; }
14275 already_AddRefed
<BrowsingContext
> GetParentIgnoreChromeBoundary(
14276 BrowsingContext
* aBC
) {
14277 // Chrome BrowsingContexts are only available in the parent process, so if
14278 // we're in a content process, we only worry about the context tree.
14279 if (XRE_IsParentProcess()) {
14280 return aBC
->Canonical()->GetParentCrossChromeBoundary();
14282 return do_AddRef(aBC
->GetParent());
14285 UniquePtr
<T
> TakeAndNextInternal() {
14286 FullscreenChange
* thisChange
= mCurrent
;
14287 MOZ_ASSERT(thisChange
->Type() == T::kType
);
14288 mCurrent
= mCurrent
->removeAndGetNext();
14289 return WrapUnique(static_cast<T
*>(thisChange
));
14291 void SkipToNextMatch() {
14293 if (mCurrent
->Type() == T::kType
) {
14294 RefPtr
<BrowsingContext
> bc
=
14295 mCurrent
->Document()->GetBrowsingContext();
14297 // Always automatically drop fullscreen changes which are
14298 // from a document detached from the doc shell.
14299 UniquePtr
<T
> change
= TakeAndNextInternal();
14300 change
->MayRejectPromise("Document is not active");
14303 while (bc
&& bc
!= mRootBCForIteration
) {
14304 bc
= GetParentIgnoreChromeBoundary(bc
);
14310 // The current one either don't have matched type, or isn't
14311 // inside the given subtree, so skip this item.
14312 mCurrent
= mCurrent
->getNext();
14316 FullscreenChange
* mCurrent
;
14317 RefPtr
<BrowsingContext
> mRootBCForIteration
;
14321 static LinkedList
<FullscreenChange
> sList
;
14325 LinkedList
<FullscreenChange
> PendingFullscreenChangeList::sList
;
14327 Document
* Document::GetFullscreenRoot() {
14328 nsCOMPtr
<Document
> root
= do_QueryReferent(mFullscreenRoot
);
14332 size_t Document::CountFullscreenElements() const {
14334 for (const nsWeakPtr
& ptr
: mTopLayer
) {
14335 if (nsCOMPtr
<Element
> elem
= do_QueryReferent(ptr
)) {
14336 if (elem
->State().HasState(ElementState::FULLSCREEN
)) {
14344 void Document::SetFullscreenRoot(Document
* aRoot
) {
14345 mFullscreenRoot
= do_GetWeakReference(aRoot
);
14348 void Document::TryCancelDialog() {
14349 // Check if the document is blocked by modal dialog
14350 for (const nsWeakPtr
& weakPtr
: Reversed(mTopLayer
)) {
14351 nsCOMPtr
<Element
> element(do_QueryReferent(weakPtr
));
14352 if (auto* dialog
= HTMLDialogElement::FromNodeOrNull(element
)) {
14353 dialog
->QueueCancelDialog();
14359 already_AddRefed
<Promise
> Document::ExitFullscreen(ErrorResult
& aRv
) {
14360 UniquePtr
<FullscreenExit
> exit
= FullscreenExit::Create(this, aRv
);
14361 RefPtr
<Promise
> promise
= exit
->GetPromise();
14362 RestorePreviousFullscreenState(std::move(exit
));
14363 return promise
.forget();
14366 static void AskWindowToExitFullscreen(Document
* aDoc
) {
14367 if (XRE_GetProcessType() == GeckoProcessType_Content
) {
14368 nsContentUtils::DispatchEventOnlyToChrome(
14369 aDoc
, ToSupports(aDoc
), u
"MozDOMFullscreen:Exit"_ns
, CanBubble::eYes
,
14370 Cancelable::eNo
, /* DefaultAction */ nullptr);
14372 if (nsPIDOMWindowOuter
* win
= aDoc
->GetWindow()) {
14373 win
->SetFullscreenInternal(FullscreenReason::ForFullscreenAPI
, false);
14378 class nsCallExitFullscreen
: public Runnable
{
14380 explicit nsCallExitFullscreen(Document
* aDoc
)
14381 : mozilla::Runnable("nsCallExitFullscreen"), mDoc(aDoc
) {}
14383 NS_IMETHOD
Run() final
{
14385 FullscreenRoots::ForEach(&AskWindowToExitFullscreen
);
14387 AskWindowToExitFullscreen(mDoc
);
14393 nsCOMPtr
<Document
> mDoc
;
14397 void Document::AsyncExitFullscreen(Document
* aDoc
) {
14398 MOZ_RELEASE_ASSERT(NS_IsMainThread());
14399 nsCOMPtr
<nsIRunnable
> exit
= new nsCallExitFullscreen(aDoc
);
14401 aDoc
->Dispatch(TaskCategory::Other
, exit
.forget());
14403 NS_DispatchToCurrentThread(exit
.forget());
14407 static uint32_t CountFullscreenSubDocuments(Document
& aDoc
) {
14408 uint32_t count
= 0;
14409 // FIXME(emilio): Should this be recursive and dig into our nested subdocs?
14410 auto subDoc
= [&count
](Document
& aSubDoc
) {
14411 if (aSubDoc
.Fullscreen()) {
14414 return CallState::Continue
;
14416 aDoc
.EnumerateSubDocuments(subDoc
);
14420 bool Document::IsFullscreenLeaf() {
14421 // A fullscreen leaf document is fullscreen, and has no fullscreen
14424 // FIXME(emilio): This doesn't seem to account for fission iframes, is that
14426 return Fullscreen() && CountFullscreenSubDocuments(*this) == 0;
14429 static Document
* GetFullscreenLeaf(Document
& aDoc
) {
14430 if (aDoc
.IsFullscreenLeaf()) {
14433 if (!aDoc
.Fullscreen()) {
14436 Document
* leaf
= nullptr;
14437 auto recurse
= [&leaf
](Document
& aSubDoc
) {
14438 leaf
= GetFullscreenLeaf(aSubDoc
);
14439 return leaf
? CallState::Stop
: CallState::Continue
;
14441 aDoc
.EnumerateSubDocuments(recurse
);
14445 static Document
* GetFullscreenLeaf(Document
* aDoc
) {
14446 if (Document
* leaf
= GetFullscreenLeaf(*aDoc
)) {
14449 // Otherwise we could be either in a non-fullscreen doc tree, or we're
14450 // below the fullscreen doc. Start the search from the root.
14451 Document
* root
= nsContentUtils::GetInProcessSubtreeRootDocument(aDoc
);
14452 return GetFullscreenLeaf(*root
);
14455 static CallState
ResetFullscreen(Document
& aDocument
) {
14456 if (Element
* fsElement
= aDocument
.GetUnretargetedFullscreenElement()) {
14457 NS_ASSERTION(CountFullscreenSubDocuments(aDocument
) <= 1,
14458 "Should have at most 1 fullscreen subdocument.");
14459 aDocument
.CleanupFullscreenState();
14460 NS_ASSERTION(!aDocument
.Fullscreen(), "Should reset fullscreen");
14461 DispatchFullscreenChange(aDocument
, fsElement
);
14462 aDocument
.EnumerateSubDocuments(ResetFullscreen
);
14464 return CallState::Continue
;
14467 // Since Document::ExitFullscreenInDocTree() could be called from
14468 // Element::UnbindFromTree() where it is not safe to synchronously run
14469 // script. This runnable is the script part of that function.
14470 class ExitFullscreenScriptRunnable
: public Runnable
{
14472 explicit ExitFullscreenScriptRunnable(Document
* aRoot
, Document
* aLeaf
)
14473 : mozilla::Runnable("ExitFullscreenScriptRunnable"),
14477 NS_IMETHOD
Run() override
{
14478 // Dispatch MozDOMFullscreen:Exited to the original fullscreen leaf
14479 // document since we want this event to follow the same path that
14480 // MozDOMFullscreen:Entered was dispatched.
14481 nsContentUtils::DispatchEventOnlyToChrome(
14482 mLeaf
, ToSupports(mLeaf
), u
"MozDOMFullscreen:Exited"_ns
,
14483 CanBubble::eYes
, Cancelable::eNo
, /* DefaultAction */ nullptr);
14484 // Ensure the window exits fullscreen, as long as we don't have
14485 // pending fullscreen requests.
14486 if (nsPIDOMWindowOuter
* win
= mRoot
->GetWindow()) {
14487 if (!mRoot
->HasPendingFullscreenRequests()) {
14488 win
->SetFullscreenInternal(FullscreenReason::ForForceExitFullscreen
,
14496 nsCOMPtr
<Document
> mRoot
;
14497 nsCOMPtr
<Document
> mLeaf
;
14501 void Document::ExitFullscreenInDocTree(Document
* aMaybeNotARootDoc
) {
14502 MOZ_ASSERT(aMaybeNotARootDoc
);
14504 // Unlock the pointer
14505 PointerLockManager::Unlock();
14507 // Resolve all promises which waiting for exit fullscreen.
14508 PendingFullscreenChangeList::Iterator
<FullscreenExit
> iter(
14509 aMaybeNotARootDoc
, PendingFullscreenChangeList::eDocumentsWithSameRoot
);
14510 while (!iter
.AtEnd()) {
14511 UniquePtr
<FullscreenExit
> exit
= iter
.TakeAndNext();
14512 exit
->MayResolvePromise();
14515 nsCOMPtr
<Document
> root
= aMaybeNotARootDoc
->GetFullscreenRoot();
14516 if (!root
|| !root
->Fullscreen()) {
14517 // If a document was detached before exiting from fullscreen, it is
14518 // possible that the root had left fullscreen state. In this case,
14519 // we would not get anything from the ResetFullscreen() call. Root's
14520 // not being a fullscreen doc also means the widget should have
14521 // exited fullscreen state. It means even if we do not return here,
14522 // we would actually do nothing below except crashing ourselves via
14523 // dispatching the "MozDOMFullscreen:Exited" event to an nonexistent
14528 // Record the fullscreen leaf document for MozDOMFullscreen:Exited.
14529 // See ExitFullscreenScriptRunnable::Run for details. We have to
14530 // record it here because we don't have such information after we
14531 // reset the fullscreen state below.
14532 Document
* fullscreenLeaf
= GetFullscreenLeaf(root
);
14534 // Walk the tree of fullscreen documents, and reset their fullscreen state.
14535 ResetFullscreen(*root
);
14537 NS_ASSERTION(!root
->Fullscreen(),
14538 "Fullscreen root should no longer be a fullscreen doc...");
14540 // Move the top-level window out of fullscreen mode.
14541 FullscreenRoots::Remove(root
);
14543 nsContentUtils::AddScriptRunner(
14544 new ExitFullscreenScriptRunnable(root
, fullscreenLeaf
));
14547 static void DispatchFullscreenNewOriginEvent(Document
* aDoc
) {
14548 RefPtr
<AsyncEventDispatcher
> asyncDispatcher
=
14549 new AsyncEventDispatcher(aDoc
, u
"MozDOMFullscreen:NewOrigin"_ns
,
14550 CanBubble::eYes
, ChromeOnlyDispatch::eYes
);
14551 asyncDispatcher
->PostDOMEvent();
14554 void Document::RestorePreviousFullscreenState(UniquePtr
<FullscreenExit
> aExit
) {
14555 NS_ASSERTION(!Fullscreen() || !FullscreenRoots::IsEmpty(),
14556 "Should have at least 1 fullscreen root when fullscreen!");
14558 if (!GetWindow()) {
14559 aExit
->MayRejectPromise("No active window");
14562 if (!Fullscreen() || FullscreenRoots::IsEmpty()) {
14563 aExit
->MayRejectPromise("Not in fullscreen mode");
14567 nsCOMPtr
<Document
> fullScreenDoc
= GetFullscreenLeaf(this);
14568 AutoTArray
<Element
*, 8> exitElements
;
14570 Document
* doc
= fullScreenDoc
;
14571 // Collect all subdocuments.
14572 for (; doc
!= this; doc
= doc
->GetInProcessParentDocument()) {
14573 Element
* fsElement
= doc
->GetUnretargetedFullscreenElement();
14574 MOZ_ASSERT(fsElement
,
14575 "Parent document of "
14576 "a fullscreen document without fullscreen element?");
14577 exitElements
.AppendElement(fsElement
);
14579 MOZ_ASSERT(doc
== this, "Must have reached this doc");
14580 // Collect all ancestor documents which we are going to change.
14581 for (; doc
; doc
= doc
->GetInProcessParentDocument()) {
14582 Element
* fsElement
= doc
->GetUnretargetedFullscreenElement();
14583 MOZ_ASSERT(fsElement
,
14584 "Ancestor of fullscreen document must also be in fullscreen");
14586 if (auto* iframe
= HTMLIFrameElement::FromNode(fsElement
)) {
14587 if (iframe
->FullscreenFlag()) {
14588 // If this is an iframe, and it explicitly requested
14589 // fullscreen, don't rollback it automatically.
14594 exitElements
.AppendElement(fsElement
);
14595 if (doc
->CountFullscreenElements() > 1) {
14600 Document
* lastDoc
= exitElements
.LastElement()->OwnerDoc();
14601 size_t fullscreenCount
= lastDoc
->CountFullscreenElements();
14602 if (!lastDoc
->GetInProcessParentDocument() && fullscreenCount
== 1) {
14603 // If we are fully exiting fullscreen, don't touch anything here,
14604 // just wait for the window to get out from fullscreen first.
14605 PendingFullscreenChangeList::Add(std::move(aExit
));
14606 AskWindowToExitFullscreen(this);
14610 // If fullscreen mode is updated the pointer should be unlocked
14611 PointerLockManager::Unlock();
14612 // All documents listed in the array except the last one are going to
14613 // completely exit from the fullscreen state.
14614 for (auto i
: IntegerRange(exitElements
.Length() - 1)) {
14615 exitElements
[i
]->OwnerDoc()->CleanupFullscreenState();
14617 // The last document will either rollback one fullscreen element, or
14618 // completely exit from the fullscreen state as well.
14619 Document
* newFullscreenDoc
;
14620 if (fullscreenCount
> 1) {
14621 DebugOnly
<bool> removedFullscreenElement
= lastDoc
->PopFullscreenElement();
14622 MOZ_ASSERT(removedFullscreenElement
);
14623 newFullscreenDoc
= lastDoc
;
14625 lastDoc
->CleanupFullscreenState();
14626 newFullscreenDoc
= lastDoc
->GetInProcessParentDocument();
14628 // Dispatch the fullscreenchange event to all document listed. Note
14629 // that the loop order is reversed so that events are dispatched in
14630 // the tree order as indicated in the spec.
14631 for (Element
* e
: Reversed(exitElements
)) {
14632 DispatchFullscreenChange(*e
->OwnerDoc(), e
);
14634 aExit
->MayResolvePromise();
14636 MOZ_ASSERT(newFullscreenDoc
,
14637 "If we were going to exit from fullscreen on "
14638 "all documents in this doctree, we should've asked the window to "
14639 "exit first instead of reaching here.");
14640 if (fullScreenDoc
!= newFullscreenDoc
&&
14641 !nsContentUtils::HaveEqualPrincipals(fullScreenDoc
, newFullscreenDoc
)) {
14642 // We've popped so enough off the stack that we've rolled back to
14643 // a fullscreen element in a parent document. If this document is
14644 // cross origin, dispatch an event to chrome so it knows to show
14646 DispatchFullscreenNewOriginEvent(newFullscreenDoc
);
14650 static void UpdateViewportScrollbarOverrideForFullscreen(Document
* aDoc
) {
14651 if (nsPresContext
* presContext
= aDoc
->GetPresContext()) {
14652 presContext
->UpdateViewportScrollStylesOverride();
14656 static void NotifyFullScreenChangedForMediaElement(Element
& aElement
) {
14657 // When a media element enters the fullscreen, we would like to notify that
14658 // to the media controller in order to update its status.
14659 if (auto* mediaElem
= HTMLMediaElement::FromNode(aElement
)) {
14660 mediaElem
->NotifyFullScreenChanged();
14664 void Document::CleanupFullscreenState() {
14665 while (PopFullscreenElement(UpdateViewport::No
)) {
14666 // Remove the next one if appropriate
14669 UpdateViewportScrollbarOverrideForFullscreen(this);
14670 mFullscreenRoot
= nullptr;
14672 // Restore the zoom level that was in place prior to entering fullscreen.
14673 if (PresShell
* presShell
= GetPresShell()) {
14674 if (presShell
->GetMobileViewportManager()) {
14675 presShell
->SetResolutionAndScaleTo(
14676 mSavedResolution
, ResolutionChangeOrigin::MainThreadRestore
);
14681 bool Document::PopFullscreenElement(UpdateViewport aUpdateViewport
) {
14682 Element
* removedElement
= TopLayerPop([](Element
* element
) -> bool {
14683 return element
->State().HasState(ElementState::FULLSCREEN
);
14686 if (!removedElement
) {
14690 MOZ_ASSERT(removedElement
->State().HasState(ElementState::FULLSCREEN
));
14691 removedElement
->RemoveStates(ElementState::FULLSCREEN
| ElementState::MODAL
);
14692 NotifyFullScreenChangedForMediaElement(*removedElement
);
14693 // Reset iframe fullscreen flag.
14694 if (auto* iframe
= HTMLIFrameElement::FromNode(removedElement
)) {
14695 iframe
->SetFullscreenFlag(false);
14697 if (aUpdateViewport
== UpdateViewport::Yes
) {
14698 UpdateViewportScrollbarOverrideForFullscreen(this);
14703 void Document::SetFullscreenElement(Element
& aElement
) {
14704 ElementState statesToAdd
= ElementState::FULLSCREEN
;
14705 if (StaticPrefs::dom_fullscreen_modal() && !IsInChromeDocShell()) {
14706 // Don't make the document modal in chrome documents, since we don't want
14707 // the browser UI like the context menu / etc to be inert.
14708 statesToAdd
|= ElementState::MODAL
;
14710 aElement
.AddStates(statesToAdd
);
14711 TopLayerPush(aElement
);
14712 NotifyFullScreenChangedForMediaElement(aElement
);
14713 UpdateViewportScrollbarOverrideForFullscreen(this);
14716 void Document::TopLayerPush(Element
& aElement
) {
14717 const bool modal
= aElement
.State().HasState(ElementState::MODAL
);
14719 TopLayerPop(aElement
);
14720 mTopLayer
.AppendElement(do_GetWeakReference(&aElement
));
14721 NS_ASSERTION(GetTopLayerTop() == &aElement
, "Should match");
14724 aElement
.AddStates(ElementState::TOPMOST_MODAL
);
14726 bool foundExistingModalElement
= false;
14727 for (const nsWeakPtr
& weakPtr
: Reversed(mTopLayer
)) {
14728 nsCOMPtr
<Element
> element(do_QueryReferent(weakPtr
));
14729 if (element
&& element
!= &aElement
&&
14730 element
->State().HasState(ElementState::TOPMOST_MODAL
)) {
14731 element
->RemoveStates(ElementState::TOPMOST_MODAL
);
14732 foundExistingModalElement
= true;
14737 if (!foundExistingModalElement
) {
14738 Element
* root
= GetRootElement();
14739 MOZ_RELEASE_ASSERT(root
, "top layer element outside of document?");
14740 if (&aElement
!= root
) {
14741 // Add inert to the root element so that the inertness is applied to the
14742 // entire document.
14743 root
->AddStates(ElementState::INERT
);
14749 void Document::AddModalDialog(HTMLDialogElement
& aDialogElement
) {
14750 aDialogElement
.AddStates(ElementState::MODAL
);
14751 TopLayerPush(aDialogElement
);
14754 void Document::RemoveModalDialog(HTMLDialogElement
& aDialogElement
) {
14755 DebugOnly
<Element
*> removedElement
= TopLayerPop(aDialogElement
);
14756 MOZ_ASSERT(removedElement
== &aDialogElement
);
14757 aDialogElement
.RemoveStates(ElementState::MODAL
);
14760 Element
* Document::TopLayerPop(FunctionRef
<bool(Element
*)> aPredicate
) {
14761 if (mTopLayer
.IsEmpty()) {
14765 // Remove the topmost element that qualifies aPredicate; This
14766 // is required is because the top layer contains not only
14767 // fullscreen elements, but also dialog elements.
14768 Element
* removedElement
= nullptr;
14769 for (auto i
: Reversed(IntegerRange(mTopLayer
.Length()))) {
14770 nsCOMPtr
<Element
> element(do_QueryReferent(mTopLayer
[i
]));
14771 if (element
&& aPredicate(element
)) {
14772 removedElement
= element
;
14773 mTopLayer
.RemoveElementAt(i
);
14778 // Pop from the stack null elements (references to elements which have
14779 // been GC'd since they were added to the stack) and elements which are
14780 // no longer in this document.
14782 // FIXME(emilio): If this loop does something, it'd violate the assertions
14783 // from GetTopLayerTop()... What gives?
14784 while (!mTopLayer
.IsEmpty()) {
14785 Element
* element
= GetTopLayerTop();
14786 if (!element
|| element
->GetComposedDoc() != this) {
14787 mTopLayer
.RemoveLastElement();
14789 // The top element of the stack is now an in-doc element. Return here.
14794 if (!removedElement
) {
14798 const bool modal
= removedElement
->State().HasState(ElementState::MODAL
);
14801 removedElement
->RemoveStates(ElementState::TOPMOST_MODAL
);
14802 bool foundExistingModalElement
= false;
14803 for (const nsWeakPtr
& weakPtr
: Reversed(mTopLayer
)) {
14804 nsCOMPtr
<Element
> element(do_QueryReferent(weakPtr
));
14805 if (element
&& element
->State().HasState(ElementState::MODAL
)) {
14806 element
->AddStates(ElementState::TOPMOST_MODAL
);
14807 foundExistingModalElement
= true;
14811 // No more modal elements, make the document not inert anymore.
14812 if (!foundExistingModalElement
) {
14813 Element
* root
= GetRootElement();
14814 if (root
&& !root
->GetBoolAttr(nsGkAtoms::inert
)) {
14815 root
->RemoveStates(ElementState::INERT
);
14820 return removedElement
;
14823 Element
* Document::TopLayerPop(Element
& aElement
) {
14824 auto predictFunc
= [&aElement
](Element
* element
) {
14825 return element
== &aElement
;
14827 return TopLayerPop(predictFunc
);
14830 void Document::GetWireframe(bool aIncludeNodes
,
14831 Nullable
<Wireframe
>& aWireframe
) {
14832 FlushPendingNotifications(FlushType::Layout
);
14833 GetWireframeWithoutFlushing(aIncludeNodes
, aWireframe
);
14836 void Document::GetWireframeWithoutFlushing(bool aIncludeNodes
,
14837 Nullable
<Wireframe
>& aWireframe
) {
14838 using FrameForPointOptions
= nsLayoutUtils::FrameForPointOptions
;
14839 using FrameForPointOption
= nsLayoutUtils::FrameForPointOption
;
14841 PresShell
* shell
= GetPresShell();
14846 nsPresContext
* pc
= shell
->GetPresContext();
14851 nsIFrame
* rootFrame
= shell
->GetRootFrame();
14856 auto& wireframe
= aWireframe
.SetValue();
14857 wireframe
.mCanvasBackground
= shell
->ComputeCanvasBackground().mColor
;
14859 FrameForPointOptions options
;
14860 options
.mBits
+= FrameForPointOption::IgnoreCrossDoc
;
14861 options
.mBits
+= FrameForPointOption::IgnorePaintSuppression
;
14862 options
.mBits
+= FrameForPointOption::OnlyVisible
;
14864 AutoTArray
<nsIFrame
*, 32> frames
;
14865 const RelativeTo relativeTo
{rootFrame
, mozilla::ViewportType::Layout
};
14866 nsLayoutUtils::GetFramesForArea(relativeTo
, pc
->GetVisibleArea(), frames
,
14869 // TODO(emilio): We could rewrite hit testing to return nsDisplayItem*s or
14870 // something perhaps, but seems hard / like it'd involve at least some extra
14871 // copying around, since they don't outlive GetFramesForArea.
14872 auto& rects
= wireframe
.mRects
.Construct();
14873 if (!rects
.SetCapacity(frames
.Length(), fallible
)) {
14876 for (nsIFrame
* frame
: Reversed(frames
)) {
14878 rectType
] = [&]() -> std::tuple
<nscolor
, WireframeRectType
> {
14879 if (frame
->IsTextFrame()) {
14880 return {frame
->StyleText()->mWebkitTextFillColor
.CalcColor(frame
),
14881 WireframeRectType::Text
};
14883 if (frame
->IsImageFrame() || frame
->IsSVGOuterSVGFrame()) {
14884 return {0, WireframeRectType::Image
};
14886 if (frame
->IsThemed()) {
14887 return {0, WireframeRectType::Background
};
14889 bool drawImage
= false;
14890 bool drawColor
= false;
14891 if (const auto* bgStyle
= nsCSSRendering::FindBackground(frame
)) {
14892 const nscolor color
= nsCSSRendering::DetermineBackgroundColor(
14893 pc
, bgStyle
, frame
, drawImage
, drawColor
);
14895 !bgStyle
->StyleBackground()->mImage
.BottomLayer().mImage
.IsNone()) {
14896 return {color
, WireframeRectType::Image
};
14898 if (drawColor
&& !frame
->IsCanvasFrame()) {
14899 // Canvas frame background already accounted for in mCanvasBackground.
14900 return {color
, WireframeRectType::Background
};
14903 return {0, WireframeRectType::Unknown
};
14906 if (rectType
== WireframeRectType::Unknown
) {
14911 CSSRect::FromAppUnits(nsLayoutUtils::TransformFrameRectToAncestor(
14912 frame
, frame
->GetRectRelativeToSelf(), relativeTo
));
14913 if ((uint32_t)r
.Area() <
14914 StaticPrefs::browser_history_wireframeAreaThreshold()) {
14918 // Can't really fail because SetCapacity succeeded.
14919 auto& taggedRect
= *rects
.AppendElement(fallible
);
14921 if (aIncludeNodes
) {
14922 if (nsIContent
* c
= frame
->GetContent()) {
14923 taggedRect
.mNode
.Construct(c
);
14926 taggedRect
.mX
= r
.x
;
14927 taggedRect
.mY
= r
.y
;
14928 taggedRect
.mWidth
= r
.width
;
14929 taggedRect
.mHeight
= r
.height
;
14930 taggedRect
.mColor
= rectColor
;
14931 taggedRect
.mType
.Construct(rectType
);
14935 Element
* Document::GetTopLayerTop() {
14936 if (mTopLayer
.IsEmpty()) {
14939 uint32_t last
= mTopLayer
.Length() - 1;
14940 nsCOMPtr
<Element
> element(do_QueryReferent(mTopLayer
[last
]));
14941 NS_ASSERTION(element
, "Should have a top layer element!");
14942 NS_ASSERTION(element
->IsInComposedDoc(),
14943 "Top layer element should be in doc");
14944 NS_ASSERTION(element
->OwnerDoc() == this,
14945 "Top layer element should be in this doc");
14949 Element
* Document::GetUnretargetedFullscreenElement() const {
14950 for (const nsWeakPtr
& weakPtr
: Reversed(mTopLayer
)) {
14951 nsCOMPtr
<Element
> element(do_QueryReferent(weakPtr
));
14952 // Per spec, the fullscreen element is the topmost element in the document’s
14953 // top layer whose fullscreen flag is set, if any, and null otherwise.
14954 if (element
&& element
->State().HasState(ElementState::FULLSCREEN
)) {
14961 nsTArray
<Element
*> Document::GetTopLayer() const {
14962 nsTArray
<Element
*> elements
;
14963 for (const nsWeakPtr
& ptr
: mTopLayer
) {
14964 if (nsCOMPtr
<Element
> elem
= do_QueryReferent(ptr
)) {
14965 elements
.AppendElement(elem
);
14971 bool Document::TopLayerContains(Element
& aElement
) const {
14972 if (mTopLayer
.IsEmpty()) {
14975 nsWeakPtr weakElement
= do_GetWeakReference(&aElement
);
14976 return mTopLayer
.Contains(weakElement
);
14979 void Document::HideAllPopoversUntil(nsINode
& aEndpoint
,
14980 bool aFocusPreviousElement
,
14981 bool aFireEvents
) {
14982 auto closeAllOpenPopovers
= [&aFocusPreviousElement
, &aFireEvents
,
14983 this]() MOZ_CAN_RUN_SCRIPT_FOR_DEFINITION
{
14984 while (RefPtr
<Element
> topmost
= GetTopmostAutoPopover()) {
14985 HidePopover(*topmost
, aFocusPreviousElement
, aFireEvents
, IgnoreErrors());
14989 if (&aEndpoint
== this) {
14990 closeAllOpenPopovers();
14994 // https://github.com/whatwg/html/pull/9198
14995 auto needRepeatingHide
= [&]() {
14996 auto autoList
= AutoPopoverList();
14997 return autoList
.Contains(&aEndpoint
) &&
14998 &aEndpoint
!= autoList
.LastElement();
15001 MOZ_ASSERT((&aEndpoint
)->IsElement() &&
15002 (&aEndpoint
)->AsElement()->IsAutoPopover());
15003 bool repeatingHide
= false;
15004 bool fireEvents
= aFireEvents
;
15006 RefPtr
<const Element
> lastToHide
= nullptr;
15007 bool foundEndpoint
= false;
15008 for (const Element
* popover
: AutoPopoverList()) {
15009 if (popover
== &aEndpoint
) {
15010 foundEndpoint
= true;
15011 } else if (foundEndpoint
) {
15012 lastToHide
= popover
;
15017 if (!foundEndpoint
) {
15018 closeAllOpenPopovers();
15022 while (lastToHide
&& lastToHide
->IsPopoverOpen()) {
15023 RefPtr
<Element
> topmost
= GetTopmostAutoPopover();
15027 HidePopover(*topmost
, aFocusPreviousElement
, fireEvents
, IgnoreErrors());
15030 repeatingHide
= needRepeatingHide();
15031 if (repeatingHide
) {
15032 fireEvents
= false;
15034 } while (repeatingHide
);
15037 MOZ_CAN_RUN_SCRIPT_BOUNDARY
void
15038 Document::HideAllPopoversWithoutRunningScript() {
15039 return HideAllPopoversUntil(*this, false, false);
15042 void Document::HidePopover(Element
& aPopover
, bool aFocusPreviousElement
,
15043 bool aFireEvents
, ErrorResult
& aRv
) {
15044 RefPtr
<nsGenericHTMLElement
> popoverHTMLEl
=
15045 nsGenericHTMLElement::FromNode(aPopover
);
15046 NS_ASSERTION(popoverHTMLEl
, "Not a HTML element");
15048 if (!popoverHTMLEl
->CheckPopoverValidity(PopoverVisibilityState::Showing
,
15053 bool wasShowingOrHiding
=
15054 popoverHTMLEl
->GetPopoverData()->IsShowingOrHiding();
15055 popoverHTMLEl
->GetPopoverData()->SetIsShowingOrHiding(true);
15056 const bool fireEvents
= aFireEvents
&& !wasShowingOrHiding
;
15057 auto cleanupHidingFlag
= MakeScopeExit([&]() {
15058 if (auto* popoverData
= popoverHTMLEl
->GetPopoverData()) {
15059 popoverData
->SetIsShowingOrHiding(wasShowingOrHiding
);
15063 if (popoverHTMLEl
->IsAutoPopover()) {
15064 HideAllPopoversUntil(*popoverHTMLEl
, aFocusPreviousElement
, fireEvents
);
15065 if (!popoverHTMLEl
->CheckPopoverValidity(PopoverVisibilityState::Showing
,
15069 // TODO: we can't always guarantee:
15070 // The last item in document's auto popover list is popoverHTMLEl.
15071 // See, https://github.com/whatwg/html/issues/9197
15072 // If popoverHTMLEl is not on top, hide popovers again without firing
15074 if (NS_WARN_IF(GetTopmostAutoPopover() != popoverHTMLEl
)) {
15075 HideAllPopoversUntil(*popoverHTMLEl
, aFocusPreviousElement
, false);
15076 if (!popoverHTMLEl
->CheckPopoverValidity(PopoverVisibilityState::Showing
,
15080 MOZ_ASSERT(GetTopmostAutoPopover() == popoverHTMLEl
,
15081 "popoverHTMLEl should be on top of auto popover list");
15085 auto* data
= popoverHTMLEl
->GetPopoverData();
15086 MOZ_ASSERT(data
, "Should have popover data");
15087 data
->SetInvoker(nullptr);
15089 // Fire beforetoggle event and re-check popover validity.
15091 // Intentionally ignore the return value here as only on open event for
15092 // beforetoggle the cancelable attribute is initialized to true.
15093 popoverHTMLEl
->FireToggleEvent(PopoverVisibilityState::Showing
,
15094 PopoverVisibilityState::Hidden
,
15095 u
"beforetoggle"_ns
);
15096 if (!popoverHTMLEl
->CheckPopoverValidity(PopoverVisibilityState::Showing
,
15102 RemovePopoverFromTopLayer(aPopover
);
15104 popoverHTMLEl
->PopoverPseudoStateUpdate(false, true);
15105 popoverHTMLEl
->GetPopoverData()->SetPopoverVisibilityState(
15106 PopoverVisibilityState::Hidden
);
15108 // Queue popover toggle event task.
15110 popoverHTMLEl
->QueuePopoverEventTask(PopoverVisibilityState::Showing
);
15113 if (aFocusPreviousElement
) {
15114 popoverHTMLEl
->FocusPreviousElementAfterHidingPopover();
15116 popoverHTMLEl
->ForgetPreviouslyFocusedElementAfterHidingPopover();
15120 nsTArray
<Element
*> Document::AutoPopoverList() const {
15121 nsTArray
<Element
*> elements
;
15122 for (const nsWeakPtr
& ptr
: mTopLayer
) {
15123 if (nsCOMPtr
<Element
> element
= do_QueryReferent(ptr
)) {
15124 if (element
&& element
->IsAutoPopover() && element
->IsPopoverOpen()) {
15125 elements
.AppendElement(element
);
15132 Element
* Document::GetTopmostAutoPopover() const {
15133 for (const nsWeakPtr
& weakPtr
: Reversed(mTopLayer
)) {
15134 nsCOMPtr
<Element
> element(do_QueryReferent(weakPtr
));
15135 if (element
&& element
->IsAutoPopover() && element
->IsPopoverOpen()) {
15142 void Document::AddToAutoPopoverList(Element
& aElement
) {
15143 MOZ_ASSERT(aElement
.IsAutoPopover());
15144 TopLayerPush(aElement
);
15147 void Document::RemoveFromAutoPopoverList(Element
& aElement
) {
15148 MOZ_ASSERT(aElement
.IsAutoPopover());
15149 TopLayerPop(aElement
);
15152 void Document::AddPopoverToTopLayer(Element
& aElement
) {
15153 MOZ_ASSERT(aElement
.GetPopoverData());
15154 TopLayerPush(aElement
);
15157 void Document::RemovePopoverFromTopLayer(Element
& aElement
) {
15158 MOZ_ASSERT(aElement
.GetPopoverData());
15159 TopLayerPop(aElement
);
15162 // Returns true if aDoc browsing context is focused.
15163 bool IsInFocusedTab(Document
* aDoc
) {
15164 BrowsingContext
* bc
= aDoc
->GetBrowsingContext();
15169 nsFocusManager
* fm
= nsFocusManager::GetFocusManager();
15174 if (XRE_IsParentProcess()) {
15175 // Keep dom/tests/mochitest/chrome/test_MozDomFullscreen_event.xhtml happy
15176 // by retaining the old code path for the parent process.
15177 nsIDocShell
* docshell
= aDoc
->GetDocShell();
15181 nsCOMPtr
<nsIDocShellTreeItem
> rootItem
;
15182 docshell
->GetInProcessRootTreeItem(getter_AddRefs(rootItem
));
15186 nsCOMPtr
<nsPIDOMWindowOuter
> rootWin
= rootItem
->GetWindow();
15191 nsCOMPtr
<nsPIDOMWindowOuter
> activeWindow
;
15192 activeWindow
= fm
->GetActiveWindow();
15193 if (!activeWindow
) {
15197 return activeWindow
== rootWin
;
15200 return fm
->GetActiveBrowsingContext() == bc
->Top();
15203 // Returns true if aDoc browsing context is focused and is also active.
15204 bool IsInActiveTab(Document
* aDoc
) {
15205 if (!IsInFocusedTab(aDoc
)) {
15209 BrowsingContext
* bc
= aDoc
->GetBrowsingContext();
15210 MOZ_ASSERT(bc
, "With no BrowsingContext, we should have failed earlier.");
15211 return bc
->IsActive();
15214 void Document::RemoteFrameFullscreenChanged(Element
* aFrameElement
) {
15215 // Ensure the frame element is the fullscreen element in this document.
15216 // If the frame element is already the fullscreen element in this document,
15217 // this has no effect.
15218 auto request
= FullscreenRequest::CreateForRemote(aFrameElement
);
15219 RequestFullscreen(std::move(request
), XRE_IsContentProcess());
15222 void Document::RemoteFrameFullscreenReverted() {
15223 UniquePtr
<FullscreenExit
> exit
= FullscreenExit::CreateForRemote(this);
15224 RestorePreviousFullscreenState(std::move(exit
));
15227 static bool HasFullscreenSubDocument(Document
& aDoc
) {
15228 uint32_t count
= CountFullscreenSubDocuments(aDoc
);
15229 NS_ASSERTION(count
<= 1,
15230 "Fullscreen docs should have at most 1 fullscreen child!");
15234 // Returns nullptr if a request for Fullscreen API is currently enabled
15235 // in the given document. Returns a static string indicates the reason
15236 // why it is not enabled otherwise.
15237 const char* Document::GetFullscreenError(CallerType aCallerType
) {
15238 if (!StaticPrefs::full_screen_api_enabled()) {
15239 return "FullscreenDeniedDisabled";
15242 if (aCallerType
== CallerType::System
) {
15243 // Chrome code can always use the fullscreen API, provided it's not
15244 // explicitly disabled.
15248 if (!IsVisible()) {
15249 return "FullscreenDeniedHidden";
15252 if (!FeaturePolicyUtils::IsFeatureAllowed(this, u
"fullscreen"_ns
)) {
15253 return "FullscreenDeniedFeaturePolicy";
15256 // Ensure that all containing elements are <iframe> and have allowfullscreen
15258 BrowsingContext
* bc
= GetBrowsingContext();
15259 if (!bc
|| !bc
->FullscreenAllowed()) {
15260 return "FullscreenDeniedContainerNotAllowed";
15266 bool Document::FullscreenElementReadyCheck(FullscreenRequest
& aRequest
) {
15267 Element
* elem
= aRequest
.Element();
15268 // Strictly speaking, this isn't part of the fullscreen element ready
15269 // check in the spec, but per steps in the spec, when an element which
15270 // is already the fullscreen element requests fullscreen, nothing
15271 // should change and no event should be dispatched, but we still need
15272 // to resolve the returned promise.
15273 Element
* fullscreenElement
= GetUnretargetedFullscreenElement();
15274 if (elem
== fullscreenElement
) {
15275 aRequest
.MayResolvePromise();
15278 if (!elem
->IsInComposedDoc()) {
15279 aRequest
.Reject("FullscreenDeniedNotInDocument");
15282 if (elem
->IsPopoverOpen()) {
15283 aRequest
.Reject("FullscreenDeniedPopoverOpen");
15286 if (elem
->OwnerDoc() != this) {
15287 aRequest
.Reject("FullscreenDeniedMovedDocument");
15290 if (!GetWindow()) {
15291 aRequest
.Reject("FullscreenDeniedLostWindow");
15294 if (const char* msg
= GetFullscreenError(aRequest
.mCallerType
)) {
15295 aRequest
.Reject(msg
);
15298 if (HasFullscreenSubDocument(*this)) {
15299 aRequest
.Reject("FullscreenDeniedSubDocFullScreen");
15302 if (elem
->IsHTMLElement(nsGkAtoms::dialog
)) {
15303 aRequest
.Reject("FullscreenDeniedHTMLDialog");
15306 if (!nsContentUtils::IsChromeDoc(this) && !IsInFocusedTab(this)) {
15307 aRequest
.Reject("FullscreenDeniedNotFocusedTab");
15310 // Deny requests when a windowed plugin is focused.
15311 nsFocusManager
* fm
= nsFocusManager::GetFocusManager();
15313 NS_WARNING("Failed to retrieve focus manager in fullscreen request.");
15314 aRequest
.MayRejectPromise("An unexpected error occurred");
15317 if (nsContentUtils::HasPluginWithUncontrolledEventDispatch(
15318 fm
->GetFocusedElement())) {
15319 aRequest
.Reject("FullscreenDeniedFocusedPlugin");
15325 static nsCOMPtr
<nsPIDOMWindowOuter
> GetRootWindow(Document
* aDoc
) {
15326 MOZ_ASSERT(XRE_IsParentProcess());
15327 nsIDocShell
* docShell
= aDoc
->GetDocShell();
15331 nsCOMPtr
<nsIDocShellTreeItem
> rootItem
;
15332 docShell
->GetInProcessRootTreeItem(getter_AddRefs(rootItem
));
15333 return rootItem
? rootItem
->GetWindow() : nullptr;
15336 static bool ShouldApplyFullscreenDirectly(Document
* aDoc
,
15337 nsPIDOMWindowOuter
* aRootWin
) {
15338 MOZ_ASSERT(XRE_IsParentProcess());
15339 // If we are in the chrome process, and the window has not been in
15340 // fullscreen, we certainly need to make that fullscreen first.
15341 if (!aRootWin
->GetFullScreen()) {
15344 // The iterator not being at end indicates there is still some
15345 // pending fullscreen request relates to this document. We have to
15346 // push the request to the pending queue so requests are handled
15347 // in the correct order.
15348 PendingFullscreenChangeList::Iterator
<FullscreenRequest
> iter(
15349 aDoc
, PendingFullscreenChangeList::eDocumentsWithSameRoot
);
15350 if (!iter
.AtEnd()) {
15354 // Same thing for exits. If we have any pending, we have to push
15355 // to the pending queue.
15356 PendingFullscreenChangeList::Iterator
<FullscreenExit
> iterExit(
15357 aDoc
, PendingFullscreenChangeList::eDocumentsWithSameRoot
);
15358 if (!iterExit
.AtEnd()) {
15362 // We have to apply the fullscreen state directly in this case,
15363 // because nsGlobalWindow::SetFullscreenInternal() will do nothing
15364 // if it is already in fullscreen. If we do not apply the state but
15365 // instead add it to the queue and wait for the window as normal,
15366 // we would get stuck.
15370 static bool CheckFullscreenAllowedElementType(const Element
* elem
) {
15371 // Per spec only HTML, <svg>, and <math> should be allowed, but
15372 // we also need to allow XUL elements right now.
15373 return elem
->IsHTMLElement() || elem
->IsXULElement() ||
15374 elem
->IsSVGElement(nsGkAtoms::svg
) ||
15375 elem
->IsMathMLElement(nsGkAtoms::math
);
15378 void Document::RequestFullscreen(UniquePtr
<FullscreenRequest
> aRequest
,
15379 bool aApplyFullscreenDirectly
) {
15380 if (XRE_IsContentProcess()) {
15381 RequestFullscreenInContentProcess(std::move(aRequest
),
15382 aApplyFullscreenDirectly
);
15384 RequestFullscreenInParentProcess(std::move(aRequest
),
15385 aApplyFullscreenDirectly
);
15389 void Document::RequestFullscreenInContentProcess(
15390 UniquePtr
<FullscreenRequest
> aRequest
, bool aApplyFullscreenDirectly
) {
15391 MOZ_ASSERT(XRE_IsContentProcess());
15393 // If we are in the content process, we can apply the fullscreen
15394 // state directly only if we have been in DOM fullscreen, because
15395 // otherwise we always need to notify the chrome.
15396 if (aApplyFullscreenDirectly
||
15397 nsContentUtils::GetInProcessSubtreeRootDocument(this)->Fullscreen()) {
15398 ApplyFullscreen(std::move(aRequest
));
15402 if (!CheckFullscreenAllowedElementType(aRequest
->Element())) {
15403 aRequest
->Reject("FullscreenDeniedNotHTMLSVGOrMathML");
15407 // We don't need to check element ready before this point, because
15408 // if we called ApplyFullscreen, it would check that for us.
15409 if (!FullscreenElementReadyCheck(*aRequest
)) {
15413 PendingFullscreenChangeList::Add(std::move(aRequest
));
15414 // If we are not the top level process, dispatch an event to make
15415 // our parent process go fullscreen first.
15416 nsContentUtils::DispatchEventOnlyToChrome(
15417 this, ToSupports(this), u
"MozDOMFullscreen:Request"_ns
, CanBubble::eYes
,
15418 Cancelable::eNo
, /* DefaultAction */ nullptr);
15421 void Document::RequestFullscreenInParentProcess(
15422 UniquePtr
<FullscreenRequest
> aRequest
, bool aApplyFullscreenDirectly
) {
15423 MOZ_ASSERT(XRE_IsParentProcess());
15424 nsCOMPtr
<nsPIDOMWindowOuter
> rootWin
= GetRootWindow(this);
15426 aRequest
->MayRejectPromise("No active window");
15430 if (aApplyFullscreenDirectly
||
15431 ShouldApplyFullscreenDirectly(this, rootWin
)) {
15432 ApplyFullscreen(std::move(aRequest
));
15436 if (!CheckFullscreenAllowedElementType(aRequest
->Element())) {
15437 aRequest
->Reject("FullscreenDeniedNotHTMLSVGOrMathML");
15441 // See if we're waiting on an exit. If so, just make this one pending.
15442 PendingFullscreenChangeList::Iterator
<FullscreenExit
> iter(
15443 this, PendingFullscreenChangeList::eDocumentsWithSameRoot
);
15444 if (!iter
.AtEnd()) {
15445 PendingFullscreenChangeList::Add(std::move(aRequest
));
15446 rootWin
->SetFullscreenInternal(FullscreenReason::ForFullscreenAPI
, true);
15450 // We don't need to check element ready before this point, because
15451 // if we called ApplyFullscreen, it would check that for us.
15452 if (!FullscreenElementReadyCheck(*aRequest
)) {
15456 PendingFullscreenChangeList::Add(std::move(aRequest
));
15457 // Make the window fullscreen.
15458 rootWin
->SetFullscreenInternal(FullscreenReason::ForFullscreenAPI
, true);
15462 bool Document::HandlePendingFullscreenRequests(Document
* aDoc
) {
15463 bool handled
= false;
15464 PendingFullscreenChangeList::Iterator
<FullscreenRequest
> iter(
15465 aDoc
, PendingFullscreenChangeList::eDocumentsWithSameRoot
);
15466 while (!iter
.AtEnd()) {
15467 UniquePtr
<FullscreenRequest
> request
= iter
.TakeAndNext();
15468 Document
* doc
= request
->Document();
15469 if (doc
->ApplyFullscreen(std::move(request
))) {
15477 void Document::ClearPendingFullscreenRequests(Document
* aDoc
) {
15478 PendingFullscreenChangeList::Iterator
<FullscreenRequest
> iter(
15479 aDoc
, PendingFullscreenChangeList::eInclusiveDescendants
);
15480 while (!iter
.AtEnd()) {
15481 UniquePtr
<FullscreenRequest
> request
= iter
.TakeAndNext();
15482 request
->MayRejectPromise("Fullscreen request aborted");
15486 bool Document::HasPendingFullscreenRequests() {
15487 PendingFullscreenChangeList::Iterator
<FullscreenRequest
> iter(
15488 this, PendingFullscreenChangeList::eDocumentsWithSameRoot
);
15489 return !iter
.AtEnd();
15492 bool Document::ApplyFullscreen(UniquePtr
<FullscreenRequest
> aRequest
) {
15493 if (!FullscreenElementReadyCheck(*aRequest
)) {
15497 RefPtr
<Document
> doc
= aRequest
->Document();
15498 doc
->HideAllPopoversWithoutRunningScript();
15500 // Stash a reference to any existing fullscreen doc, we'll use this later
15501 // to detect if the origin which is fullscreen has changed.
15502 nsCOMPtr
<Document
> previousFullscreenDoc
= GetFullscreenLeaf(this);
15504 // Stores a list of documents which we must dispatch "fullscreenchange"
15505 // too. We're required by the spec to dispatch the events in root-to-leaf
15506 // order, but we traverse the doctree in a leaf-to-root order, so we save
15507 // references to the documents we must dispatch to so that we get the order
15509 AutoTArray
<Document
*, 8> changed
;
15511 // Remember the root document, so that if a fullscreen document is hidden
15512 // we can reset fullscreen state in the remaining visible fullscreen
15514 Document
* fullScreenRootDoc
=
15515 nsContentUtils::GetInProcessSubtreeRootDocument(this);
15517 // If a document is already in fullscreen, then unlock the mouse pointer
15518 // before setting a new document to fullscreen
15519 PointerLockManager::Unlock();
15521 // Set the fullscreen element. This sets the fullscreen style on the
15522 // element, and the fullscreen-ancestor styles on ancestors of the element
15523 // in this document.
15524 Element
* elem
= aRequest
->Element();
15525 SetFullscreenElement(*elem
);
15526 // Set the iframe fullscreen flag.
15527 if (auto* iframe
= HTMLIFrameElement::FromNode(elem
)) {
15528 iframe
->SetFullscreenFlag(true);
15530 changed
.AppendElement(this);
15532 // Propagate up the document hierarchy, setting the fullscreen element as
15533 // the element's container in ancestor documents. This also sets the
15534 // appropriate css styles as well. Note we don't propagate down the
15535 // document hierarchy, the fullscreen element (or its container) is not
15536 // visible there. Stop when we reach the root document.
15537 Document
* child
= this;
15539 child
->SetFullscreenRoot(fullScreenRootDoc
);
15541 // When entering fullscreen, reset the RCD's resolution to the intrinsic
15542 // resolution, otherwise the fullscreen content could be sized larger than
15543 // the screen (since fullscreen is implemented using position:fixed and
15544 // fixed elements are sized to the layout viewport).
15545 // This also ensures that things like video controls aren't zoomed in
15546 // when in fullscreen mode.
15547 if (PresShell
* presShell
= child
->GetPresShell()) {
15548 if (RefPtr
<MobileViewportManager
> manager
=
15549 presShell
->GetMobileViewportManager()) {
15550 // Save the previous resolution so it can be restored.
15551 child
->mSavedResolution
= presShell
->GetResolution();
15552 presShell
->SetResolutionAndScaleTo(
15553 manager
->ComputeIntrinsicResolution(),
15554 ResolutionChangeOrigin::MainThreadRestore
);
15558 NS_ASSERTION(child
->GetFullscreenRoot() == fullScreenRootDoc
,
15559 "Fullscreen root should be set!");
15560 if (child
== fullScreenRootDoc
) {
15564 Element
* element
= child
->GetEmbedderElement();
15566 // We've reached the root.No more changes need to be made
15567 // to the top layer stacks of documents further up the tree.
15571 Document
* parent
= child
->GetInProcessParentDocument();
15572 parent
->SetFullscreenElement(*element
);
15573 changed
.AppendElement(parent
);
15577 FullscreenRoots::Add(this);
15579 // If it is the first entry of the fullscreen, trigger an event so
15580 // that the UI can response to this change, e.g. hide chrome, or
15581 // notifying parent process to enter fullscreen. Note that chrome
15582 // code may also want to listen to MozDOMFullscreen:NewOrigin event
15583 // to pop up warning UI.
15584 if (!previousFullscreenDoc
) {
15585 nsContentUtils::DispatchEventOnlyToChrome(
15586 this, ToSupports(elem
), u
"MozDOMFullscreen:Entered"_ns
, CanBubble::eYes
,
15587 Cancelable::eNo
, /* DefaultAction */ nullptr);
15590 // The origin which is fullscreen gets changed. Trigger an event so
15591 // that the chrome knows to pop up a warning UI. Note that
15592 // previousFullscreenDoc == nullptr upon first entry, we show the warning UI
15593 // directly as soon as chrome document goes into fullscreen state. Also note
15594 // that, in a multi-process browser, the code in content process is
15595 // responsible for sending message with the origin to its parent, and the
15596 // parent shouldn't rely on this event itself.
15597 if (aRequest
->mShouldNotifyNewOrigin
&& previousFullscreenDoc
&&
15598 !nsContentUtils::HaveEqualPrincipals(previousFullscreenDoc
, this)) {
15599 DispatchFullscreenNewOriginEvent(this);
15602 // Dispatch "fullscreenchange" events. Note that the loop order is
15603 // reversed so that events are dispatched in the tree order as
15604 // indicated in the spec.
15605 for (Document
* d
: Reversed(changed
)) {
15606 DispatchFullscreenChange(*d
, d
->GetUnretargetedFullscreenElement());
15608 aRequest
->MayResolvePromise();
15612 void Document::ClearOrientationPendingPromise() {
15613 mOrientationPendingPromise
= nullptr;
15616 bool Document::SetOrientationPendingPromise(Promise
* aPromise
) {
15617 if (mIsGoingAway
) {
15621 mOrientationPendingPromise
= aPromise
;
15625 void Document::UpdateVisibilityState(DispatchVisibilityChange aDispatchEvent
) {
15626 dom::VisibilityState oldState
= mVisibilityState
;
15627 mVisibilityState
= ComputeVisibilityState();
15628 if (oldState
!= mVisibilityState
) {
15629 if (aDispatchEvent
== DispatchVisibilityChange::Yes
) {
15630 nsContentUtils::DispatchTrustedEvent(this, ToSupports(this),
15631 u
"visibilitychange"_ns
,
15632 CanBubble::eYes
, Cancelable::eNo
);
15634 NotifyActivityChanged();
15635 if (mVisibilityState
== dom::VisibilityState::Visible
) {
15636 MaybeActiveMediaComponents();
15639 bool visible
= !Hidden();
15640 for (auto* listener
: mWorkerListeners
) {
15641 listener
->OnVisible(visible
);
15646 void Document::AddWorkerDocumentListener(WorkerDocumentListener
* aListener
) {
15647 mWorkerListeners
.Insert(aListener
);
15648 aListener
->OnVisible(!Hidden());
15651 void Document::RemoveWorkerDocumentListener(WorkerDocumentListener
* aListener
) {
15652 mWorkerListeners
.Remove(aListener
);
15655 VisibilityState
Document::ComputeVisibilityState() const {
15656 // We have to check a few pieces of information here:
15657 // 1) Are we in bfcache (!IsVisible())? If so, nothing else matters.
15658 // 2) Do we have an outer window? If not, we're hidden. Note that we don't
15659 // want to use GetWindow here because it does weird groveling for windows
15661 // 3) Is our outer window background? If so, we're hidden.
15662 // Otherwise, we're visible.
15663 if (!IsVisible() || !mWindow
|| !mWindow
->GetOuterWindow() ||
15664 mWindow
->GetOuterWindow()->IsBackground()) {
15665 return dom::VisibilityState::Hidden
;
15668 return dom::VisibilityState::Visible
;
15671 void Document::PostVisibilityUpdateEvent() {
15672 nsCOMPtr
<nsIRunnable
> event
= NewRunnableMethod
<DispatchVisibilityChange
>(
15673 "Document::UpdateVisibilityState", this, &Document::UpdateVisibilityState
,
15674 DispatchVisibilityChange::Yes
);
15675 Dispatch(TaskCategory::Other
, event
.forget());
15678 void Document::MaybeActiveMediaComponents() {
15679 auto* window
= GetWindow();
15680 if (!window
|| !window
->ShouldDelayMediaFromStart()) {
15683 window
->ActivateMediaComponents();
15686 void Document::DocAddSizeOfExcludingThis(nsWindowSizes
& aWindowSizes
) const {
15687 nsINode::AddSizeOfExcludingThis(aWindowSizes
,
15688 &aWindowSizes
.mDOMSizes
.mDOMOtherSize
);
15690 for (nsIContent
* kid
= GetFirstChild(); kid
; kid
= kid
->GetNextSibling()) {
15691 AddSizeOfNodeTree(*kid
, aWindowSizes
);
15694 // IMPORTANT: for our ComputedValues measurements, we want to measure
15695 // ComputedValues accessible from DOM elements before ComputedValues not
15696 // accessible from DOM elements (i.e. accessible only from the frame tree).
15698 // Therefore, the measurement of the Document superclass must happen after
15699 // the measurement of DOM nodes (above), because Document contains the
15700 // PresShell, which contains the frame tree.
15702 mPresShell
->AddSizeOfIncludingThis(aWindowSizes
);
15705 mStyleSet
->AddSizeOfIncludingThis(aWindowSizes
);
15707 aWindowSizes
.mPropertyTablesSize
+=
15708 mPropertyTable
.SizeOfExcludingThis(aWindowSizes
.mState
.mMallocSizeOf
);
15710 if (EventListenerManager
* elm
= GetExistingListenerManager()) {
15711 aWindowSizes
.mDOMEventListenersCount
+= elm
->ListenerCount();
15714 if (mNodeInfoManager
) {
15715 mNodeInfoManager
->AddSizeOfIncludingThis(aWindowSizes
);
15718 aWindowSizes
.mDOMSizes
.mDOMMediaQueryLists
+=
15719 mDOMMediaQueryLists
.sizeOfExcludingThis(
15720 aWindowSizes
.mState
.mMallocSizeOf
);
15722 for (const MediaQueryList
* mql
: mDOMMediaQueryLists
) {
15723 aWindowSizes
.mDOMSizes
.mDOMMediaQueryLists
+=
15724 mql
->SizeOfExcludingThis(aWindowSizes
.mState
.mMallocSizeOf
);
15727 DocumentOrShadowRoot::AddSizeOfExcludingThis(aWindowSizes
);
15729 for (auto& sheetArray
: mAdditionalSheets
) {
15730 AddSizeOfOwnedSheetArrayExcludingThis(aWindowSizes
, sheetArray
);
15732 // Lumping in the loader with the style-sheets size is not ideal,
15733 // but most of the things in there are in fact stylesheets, so it
15734 // doesn't seem worthwhile to separate it out.
15735 // This can be null if we've already been unlinked.
15737 aWindowSizes
.mLayoutStyleSheetsSize
+=
15738 mCSSLoader
->SizeOfIncludingThis(aWindowSizes
.mState
.mMallocSizeOf
);
15741 if (mResizeObserverController
) {
15742 mResizeObserverController
->AddSizeOfIncludingThis(aWindowSizes
);
15745 aWindowSizes
.mDOMSizes
.mDOMOtherSize
+=
15746 mAttrStyleSheet
? mAttrStyleSheet
->DOMSizeOfIncludingThis(
15747 aWindowSizes
.mState
.mMallocSizeOf
)
15750 aWindowSizes
.mDOMSizes
.mDOMOtherSize
+=
15751 mStyledLinks
.ShallowSizeOfExcludingThis(
15752 aWindowSizes
.mState
.mMallocSizeOf
);
15754 // Measurement of the following members may be added later if DMD finds it
15756 // - mMidasCommandManager
15760 void Document::DocAddSizeOfIncludingThis(nsWindowSizes
& aWindowSizes
) const {
15761 aWindowSizes
.mDOMSizes
.mDOMOtherSize
+=
15762 aWindowSizes
.mState
.mMallocSizeOf(this);
15763 DocAddSizeOfExcludingThis(aWindowSizes
);
15766 void Document::AddSizeOfExcludingThis(nsWindowSizes
& aSizes
,
15767 size_t* aNodeSize
) const {
15768 // This AddSizeOfExcludingThis() overrides the one from nsINode. But
15769 // nsDocuments can only appear at the top of the DOM tree, and we use the
15770 // specialized DocAddSizeOfExcludingThis() in that case. So this should never
15776 void Document::AddSizeOfNodeTree(nsINode
& aNode
, nsWindowSizes
& aWindowSizes
) {
15777 size_t nodeSize
= 0;
15778 aNode
.AddSizeOfIncludingThis(aWindowSizes
, &nodeSize
);
15780 // This is where we transfer the nodeSize obtained from
15781 // nsINode::AddSizeOfIncludingThis() to a value in nsWindowSizes.
15782 switch (aNode
.NodeType()) {
15783 case nsINode::ELEMENT_NODE
:
15784 aWindowSizes
.mDOMSizes
.mDOMElementNodesSize
+= nodeSize
;
15786 case nsINode::TEXT_NODE
:
15787 aWindowSizes
.mDOMSizes
.mDOMTextNodesSize
+= nodeSize
;
15789 case nsINode::CDATA_SECTION_NODE
:
15790 aWindowSizes
.mDOMSizes
.mDOMCDATANodesSize
+= nodeSize
;
15792 case nsINode::COMMENT_NODE
:
15793 aWindowSizes
.mDOMSizes
.mDOMCommentNodesSize
+= nodeSize
;
15796 aWindowSizes
.mDOMSizes
.mDOMOtherSize
+= nodeSize
;
15800 if (EventListenerManager
* elm
= aNode
.GetExistingListenerManager()) {
15801 aWindowSizes
.mDOMEventListenersCount
+= elm
->ListenerCount();
15804 if (aNode
.IsContent()) {
15805 nsTArray
<nsIContent
*> anonKids
;
15806 nsContentUtils::AppendNativeAnonymousChildren(aNode
.AsContent(), anonKids
,
15807 nsIContent::eAllChildren
);
15808 for (nsIContent
* anonKid
: anonKids
) {
15809 AddSizeOfNodeTree(*anonKid
, aWindowSizes
);
15812 if (auto* element
= Element::FromNode(aNode
)) {
15813 if (ShadowRoot
* shadow
= element
->GetShadowRoot()) {
15814 AddSizeOfNodeTree(*shadow
, aWindowSizes
);
15819 // NOTE(emilio): If you feel smart and want to change this function to use
15820 // GetNextNode(), think twice, since you'd need to handle <xbl:content> in a
15821 // sane way, and kids of <content> won't point to the parent, so we'd never
15822 // find the root node where we should stop at.
15823 for (nsIContent
* kid
= aNode
.GetFirstChild(); kid
;
15824 kid
= kid
->GetNextSibling()) {
15825 AddSizeOfNodeTree(*kid
, aWindowSizes
);
15829 already_AddRefed
<Document
> Document::Constructor(const GlobalObject
& aGlobal
,
15831 nsCOMPtr
<nsIScriptGlobalObject
> global
=
15832 do_QueryInterface(aGlobal
.GetAsSupports());
15834 rv
.Throw(NS_ERROR_UNEXPECTED
);
15838 nsCOMPtr
<nsIScriptObjectPrincipal
> prin
=
15839 do_QueryInterface(aGlobal
.GetAsSupports());
15841 rv
.Throw(NS_ERROR_UNEXPECTED
);
15845 nsCOMPtr
<nsIURI
> uri
;
15846 NS_NewURI(getter_AddRefs(uri
), "about:blank");
15848 rv
.Throw(NS_ERROR_OUT_OF_MEMORY
);
15852 nsCOMPtr
<Document
> doc
;
15853 nsresult res
= NS_NewDOMDocument(getter_AddRefs(doc
), VoidString(), u
""_ns
,
15854 nullptr, uri
, uri
, prin
->GetPrincipal(),
15855 true, global
, DocumentFlavorPlain
);
15856 if (NS_FAILED(res
)) {
15861 doc
->SetReadyStateInternal(Document::READYSTATE_COMPLETE
);
15863 return doc
.forget();
15866 XPathExpression
* Document::CreateExpression(const nsAString
& aExpression
,
15867 XPathNSResolver
* aResolver
,
15869 return XPathEvaluator()->CreateExpression(aExpression
, aResolver
, rv
);
15872 nsINode
* Document::CreateNSResolver(nsINode
& aNodeResolver
) {
15873 return XPathEvaluator()->CreateNSResolver(aNodeResolver
);
15876 already_AddRefed
<XPathResult
> Document::Evaluate(
15877 JSContext
* aCx
, const nsAString
& aExpression
, nsINode
& aContextNode
,
15878 XPathNSResolver
* aResolver
, uint16_t aType
, JS::Handle
<JSObject
*> aResult
,
15880 return XPathEvaluator()->Evaluate(aCx
, aExpression
, aContextNode
, aResolver
,
15881 aType
, aResult
, rv
);
15884 already_AddRefed
<nsIAppWindow
> Document::GetAppWindowIfToplevelChrome() const {
15885 nsCOMPtr
<nsIDocShellTreeItem
> item
= GetDocShell();
15889 nsCOMPtr
<nsIDocShellTreeOwner
> owner
;
15890 item
->GetTreeOwner(getter_AddRefs(owner
));
15891 nsCOMPtr
<nsIAppWindow
> appWin
= do_GetInterface(owner
);
15895 nsCOMPtr
<nsIDocShell
> appWinShell
;
15896 appWin
->GetDocShell(getter_AddRefs(appWinShell
));
15897 if (!SameCOMIdentity(appWinShell
, item
)) {
15900 return appWin
.forget();
15903 WindowContext
* Document::GetTopLevelWindowContext() const {
15904 WindowContext
* windowContext
= GetWindowContext();
15905 return windowContext
? windowContext
->TopWindowContext() : nullptr;
15908 Document
* Document::GetTopLevelContentDocumentIfSameProcess() {
15911 if (!mLoadedAsData
) {
15914 nsCOMPtr
<nsPIDOMWindowInner
> window
= do_QueryInterface(GetScopeObject());
15919 parent
= window
->GetExtantDoc();
15926 if (parent
->IsTopLevelContentDocument()) {
15930 // If we ever have a non-content parent before we hit a toplevel content
15931 // parent, then we're never going to find one. Just bail.
15932 if (!parent
->IsContentDocument()) {
15936 parent
= parent
->GetInProcessParentDocument();
15942 const Document
* Document::GetTopLevelContentDocumentIfSameProcess() const {
15943 return const_cast<Document
*>(this)->GetTopLevelContentDocumentIfSameProcess();
15946 void Document::PropagateImageUseCounters(Document
* aReferencingDocument
) {
15947 MOZ_ASSERT(IsBeingUsedAsImage());
15948 MOZ_ASSERT(aReferencingDocument
);
15950 if (!aReferencingDocument
->mShouldReportUseCounters
) {
15951 // No need to propagate use counters to a document that itself won't report
15956 MOZ_LOG(gUseCountersLog
, LogLevel::Debug
,
15957 ("PropagateImageUseCounters from %s to %s",
15958 nsContentUtils::TruncatedURLForDisplay(mDocumentURI
).get(),
15959 nsContentUtils::TruncatedURLForDisplay(
15960 aReferencingDocument
->mDocumentURI
)
15963 if (aReferencingDocument
->IsBeingUsedAsImage()) {
15965 "Page use counters from nested image documents may not "
15966 "propagate to the top-level document (bug 1657805)");
15969 SetCssUseCounterBits();
15970 aReferencingDocument
->mChildDocumentUseCounters
|= mUseCounters
;
15971 aReferencingDocument
->mChildDocumentUseCounters
|= mChildDocumentUseCounters
;
15974 bool Document::HasScriptsBlockedBySandbox() const {
15975 return mSandboxFlags
& SANDBOXED_SCRIPTS
;
15978 // Some use-counter sanity-checking.
15979 static_assert(size_t(eUseCounter_EndCSSProperties
) -
15980 size_t(eUseCounter_FirstCSSProperty
) ==
15981 size_t(eCSSProperty_COUNT_with_aliases
),
15982 "We should have the right amount of CSS property use counters");
15983 static_assert(size_t(eUseCounter_Count
) -
15984 size_t(eUseCounter_FirstCountedUnknownProperty
) ==
15985 size_t(CountedUnknownProperty::Count
),
15986 "We should have the right amount of counted unknown properties"
15988 static_assert(size_t(eUseCounter_Count
) * 2 ==
15989 size_t(Telemetry::HistogramUseCounterCount
),
15990 "There should be two histograms (document and page)"
15991 " for each use counter");
15993 #define ASSERT_CSS_COUNTER(id_, method_) \
15994 static_assert(size_t(eUseCounter_property_##method_) - \
15995 size_t(eUseCounter_FirstCSSProperty) == \
15997 "Order for CSS counters and CSS property id should match");
15998 #define CSS_PROP_PUBLIC_OR_PRIVATE(publicname_, privatename_) privatename_
15999 #define CSS_PROP_LONGHAND(name_, id_, method_, ...) \
16000 ASSERT_CSS_COUNTER(eCSSProperty_##id_, method_)
16001 #define CSS_PROP_SHORTHAND(name_, id_, method_, ...) \
16002 ASSERT_CSS_COUNTER(eCSSProperty_##id_, method_)
16003 #define CSS_PROP_ALIAS(name_, aliasid_, id_, method_, ...) \
16004 ASSERT_CSS_COUNTER(eCSSPropertyAlias_##aliasid_, method_)
16005 #include "mozilla/ServoCSSPropList.h"
16006 #undef CSS_PROP_ALIAS
16007 #undef CSS_PROP_SHORTHAND
16008 #undef CSS_PROP_LONGHAND
16009 #undef CSS_PROP_PUBLIC_OR_PRIVATE
16010 #undef ASSERT_CSS_COUNTER
16012 void Document::SetCssUseCounterBits() {
16013 if (StaticPrefs::layout_css_use_counters_enabled()) {
16014 for (size_t i
= 0; i
< eCSSProperty_COUNT_with_aliases
; ++i
) {
16015 auto id
= nsCSSPropertyID(i
);
16016 if (Servo_IsPropertyIdRecordedInUseCounter(mStyleUseCounters
.get(), id
)) {
16017 SetUseCounter(nsCSSProps::UseCounterFor(id
));
16022 if (StaticPrefs::layout_css_use_counters_unimplemented_enabled()) {
16023 for (size_t i
= 0; i
< size_t(CountedUnknownProperty::Count
); ++i
) {
16024 auto id
= CountedUnknownProperty(i
);
16025 if (Servo_IsUnknownPropertyRecordedInUseCounter(mStyleUseCounters
.get(),
16027 SetUseCounter(UseCounter(eUseCounter_FirstCountedUnknownProperty
+ i
));
16033 void Document::InitUseCounters() {
16034 // We can be called more than once, e.g. when session history navigation shows
16035 // us a second time.
16036 if (mUseCountersInitialized
) {
16039 mUseCountersInitialized
= true;
16041 static_assert(Telemetry::HistogramUseCounterCount
> 0);
16043 if (!ShouldIncludeInTelemetry(/* aAllowExtensionURIs = */ true)) {
16047 // Now we know for sure that we should report use counters from this document.
16048 mShouldReportUseCounters
= true;
16050 WindowContext
* top
= GetWindowContextForPageUseCounters();
16052 // This is the case for SVG image documents. They are not displayed in a
16053 // window, but we still do want to record document use counters for them.
16055 // Page use counter propagation is handled in PropagateImageUseCounters,
16056 // so there is no need to use the cross-process machinery to send them.
16057 MOZ_LOG(gUseCountersLog
, LogLevel::Debug
,
16058 ("InitUseCounters for a non-displayed document [%s]",
16059 nsContentUtils::TruncatedURLForDisplay(mDocumentURI
).get()));
16063 RefPtr
<WindowGlobalChild
> wgc
= GetWindowGlobalChild();
16068 MOZ_LOG(gUseCountersLog
, LogLevel::Debug
,
16069 ("InitUseCounters for a displayed document: %" PRIu64
" -> %" PRIu64
16071 wgc
->InnerWindowId(), top
->Id(),
16072 nsContentUtils::TruncatedURLForDisplay(mDocumentURI
).get()));
16074 // Inform the parent process that we will send it page use counters later on.
16075 wgc
->SendExpectPageUseCounters(top
);
16076 mShouldSendPageUseCounters
= true;
16079 // We keep separate counts for individual documents and top-level
16080 // pages to more accurately track how many web pages might break if
16081 // certain features were removed. Consider the case of a single
16082 // HTML document with several SVG images and/or iframes with
16083 // sub-documents of their own. If we maintained a single set of use
16084 // counters and all the sub-documents use a particular feature, then
16085 // telemetry would indicate that we would be breaking N documents if
16086 // that feature were removed. Whereas with a document/top-level
16087 // page split, we can see that N documents would be affected, but
16088 // only a single web page would be affected.
16090 // The difference between the values of these two histograms and the
16091 // related use counters below tell us how many pages did *not* use
16092 // the feature in question. For instance, if we see that a given
16093 // session has destroyed 30 content documents, but a particular use
16094 // counter shows only a count of 5, we can infer that the use
16095 // counter was *not* used in 25 of those 30 documents.
16097 // We do things this way, rather than accumulating a boolean flag
16098 // for each use counter, to avoid sending histograms for features
16099 // that don't get widely used. Doing things in this fashion means
16100 // smaller telemetry payloads and faster processing on the server
16102 void Document::ReportDocumentUseCounters() {
16103 if (!mShouldReportUseCounters
|| mReportedDocumentUseCounters
) {
16107 mReportedDocumentUseCounters
= true;
16109 // Note that a document is being destroyed. See the comment above for how
16110 // use counter histograms are interpreted relative to this measurement.
16111 // TOP_LEVEL_CONTENT_DOCUMENTS_DESTROYED is recorded in
16112 // WindowGlobalParent::FinishAccumulatingPageUseCounters.
16113 Telemetry::Accumulate(Telemetry::CONTENT_DOCUMENTS_DESTROYED
, 1);
16115 // Ask all of our resource documents to report their own document use
16117 EnumerateExternalResources([](Document
& aDoc
) {
16118 aDoc
.ReportDocumentUseCounters();
16119 return CallState::Continue
;
16122 // Copy StyleUseCounters into our document use counters.
16123 SetCssUseCounterBits();
16125 Maybe
<nsCString
> urlForLogging
;
16126 const bool dumpCounters
= StaticPrefs::dom_use_counters_dump_document();
16127 if (dumpCounters
) {
16128 urlForLogging
.emplace(
16129 nsContentUtils::TruncatedURLForDisplay(GetDocumentURI()));
16132 // Report our per-document use counters.
16133 for (int32_t c
= 0; c
< eUseCounter_Count
; ++c
) {
16134 auto uc
= static_cast<UseCounter
>(c
);
16135 if (!mUseCounters
[uc
]) {
16139 auto id
= static_cast<Telemetry::HistogramID
>(
16140 Telemetry::HistogramFirstUseCounter
+ uc
* 2);
16141 if (dumpCounters
) {
16142 printf_stderr("USE_COUNTER_DOCUMENT: %s - %s\n",
16143 Telemetry::GetHistogramName(id
), urlForLogging
->get());
16145 Telemetry::Accumulate(id
, 1);
16149 void Document::SendPageUseCounters() {
16150 if (!mShouldReportUseCounters
|| !mShouldSendPageUseCounters
) {
16154 // Ask all of our resource documents to send their own document use
16155 // counters to the parent process to be counted as page use counters.
16156 EnumerateExternalResources([](Document
& aDoc
) {
16157 aDoc
.SendPageUseCounters();
16158 return CallState::Continue
;
16161 // Send our use counters to the parent process to accumulate them towards the
16162 // page use counters for the top-level document.
16164 // We take our own document use counters (those in mUseCounters) and any child
16165 // document use counters (those in mChildDocumentUseCounters) that have been
16166 // explicitly propagated up to us, which includes resource documents, static
16167 // clones, and SVG images.
16168 RefPtr
<WindowGlobalChild
> wgc
= GetWindowGlobalChild();
16170 MOZ_ASSERT_UNREACHABLE(
16171 "SendPageUseCounters should be called while we still have access "
16172 "to our WindowContext");
16173 MOZ_LOG(gUseCountersLog
, LogLevel::Debug
,
16174 (" > too late to send page use counters"));
16178 MOZ_LOG(gUseCountersLog
, LogLevel::Debug
,
16179 ("Sending page use counters: from WindowContext %" PRIu64
" [%s]",
16180 wgc
->WindowContext()->Id(),
16181 nsContentUtils::TruncatedURLForDisplay(GetDocumentURI()).get()));
16183 // Copy StyleUseCounters into our document use counters.
16184 SetCssUseCounterBits();
16186 UseCounters counters
= mUseCounters
| mChildDocumentUseCounters
;
16187 wgc
->SendAccumulatePageUseCounters(counters
);
16190 bool Document::RecomputeResistFingerprinting() {
16191 const bool previous
= mShouldResistFingerprinting
;
16193 if (mParentDocument
&&
16194 (NodePrincipal()->Equals(mParentDocument
->NodePrincipal()) ||
16195 NodePrincipal()->GetIsNullPrincipal())) {
16196 // If we have a parent document, defer to it only when we have a null
16197 // principal (e.g. a sandboxed iframe or a data: uri) or when the parent
16198 // document's principal matches. This means we will defer about:blank,
16199 // about:srcdoc, blob and same-origin iframes to the parent, but not
16200 // cross-origin iframes.
16201 mShouldResistFingerprinting
= !nsContentUtils::IsChromeDoc(this) &&
16202 mParentDocument
->ShouldResistFingerprinting(
16203 RFPTarget::IsAlwaysEnabledForPrecompute
);
16205 mShouldResistFingerprinting
=
16206 !nsContentUtils::IsChromeDoc(this) &&
16207 nsContentUtils::ShouldResistFingerprinting(
16208 mChannel
, RFPTarget::IsAlwaysEnabledForPrecompute
);
16211 return previous
!= mShouldResistFingerprinting
;
16214 bool Document::ShouldResistFingerprinting(RFPTarget aTarget
) const {
16215 return mShouldResistFingerprinting
&& nsRFPService::IsRFPEnabledFor(aTarget
);
16218 WindowContext
* Document::GetWindowContextForPageUseCounters() const {
16219 if (mDisplayDocument
) {
16220 // If we are a resource document, then go through it to find the
16221 // top-level document.
16222 return mDisplayDocument
->GetWindowContextForPageUseCounters();
16225 if (mOriginalDocument
) {
16226 // For static clones (print preview documents), contribute page use counters
16227 // towards the original document.
16228 return mOriginalDocument
->GetWindowContextForPageUseCounters();
16231 WindowContext
* wc
= GetTopLevelWindowContext();
16232 if (!wc
|| !wc
->GetBrowsingContext()->IsContent()) {
16239 void Document::UpdateIntersectionObservations(TimeStamp aNowTime
) {
16240 if (mIntersectionObservers
.IsEmpty()) {
16244 DOMHighResTimeStamp time
= 0;
16245 if (nsPIDOMWindowInner
* win
= GetInnerWindow()) {
16246 if (Performance
* perf
= win
->GetPerformance()) {
16247 time
= perf
->TimeStampToDOMHighResForRendering(aNowTime
);
16251 const auto observers
= ToTArray
<nsTArray
<RefPtr
<DOMIntersectionObserver
>>>(
16252 mIntersectionObservers
);
16253 for (const auto& observer
: observers
) {
16255 observer
->Update(*this, time
);
16260 void Document::ScheduleIntersectionObserverNotification() {
16261 if (mIntersectionObservers
.IsEmpty()) {
16264 MOZ_RELEASE_ASSERT(NS_IsMainThread());
16265 nsCOMPtr
<nsIRunnable
> notification
=
16266 NewRunnableMethod("Document::NotifyIntersectionObservers", this,
16267 &Document::NotifyIntersectionObservers
);
16268 Dispatch(TaskCategory::Other
, notification
.forget());
16271 void Document::NotifyIntersectionObservers() {
16272 const auto observers
= ToTArray
<nsTArray
<RefPtr
<DOMIntersectionObserver
>>>(
16273 mIntersectionObservers
);
16274 for (const auto& observer
: observers
) {
16276 // MOZ_KnownLive because the 'observers' array guarantees to keep it
16278 MOZ_KnownLive(observer
)->Notify();
16283 DOMIntersectionObserver
& Document::EnsureLazyLoadImageObserver() {
16284 if (!mLazyLoadImageObserver
) {
16285 mLazyLoadImageObserver
=
16286 DOMIntersectionObserver::CreateLazyLoadObserver(*this);
16288 return *mLazyLoadImageObserver
;
16291 DOMIntersectionObserver
& Document::EnsureContentVisibilityObserver() {
16292 if (!mContentVisibilityObserver
) {
16293 mContentVisibilityObserver
=
16294 DOMIntersectionObserver::CreateContentVisibilityObserver(*this);
16296 return *mContentVisibilityObserver
;
16299 void Document::ObserveForContentVisibility(Element
& aElement
) {
16300 EnsureContentVisibilityObserver().Observe(aElement
);
16303 void Document::UnobserveForContentVisibility(Element
& aElement
) {
16304 if (mContentVisibilityObserver
) {
16305 mContentVisibilityObserver
->Unobserve(aElement
);
16309 ResizeObserver
& Document::EnsureLastRememberedSizeObserver() {
16310 if (!mLastRememberedSizeObserver
) {
16311 mLastRememberedSizeObserver
=
16312 ResizeObserver::CreateLastRememberedSizeObserver(*this);
16314 return *mLastRememberedSizeObserver
;
16317 void Document::ObserveForLastRememberedSize(Element
& aElement
) {
16318 if (NS_WARN_IF(!IsActive())) {
16321 // Options are initialized with ResizeObserverBoxOptions::Content_box by
16322 // default, which is what we want.
16323 static ResizeObserverOptions options
;
16324 EnsureLastRememberedSizeObserver().Observe(aElement
, options
);
16327 void Document::UnobserveForLastRememberedSize(Element
& aElement
) {
16328 if (mLastRememberedSizeObserver
) {
16329 mLastRememberedSizeObserver
->Unobserve(aElement
);
16333 void Document::NotifyLayerManagerRecreated() {
16334 NotifyActivityChanged();
16335 EnumerateSubDocuments([](Document
& aSubDoc
) {
16336 aSubDoc
.NotifyLayerManagerRecreated();
16337 return CallState::Continue
;
16341 XPathEvaluator
* Document::XPathEvaluator() {
16342 if (!mXPathEvaluator
) {
16343 mXPathEvaluator
.reset(new dom::XPathEvaluator(this));
16345 return mXPathEvaluator
.get();
16348 already_AddRefed
<nsIDocumentEncoder
> Document::GetCachedEncoder() {
16349 return mCachedEncoder
.forget();
16352 void Document::SetCachedEncoder(already_AddRefed
<nsIDocumentEncoder
> aEncoder
) {
16353 mCachedEncoder
= aEncoder
;
16356 nsILoadContext
* Document::GetLoadContext() const { return mDocumentContainer
; }
16358 nsIDocShell
* Document::GetDocShell() const { return mDocumentContainer
; }
16360 void Document::SetStateObject(nsIStructuredCloneContainer
* scContainer
) {
16361 mStateObjectContainer
= scContainer
;
16362 mCachedStateObject
= JS::UndefinedValue();
16363 mCachedStateObjectValid
= false;
16366 bool Document::ComputeDocumentLWTheme() const {
16367 if (!NodePrincipal()->IsSystemPrincipal()) {
16371 Element
* element
= GetRootElement();
16372 return element
&& element
->AttrValueIs(kNameSpaceID_None
, nsGkAtoms::lwtheme
,
16373 nsGkAtoms::_true
, eCaseMatters
);
16376 already_AddRefed
<Element
> Document::CreateHTMLElement(nsAtom
* aTag
) {
16377 RefPtr
<mozilla::dom::NodeInfo
> nodeInfo
;
16378 nodeInfo
= mNodeInfoManager
->GetNodeInfo(aTag
, nullptr, kNameSpaceID_XHTML
,
16380 MOZ_ASSERT(nodeInfo
, "GetNodeInfo should never fail");
16382 nsCOMPtr
<Element
> element
;
16383 DebugOnly
<nsresult
> rv
=
16384 NS_NewHTMLElement(getter_AddRefs(element
), nodeInfo
.forget(),
16385 mozilla::dom::NOT_FROM_PARSER
);
16387 MOZ_ASSERT(NS_SUCCEEDED(rv
), "NS_NewHTMLElement should never fail");
16388 return element
.forget();
16391 void AutoWalkBrowsingContextGroup::SuppressBrowsingContext(
16392 BrowsingContext
* aContext
) {
16393 aContext
->PreOrderWalk([&](BrowsingContext
* aBC
) {
16394 if (nsCOMPtr
<nsPIDOMWindowOuter
> win
= aBC
->GetDOMWindow()) {
16395 if (RefPtr
<Document
> doc
= win
->GetExtantDoc()) {
16396 SuppressDocument(doc
);
16397 mDocuments
.AppendElement(doc
);
16403 void AutoWalkBrowsingContextGroup::SuppressBrowsingContextGroup(
16404 BrowsingContextGroup
* aGroup
) {
16405 for (const auto& bc
: aGroup
->Toplevels()) {
16406 SuppressBrowsingContext(bc
);
16410 nsAutoSyncOperation::nsAutoSyncOperation(Document
* aDoc
,
16411 SyncOperationBehavior aSyncBehavior
)
16412 : mSyncBehavior(aSyncBehavior
) {
16413 mMicroTaskLevel
= 0;
16414 if (CycleCollectedJSContext
* ccjs
= CycleCollectedJSContext::Get()) {
16415 mMicroTaskLevel
= ccjs
->MicroTaskLevel();
16416 ccjs
->SetMicroTaskLevel(0);
16419 mBrowsingContext
= aDoc
->GetBrowsingContext();
16420 if (InputTaskManager::CanSuspendInputEvent()) {
16421 if (auto* bcg
= aDoc
->GetDocGroup()->GetBrowsingContextGroup()) {
16422 SuppressBrowsingContextGroup(bcg
);
16424 } else if (mBrowsingContext
) {
16425 SuppressBrowsingContext(mBrowsingContext
->Top());
16427 if (mBrowsingContext
&&
16428 mSyncBehavior
== SyncOperationBehavior::eSuspendInput
&&
16429 InputTaskManager::CanSuspendInputEvent()) {
16430 mBrowsingContext
->Group()->IncInputEventSuspensionLevel();
16435 void nsAutoSyncOperation::SuppressDocument(Document
* aDoc
) {
16436 if (nsCOMPtr
<nsPIDOMWindowInner
> win
= aDoc
->GetInnerWindow()) {
16437 win
->TimeoutManager().BeginSyncOperation();
16439 aDoc
->SetIsInSyncOperation(true);
16442 void nsAutoSyncOperation::UnsuppressDocument(Document
* aDoc
) {
16443 if (nsCOMPtr
<nsPIDOMWindowInner
> win
= aDoc
->GetInnerWindow()) {
16444 win
->TimeoutManager().EndSyncOperation();
16446 aDoc
->SetIsInSyncOperation(false);
16449 nsAutoSyncOperation::~nsAutoSyncOperation() {
16450 UnsuppressDocuments();
16451 CycleCollectedJSContext
* ccjs
= CycleCollectedJSContext::Get();
16453 ccjs
->SetMicroTaskLevel(mMicroTaskLevel
);
16455 if (mBrowsingContext
&&
16456 mSyncBehavior
== SyncOperationBehavior::eSuspendInput
&&
16457 InputTaskManager::CanSuspendInputEvent()) {
16458 mBrowsingContext
->Group()->DecInputEventSuspensionLevel();
16462 void Document::SetIsInSyncOperation(bool aSync
) {
16463 if (CycleCollectedJSContext
* ccjs
= CycleCollectedJSContext::Get()) {
16464 ccjs
->UpdateMicroTaskSuppressionGeneration();
16468 ++mInSyncOperationCount
;
16470 --mInSyncOperationCount
;
16474 gfxUserFontSet
* Document::GetUserFontSet() {
16475 if (!mFontFaceSet
) {
16479 return mFontFaceSet
->GetImpl();
16482 void Document::FlushUserFontSet() {
16483 if (!mFontFaceSetDirty
) {
16487 mFontFaceSetDirty
= false;
16489 if (gfxPlatform::GetPlatform()->DownloadableFontsEnabled()) {
16490 nsTArray
<nsFontFaceRuleContainer
> rules
;
16491 RefPtr
<PresShell
> presShell
= GetPresShell();
16493 MOZ_ASSERT(mStyleSetFilled
);
16494 mStyleSet
->AppendFontFaceRules(rules
);
16497 if (!mFontFaceSet
&& !rules
.IsEmpty()) {
16498 mFontFaceSet
= FontFaceSet::CreateForDocument(this);
16501 bool changed
= false;
16502 if (mFontFaceSet
) {
16503 changed
= mFontFaceSet
->UpdateRules(rules
);
16506 // We need to enqueue a style change reflow (for later) to
16507 // reflect that we're modifying @font-face rules. (However,
16508 // without a reflow, nothing will happen to start any downloads
16509 // that are needed.)
16510 if (changed
&& presShell
) {
16511 if (nsPresContext
* presContext
= presShell
->GetPresContext()) {
16512 presContext
->UserFontSetUpdated();
16518 void Document::MarkUserFontSetDirty() {
16519 if (mFontFaceSetDirty
) {
16522 mFontFaceSetDirty
= true;
16523 if (PresShell
* presShell
= GetPresShell()) {
16524 presShell
->EnsureStyleFlush();
16528 FontFaceSet
* Document::Fonts() {
16529 if (!mFontFaceSet
) {
16530 mFontFaceSet
= FontFaceSet::CreateForDocument(this);
16531 FlushUserFontSet();
16533 return mFontFaceSet
;
16536 void Document::ReportHasScrollLinkedEffect(const TimeStamp
& aTimeStamp
) {
16537 MOZ_ASSERT(!aTimeStamp
.IsNull());
16539 if (!mLastScrollLinkedEffectDetectionTime
.IsNull() &&
16540 mLastScrollLinkedEffectDetectionTime
>= aTimeStamp
) {
16544 if (mLastScrollLinkedEffectDetectionTime
.IsNull()) {
16545 // Report to console just once.
16546 nsContentUtils::ReportToConsole(
16547 nsIScriptError::warningFlag
, "Async Pan/Zoom"_ns
, this,
16548 nsContentUtils::eLAYOUT_PROPERTIES
, "ScrollLinkedEffectFound3");
16551 mLastScrollLinkedEffectDetectionTime
= aTimeStamp
;
16554 bool Document::HasScrollLinkedEffect() const {
16555 if (nsPresContext
* pc
= GetPresContext()) {
16556 return mLastScrollLinkedEffectDetectionTime
==
16557 pc
->RefreshDriver()->MostRecentRefresh();
16563 void Document::SetSHEntryHasUserInteraction(bool aHasInteraction
) {
16564 if (RefPtr
<WindowContext
> topWc
= GetTopLevelWindowContext()) {
16565 // Setting has user interction on a discarded browsing context has
16567 Unused
<< topWc
->SetSHEntryHasUserInteraction(aHasInteraction
);
16571 bool Document::GetSHEntryHasUserInteraction() {
16572 if (RefPtr
<WindowContext
> topWc
= GetTopLevelWindowContext()) {
16573 return topWc
->GetSHEntryHasUserInteraction();
16578 void Document::SetUserHasInteracted() {
16579 MOZ_LOG(gUserInteractionPRLog
, LogLevel::Debug
,
16580 ("Document %p has been interacted by user.", this));
16582 // We maybe need to update the user-interaction permission.
16583 MaybeStoreUserInteractionAsPermission();
16585 // For purposes of reducing irrelevant session history entries on
16586 // the back button, we annotate entries with whether they had user
16587 // interaction. This is gated on its own flag on the WindowContext
16588 // (instead of mUserHasInteracted) to account for the fact that multiple
16589 // top-level SH entries can be associated with the same document.
16590 // Thus, whenever we create a new SH entry for this document,
16591 // this flag is reset.
16592 if (!GetSHEntryHasUserInteraction()) {
16593 nsIDocShell
* docShell
= GetDocShell();
16595 nsCOMPtr
<nsISHEntry
> currentEntry
;
16598 docShell
->GetCurrentSHEntry(getter_AddRefs(currentEntry
), &oshe
);
16599 if (!NS_WARN_IF(NS_FAILED(rv
)) && currentEntry
) {
16600 currentEntry
->SetHasUserInteraction(true);
16603 SetSHEntryHasUserInteraction(true);
16606 if (mUserHasInteracted
) {
16610 mUserHasInteracted
= true;
16613 nsCOMPtr
<nsILoadInfo
> loadInfo
= mChannel
->LoadInfo();
16614 loadInfo
->SetDocumentHasUserInteracted(true);
16616 // Tell the parent process about user interaction
16617 if (auto* wgc
= GetWindowGlobalChild()) {
16618 wgc
->SendUpdateDocumentHasUserInteracted(true);
16621 MaybeAllowStorageForOpenerAfterUserInteraction();
16624 BrowsingContext
* Document::GetBrowsingContext() const {
16625 nsCOMPtr
<nsIDocShell
> docshell(mDocumentContainer
);
16626 return docshell
? docshell
->GetBrowsingContext() : nullptr;
16629 void Document::NotifyUserGestureActivation() {
16630 if (RefPtr
<BrowsingContext
> bc
= GetBrowsingContext()) {
16631 bc
->PreOrderWalk([&](BrowsingContext
* aBC
) {
16632 WindowContext
* windowContext
= aBC
->GetCurrentWindowContext();
16633 if (!windowContext
) {
16637 nsIDocShell
* docShell
= aBC
->GetDocShell();
16642 Document
* document
= docShell
->GetDocument();
16647 // XXXedgar we probably could just check `IsInProcess()` after fission
16649 if (NodePrincipal()->Equals(document
->NodePrincipal())) {
16650 windowContext
->NotifyUserGestureActivation();
16654 for (bc
= bc
->GetParent(); bc
; bc
= bc
->GetParent()) {
16655 if (WindowContext
* windowContext
= bc
->GetCurrentWindowContext()) {
16656 windowContext
->NotifyUserGestureActivation();
16662 bool Document::HasBeenUserGestureActivated() {
16663 RefPtr
<WindowContext
> wc
= GetWindowContext();
16664 return wc
&& wc
->HasBeenUserGestureActivated();
16667 DOMHighResTimeStamp
Document::LastUserGestureTimeStamp() {
16668 if (RefPtr
<WindowContext
> wc
= GetWindowContext()) {
16669 if (nsGlobalWindowInner
* innerWindow
= wc
->GetInnerWindow()) {
16670 if (Performance
* perf
= innerWindow
->GetPerformance()) {
16671 return perf
->GetDOMTiming()->TimeStampToDOMHighRes(
16672 wc
->GetUserGestureStart());
16678 "Unable to calculate DOMHighResTimeStamp for LastUserGestureTimeStamp");
16682 void Document::ClearUserGestureActivation() {
16683 if (RefPtr
<BrowsingContext
> bc
= GetBrowsingContext()) {
16685 bc
->PreOrderWalk([&](BrowsingContext
* aBC
) {
16686 if (WindowContext
* windowContext
= aBC
->GetCurrentWindowContext()) {
16687 windowContext
->NotifyResetUserGestureActivation();
16693 bool Document::HasValidTransientUserGestureActivation() const {
16694 RefPtr
<WindowContext
> wc
= GetWindowContext();
16695 return wc
&& wc
->HasValidTransientUserGestureActivation();
16698 bool Document::ConsumeTransientUserGestureActivation() {
16699 RefPtr
<WindowContext
> wc
= GetWindowContext();
16700 return wc
&& wc
->ConsumeTransientUserGestureActivation();
16703 void Document::SetDocTreeHadMedia() {
16704 RefPtr
<WindowContext
> topWc
= GetTopLevelWindowContext();
16705 if (topWc
&& !topWc
->IsDiscarded() && !topWc
->GetDocTreeHadMedia()) {
16706 MOZ_ALWAYS_SUCCEEDS(topWc
->SetDocTreeHadMedia(true));
16710 void Document::MaybeAllowStorageForOpenerAfterUserInteraction() {
16711 if (!CookieJarSettings()->GetRejectThirdPartyContexts()) {
16715 // This will probably change for project fission, but currently this document
16716 // and the opener are on the same process. In the future, we should make this
16718 nsPIDOMWindowInner
* inner
= GetInnerWindow();
16719 if (NS_WARN_IF(!inner
)) {
16723 uint32_t cookieBehavior
= CookieJarSettings()->GetCookieBehavior();
16724 if (cookieBehavior
== nsICookieService::BEHAVIOR_REJECT_TRACKER
||
16726 nsICookieService::BEHAVIOR_REJECT_TRACKER_AND_PARTITION_FOREIGN
) {
16727 // We care about first-party tracking resources only.
16728 if (!nsContentUtils::IsFirstPartyTrackingResourceWindow(inner
)) {
16732 MOZ_ASSERT(net::CookieJarSettings::IsRejectThirdPartyWithExceptions(
16736 auto* outer
= nsGlobalWindowOuter::Cast(inner
->GetOuterWindow());
16737 if (NS_WARN_IF(!outer
)) {
16741 RefPtr
<BrowsingContext
> openerBC
= outer
->GetOpenerBrowsingContext();
16747 // We want to ensure the following check works for both fission mode and
16748 // non-fission mode:
16749 // "If the opener is not a 3rd party and if this window is not a 3rd party
16750 // with respect to the opener, we should not continue."
16752 // In non-fission mode, the opener and the opened window are in the same
16753 // process, we can use AntiTrackingUtils::IsThirdPartyWindow to do the check.
16754 // In fission mode, if this window is not a 3rd party with respect to the
16755 // opener, they must be in the same process, so we can still use
16756 // IsThirdPartyWindow(openerInner) to continue to check if the opener is a 3rd
16758 if (openerBC
->IsInProcess()) {
16759 nsCOMPtr
<nsPIDOMWindowOuter
> outerOpener
= openerBC
->GetDOMWindow();
16760 if (NS_WARN_IF(!outerOpener
)) {
16764 nsCOMPtr
<nsPIDOMWindowInner
> openerInner
=
16765 outerOpener
->GetCurrentInnerWindow();
16766 if (NS_WARN_IF(!openerInner
)) {
16770 RefPtr
<Document
> openerDocument
= openerInner
->GetExtantDoc();
16771 if (NS_WARN_IF(!openerDocument
)) {
16775 nsCOMPtr
<nsIURI
> openerURI
= openerDocument
->GetDocumentURI();
16776 if (NS_WARN_IF(!openerURI
)) {
16780 // If the opener is not a 3rd party and if this window is not
16781 // a 3rd party with respect to the opener, we should not continue.
16782 if (!AntiTrackingUtils::IsThirdPartyWindow(inner
, openerURI
) &&
16783 !AntiTrackingUtils::IsThirdPartyWindow(openerInner
, nullptr)) {
16788 // We don't care when the asynchronous work finishes here.
16789 Unused
<< StorageAccessAPIHelper::AllowAccessFor(
16790 NodePrincipal(), openerBC
,
16791 ContentBlockingNotifier::eOpenerAfterUserInteraction
);
16796 // Documents can stay alive for days. We don't want to update the permission
16797 // value at any user-interaction, and, using a timer triggered any X seconds
16798 // should be good enough. 'X' is taken from
16799 // privacy.userInteraction.document.interval pref.
16800 // We also want to store the user-interaction before shutting down, and, for
16801 // this reason, this class implements nsIAsyncShutdownBlocker interface.
16802 class UserInteractionTimer final
: public Runnable
,
16803 public nsITimerCallback
,
16804 public nsIAsyncShutdownBlocker
{
16806 NS_DECL_ISUPPORTS_INHERITED
16808 explicit UserInteractionTimer(Document
* aDocument
)
16809 : Runnable("UserInteractionTimer"),
16810 mPrincipal(aDocument
->NodePrincipal()),
16811 mDocument(do_GetWeakReference(aDocument
)) {
16812 static int32_t userInteractionTimerId
= 0;
16813 // Blocker names must be unique. Let's create it now because when needed,
16814 // the document could be already gone.
16815 mBlockerName
.AppendPrintf("UserInteractionTimer %d for document %p",
16816 ++userInteractionTimerId
, aDocument
);
16819 // Runnable interface
16823 uint32_t interval
=
16824 StaticPrefs::privacy_userInteraction_document_interval();
16829 RefPtr
<UserInteractionTimer
> self
= this;
16831 MakeScopeExit([self
] { self
->CancelTimerAndStoreUserInteraction(); });
16833 nsresult rv
= NS_NewTimerWithCallback(
16834 getter_AddRefs(mTimer
), this, interval
* 1000, nsITimer::TYPE_ONE_SHOT
);
16835 NS_ENSURE_SUCCESS(rv
, NS_OK
);
16837 nsCOMPtr
<nsIAsyncShutdownClient
> phase
= GetShutdownPhase();
16838 NS_ENSURE_TRUE(!!phase
, NS_OK
);
16840 rv
= phase
->AddBlocker(this, NS_LITERAL_STRING_FROM_CSTRING(__FILE__
),
16841 __LINE__
, u
"UserInteractionTimer shutdown"_ns
);
16842 NS_ENSURE_SUCCESS(rv
, NS_OK
);
16848 // nsITimerCallback interface
16851 Notify(nsITimer
* aTimer
) override
{
16852 StoreUserInteraction();
16856 #ifdef MOZ_COLLECTING_RUNNABLE_TELEMETRY
16857 using nsINamed::GetName
;
16860 // nsIAsyncShutdownBlocker interface
16863 GetName(nsAString
& aName
) override
{
16864 aName
= mBlockerName
;
16869 BlockShutdown(nsIAsyncShutdownClient
* aClient
) override
{
16870 CancelTimerAndStoreUserInteraction();
16875 GetState(nsIPropertyBag
**) override
{ return NS_OK
; }
16878 ~UserInteractionTimer() = default;
16880 void StoreUserInteraction() {
16881 // Remove the shutting down blocker
16882 nsCOMPtr
<nsIAsyncShutdownClient
> phase
= GetShutdownPhase();
16884 phase
->RemoveBlocker(this);
16887 // If the document is not gone, let's reset its timer flag.
16888 nsCOMPtr
<Document
> document
= do_QueryReferent(mDocument
);
16890 ContentBlockingUserInteraction::Observe(mPrincipal
);
16891 document
->ResetUserInteractionTimer();
16895 void CancelTimerAndStoreUserInteraction() {
16901 StoreUserInteraction();
16904 static already_AddRefed
<nsIAsyncShutdownClient
> GetShutdownPhase() {
16905 nsCOMPtr
<nsIAsyncShutdownService
> svc
= services::GetAsyncShutdownService();
16906 NS_ENSURE_TRUE(!!svc
, nullptr);
16908 nsCOMPtr
<nsIAsyncShutdownClient
> phase
;
16909 nsresult rv
= svc
->GetXpcomWillShutdown(getter_AddRefs(phase
));
16910 NS_ENSURE_SUCCESS(rv
, nullptr);
16912 return phase
.forget();
16915 nsCOMPtr
<nsIPrincipal
> mPrincipal
;
16916 nsWeakPtr mDocument
;
16918 nsCOMPtr
<nsITimer
> mTimer
;
16920 nsString mBlockerName
;
16923 NS_IMPL_ISUPPORTS_INHERITED(UserInteractionTimer
, Runnable
, nsITimerCallback
,
16924 nsIAsyncShutdownBlocker
)
16928 void Document::MaybeStoreUserInteractionAsPermission() {
16929 // We care about user-interaction stored only for top-level documents
16930 // and documents with access to the Storage Access API
16931 if (!IsTopLevelContentDocument()) {
16933 nsresult rv
= HasStorageAccessSync(hasSA
);
16934 if (NS_FAILED(rv
) || !hasSA
) {
16939 if (!mUserHasInteracted
) {
16940 // First interaction, let's store this info now.
16941 ContentBlockingUserInteraction::Observe(NodePrincipal());
16945 if (mHasUserInteractionTimerScheduled
) {
16949 nsCOMPtr
<nsIRunnable
> task
= new UserInteractionTimer(this);
16950 nsresult rv
= NS_DispatchToCurrentThreadQueue(task
.forget(), 2500,
16951 EventQueuePriority::Idle
);
16952 if (NS_WARN_IF(NS_FAILED(rv
))) {
16956 // This value will be reset by the timer.
16957 mHasUserInteractionTimerScheduled
= true;
16960 void Document::ResetUserInteractionTimer() {
16961 mHasUserInteractionTimerScheduled
= false;
16964 bool Document::IsExtensionPage() const {
16965 return BasePrincipal::Cast(NodePrincipal())->AddonPolicy();
16968 void Document::AddResizeObserver(ResizeObserver
& aObserver
) {
16969 if (!mResizeObserverController
) {
16970 mResizeObserverController
= MakeUnique
<ResizeObserverController
>(this);
16972 mResizeObserverController
->AddResizeObserver(aObserver
);
16975 void Document::RemoveResizeObserver(ResizeObserver
& aObserver
) {
16976 MOZ_DIAGNOSTIC_ASSERT(mResizeObserverController
, "No controller?");
16977 if (MOZ_UNLIKELY(!mResizeObserverController
)) {
16980 mResizeObserverController
->RemoveResizeObserver(aObserver
);
16983 PermissionDelegateHandler
* Document::GetPermissionDelegateHandler() {
16984 if (!mPermissionDelegateHandler
) {
16985 mPermissionDelegateHandler
=
16986 mozilla::MakeAndAddRef
<PermissionDelegateHandler
>(this);
16989 if (!mPermissionDelegateHandler
->Initialize()) {
16990 mPermissionDelegateHandler
= nullptr;
16993 return mPermissionDelegateHandler
;
16996 void Document::ScheduleResizeObserversNotification() const {
16997 if (!mResizeObserverController
) {
17001 mResizeObserverController
->ScheduleNotification();
17004 void Document::ClearStaleServoData() {
17005 DocumentStyleRootIterator
iter(this);
17006 while (Element
* root
= iter
.GetNextStyleRoot()) {
17007 RestyleManager::ClearServoDataFromSubtree(root
);
17011 Selection
* Document::GetSelection(ErrorResult
& aRv
) {
17012 nsCOMPtr
<nsPIDOMWindowInner
> window
= GetInnerWindow();
17017 if (!window
->IsCurrentInnerWindow()) {
17021 return nsGlobalWindowInner::Cast(window
)->GetSelection(aRv
);
17024 void Document::MakeBrowsingContextNonSynthetic() {
17025 if (nsContentUtils::ShouldHideObjectOrEmbedImageDocument()) {
17026 if (BrowsingContext
* bc
= GetBrowsingContext()) {
17027 if (bc
->GetSyntheticDocumentContainer()) {
17028 Unused
<< bc
->SetSyntheticDocumentContainer(false);
17034 nsresult
Document::HasStorageAccessSync(bool& aHasStorageAccess
) {
17035 // Step 1: check if cookie permissions are available or denied to this
17036 // document's principal
17037 nsCOMPtr
<nsPIDOMWindowInner
> inner
= GetInnerWindow();
17039 aHasStorageAccess
= false;
17042 Maybe
<bool> resultBecauseCookiesApproved
=
17043 StorageAccessAPIHelper::CheckCookiesPermittedDecidesStorageAccessAPI(
17044 CookieJarSettings(), NodePrincipal());
17045 if (resultBecauseCookiesApproved
.isSome()) {
17046 if (resultBecauseCookiesApproved
.value()) {
17047 aHasStorageAccess
= true;
17050 aHasStorageAccess
= false;
17055 // Step 2: Check if the browser settings determine whether or not this
17056 // document has access to its unpartitioned cookies.
17057 bool isThirdPartyDocument
= AntiTrackingUtils::IsThirdPartyDocument(this);
17058 bool isOnRejectForeignAllowList
= RejectForeignAllowList::Check(this);
17059 bool isOnThirdPartySkipList
= false;
17061 nsCOMPtr
<nsILoadInfo
> loadInfo
= mChannel
->LoadInfo();
17062 isOnThirdPartySkipList
= loadInfo
->GetStoragePermission() ==
17063 nsILoadInfo::StoragePermissionAllowListed
;
17065 bool isThirdPartyTracker
=
17066 nsContentUtils::IsThirdPartyTrackingResourceWindow(inner
);
17067 Maybe
<bool> resultBecauseBrowserSettings
=
17068 StorageAccessAPIHelper::CheckBrowserSettingsDecidesStorageAccessAPI(
17069 CookieJarSettings(), isThirdPartyDocument
, isOnRejectForeignAllowList
,
17070 isOnThirdPartySkipList
, isThirdPartyTracker
);
17071 if (resultBecauseBrowserSettings
.isSome()) {
17072 if (resultBecauseBrowserSettings
.value()) {
17073 aHasStorageAccess
= true;
17076 aHasStorageAccess
= false;
17081 // Step 3: Check if the location of this call (embedded, top level, same-site)
17082 // determines if cookies are permitted or not.
17083 Maybe
<bool> resultBecauseCallContext
=
17084 StorageAccessAPIHelper::CheckCallingContextDecidesStorageAccessAPI(this,
17086 if (resultBecauseCallContext
.isSome()) {
17087 if (resultBecauseCallContext
.value()) {
17088 aHasStorageAccess
= true;
17091 aHasStorageAccess
= false;
17096 // Step 4: Check if the permissions for this document determine if if has
17097 // access or is denied cookies.
17098 Maybe
<bool> resultBecausePreviousPermission
=
17099 StorageAccessAPIHelper::CheckExistingPermissionDecidesStorageAccessAPI(
17101 if (resultBecausePreviousPermission
.isSome()) {
17102 if (resultBecausePreviousPermission
.value()) {
17103 aHasStorageAccess
= true;
17106 aHasStorageAccess
= false;
17110 // If you get here, we default to not giving you permission.
17111 aHasStorageAccess
= false;
17115 already_AddRefed
<mozilla::dom::Promise
> Document::HasStorageAccess(
17116 mozilla::ErrorResult
& aRv
) {
17117 nsIGlobalObject
* global
= GetScopeObject();
17119 aRv
.Throw(NS_ERROR_NOT_AVAILABLE
);
17123 RefPtr
<Promise
> promise
=
17124 Promise::Create(global
, aRv
, Promise::ePropagateUserInteraction
);
17125 if (aRv
.Failed()) {
17129 bool hasStorageAccess
;
17130 nsresult rv
= HasStorageAccessSync(hasStorageAccess
);
17131 if (NS_FAILED(rv
)) {
17132 promise
->MaybeRejectWithUndefined();
17134 promise
->MaybeResolve(hasStorageAccess
);
17137 return promise
.forget();
17140 RefPtr
<Document::GetContentBlockingEventsPromise
>
17141 Document::GetContentBlockingEvents() {
17142 RefPtr
<WindowGlobalChild
> wgc
= GetWindowGlobalChild();
17147 return wgc
->SendGetContentBlockingEvents()->Then(
17148 GetCurrentSerialEventTarget(), __func__
,
17149 [](const WindowGlobalChild::GetContentBlockingEventsPromise::
17150 ResolveOrRejectValue
& aValue
) {
17151 if (aValue
.IsResolve()) {
17152 return Document::GetContentBlockingEventsPromise::CreateAndResolve(
17153 aValue
.ResolveValue(), __func__
);
17156 return Document::GetContentBlockingEventsPromise::CreateAndReject(
17161 StorageAccessAPIHelper::PerformPermissionGrant
17162 Document::CreatePermissionGrantPromise(
17163 nsPIDOMWindowInner
* aInnerWindow
, nsIPrincipal
* aPrincipal
,
17164 bool aHasUserInteraction
, const Maybe
<nsCString
>& aTopLevelBaseDomain
) {
17165 MOZ_ASSERT(aInnerWindow
);
17166 MOZ_ASSERT(aPrincipal
);
17167 RefPtr
<Document
> self(this);
17168 RefPtr
<nsPIDOMWindowInner
> inner(aInnerWindow
);
17169 RefPtr
<nsIPrincipal
> principal(aPrincipal
);
17171 return [inner
, self
, principal
, aHasUserInteraction
, aTopLevelBaseDomain
]() {
17172 // Create the user prompt
17173 RefPtr
<StorageAccessAPIHelper::StorageAccessPermissionGrantPromise::Private
>
17174 p
= new StorageAccessAPIHelper::StorageAccessPermissionGrantPromise::
17176 RefPtr
<StorageAccessPermissionRequest
> sapr
=
17177 StorageAccessPermissionRequest::Create(
17178 inner
, principal
, aTopLevelBaseDomain
,
17181 Telemetry::AccumulateCategorical(
17182 Telemetry::LABELS_STORAGE_ACCESS_API_UI::Allow
);
17183 p
->Resolve(StorageAccessAPIHelper::eAllow
, __func__
);
17187 Telemetry::AccumulateCategorical(
17188 Telemetry::LABELS_STORAGE_ACCESS_API_UI::Deny
);
17189 p
->Reject(false, __func__
);
17192 using PromptResult
= ContentPermissionRequestBase::PromptResult
;
17193 PromptResult pr
= sapr
->CheckPromptPrefs();
17195 if (pr
== PromptResult::Pending
) {
17196 // We're about to show a prompt, record the request attempt
17197 Telemetry::AccumulateCategorical(
17198 Telemetry::LABELS_STORAGE_ACCESS_API_UI::Request
);
17201 // Try to auto-grant the storage access so the user doesn't see the prompt.
17202 self
->AutomaticStorageAccessPermissionCanBeGranted(aHasUserInteraction
)
17204 GetCurrentSerialEventTarget(), __func__
,
17205 // If the autogrant check didn't fail, call this function
17206 [p
, pr
, sapr
, inner
](
17207 const Document::AutomaticStorageAccessPermissionGrantPromise::
17208 ResolveOrRejectValue
& aValue
) -> void {
17209 // Make a copy because we can't modified copy-captured lambda
17211 PromptResult pr2
= pr
;
17213 // If the user didn't already click "allow" and we can autogrant,
17215 bool storageAccessCanBeGrantedAutomatically
=
17216 aValue
.IsResolve() && aValue
.ResolveValue();
17217 bool autoGrant
= false;
17218 if (pr2
== PromptResult::Pending
&&
17219 storageAccessCanBeGrantedAutomatically
) {
17220 pr2
= PromptResult::Granted
;
17223 Telemetry::AccumulateCategorical(
17224 Telemetry::LABELS_STORAGE_ACCESS_API_UI::
17225 AllowAutomatically
);
17228 // If we can complete the permission request, do so.
17229 if (pr2
!= PromptResult::Pending
) {
17230 MOZ_ASSERT_IF(pr2
!= PromptResult::Granted
,
17231 pr2
== PromptResult::Denied
);
17232 if (pr2
== PromptResult::Granted
) {
17233 StorageAccessAPIHelper::StorageAccessPromptChoices choice
=
17234 StorageAccessAPIHelper::eAllow
;
17236 choice
= StorageAccessAPIHelper::eAllowAutoGrant
;
17239 p
->Resolve(choice
, __func__
);
17241 sapr
->MaybeDelayAutomaticGrants()->Then(
17242 GetCurrentSerialEventTarget(), __func__
,
17243 [p
, choice
] { p
->Resolve(choice
, __func__
); },
17244 [p
] { p
->Reject(false, __func__
); });
17248 p
->Reject(false, __func__
);
17252 // If we get here, the auto-decision failed and we need to
17253 // wait for the user prompt to complete.
17254 sapr
->RequestDelayedTask(
17255 inner
->EventTargetFor(TaskCategory::Other
),
17256 ContentPermissionRequestBase::DelayedTaskType::Request
);
17263 already_AddRefed
<mozilla::dom::Promise
> Document::RequestStorageAccess(
17264 mozilla::ErrorResult
& aRv
) {
17265 nsIGlobalObject
* global
= GetScopeObject();
17267 aRv
.Throw(NS_ERROR_NOT_AVAILABLE
);
17271 RefPtr
<Promise
> promise
= Promise::Create(global
, aRv
);
17272 if (aRv
.Failed()) {
17276 // Step 0: Check that we have user activation before proceeding to prevent
17277 // rapid calls to the API to leak information.
17278 if (!HasValidTransientUserGestureActivation()) {
17279 // Report an error to the console for this case
17280 nsContentUtils::ReportToConsole(nsIScriptError::errorFlag
,
17281 nsLiteralCString("requestStorageAccess"),
17282 this, nsContentUtils::eDOM_PROPERTIES
,
17283 "RequestStorageAccessUserGesture");
17284 ConsumeTransientUserGestureActivation();
17285 promise
->MaybeRejectWithNotAllowedError(
17286 "requestStorageAccess not allowed"_ns
);
17287 return promise
.forget();
17290 // Get a pointer to the inner window- We need this for convenience sake
17291 RefPtr
<nsPIDOMWindowInner
> inner
= GetInnerWindow();
17293 ConsumeTransientUserGestureActivation();
17294 promise
->MaybeRejectWithNotAllowedError(
17295 "requestStorageAccess not allowed"_ns
);
17296 return promise
.forget();
17299 // Step 1: Check if the principal calling this has a permission that lets
17300 // them use cookies or forbids them from using cookies.
17301 // This is outside of the spec of the StorageAccess API, but makes the return
17302 // values to have proper semantics.
17303 Maybe
<bool> resultBecauseCookiesApproved
=
17304 StorageAccessAPIHelper::CheckCookiesPermittedDecidesStorageAccessAPI(
17305 CookieJarSettings(), NodePrincipal());
17306 if (resultBecauseCookiesApproved
.isSome()) {
17307 if (resultBecauseCookiesApproved
.value()) {
17308 promise
->MaybeResolveWithUndefined();
17309 return promise
.forget();
17311 ConsumeTransientUserGestureActivation();
17312 promise
->MaybeRejectWithNotAllowedError(
17313 "requestStorageAccess not allowed"_ns
);
17314 return promise
.forget();
17318 // Step 2: Check if the browser settings always allow or deny cookies.
17319 // We should always return a resolved promise if the cookieBehavior is ACCEPT.
17320 // This is outside of the spec of the StorageAccess API, but makes the return
17321 // values to have proper semantics.
17322 bool isThirdPartyDocument
= AntiTrackingUtils::IsThirdPartyDocument(this);
17323 bool isOnRejectForeignAllowList
= RejectForeignAllowList::Check(this);
17324 bool isOnThirdPartySkipList
= false;
17326 nsCOMPtr
<nsILoadInfo
> loadInfo
= mChannel
->LoadInfo();
17327 isOnThirdPartySkipList
= loadInfo
->GetStoragePermission() ==
17328 nsILoadInfo::StoragePermissionAllowListed
;
17330 bool isThirdPartyTracker
=
17331 nsContentUtils::IsThirdPartyTrackingResourceWindow(inner
);
17332 Maybe
<bool> resultBecauseBrowserSettings
=
17333 StorageAccessAPIHelper::CheckBrowserSettingsDecidesStorageAccessAPI(
17334 CookieJarSettings(), isThirdPartyDocument
, isOnRejectForeignAllowList
,
17335 isOnThirdPartySkipList
, isThirdPartyTracker
);
17336 if (resultBecauseBrowserSettings
.isSome()) {
17337 if (resultBecauseBrowserSettings
.value()) {
17338 promise
->MaybeResolveWithUndefined();
17339 return promise
.forget();
17341 ConsumeTransientUserGestureActivation();
17342 promise
->MaybeRejectWithNotAllowedError(
17343 "requestStorageAccess not allowed"_ns
);
17344 return promise
.forget();
17348 // Step 3: Check if the Document calling requestStorageAccess has anything to
17349 // gain from storage access. It should be embedded, non-null, etc.
17350 Maybe
<bool> resultBecauseCallContext
=
17351 StorageAccessAPIHelper::CheckCallingContextDecidesStorageAccessAPI(this,
17353 if (resultBecauseCallContext
.isSome()) {
17354 if (resultBecauseCallContext
.value()) {
17355 promise
->MaybeResolveWithUndefined();
17356 return promise
.forget();
17358 ConsumeTransientUserGestureActivation();
17359 promise
->MaybeRejectWithNotAllowedError(
17360 "requestStorageAccess not allowed"_ns
);
17361 return promise
.forget();
17365 // Step 4: Check if we already allowed or denied storage access for this
17366 // document's storage key.
17367 Maybe
<bool> resultBecausePreviousPermission
=
17368 StorageAccessAPIHelper::CheckExistingPermissionDecidesStorageAccessAPI(
17370 if (resultBecausePreviousPermission
.isSome()) {
17371 if (resultBecausePreviousPermission
.value()) {
17372 promise
->MaybeResolveWithUndefined();
17373 return promise
.forget();
17375 ConsumeTransientUserGestureActivation();
17376 promise
->MaybeRejectWithNotAllowedError(
17377 "requestStorageAccess not allowed"_ns
);
17378 return promise
.forget();
17382 // Get pointers to some objects that will be used in the async portion
17383 RefPtr
<BrowsingContext
> bc
= GetBrowsingContext();
17384 RefPtr
<nsGlobalWindowOuter
> outer
=
17385 nsGlobalWindowOuter::Cast(inner
->GetOuterWindow());
17387 ConsumeTransientUserGestureActivation();
17388 promise
->MaybeRejectWithNotAllowedError(
17389 "requestStorageAccess not allowed"_ns
);
17390 return promise
.forget();
17392 RefPtr
<Document
> self(this);
17394 // Consume user activation before entering the async part of this method.
17395 // This prevents usage of other transient activation-gated APIs.
17396 ConsumeTransientUserGestureActivation();
17398 // Step 5. Start an async call to request storage access. This will either
17399 // perform an automatic decision or notify the user, then perform some follow
17400 // on work changing state to reflect the result of the API. If it resolves,
17401 // the request was granted. If it rejects it was denied.
17402 StorageAccessAPIHelper::RequestStorageAccessAsyncHelper(
17403 this, inner
, bc
, NodePrincipal(), true,
17404 ContentBlockingNotifier::eStorageAccessAPI
, true)
17406 GetCurrentSerialEventTarget(), __func__
,
17407 [self
, inner
, promise
] {
17408 inner
->SaveStorageAccessPermissionGranted();
17409 self
->NotifyUserGestureActivation();
17410 promise
->MaybeResolveWithUndefined();
17413 promise
->MaybeRejectWithNotAllowedError(
17414 "requestStorageAccess not allowed"_ns
);
17417 return promise
.forget();
17420 already_AddRefed
<mozilla::dom::Promise
> Document::RequestStorageAccessForOrigin(
17421 const nsAString
& aThirdPartyOrigin
, const bool aRequireUserActivation
,
17422 mozilla::ErrorResult
& aRv
) {
17423 nsIGlobalObject
* global
= GetScopeObject();
17425 aRv
.Throw(NS_ERROR_NOT_AVAILABLE
);
17428 RefPtr
<Promise
> promise
= Promise::Create(global
, aRv
);
17429 if (aRv
.Failed()) {
17433 // Step 0: Check that we have user activation before proceeding to prevent
17434 // rapid calls to the API to leak information.
17435 if (aRequireUserActivation
&& !HasValidTransientUserGestureActivation()) {
17436 // Report an error to the console for this case
17437 nsContentUtils::ReportToConsole(nsIScriptError::errorFlag
,
17438 nsLiteralCString("requestStorageAccess"),
17439 this, nsContentUtils::eDOM_PROPERTIES
,
17440 "RequestStorageAccessUserGesture");
17441 ConsumeTransientUserGestureActivation();
17442 promise
->MaybeRejectWithNotAllowedError(
17443 "requestStorageAccess not allowed"_ns
);
17444 return promise
.forget();
17447 // Step 1: Check if the provided URI is different-site to this Document
17448 nsCOMPtr
<nsIURI
> thirdPartyURI
;
17449 nsresult rv
= NS_NewURI(getter_AddRefs(thirdPartyURI
), aThirdPartyOrigin
);
17450 if (NS_WARN_IF(NS_FAILED(rv
))) {
17454 bool isThirdPartyDocument
;
17455 rv
= NodePrincipal()->IsThirdPartyURI(thirdPartyURI
, &isThirdPartyDocument
);
17456 if (NS_WARN_IF(NS_FAILED(rv
))) {
17460 bool isOnRejectForeignAllowList
=
17461 RejectForeignAllowList::Check(thirdPartyURI
);
17462 Maybe
<bool> resultBecauseBrowserSettings
=
17463 StorageAccessAPIHelper::CheckBrowserSettingsDecidesStorageAccessAPI(
17464 CookieJarSettings(), isThirdPartyDocument
, isOnRejectForeignAllowList
,
17466 if (resultBecauseBrowserSettings
.isSome()) {
17467 if (resultBecauseBrowserSettings
.value()) {
17468 promise
->MaybeResolveWithUndefined();
17469 return promise
.forget();
17471 ConsumeTransientUserGestureActivation();
17472 promise
->MaybeRejectWithNotAllowedError(
17473 "requestStorageAccess not allowed"_ns
);
17474 return promise
.forget();
17477 // Step 2: Check that this Document is same-site to the top, and check that
17478 // we have user activation if we require it.
17479 Maybe
<bool> resultBecauseCallContext
= StorageAccessAPIHelper::
17480 CheckSameSiteCallingContextDecidesStorageAccessAPI(
17481 this, aRequireUserActivation
);
17482 if (resultBecauseCallContext
.isSome()) {
17483 if (resultBecauseCallContext
.value()) {
17484 promise
->MaybeResolveWithUndefined();
17485 return promise
.forget();
17487 ConsumeTransientUserGestureActivation();
17488 promise
->MaybeRejectWithNotAllowedError(
17489 "requestStorageAccess not allowed"_ns
);
17490 return promise
.forget();
17493 // Step 3: Get some useful variables that can be captured by the lambda for
17494 // the asynchronous portion
17495 RefPtr
<BrowsingContext
> bc
= GetBrowsingContext();
17496 nsCOMPtr
<nsPIDOMWindowInner
> inner
= GetInnerWindow();
17498 ConsumeTransientUserGestureActivation();
17499 promise
->MaybeRejectWithNotAllowedError(
17500 "requestStorageAccess not allowed"_ns
);
17501 return promise
.forget();
17503 RefPtr
<nsGlobalWindowOuter
> outer
=
17504 nsGlobalWindowOuter::Cast(inner
->GetOuterWindow());
17506 ConsumeTransientUserGestureActivation();
17507 promise
->MaybeRejectWithNotAllowedError(
17508 "requestStorageAccess not allowed"_ns
);
17509 return promise
.forget();
17511 nsCOMPtr
<nsIPrincipal
> principal
= BasePrincipal::CreateContentPrincipal(
17512 thirdPartyURI
, NodePrincipal()->OriginAttributesRef());
17514 ConsumeTransientUserGestureActivation();
17515 promise
->MaybeRejectWithNotAllowedError(
17516 "requestStorageAccess not allowed"_ns
);
17517 return promise
.forget();
17520 RefPtr
<Document
> self(this);
17521 bool hasUserActivation
= HasValidTransientUserGestureActivation();
17523 // Consume user activation before entering the async part of this method.
17524 // This prevents usage of other transient activation-gated APIs.
17525 ConsumeTransientUserGestureActivation();
17527 // Step 4a: Start the async part of this function. Check the cookie
17528 // permission, but this can't be done in this process. We needs the cookie
17529 // permission of the URL as if it were embedded on this page, so we need to
17530 // make this check in the ContentParent.
17531 StorageAccessAPIHelper::AsyncCheckCookiesPermittedDecidesStorageAccessAPI(
17532 GetBrowsingContext(), principal
)
17534 GetCurrentSerialEventTarget(), __func__
,
17535 [inner
, thirdPartyURI
, bc
, principal
, hasUserActivation
, self
,
17536 promise
](Maybe
<bool> cookieResult
) {
17537 // Handle the result of the cookie permission check that took place
17538 // in the ContentParent.
17539 if (cookieResult
.isSome()) {
17540 if (cookieResult
.value()) {
17541 return MozPromise
<int, bool, true>::CreateAndResolve(true,
17544 return MozPromise
<int, bool, true>::CreateAndReject(false,
17548 // Step 4b: Check for the existing storage access permission
17549 nsAutoCString type
;
17551 AntiTrackingUtils::CreateStoragePermissionKey(principal
, type
);
17553 return MozPromise
<int, bool, true>::CreateAndReject(false,
17556 if (AntiTrackingUtils::CheckStoragePermission(
17557 self
->NodePrincipal(), type
,
17558 nsContentUtils::IsInPrivateBrowsing(self
), nullptr, 0)) {
17559 return MozPromise
<int, bool, true>::CreateAndResolve(true,
17563 // Step 4c: Try to request storage access, either automatically or
17564 // with a user-prompt. This is the part that is async in the
17565 // typical requestStorageAccess function.
17566 return StorageAccessAPIHelper::RequestStorageAccessAsyncHelper(
17567 self
, inner
, bc
, principal
, hasUserActivation
,
17568 ContentBlockingNotifier::ePrivilegeStorageAccessForOriginAPI
,
17571 // If the IPC rejects, we should reject our promise here which will
17572 // cause a rejection of the promise we already returned
17574 return MozPromise
<int, bool, true>::CreateAndReject(false,
17578 GetCurrentSerialEventTarget(), __func__
,
17579 // If the previous handlers resolved, we should reinstate user
17580 // activation and resolve the promise we returned in Step 5.
17581 [self
, inner
, promise
] {
17582 inner
->SaveStorageAccessPermissionGranted();
17583 self
->NotifyUserGestureActivation();
17584 promise
->MaybeResolveWithUndefined();
17586 // If the previous handler rejected, we should reject the promise
17587 // returned by this function.
17589 promise
->MaybeRejectWithNotAllowedError(
17590 "requestStorageAccess not allowed"_ns
);
17593 // Step 5: While the async stuff is happening, we should return the promise so
17594 // our caller can continue executing.
17595 return promise
.forget();
17598 already_AddRefed
<Promise
> Document::RequestStorageAccessUnderSite(
17599 const nsAString
& aSerializedSite
, ErrorResult
& aRv
) {
17600 nsIGlobalObject
* global
= GetScopeObject();
17602 aRv
.Throw(NS_ERROR_NOT_AVAILABLE
);
17605 RefPtr
<Promise
> promise
= Promise::Create(global
, aRv
);
17606 if (aRv
.Failed()) {
17610 // Check that we have user activation before proceeding to prevent
17611 // rapid calls to the API to leak information.
17612 if (!ConsumeTransientUserGestureActivation()) {
17613 // Report an error to the console for this case
17614 nsContentUtils::ReportToConsole(
17615 nsIScriptError::errorFlag
, "requestStorageAccess"_ns
, this,
17616 nsContentUtils::eDOM_PROPERTIES
, "RequestStorageAccessUserGesture");
17617 promise
->MaybeRejectWithUndefined();
17618 return promise
.forget();
17621 // Check if the provided URI is different-site to this Document
17622 nsCOMPtr
<nsIURI
> siteURI
;
17623 nsresult rv
= NS_NewURI(getter_AddRefs(siteURI
), aSerializedSite
);
17624 if (NS_WARN_IF(NS_FAILED(rv
))) {
17625 promise
->MaybeRejectWithUndefined();
17626 return promise
.forget();
17628 bool isCrossSiteArgument
;
17629 rv
= NodePrincipal()->IsThirdPartyURI(siteURI
, &isCrossSiteArgument
);
17630 if (NS_WARN_IF(NS_FAILED(rv
))) {
17634 if (!isCrossSiteArgument
) {
17635 promise
->MaybeRejectWithUndefined();
17636 return promise
.forget();
17639 // Check if this party has broad cookie permissions.
17640 Maybe
<bool> resultBecauseCookiesApproved
=
17641 StorageAccessAPIHelper::CheckCookiesPermittedDecidesStorageAccessAPI(
17642 CookieJarSettings(), NodePrincipal());
17643 if (resultBecauseCookiesApproved
.isSome()) {
17644 if (resultBecauseCookiesApproved
.value()) {
17645 promise
->MaybeResolveWithUndefined();
17646 return promise
.forget();
17648 promise
->MaybeRejectWithUndefined();
17649 return promise
.forget();
17652 // Check if browser settings preclude this document getting storage
17653 // access under the provided site
17654 bool isOnRejectForeignAllowList
= RejectForeignAllowList::Check(this);
17655 Maybe
<bool> resultBecauseBrowserSettings
=
17656 StorageAccessAPIHelper::CheckBrowserSettingsDecidesStorageAccessAPI(
17657 CookieJarSettings(), true, isOnRejectForeignAllowList
, false, true);
17658 if (resultBecauseBrowserSettings
.isSome()) {
17659 if (resultBecauseBrowserSettings
.value()) {
17660 promise
->MaybeResolveWithUndefined();
17661 return promise
.forget();
17663 promise
->MaybeRejectWithUndefined();
17664 return promise
.forget();
17667 // Check that this Document is same-site to the top
17668 Maybe
<bool> resultBecauseCallContext
= StorageAccessAPIHelper::
17669 CheckSameSiteCallingContextDecidesStorageAccessAPI(this, false);
17670 if (resultBecauseCallContext
.isSome()) {
17671 if (resultBecauseCallContext
.value()) {
17672 promise
->MaybeResolveWithUndefined();
17673 return promise
.forget();
17675 promise
->MaybeRejectWithUndefined();
17676 return promise
.forget();
17679 nsCOMPtr
<nsIPrincipal
> principal(NodePrincipal());
17681 // Test if the permission this is requesting is already set
17682 nsCOMPtr
<nsIPrincipal
> argumentPrincipal
=
17683 BasePrincipal::CreateContentPrincipal(
17684 siteURI
, NodePrincipal()->OriginAttributesRef());
17685 if (!argumentPrincipal
) {
17686 ConsumeTransientUserGestureActivation();
17687 promise
->MaybeRejectWithUndefined();
17688 return promise
.forget();
17690 nsCString originNoSuffix
;
17691 rv
= NodePrincipal()->GetOriginNoSuffix(originNoSuffix
);
17692 if (NS_WARN_IF(NS_FAILED(rv
))) {
17693 promise
->MaybeRejectWithUndefined();
17694 return promise
.forget();
17697 ContentChild
* cc
= ContentChild::GetSingleton();
17699 RefPtr
<Document
> self(this);
17700 cc
->SendTestStorageAccessPermission(argumentPrincipal
, originNoSuffix
)
17702 GetCurrentSerialEventTarget(), __func__
,
17704 self
](const ContentChild::TestStorageAccessPermissionPromise::
17705 ResolveValueType
& aResult
) {
17707 return StorageAccessAPIHelper::
17708 StorageAccessPermissionGrantPromise::CreateAndResolve(
17709 StorageAccessAPIHelper::eAllow
, __func__
);
17711 // Get a grant for the storage access permission that will be set
17712 // when this is completed in the embedding context
17713 nsCString serializedSite
;
17714 RefPtr
<nsEffectiveTLDService
> etld
=
17715 nsEffectiveTLDService::GetInstance();
17717 return StorageAccessAPIHelper::
17718 StorageAccessPermissionGrantPromise::CreateAndReject(
17721 nsresult rv
= etld
->GetSite(siteURI
, serializedSite
);
17722 if (NS_FAILED(rv
)) {
17723 return StorageAccessAPIHelper::
17724 StorageAccessPermissionGrantPromise::CreateAndReject(
17727 return self
->CreatePermissionGrantPromise(
17728 self
->GetInnerWindow(), self
->NodePrincipal(), true,
17729 Some(serializedSite
))();
17731 [](const ContentChild::TestStorageAccessPermissionPromise::
17732 RejectValueType
& aResult
) {
17733 return StorageAccessAPIHelper::StorageAccessPermissionGrantPromise::
17734 CreateAndReject(false, __func__
);
17737 GetCurrentSerialEventTarget(), __func__
,
17738 [promise
, principal
, siteURI
](int result
) {
17739 ContentChild
* cc
= ContentChild::GetSingleton();
17741 // TODO(bug 1778561): Make this work in non-content processes.
17742 promise
->MaybeRejectWithUndefined();
17745 // Set a permission in the parent process that this document wants
17746 // storage access under the argument's site, resolving our returned
17747 // promise on success
17748 cc
->SendSetAllowStorageAccessRequestFlag(principal
, siteURI
)
17750 GetCurrentSerialEventTarget(), __func__
,
17751 [promise
](bool success
) {
17753 promise
->MaybeResolveWithUndefined();
17755 promise
->MaybeRejectWithUndefined();
17758 [promise
](mozilla::ipc::ResponseRejectReason reason
) {
17759 promise
->MaybeRejectWithUndefined();
17762 [promise
](bool result
) { promise
->MaybeRejectWithUndefined(); });
17764 // Return the promise that is resolved in the async handler above
17765 return promise
.forget();
17768 already_AddRefed
<Promise
> Document::CompleteStorageAccessRequestFromSite(
17769 const nsAString
& aSerializedOrigin
, ErrorResult
& aRv
) {
17770 nsIGlobalObject
* global
= GetScopeObject();
17772 aRv
.Throw(NS_ERROR_NOT_AVAILABLE
);
17775 RefPtr
<Promise
> promise
= Promise::Create(global
, aRv
);
17776 if (aRv
.Failed()) {
17780 // Check that the provided URI is different-site to this Document
17781 nsCOMPtr
<nsIURI
> argumentURI
;
17782 nsresult rv
= NS_NewURI(getter_AddRefs(argumentURI
), aSerializedOrigin
);
17783 if (NS_WARN_IF(NS_FAILED(rv
))) {
17784 promise
->MaybeRejectWithUndefined();
17785 return promise
.forget();
17787 bool isCrossSiteArgument
;
17788 rv
= NodePrincipal()->IsThirdPartyURI(argumentURI
, &isCrossSiteArgument
);
17789 if (NS_WARN_IF(NS_FAILED(rv
))) {
17793 if (!isCrossSiteArgument
) {
17794 promise
->MaybeRejectWithUndefined();
17795 return promise
.forget();
17798 // Check if browser settings preclude this document getting storage
17799 // access under the provided site
17800 bool isOnRejectForeignAllowList
= RejectForeignAllowList::Check(argumentURI
);
17801 Maybe
<bool> resultBecauseBrowserSettings
=
17802 StorageAccessAPIHelper::CheckBrowserSettingsDecidesStorageAccessAPI(
17803 CookieJarSettings(), true, isOnRejectForeignAllowList
, false, true);
17804 if (resultBecauseBrowserSettings
.isSome()) {
17805 if (resultBecauseBrowserSettings
.value()) {
17806 promise
->MaybeResolveWithUndefined();
17807 return promise
.forget();
17809 promise
->MaybeRejectWithUndefined();
17810 return promise
.forget();
17813 // Check that this Document is same-site to the top
17814 Maybe
<bool> resultBecauseCallContext
= StorageAccessAPIHelper::
17815 CheckSameSiteCallingContextDecidesStorageAccessAPI(this, false);
17816 if (resultBecauseCallContext
.isSome()) {
17817 if (resultBecauseCallContext
.value()) {
17818 promise
->MaybeResolveWithUndefined();
17819 return promise
.forget();
17821 promise
->MaybeRejectWithUndefined();
17822 return promise
.forget();
17825 // Create principal of the embedded site requesting storage access
17826 nsCOMPtr
<nsIPrincipal
> principal
= BasePrincipal::CreateContentPrincipal(
17827 argumentURI
, NodePrincipal()->OriginAttributesRef());
17829 promise
->MaybeRejectWithUndefined();
17830 return promise
.forget();
17833 // Get versions of these objects that we can use in lambdas for callbacks
17834 RefPtr
<Document
> self(this);
17835 RefPtr
<BrowsingContext
> bc
= GetBrowsingContext();
17836 nsCOMPtr
<nsPIDOMWindowInner
> inner
= GetInnerWindow();
17838 // Test that the permission was set by a call to RequestStorageAccessUnderSite
17839 // from a top level document that is same-site with the argument
17840 ContentChild
* cc
= ContentChild::GetSingleton();
17842 // TODO(bug 1778561): Make this work in non-content processes.
17843 promise
->MaybeRejectWithUndefined();
17844 return promise
.forget();
17846 cc
->SendTestAllowStorageAccessRequestFlag(NodePrincipal(), argumentURI
)
17848 GetCurrentSerialEventTarget(), __func__
,
17849 [inner
, bc
, self
, principal
](bool success
) {
17851 // If that resolved with true, check that we don't already have a
17852 // permission that gives cookie access.
17853 return StorageAccessAPIHelper::
17854 AsyncCheckCookiesPermittedDecidesStorageAccessAPI(bc
,
17857 return MozPromise
<Maybe
<bool>, nsresult
, true>::CreateAndReject(
17858 NS_ERROR_FAILURE
, __func__
);
17860 [](mozilla::ipc::ResponseRejectReason reason
) {
17861 return MozPromise
<Maybe
<bool>, nsresult
, true>::CreateAndReject(
17862 NS_ERROR_FAILURE
, __func__
);
17865 GetCurrentSerialEventTarget(), __func__
,
17866 [inner
, bc
, principal
, self
, promise
](Maybe
<bool> cookieResult
) {
17867 // Handle the result of the cookie permission check that took place
17868 // in the ContentParent.
17869 if (cookieResult
.isSome()) {
17870 if (cookieResult
.value()) {
17871 return StorageAccessAPIHelper::
17872 StorageAccessPermissionGrantPromise::CreateAndResolve(
17873 StorageAccessAPIHelper::eAllowAutoGrant
, __func__
);
17875 return StorageAccessAPIHelper::
17876 StorageAccessPermissionGrantPromise::CreateAndReject(
17880 // Check for the existing storage access permission
17881 nsAutoCString type
;
17883 AntiTrackingUtils::CreateStoragePermissionKey(principal
, type
);
17885 return StorageAccessAPIHelper::
17886 StorageAccessPermissionGrantPromise::CreateAndReject(
17889 if (AntiTrackingUtils::CheckStoragePermission(
17890 self
->NodePrincipal(), type
,
17891 nsContentUtils::IsInPrivateBrowsing(self
), nullptr, 0)) {
17892 return StorageAccessAPIHelper::
17893 StorageAccessPermissionGrantPromise::CreateAndResolve(
17894 StorageAccessAPIHelper::eAllowAutoGrant
, __func__
);
17897 // Try to request storage access, ignoring the final checks.
17898 // We ignore the final checks because this is where the "grant"
17899 // either by prompt doorhanger or autogrant takes place. We already
17900 // gathered an equivalent grant in requestStorageAccessUnderSite.
17901 return StorageAccessAPIHelper::RequestStorageAccessAsyncHelper(
17902 self
, inner
, bc
, principal
, true,
17903 ContentBlockingNotifier::eStorageAccessAPI
, false);
17905 // If the IPC rejects, we should reject our promise here which will
17906 // cause a rejection of the promise we already returned
17908 return MozPromise
<int, bool, true>::CreateAndReject(false,
17912 GetCurrentSerialEventTarget(), __func__
,
17913 // If the previous handlers resolved, we should reinstate user
17914 // activation and resolve the promise we returned in Step 5.
17915 [self
, inner
, promise
] {
17916 inner
->SaveStorageAccessPermissionGranted();
17917 promise
->MaybeResolveWithUndefined();
17919 // If the previous handler rejected, we should reject the promise
17920 // returned by this function.
17921 [promise
] { promise
->MaybeRejectWithUndefined(); });
17923 return promise
.forget();
17926 RefPtr
<Document::AutomaticStorageAccessPermissionGrantPromise
>
17927 Document::AutomaticStorageAccessPermissionCanBeGranted(bool hasUserActivation
) {
17928 // requestStorageAccessForOrigin may not require user activation. If we don't
17929 // have user activation at this point we should always show the prompt.
17930 if (!hasUserActivation
||
17931 !StaticPrefs::privacy_antitracking_enableWebcompat()) {
17932 return AutomaticStorageAccessPermissionGrantPromise::CreateAndResolve(
17935 if (XRE_IsContentProcess()) {
17936 // In the content process, we need to ask the parent process to compute
17937 // this. The reason is that nsIPermissionManager::GetAllWithTypePrefix()
17938 // isn't accessible in the content process.
17939 ContentChild
* cc
= ContentChild::GetSingleton();
17942 return cc
->SendAutomaticStorageAccessPermissionCanBeGranted(NodePrincipal())
17943 ->Then(GetCurrentSerialEventTarget(), __func__
,
17944 [](const ContentChild::
17945 AutomaticStorageAccessPermissionCanBeGrantedPromise::
17946 ResolveOrRejectValue
& aValue
) {
17947 if (aValue
.IsResolve()) {
17948 return AutomaticStorageAccessPermissionGrantPromise::
17949 CreateAndResolve(aValue
.ResolveValue(), __func__
);
17952 return AutomaticStorageAccessPermissionGrantPromise::
17953 CreateAndReject(false, __func__
);
17957 if (XRE_IsParentProcess()) {
17958 // In the parent process, we can directly compute this.
17959 return AutomaticStorageAccessPermissionGrantPromise::CreateAndResolve(
17960 AutomaticStorageAccessPermissionCanBeGranted(NodePrincipal()),
17964 return AutomaticStorageAccessPermissionGrantPromise::CreateAndReject(
17968 bool Document::AutomaticStorageAccessPermissionCanBeGranted(
17969 nsIPrincipal
* aPrincipal
) {
17970 if (!StaticPrefs::dom_storage_access_auto_grants()) {
17974 if (!ContentBlockingUserInteraction::Exists(aPrincipal
)) {
17978 nsCOMPtr
<nsIBrowserUsage
> bu
= do_ImportModule(
17979 "resource:///modules/BrowserUsageTelemetry.jsm", fallible
);
17980 if (NS_WARN_IF(!bu
)) {
17984 uint32_t uniqueDomainsVisitedInPast24Hours
= 0;
17985 nsresult rv
= bu
->GetUniqueDomainsVisitedInPast24Hours(
17986 &uniqueDomainsVisitedInPast24Hours
);
17987 if (NS_WARN_IF(NS_FAILED(rv
))) {
17991 Maybe
<size_t> maybeOriginsThirdPartyHasAccessTo
=
17992 AntiTrackingUtils::CountSitesAllowStorageAccess(aPrincipal
);
17993 if (maybeOriginsThirdPartyHasAccessTo
.isNothing()) {
17996 size_t originsThirdPartyHasAccessTo
=
17997 maybeOriginsThirdPartyHasAccessTo
.value();
17999 // one percent of the number of top-levels origins visited in the current
18000 // session (but not to exceed 24 hours), or the value of the
18001 // dom.storage_access.max_concurrent_auto_grants preference, whichever is
18003 size_t maxConcurrentAutomaticGrants
= std::max(
18004 std::max(int(std::floor(uniqueDomainsVisitedInPast24Hours
/ 100)),
18005 StaticPrefs::dom_storage_access_max_concurrent_auto_grants()),
18008 return originsThirdPartyHasAccessTo
< maxConcurrentAutomaticGrants
;
18011 void Document::RecordNavigationTiming(ReadyState aReadyState
) {
18012 if (!XRE_IsContentProcess()) {
18015 if (!IsTopLevelContentDocument()) {
18018 // If we dont have the timing yet (mostly because the doc is still loading),
18019 // get it from docshell.
18020 RefPtr
<nsDOMNavigationTiming
> timing
= mTiming
;
18022 if (!mDocumentContainer
) {
18025 timing
= mDocumentContainer
->GetNavigationTiming();
18030 TimeStamp startTime
= timing
->GetNavigationStartTimeStamp();
18031 switch (aReadyState
) {
18032 case READYSTATE_LOADING
:
18033 if (!mDOMLoadingSet
) {
18034 Telemetry::AccumulateTimeDelta(Telemetry::TIME_TO_DOM_LOADING_MS
,
18036 mDOMLoadingSet
= true;
18039 case READYSTATE_INTERACTIVE
:
18040 if (!mDOMInteractiveSet
) {
18041 Telemetry::AccumulateTimeDelta(Telemetry::TIME_TO_DOM_INTERACTIVE_MS
,
18043 mDOMInteractiveSet
= true;
18046 case READYSTATE_COMPLETE
:
18047 if (!mDOMCompleteSet
) {
18048 Telemetry::AccumulateTimeDelta(Telemetry::TIME_TO_DOM_COMPLETE_MS
,
18050 mDOMCompleteSet
= true;
18054 NS_WARNING("Unexpected ReadyState value");
18059 bool Document::ModuleScriptsEnabled() {
18060 return nsContentUtils::IsChromeDoc(this) ||
18061 StaticPrefs::dom_moduleScripts_enabled();
18064 bool Document::ImportMapsEnabled() {
18065 return nsContentUtils::IsChromeDoc(this) ||
18066 StaticPrefs::dom_importMaps_enabled();
18069 void Document::ReportShadowDOMUsage() {
18070 nsPIDOMWindowInner
* inner
= GetInnerWindow();
18071 if (NS_WARN_IF(!inner
)) {
18075 WindowContext
* wc
= inner
->GetWindowContext();
18076 if (NS_WARN_IF(!wc
|| wc
->IsDiscarded())) {
18080 WindowContext
* topWc
= wc
->TopWindowContext();
18081 if (topWc
->GetHasReportedShadowDOMUsage()) {
18085 MOZ_ALWAYS_SUCCEEDS(topWc
->SetHasReportedShadowDOMUsage(true));
18089 bool Document::StorageAccessSandboxed(uint32_t aSandboxFlags
) {
18090 return StaticPrefs::dom_storage_access_enabled() &&
18091 (aSandboxFlags
& SANDBOXED_STORAGE_ACCESS
) != 0;
18094 bool Document::StorageAccessSandboxed() const {
18095 return Document::StorageAccessSandboxed(GetSandboxFlags());
18098 bool Document::GetCachedSizes(nsTabSizes
* aSizes
) {
18099 if (mCachedTabSizeGeneration
== 0 ||
18100 GetGeneration() != mCachedTabSizeGeneration
) {
18103 aSizes
->mDom
+= mCachedTabSizes
.mDom
;
18104 aSizes
->mStyle
+= mCachedTabSizes
.mStyle
;
18105 aSizes
->mOther
+= mCachedTabSizes
.mOther
;
18109 void Document::SetCachedSizes(nsTabSizes
* aSizes
) {
18110 mCachedTabSizes
.mDom
= aSizes
->mDom
;
18111 mCachedTabSizes
.mStyle
= aSizes
->mStyle
;
18112 mCachedTabSizes
.mOther
= aSizes
->mOther
;
18113 mCachedTabSizeGeneration
= GetGeneration();
18116 already_AddRefed
<nsAtom
> Document::GetContentLanguageAsAtomForStyle() const {
18117 nsAutoString contentLang
;
18118 GetContentLanguage(contentLang
);
18119 contentLang
.StripWhitespace();
18121 // Content-Language may be a comma-separated list of language codes,
18122 // in which case the HTML5 spec says to treat it as unknown
18123 if (!contentLang
.IsEmpty() && !contentLang
.Contains(char16_t(','))) {
18124 return NS_Atomize(contentLang
);
18130 already_AddRefed
<nsAtom
> Document::GetLanguageForStyle() const {
18131 RefPtr
<nsAtom
> lang
= GetContentLanguageAsAtomForStyle();
18133 lang
= mLanguageFromCharset
;
18135 return lang
.forget();
18138 const LangGroupFontPrefs
* Document::GetFontPrefsForLang(
18139 nsAtom
* aLanguage
, bool* aNeedsToCache
) const {
18140 nsAtom
* lang
= aLanguage
? aLanguage
: mLanguageFromCharset
.get();
18141 return StaticPresData::Get()->GetFontPrefsForLang(lang
, aNeedsToCache
);
18144 void Document::DoCacheAllKnownLangPrefs() {
18145 MOZ_ASSERT(mMayNeedFontPrefsUpdate
);
18146 RefPtr
<nsAtom
> lang
= GetLanguageForStyle();
18147 StaticPresData
* data
= StaticPresData::Get();
18148 data
->GetFontPrefsForLang(lang
? lang
.get() : mLanguageFromCharset
.get());
18149 data
->GetFontPrefsForLang(nsGkAtoms::x_math
);
18150 // https://bugzilla.mozilla.org/show_bug.cgi?id=1362599#c12
18151 data
->GetFontPrefsForLang(nsGkAtoms::Unicode
);
18152 for (const auto& key
: mLanguagesUsed
) {
18153 data
->GetFontPrefsForLang(key
);
18155 mMayNeedFontPrefsUpdate
= false;
18158 void Document::RecomputeLanguageFromCharset() {
18159 nsLanguageAtomService
* service
= nsLanguageAtomService::GetService();
18160 RefPtr
<nsAtom
> language
= service
->LookupCharSet(mCharacterSet
);
18161 if (language
== nsGkAtoms::Unicode
) {
18162 language
= service
->GetLocaleLanguage();
18165 if (language
== mLanguageFromCharset
) {
18169 mMayNeedFontPrefsUpdate
= true;
18170 mLanguageFromCharset
= std::move(language
);
18173 nsICookieJarSettings
* Document::CookieJarSettings() {
18174 // If we are here, this is probably a javascript: URL document. In any case,
18175 // we must have a nsCookieJarSettings. Let's create it.
18176 if (!mCookieJarSettings
) {
18177 Document
* inProcessParent
= GetInProcessParentDocument();
18179 if (inProcessParent
) {
18180 mCookieJarSettings
= net::CookieJarSettings::Create(
18181 inProcessParent
->CookieJarSettings()->GetCookieBehavior(),
18182 mozilla::net::CookieJarSettings::Cast(
18183 inProcessParent
->CookieJarSettings())
18184 ->GetPartitionKey(),
18185 inProcessParent
->CookieJarSettings()->GetIsFirstPartyIsolated(),
18186 inProcessParent
->CookieJarSettings()
18187 ->GetIsOnContentBlockingAllowList(),
18188 inProcessParent
->CookieJarSettings()
18189 ->GetShouldResistFingerprinting());
18191 // Inherit the fingerprinting random key from the parent.
18192 nsTArray
<uint8_t> randomKey
;
18193 nsresult rv
= inProcessParent
->CookieJarSettings()
18194 ->GetFingerprintingRandomizationKey(randomKey
);
18196 if (NS_SUCCEEDED(rv
)) {
18197 net::CookieJarSettings::Cast(mCookieJarSettings
)
18198 ->SetFingerprintingRandomizationKey(randomKey
);
18201 mCookieJarSettings
= net::CookieJarSettings::Create(NodePrincipal());
18204 if (auto* wgc
= GetWindowGlobalChild()) {
18205 net::CookieJarSettingsArgs csArgs
;
18206 net::CookieJarSettings::Cast(mCookieJarSettings
)->Serialize(csArgs
);
18207 // Update cookie settings in the parent process
18208 if (!wgc
->SendUpdateCookieJarSettings(csArgs
)) {
18210 "Failed to update document's cookie jar settings on the "
18211 "WindowGlobalParent");
18216 return mCookieJarSettings
;
18219 bool Document::HasStorageAccessPermissionGranted() {
18220 // The HasStoragePermission flag in LoadInfo remains fixed when
18221 // it is set in the parent process, so we need to check the cache
18222 // to see if the permission is granted afterwards.
18223 nsPIDOMWindowInner
* inner
= GetInnerWindow();
18224 if (inner
&& inner
->HasStorageAccessPermissionGranted()) {
18232 nsCOMPtr
<nsILoadInfo
> loadInfo
= mChannel
->LoadInfo();
18233 return loadInfo
->GetStoragePermission() != nsILoadInfo::NoStoragePermission
;
18236 bool Document::HasStorageAccessPermissionGrantedByAllowList() {
18237 // We only care about if the document gets the storage permission via the
18238 // allow list here. So we don't check the storage access cache in the inner
18245 nsCOMPtr
<nsILoadInfo
> loadInfo
= mChannel
->LoadInfo();
18246 return loadInfo
->GetStoragePermission() ==
18247 nsILoadInfo::StoragePermissionAllowListed
;
18250 nsIPrincipal
* Document::EffectiveStoragePrincipal() const {
18252 privacy_partition_always_partition_third_party_non_cookie_storage()) {
18253 return EffectiveCookiePrincipal();
18256 nsPIDOMWindowInner
* inner
= GetInnerWindow();
18258 return NodePrincipal();
18261 // Return our cached storage principal if one exists.
18262 if (mActiveStoragePrincipal
) {
18263 return mActiveStoragePrincipal
;
18266 // Calling StorageAllowedForDocument will notify the ContentBlockLog. This
18267 // loads TrackingDBService.jsm, which in turn pulls in osfile.jsm, making us
18268 // fail // browser/base/content/test/performance/browser_startup.js. To avoid
18269 // that, we short-circuit the check here by allowing storage access to system
18270 // and addon principles, avoiding the test-failure.
18271 nsIPrincipal
* principal
= NodePrincipal();
18272 if (principal
&& (principal
->IsSystemPrincipal() ||
18273 principal
->GetIsAddonOrExpandedAddonPrincipal())) {
18274 return mActiveStoragePrincipal
= NodePrincipal();
18277 auto cookieJarSettings
= const_cast<Document
*>(this)->CookieJarSettings();
18278 if (cookieJarSettings
->GetIsOnContentBlockingAllowList()) {
18279 return mActiveStoragePrincipal
= NodePrincipal();
18282 StorageAccess storageAccess
= StorageAllowedForDocument(this);
18283 if (!ShouldPartitionStorage(storageAccess
) ||
18284 !StoragePartitioningEnabled(storageAccess
, cookieJarSettings
)) {
18285 return mActiveStoragePrincipal
= NodePrincipal();
18288 Unused
<< NS_WARN_IF(NS_FAILED(StoragePrincipalHelper::GetPrincipal(
18289 nsGlobalWindowInner::Cast(inner
),
18290 StoragePrincipalHelper::eForeignPartitionedPrincipal
,
18291 getter_AddRefs(mActiveStoragePrincipal
))));
18292 return mActiveStoragePrincipal
;
18295 nsIPrincipal
* Document::EffectiveCookiePrincipal() const {
18296 nsPIDOMWindowInner
* inner
= GetInnerWindow();
18298 return NodePrincipal();
18301 // Return our cached storage principal if one exists.
18302 if (mActiveCookiePrincipal
) {
18303 return mActiveCookiePrincipal
;
18306 // We use the lower-level ContentBlocking API here to ensure this
18307 // check doesn't send notifications.
18308 uint32_t rejectedReason
= 0;
18309 if (ShouldAllowAccessFor(inner
, GetDocumentURI(), &rejectedReason
)) {
18310 return mActiveCookiePrincipal
= NodePrincipal();
18313 // Let's use the storage principal only if we need to partition the cookie
18314 // jar. When the permission is granted, access will be different and the
18315 // normal principal will be used.
18316 if (ShouldPartitionStorage(rejectedReason
) &&
18317 !StoragePartitioningEnabled(
18318 rejectedReason
, const_cast<Document
*>(this)->CookieJarSettings())) {
18319 return mActiveCookiePrincipal
= NodePrincipal();
18322 return mActiveCookiePrincipal
= mPartitionedPrincipal
;
18325 nsIPrincipal
* Document::GetPrincipalForPrefBasedHacks() const {
18326 // If the document is sandboxed document or data: document, we should
18327 // get URI of the parent document.
18328 for (const Document
* document
= this;
18329 document
&& document
->IsContentDocument();
18330 document
= document
->GetInProcessParentDocument()) {
18331 // The document URI may be about:blank even if it comes from actual web
18332 // site. Therefore, we need to check the URI of its principal.
18333 nsIPrincipal
* principal
= document
->NodePrincipal();
18334 if (principal
->GetIsNullPrincipal()) {
18342 void Document::SetIsInitialDocument(bool aIsInitialDocument
) {
18343 mIsInitialDocumentInWindow
= aIsInitialDocument
;
18345 // Asynchronously tell the parent process that we are, or are no longer, the
18346 // initial document. This happens async.
18347 if (auto* wgc
= GetWindowGlobalChild()) {
18348 wgc
->SendSetIsInitialDocument(aIsInitialDocument
);
18353 void Document::AddToplevelLoadingDocument(Document
* aDoc
) {
18354 MOZ_ASSERT(aDoc
&& aDoc
->IsTopLevelContentDocument());
18355 // Currently we're interested in foreground documents only, so bail out early.
18356 if (aDoc
->IsInBackgroundWindow() || !XRE_IsContentProcess()) {
18360 if (!sLoadingForegroundTopLevelContentDocument
) {
18361 sLoadingForegroundTopLevelContentDocument
= new AutoTArray
<Document
*, 8>();
18362 mozilla::ipc::IdleSchedulerChild
* idleScheduler
=
18363 mozilla::ipc::IdleSchedulerChild::GetMainThreadIdleScheduler();
18364 if (idleScheduler
) {
18365 idleScheduler
->SendRunningPrioritizedOperation();
18368 if (!sLoadingForegroundTopLevelContentDocument
->Contains(aDoc
)) {
18369 sLoadingForegroundTopLevelContentDocument
->AppendElement(aDoc
);
18374 void Document::RemoveToplevelLoadingDocument(Document
* aDoc
) {
18375 MOZ_ASSERT(aDoc
&& aDoc
->IsTopLevelContentDocument());
18376 if (sLoadingForegroundTopLevelContentDocument
) {
18377 sLoadingForegroundTopLevelContentDocument
->RemoveElement(aDoc
);
18378 if (sLoadingForegroundTopLevelContentDocument
->IsEmpty()) {
18379 delete sLoadingForegroundTopLevelContentDocument
;
18380 sLoadingForegroundTopLevelContentDocument
= nullptr;
18382 mozilla::ipc::IdleSchedulerChild
* idleScheduler
=
18383 mozilla::ipc::IdleSchedulerChild::GetMainThreadIdleScheduler();
18384 if (idleScheduler
) {
18385 idleScheduler
->SendPrioritizedOperationDone();
18391 ColorScheme
Document::DefaultColorScheme() const {
18392 return LookAndFeel::ColorSchemeForStyle(*this, {GetColorSchemeBits()});
18395 ColorScheme
Document::PreferredColorScheme(IgnoreRFP aIgnoreRFP
) const {
18396 if (ShouldResistFingerprinting(RFPTarget::CSSPrefersColorScheme
) &&
18397 aIgnoreRFP
== IgnoreRFP::No
) {
18398 return ColorScheme::Light
;
18401 if (nsPresContext
* pc
= GetPresContext()) {
18402 if (auto scheme
= pc
->GetOverriddenOrEmbedderColorScheme()) {
18407 // NOTE(emilio): We use IsInChromeDocShell rather than IsChromeDoc
18408 // intentionally, to make chrome documents in content docshells (like about
18409 // pages) use the content color scheme.
18410 if (IsInChromeDocShell()) {
18411 return LookAndFeel::ColorSchemeForChrome();
18413 return LookAndFeel::PreferredColorSchemeForContent();
18416 bool Document::HasRecentlyStartedForegroundLoads() {
18417 if (!sLoadingForegroundTopLevelContentDocument
) {
18421 for (size_t i
= 0; i
< sLoadingForegroundTopLevelContentDocument
->Length();
18423 Document
* doc
= sLoadingForegroundTopLevelContentDocument
->ElementAt(i
);
18424 // A page loaded in foreground could be in background now.
18425 if (!doc
->IsInBackgroundWindow()) {
18426 nsPIDOMWindowInner
* win
= doc
->GetInnerWindow();
18428 Performance
* perf
= win
->GetPerformance();
18430 perf
->Now() < StaticPrefs::page_load_deprioritization_period()) {
18437 // Didn't find any loading foreground documents, just clear the array.
18438 delete sLoadingForegroundTopLevelContentDocument
;
18439 sLoadingForegroundTopLevelContentDocument
= nullptr;
18441 mozilla::ipc::IdleSchedulerChild
* idleScheduler
=
18442 mozilla::ipc::IdleSchedulerChild::GetMainThreadIdleScheduler();
18443 if (idleScheduler
) {
18444 idleScheduler
->SendPrioritizedOperationDone();
18449 void Document::AddPendingFrameStaticClone(nsFrameLoaderOwner
* aElement
,
18450 nsFrameLoader
* aStaticCloneOf
) {
18451 PendingFrameStaticClone
* clone
= mPendingFrameStaticClones
.AppendElement();
18452 clone
->mElement
= aElement
;
18453 clone
->mStaticCloneOf
= aStaticCloneOf
;
18456 bool Document::ShouldAvoidNativeTheme() const {
18457 return StaticPrefs::widget_non_native_theme_enabled() &&
18458 (!IsInChromeDocShell() || XRE_IsContentProcess());
18461 bool Document::UseRegularPrincipal() const {
18462 return EffectiveStoragePrincipal() == NodePrincipal();
18465 bool Document::HasThirdPartyChannel() {
18466 nsCOMPtr
<nsIChannel
> channel
= GetChannel();
18468 // We assume that the channel is a third-party by default.
18469 bool thirdParty
= true;
18471 nsCOMPtr
<mozIThirdPartyUtil
> thirdPartyUtil
=
18472 components::ThirdPartyUtil::Service();
18473 if (!thirdPartyUtil
) {
18477 // Check that if the channel is a third-party to its parent.
18479 thirdPartyUtil
->IsThirdPartyChannel(channel
, nullptr, &thirdParty
);
18480 if (NS_FAILED(rv
)) {
18481 // Assume third-party in case of failure
18488 if (mParentDocument
) {
18489 return mParentDocument
->HasThirdPartyChannel();
18495 bool Document::ShouldIncludeInTelemetry(bool aAllowExtensionURIs
) {
18496 if (!(IsContentDocument() || IsResourceDoc())) {
18500 if (!aAllowExtensionURIs
&&
18501 NodePrincipal()->GetIsAddonOrExpandedAddonPrincipal()) {
18505 return !NodePrincipal()->SchemeIs("about") &&
18506 !NodePrincipal()->SchemeIs("chrome") &&
18507 !NodePrincipal()->SchemeIs("resource");
18510 void Document::GetConnectedShadowRoots(
18511 nsTArray
<RefPtr
<ShadowRoot
>>& aOut
) const {
18512 AppendToArray(aOut
, mComposedShadowRoots
);
18515 bool Document::HasPictureInPictureChildElement() const {
18516 return mPictureInPictureChildElementCount
> 0;
18519 void Document::EnableChildElementInPictureInPictureMode() {
18520 mPictureInPictureChildElementCount
++;
18521 MOZ_ASSERT(mPictureInPictureChildElementCount
>= 0);
18524 void Document::DisableChildElementInPictureInPictureMode() {
18525 mPictureInPictureChildElementCount
--;
18526 MOZ_ASSERT(mPictureInPictureChildElementCount
>= 0);
18529 void Document::AddMediaElementWithMSE() {
18530 if (mMediaElementWithMSECount
++ == 0) {
18531 if (WindowGlobalChild
* wgc
= GetWindowGlobalChild()) {
18532 wgc
->BlockBFCacheFor(BFCacheStatus::CONTAINS_MSE_CONTENT
);
18537 void Document::RemoveMediaElementWithMSE() {
18538 MOZ_ASSERT(mMediaElementWithMSECount
> 0);
18539 if (--mMediaElementWithMSECount
== 0) {
18540 if (WindowGlobalChild
* wgc
= GetWindowGlobalChild()) {
18541 wgc
->UnblockBFCacheFor(BFCacheStatus::CONTAINS_MSE_CONTENT
);
18546 void Document::UnregisterFromMemoryReportingForDataDocument() {
18547 if (!mAddedToMemoryReportingAsDataDocument
) {
18550 mAddedToMemoryReportingAsDataDocument
= false;
18551 nsIGlobalObject
* global
= GetScopeObject();
18553 if (nsPIDOMWindowInner
* win
= global
->AsInnerWindow()) {
18554 nsGlobalWindowInner::Cast(win
)->UnregisterDataDocumentForMemoryReporting(
18559 void Document::OOPChildLoadStarted(BrowserBridgeChild
* aChild
) {
18560 MOZ_DIAGNOSTIC_ASSERT(!mOOPChildrenLoading
.Contains(aChild
));
18561 mOOPChildrenLoading
.AppendElement(aChild
);
18562 if (mOOPChildrenLoading
.Length() == 1) {
18563 // Let's block unload so that we're blocked from going into the BFCache
18564 // until the child has actually notified us that it has done loading.
18569 void Document::OOPChildLoadDone(BrowserBridgeChild
* aChild
) {
18570 // aChild will not be in the list if nsDocLoader::Stop() was called, since
18571 // that clears mOOPChildrenLoading. It also dispatches the 'load' event,
18572 // so we don't need to call DocLoaderIsEmpty in that case.
18573 if (mOOPChildrenLoading
.RemoveElement(aChild
)) {
18574 if (mOOPChildrenLoading
.IsEmpty()) {
18575 UnblockOnload(false);
18577 RefPtr
<nsDocLoader
> docLoader(mDocumentContainer
);
18579 docLoader
->OOPChildrenLoadingIsEmpty();
18584 void Document::ClearOOPChildrenLoading() {
18585 nsTArray
<const BrowserBridgeChild
*> oopChildrenLoading
;
18586 mOOPChildrenLoading
.SwapElements(oopChildrenLoading
);
18587 if (!oopChildrenLoading
.IsEmpty()) {
18588 UnblockOnload(false);
18592 HighlightRegistry
& Document::HighlightRegistry() {
18593 if (!mHighlightRegistry
) {
18594 mHighlightRegistry
= MakeRefPtr
<class HighlightRegistry
>(this);
18596 return *mHighlightRegistry
;
18599 } // namespace mozilla::dom