1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
8 * Base class for all our document implementations.
11 #include "mozilla/dom/Document.h"
12 #include "mozilla/dom/DocumentInlines.h"
20 #include <initializer_list>
23 #include <type_traits>
25 #include "ErrorList.h"
26 #include "ExpandedPrincipal.h"
27 #include "MainThreadUtils.h"
28 #include "MobileViewportManager.h"
29 #include "NodeUbiReporting.h"
30 #include "PLDHashTable.h"
31 #include "StorageAccessPermissionRequest.h"
32 #include "ThirdPartyUtil.h"
34 #include "gfxPlatform.h"
35 #include "imgIContainer.h"
36 #include "imgLoader.h"
37 #include "imgRequestProxy.h"
40 #include "mozAutoDocUpdate.h"
41 #include "mozIDOMWindow.h"
42 #include "mozIThirdPartyUtil.h"
43 #include "mozilla/AntiTrackingUtils.h"
44 #include "mozilla/ArrayIterator.h"
45 #include "mozilla/ArrayUtils.h"
46 #include "mozilla/AsyncEventDispatcher.h"
47 #include "mozilla/Base64.h"
48 #include "mozilla/BasePrincipal.h"
49 #include "mozilla/BounceTrackingProtection.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/ProfilerMarkers.h"
58 #include "mozilla/AttributeStyles.h"
59 #include "mozilla/DocumentStyleRootIterator.h"
60 #include "mozilla/EditorBase.h"
61 #include "mozilla/EditorCommands.h"
62 #include "mozilla/Encoding.h"
63 #include "mozilla/ErrorResult.h"
64 #include "mozilla/EventDispatcher.h"
65 #include "mozilla/EventListenerManager.h"
66 #include "mozilla/EventQueue.h"
67 #include "mozilla/EventStateManager.h"
68 #include "mozilla/ExtensionPolicyService.h"
69 #include "mozilla/FullscreenChange.h"
70 #include "mozilla/GlobalStyleSheetCache.h"
71 #include "mozilla/MappedDeclarationsBuilder.h"
72 #include "mozilla/HTMLEditor.h"
73 #include "mozilla/HoldDropJSObjects.h"
74 #include "mozilla/IdentifierMapEntry.h"
75 #include "mozilla/InputTaskManager.h"
76 #include "mozilla/IntegerRange.h"
77 #include "mozilla/InternalMutationEvent.h"
78 #include "mozilla/Likely.h"
79 #include "mozilla/Logging.h"
80 #include "mozilla/LookAndFeel.h"
81 #include "mozilla/MacroForEach.h"
82 #include "mozilla/Maybe.h"
83 #include "mozilla/MediaFeatureChange.h"
84 #include "mozilla/MediaManager.h"
85 #include "mozilla/MemoryReporting.h"
86 #include "mozilla/NullPrincipal.h"
87 #include "mozilla/OriginAttributes.h"
88 #include "mozilla/OwningNonNull.h"
89 #include "mozilla/PendingFullscreenEvent.h"
90 #include "mozilla/PermissionDelegateHandler.h"
91 #include "mozilla/PermissionManager.h"
92 #include "mozilla/Preferences.h"
93 #include "mozilla/PreloadHashKey.h"
94 #include "mozilla/PresShell.h"
95 #include "mozilla/PresShellForwards.h"
96 #include "mozilla/PresShellInlines.h"
97 #include "mozilla/PseudoStyleType.h"
98 #include "mozilla/RefCountType.h"
99 #include "mozilla/RelativeTo.h"
100 #include "mozilla/RestyleManager.h"
101 #include "mozilla/ReverseIterator.h"
102 #include "mozilla/SchedulerGroup.h"
103 #include "mozilla/ScrollTimelineAnimationTracker.h"
104 #include "mozilla/SMILAnimationController.h"
105 #include "mozilla/SMILTimeContainer.h"
106 #include "mozilla/ScopeExit.h"
107 #include "mozilla/Components.h"
108 #include "mozilla/SVGUtils.h"
109 #include "mozilla/ServoStyleConsts.h"
110 #include "mozilla/ServoTypes.h"
111 #include "mozilla/SizeOfState.h"
112 #include "mozilla/Span.h"
113 #include "mozilla/Sprintf.h"
114 #include "mozilla/StaticAnalysisFunctions.h"
115 #include "mozilla/StaticPrefs_apz.h"
116 #include "mozilla/StaticPrefs_browser.h"
117 #include "mozilla/StaticPrefs_docshell.h"
118 #include "mozilla/StaticPrefs_dom.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/TelemetryScalarEnums.h"
133 #include "mozilla/TextControlElement.h"
134 #include "mozilla/TextEditor.h"
135 #include "mozilla/TypedEnumBits.h"
136 #include "mozilla/URLDecorationStripper.h"
137 #include "mozilla/URLExtraData.h"
138 #include "mozilla/Unused.h"
139 #include "mozilla/css/ImageLoader.h"
140 #include "mozilla/css/Loader.h"
141 #include "mozilla/css/Rule.h"
142 #include "mozilla/css/SheetParsingMode.h"
143 #include "mozilla/dom/AnonymousContent.h"
144 #include "mozilla/dom/BlobURLProtocolHandler.h"
145 #include "mozilla/dom/BrowserChild.h"
146 #include "mozilla/dom/BrowsingContext.h"
147 #include "mozilla/dom/BrowsingContextGroup.h"
148 #include "mozilla/dom/CanonicalBrowsingContext.h"
149 #include "mozilla/dom/CanvasRenderingContextHelper.h"
150 #include "mozilla/dom/CDATASection.h"
151 #include "mozilla/dom/CSPDictionariesBinding.h"
152 #include "mozilla/dom/ChromeObserver.h"
153 #include "mozilla/dom/ClientInfo.h"
154 #include "mozilla/dom/ClientState.h"
155 #include "mozilla/dom/CloseWatcherManager.h"
156 #include "mozilla/dom/Comment.h"
157 #include "mozilla/dom/ContentChild.h"
158 #include "mozilla/dom/CSSBinding.h"
159 #include "mozilla/dom/CSSCustomPropertyRegisteredEvent.h"
160 #include "mozilla/dom/DOMImplementation.h"
161 #include "mozilla/dom/DOMIntersectionObserver.h"
162 #include "mozilla/dom/DOMStringList.h"
163 #include "mozilla/dom/DocGroup.h"
164 #include "mozilla/dom/DocumentBinding.h"
165 #include "mozilla/dom/DocumentFragment.h"
166 #include "mozilla/dom/DocumentL10n.h"
167 #include "mozilla/dom/DocumentTimeline.h"
168 #include "mozilla/dom/DocumentType.h"
169 #include "mozilla/dom/ElementBinding.h"
170 #include "mozilla/dom/ErrorEvent.h"
171 #include "mozilla/dom/Event.h"
172 #include "mozilla/dom/EventListenerBinding.h"
173 #include "mozilla/dom/FailedCertSecurityInfoBinding.h"
174 #include "mozilla/dom/FeaturePolicy.h"
175 #include "mozilla/dom/FeaturePolicyUtils.h"
176 #include "mozilla/dom/FontFaceSet.h"
177 #include "mozilla/dom/FragmentDirective.h"
178 #include "mozilla/dom/fragmentdirectives_ffi_generated.h"
179 #include "mozilla/dom/FromParser.h"
180 #include "mozilla/dom/HighlightRegistry.h"
181 #include "mozilla/dom/HTMLAllCollection.h"
182 #include "mozilla/dom/HTMLBodyElement.h"
183 #include "mozilla/dom/HTMLCollectionBinding.h"
184 #include "mozilla/dom/HTMLDialogElement.h"
185 #include "mozilla/dom/HTMLEmbedElement.h"
186 #include "mozilla/dom/HTMLFormElement.h"
187 #include "mozilla/dom/HTMLIFrameElement.h"
188 #include "mozilla/dom/HTMLImageElement.h"
189 #include "mozilla/dom/HTMLInputElement.h"
190 #include "mozilla/dom/HTMLLinkElement.h"
191 #include "mozilla/dom/HTMLMediaElement.h"
192 #include "mozilla/dom/HTMLMetaElement.h"
193 #include "mozilla/dom/HTMLObjectElement.h"
194 #include "mozilla/dom/HTMLSharedElement.h"
195 #include "mozilla/dom/HTMLTextAreaElement.h"
196 #include "mozilla/dom/ImageTracker.h"
197 #include "mozilla/dom/InspectorUtils.h"
198 #include "mozilla/dom/InteractiveWidget.h"
199 #include "mozilla/dom/Link.h"
200 #include "mozilla/dom/MediaQueryList.h"
201 #include "mozilla/dom/MediaSource.h"
202 #include "mozilla/dom/MutationObservers.h"
203 #include "mozilla/dom/NameSpaceConstants.h"
204 #include "mozilla/dom/Navigator.h"
205 #include "mozilla/dom/NetErrorInfoBinding.h"
206 #include "mozilla/dom/NodeInfo.h"
207 #include "mozilla/dom/NodeIterator.h"
208 #include "mozilla/dom/nsHTTPSOnlyUtils.h"
209 #include "mozilla/dom/PContentChild.h"
210 #include "mozilla/dom/PWindowGlobalChild.h"
211 #include "mozilla/dom/PageTransitionEvent.h"
212 #include "mozilla/dom/PageTransitionEventBinding.h"
213 #include "mozilla/dom/Performance.h"
214 #include "mozilla/dom/PermissionMessageUtils.h"
215 #include "mozilla/dom/PostMessageEvent.h"
216 #include "mozilla/dom/ProcessingInstruction.h"
217 #include "mozilla/dom/Promise.h"
218 #include "mozilla/dom/PromiseNativeHandler.h"
219 #include "mozilla/dom/RemoteBrowser.h"
220 #include "mozilla/dom/ResizeObserver.h"
221 #include "mozilla/dom/RustTypes.h"
222 #include "mozilla/dom/SVGElement.h"
223 #include "mozilla/dom/SVGDocument.h"
224 #include "mozilla/dom/SVGSVGElement.h"
225 #include "mozilla/dom/SVGUseElement.h"
226 #include "mozilla/dom/ScriptLoader.h"
227 #include "mozilla/dom/ScriptSettings.h"
228 #include "mozilla/dom/Selection.h"
229 #include "mozilla/dom/ServiceWorkerContainer.h"
230 #include "mozilla/dom/ServiceWorkerDescriptor.h"
231 #include "mozilla/dom/ServiceWorkerManager.h"
232 #include "mozilla/dom/ShadowIncludingTreeIterator.h"
233 #include "mozilla/dom/ShadowRoot.h"
234 #include "mozilla/dom/StyleSheetApplicableStateChangeEvent.h"
235 #include "mozilla/dom/StyleSheetApplicableStateChangeEventBinding.h"
236 #include "mozilla/dom/StyleSheetList.h"
237 #include "mozilla/dom/StyleSheetRemovedEvent.h"
238 #include "mozilla/dom/StyleSheetRemovedEventBinding.h"
239 #include "mozilla/dom/TimeoutManager.h"
240 #include "mozilla/dom/ToggleEvent.h"
241 #include "mozilla/dom/Touch.h"
242 #include "mozilla/dom/TouchEvent.h"
243 #include "mozilla/dom/TreeOrderedArrayInlines.h"
244 #include "mozilla/dom/TreeWalker.h"
245 #include "mozilla/dom/TrustedHTML.h"
246 #include "mozilla/dom/TrustedTypeUtils.h"
247 #include "mozilla/dom/TrustedTypesConstants.h"
248 #include "mozilla/dom/URL.h"
249 #include "mozilla/dom/UseCounterMetrics.h"
250 #include "mozilla/dom/UserActivation.h"
251 #include "mozilla/dom/ViewTransition.h"
252 #include "mozilla/dom/WakeLockJS.h"
253 #include "mozilla/dom/WakeLockSentinel.h"
254 #include "mozilla/dom/WindowBinding.h"
255 #include "mozilla/dom/WindowContext.h"
256 #include "mozilla/dom/WindowGlobalChild.h"
257 #include "mozilla/dom/WindowProxyHolder.h"
258 #include "mozilla/dom/WorkerDocumentListener.h"
259 #include "mozilla/dom/XPathEvaluator.h"
260 #include "mozilla/dom/XPathExpression.h"
261 #include "mozilla/dom/nsCSPContext.h"
262 #include "mozilla/dom/nsCSPUtils.h"
263 #include "mozilla/extensions/WebExtensionPolicy.h"
264 #include "mozilla/fallible.h"
265 #include "mozilla/gfx/BaseCoord.h"
266 #include "mozilla/gfx/BaseSize.h"
267 #include "mozilla/gfx/Coord.h"
268 #include "mozilla/gfx/Point.h"
269 #include "mozilla/gfx/ScaleFactor.h"
270 #include "mozilla/glean/DomMetrics.h"
271 #include "mozilla/glean/DomUseCounterMetrics.h"
272 #include "mozilla/intl/LocaleService.h"
273 #include "mozilla/ipc/IdleSchedulerChild.h"
274 #include "mozilla/ipc/MessageChannel.h"
275 #include "mozilla/net/ChannelEventQueue.h"
276 #include "mozilla/net/Cookie.h"
277 #include "mozilla/net/CookieCommons.h"
278 #include "mozilla/net/CookieJarSettings.h"
279 #include "mozilla/net/CookieParser.h"
280 #include "mozilla/net/NeckoChannelParams.h"
281 #include "mozilla/net/RequestContextService.h"
282 #include "nsAboutProtocolUtils.h"
283 #include "nsAttrValue.h"
284 #include "nsAttrValueInlines.h"
285 #include "nsBaseHashtable.h"
286 #include "nsBidiUtils.h"
288 #include "nsCSSPropertyID.h"
289 #include "nsCSSProps.h"
290 #include "nsCSSPseudoElements.h"
291 #include "nsCSSRendering.h"
292 #include "nsCanvasFrame.h"
293 #include "nsCaseTreatment.h"
294 #include "nsCharsetSource.h"
295 #include "nsCommandManager.h"
296 #include "nsCommandParams.h"
297 #include "nsComponentManagerUtils.h"
298 #include "nsContentCreatorFunctions.h"
299 #include "nsContentList.h"
300 #include "nsContentPermissionHelper.h"
301 #include "nsContentSecurityUtils.h"
302 #include "nsContentUtils.h"
304 #include "nsCycleCollectionNoteChild.h"
305 #include "nsCycleCollectionTraversalCallback.h"
306 #include "nsDOMAttributeMap.h"
307 #include "nsDOMCaretPosition.h"
308 #include "nsDOMNavigationTiming.h"
309 #include "nsDOMString.h"
310 #include "nsDeviceContext.h"
311 #include "nsDocShell.h"
312 #include "nsDocShellLoadTypes.h"
314 #include "nsEscape.h"
315 #include "nsFocusManager.h"
316 #include "nsFrameLoader.h"
317 #include "nsFrameLoaderOwner.h"
318 #include "nsGenericHTMLElement.h"
319 #include "nsGlobalWindowInner.h"
320 #include "nsGlobalWindowOuter.h"
321 #include "nsHTMLDocument.h"
322 #include "nsHtml5Module.h"
323 #include "nsHtml5Parser.h"
324 #include "nsHtml5TreeOpExecutor.h"
325 #include "nsIAsyncShutdown.h"
326 #include "nsIAuthPrompt.h"
327 #include "nsIAuthPrompt2.h"
328 #include "nsIBFCacheEntry.h"
329 #include "nsIBaseWindow.h"
330 #include "nsIBrowserChild.h"
331 #include "nsIBrowserUsage.h"
332 #include "nsICSSLoaderObserver.h"
333 #include "nsICategoryManager.h"
334 #include "nsICertOverrideService.h"
335 #include "nsIContent.h"
336 #include "nsIContentInlines.h"
337 #include "nsIContentPolicy.h"
338 #include "nsIContentSecurityPolicy.h"
339 #include "nsIContentSink.h"
340 #include "nsICookieJarSettings.h"
341 #include "nsICookieService.h"
342 #include "nsIDOMXULCommandDispatcher.h"
343 #include "nsIDocShell.h"
344 #include "nsIDocShellTreeItem.h"
345 #include "nsIDocumentActivity.h"
346 #include "nsIDocumentEncoder.h"
347 #include "nsIDocumentLoader.h"
348 #include "nsIDocumentLoaderFactory.h"
349 #include "nsIDocumentObserver.h"
350 #include "nsIDNSService.h"
351 #include "nsIEditingSession.h"
352 #include "nsIEditor.h"
353 #include "nsIEffectiveTLDService.h"
355 #include "nsIFileChannel.h"
356 #include "nsIFrame.h"
357 #include "nsIGlobalObject.h"
358 #include "nsIHTMLCollection.h"
359 #include "nsIHttpChannel.h"
360 #include "nsIHttpChannelInternal.h"
361 #include "nsIIOService.h"
362 #include "nsIImageLoadingContent.h"
363 #include "nsIInlineSpellChecker.h"
364 #include "nsIInputStreamChannel.h"
365 #include "nsIInterfaceRequestorUtils.h"
366 #include "nsILayoutHistoryState.h"
367 #include "nsIMultiPartChannel.h"
368 #include "nsIMutationObserver.h"
369 #include "nsINSSErrorsService.h"
370 #include "nsINamed.h"
371 #include "nsINodeList.h"
372 #include "nsIObjectLoadingContent.h"
373 #include "nsIObserverService.h"
374 #include "nsIPermission.h"
375 #include "nsIPrompt.h"
376 #include "nsIPropertyBag2.h"
377 #include "nsIPublicKeyPinningService.h"
378 #include "nsIReferrerInfo.h"
379 #include "nsIRefreshURI.h"
380 #include "nsIRequest.h"
381 #include "nsIRequestContext.h"
382 #include "nsIRunnable.h"
383 #include "nsISHEntry.h"
384 #include "nsIScriptElement.h"
385 #include "nsIScriptError.h"
386 #include "nsIScriptGlobalObject.h"
387 #include "nsIScriptSecurityManager.h"
388 #include "nsISecurityConsoleMessage.h"
389 #include "nsISelectionController.h"
390 #include "nsISerialEventTarget.h"
391 #include "nsISimpleEnumerator.h"
392 #include "nsISiteSecurityService.h"
393 #include "nsISocketProvider.h"
394 #include "nsISpeculativeConnect.h"
395 #include "nsIStructuredCloneContainer.h"
396 #include "nsIThread.h"
397 #include "nsITimedChannel.h"
398 #include "nsITimer.h"
399 #include "nsITransportSecurityInfo.h"
400 #include "nsIURIMutator.h"
401 #include "nsIVariant.h"
402 #include "nsIWeakReference.h"
403 #include "nsIWebNavigation.h"
404 #include "nsIWidget.h"
405 #include "nsIX509Cert.h"
406 #include "nsIX509CertValidity.h"
407 #include "nsIXMLContentSink.h"
408 #include "nsIHTMLContentSink.h"
409 #include "nsIXULRuntime.h"
410 #include "nsImageLoadingContent.h"
411 #include "nsImportModule.h"
412 #include "nsLanguageAtomService.h"
413 #include "nsLayoutUtils.h"
414 #include "nsMimeTypes.h"
415 #include "nsNetCID.h"
416 #include "nsNetUtil.h"
417 #include "nsNodeInfoManager.h"
418 #include "nsObjectLoadingContent.h"
419 #include "nsPIDOMWindowInlines.h"
420 #include "nsPIWindowRoot.h"
422 #include "nsPointerHashKeys.h"
423 #include "nsPresContext.h"
424 #include "nsQueryFrame.h"
425 #include "nsQueryObject.h"
428 #include "nsRefreshDriver.h"
429 #include "nsSandboxFlags.h"
430 #include "nsSerializationHelper.h"
431 #include "nsServiceManagerUtils.h"
432 #include "nsStringFlags.h"
433 #include "nsStyleUtil.h"
434 #include "nsStringIterator.h"
435 #include "nsStyleSheetService.h"
436 #include "nsStyleStruct.h"
437 #include "nsTextControlFrame.h"
438 #include "nsSubDocumentFrame.h"
439 #include "nsTextNode.h"
440 #include "nsUnicharUtils.h"
441 #include "nsWrapperCache.h"
442 #include "nsWrapperCacheInlines.h"
443 #include "nsXPCOMCID.h"
444 #include "nsXULAppAPI.h"
445 #include "prthread.h"
448 #include "xpcpublic.h"
450 // XXX Must be included after mozilla/Encoding.h
451 #include "encoding_rs.h"
453 #include "mozilla/dom/XULBroadcastManager.h"
454 #include "mozilla/dom/XULPersist.h"
455 #include "nsIAppWindow.h"
456 #include "nsXULPrototypeDocument.h"
457 #include "nsXULCommandDispatcher.h"
458 #include "nsXULPopupManager.h"
459 #include "nsIDocShellTreeOwner.h"
461 #define XML_DECLARATION_BITS_DECLARATION_EXISTS (1 << 0)
462 #define XML_DECLARATION_BITS_ENCODING_EXISTS (1 << 1)
463 #define XML_DECLARATION_BITS_STANDALONE_EXISTS (1 << 2)
464 #define XML_DECLARATION_BITS_STANDALONE_YES (1 << 3)
466 #define NS_MAX_DOCUMENT_WRITE_DEPTH 20
468 mozilla::LazyLogModule
gPageCacheLog("PageCache");
469 mozilla::LazyLogModule
gSHIPBFCacheLog("SHIPBFCache");
470 mozilla::LazyLogModule
gTimeoutDeferralLog("TimeoutDefer");
471 mozilla::LazyLogModule
gUseCountersLog("UseCounters");
479 class Document::HeaderData
{
481 HeaderData(nsAtom
* aField
, const nsAString
& aData
)
482 : mField(aField
), mData(aData
) {}
485 // Delete iteratively to avoid blowing up the stack, though it shouldn't
486 // happen in practice.
487 UniquePtr
<HeaderData
> next
= std::move(mNext
);
489 next
= std::move(next
->mNext
);
493 RefPtr
<nsAtom
> mField
;
495 UniquePtr
<HeaderData
> mNext
;
498 AutoTArray
<Document
*, 8>* Document::sLoadingForegroundTopLevelContentDocument
=
501 static LazyLogModule
gDocumentLeakPRLog("DocumentLeak");
502 static LazyLogModule
gCspPRLog("CSP");
503 LazyLogModule
gUserInteractionPRLog("UserInteraction");
505 static nsresult
GetHttpChannelHelper(nsIChannel
* aChannel
,
506 nsIHttpChannel
** aHttpChannel
) {
507 nsCOMPtr
<nsIHttpChannel
> httpChannel
= do_QueryInterface(aChannel
);
509 httpChannel
.forget(aHttpChannel
);
513 nsCOMPtr
<nsIMultiPartChannel
> multipart
= do_QueryInterface(aChannel
);
515 *aHttpChannel
= nullptr;
519 nsCOMPtr
<nsIChannel
> baseChannel
;
520 nsresult rv
= multipart
->GetBaseChannel(getter_AddRefs(baseChannel
));
521 if (NS_WARN_IF(NS_FAILED(rv
))) {
525 httpChannel
= do_QueryInterface(baseChannel
);
526 httpChannel
.forget(aHttpChannel
);
533 #define NAME_NOT_VALID ((nsSimpleContentList*)1)
535 IdentifierMapEntry::IdentifierMapEntry(
536 const IdentifierMapEntry::DependentAtomOrString
* aKey
)
537 : mKey(aKey
? *aKey
: nullptr) {}
539 void IdentifierMapEntry::Traverse(
540 nsCycleCollectionTraversalCallback
* aCallback
) {
541 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*aCallback
,
542 "mIdentifierMap mNameContentList");
543 aCallback
->NoteXPCOMChild(static_cast<nsINodeList
*>(mNameContentList
));
546 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*aCallback
,
547 "mIdentifierMap mImageElement element");
548 nsIContent
* imageElement
= mImageElement
;
549 aCallback
->NoteXPCOMChild(imageElement
);
553 bool IdentifierMapEntry::IsEmpty() {
554 return mIdContentList
->IsEmpty() && !mNameContentList
&& !mChangeCallbacks
&&
558 bool IdentifierMapEntry::HasNameElement() const {
559 return mNameContentList
&& mNameContentList
->Length() != 0;
562 void IdentifierMapEntry::AddContentChangeCallback(
563 Document::IDTargetObserver aCallback
, void* aData
, bool aForImage
) {
564 if (!mChangeCallbacks
) {
565 mChangeCallbacks
= MakeUnique
<nsTHashtable
<ChangeCallbackEntry
>>();
568 ChangeCallback cc
= {aCallback
, aData
, aForImage
};
569 mChangeCallbacks
->PutEntry(cc
);
572 void IdentifierMapEntry::RemoveContentChangeCallback(
573 Document::IDTargetObserver aCallback
, void* aData
, bool aForImage
) {
574 if (!mChangeCallbacks
) return;
575 ChangeCallback cc
= {aCallback
, aData
, aForImage
};
576 mChangeCallbacks
->RemoveEntry(cc
);
577 if (mChangeCallbacks
->Count() == 0) {
578 mChangeCallbacks
= nullptr;
582 void IdentifierMapEntry::FireChangeCallbacks(Element
* aOldElement
,
583 Element
* aNewElement
,
585 if (!mChangeCallbacks
) return;
587 for (auto iter
= mChangeCallbacks
->Iter(); !iter
.Done(); iter
.Next()) {
588 IdentifierMapEntry::ChangeCallbackEntry
* entry
= iter
.Get();
589 // Don't fire image changes for non-image observers, and don't fire element
590 // changes for image observers when an image override is active.
591 if (entry
->mKey
.mForImage
? (mImageElement
&& !aImageOnly
) : aImageOnly
) {
595 if (!entry
->mKey
.mCallback(aOldElement
, aNewElement
, entry
->mKey
.mData
)) {
601 void IdentifierMapEntry::AddIdElement(Element
* aElement
) {
602 MOZ_ASSERT(aElement
, "Must have element");
603 MOZ_ASSERT(!mIdContentList
->Contains(nullptr), "Why is null in our list?");
605 size_t index
= mIdContentList
.Insert(*aElement
);
607 Element
* oldElement
= mIdContentList
->SafeElementAt(1);
608 FireChangeCallbacks(oldElement
, aElement
);
612 void IdentifierMapEntry::RemoveIdElement(Element
* aElement
) {
613 MOZ_ASSERT(aElement
, "Missing element");
615 // This should only be called while the document is in an update.
616 // Assertions near the call to this method guarantee this.
618 // This could fire in OOM situations
619 // Only assert this in HTML documents for now as XUL does all sorts of weird
621 NS_ASSERTION(!aElement
->OwnerDoc()->IsHTMLDocument() ||
622 mIdContentList
->Contains(aElement
),
623 "Removing id entry that doesn't exist");
625 // XXXbz should this ever Compact() I guess when all the content is gone
626 // we'll just get cleaned up in the natural order of things...
627 Element
* currentElement
= mIdContentList
->SafeElementAt(0);
628 mIdContentList
.RemoveElement(*aElement
);
629 if (currentElement
== aElement
) {
630 FireChangeCallbacks(currentElement
, mIdContentList
->SafeElementAt(0));
634 void IdentifierMapEntry::SetImageElement(Element
* aElement
) {
635 Element
* oldElement
= GetImageIdElement();
636 mImageElement
= aElement
;
637 Element
* newElement
= GetImageIdElement();
638 if (oldElement
!= newElement
) {
639 FireChangeCallbacks(oldElement
, newElement
, true);
643 void IdentifierMapEntry::ClearAndNotify() {
644 Element
* currentElement
= mIdContentList
->SafeElementAt(0);
645 mIdContentList
.Clear();
646 if (currentElement
) {
647 FireChangeCallbacks(currentElement
, nullptr);
649 mNameContentList
= nullptr;
651 SetImageElement(nullptr);
653 mChangeCallbacks
= nullptr;
658 class SimpleHTMLCollection final
: public nsSimpleContentList
,
659 public nsIHTMLCollection
{
661 explicit SimpleHTMLCollection(nsINode
* aRoot
) : nsSimpleContentList(aRoot
) {}
663 NS_DECL_ISUPPORTS_INHERITED
665 virtual nsINode
* GetParentObject() override
{
666 return nsSimpleContentList::GetParentObject();
668 virtual uint32_t Length() override
{ return nsSimpleContentList::Length(); }
669 virtual Element
* GetElementAt(uint32_t aIndex
) override
{
670 return mElements
.SafeElementAt(aIndex
)->AsElement();
673 virtual Element
* GetFirstNamedElement(const nsAString
& aName
,
674 bool& aFound
) override
{
676 RefPtr
<nsAtom
> name
= NS_Atomize(aName
);
677 for (uint32_t i
= 0; i
< mElements
.Length(); i
++) {
678 MOZ_DIAGNOSTIC_ASSERT(mElements
[i
]);
679 Element
* element
= mElements
[i
]->AsElement();
680 if (element
->GetID() == name
||
681 (element
->HasName() &&
682 element
->GetParsedAttr(nsGkAtoms::name
)->GetAtomValue() == name
)) {
690 virtual void GetSupportedNames(nsTArray
<nsString
>& aNames
) override
{
691 AutoTArray
<nsAtom
*, 8> atoms
;
692 for (uint32_t i
= 0; i
< mElements
.Length(); i
++) {
693 MOZ_DIAGNOSTIC_ASSERT(mElements
[i
]);
694 Element
* element
= mElements
[i
]->AsElement();
696 nsAtom
* id
= element
->GetID();
697 MOZ_ASSERT(id
!= nsGkAtoms::_empty
);
698 if (id
&& !atoms
.Contains(id
)) {
699 atoms
.AppendElement(id
);
702 if (element
->HasName()) {
703 nsAtom
* name
= element
->GetParsedAttr(nsGkAtoms::name
)->GetAtomValue();
704 MOZ_ASSERT(name
&& name
!= nsGkAtoms::_empty
);
705 if (name
&& !atoms
.Contains(name
)) {
706 atoms
.AppendElement(name
);
711 nsString
* names
= aNames
.AppendElements(atoms
.Length());
712 for (uint32_t i
= 0; i
< atoms
.Length(); i
++) {
713 atoms
[i
]->ToString(names
[i
]);
717 virtual JSObject
* GetWrapperPreserveColorInternal() override
{
718 return nsWrapperCache::GetWrapperPreserveColor();
720 virtual void PreserveWrapperInternal(
721 nsISupports
* aScriptObjectHolder
) override
{
722 nsWrapperCache::PreserveWrapper(aScriptObjectHolder
);
724 virtual JSObject
* WrapObject(JSContext
* aCx
,
725 JS::Handle
<JSObject
*> aGivenProto
) override
{
726 return HTMLCollection_Binding::Wrap(aCx
, this, aGivenProto
);
729 using nsBaseContentList::Item
;
732 virtual ~SimpleHTMLCollection() = default;
735 NS_IMPL_ISUPPORTS_INHERITED(SimpleHTMLCollection
, nsSimpleContentList
,
740 void IdentifierMapEntry::AddNameElement(nsINode
* aNode
, Element
* aElement
) {
741 if (!mNameContentList
) {
742 mNameContentList
= new dom::SimpleHTMLCollection(aNode
);
745 mNameContentList
->AppendElement(aElement
);
748 void IdentifierMapEntry::RemoveNameElement(Element
* aElement
) {
749 if (mNameContentList
) {
750 mNameContentList
->RemoveElement(aElement
);
754 bool IdentifierMapEntry::HasIdElementExposedAsHTMLDocumentProperty() const {
755 Element
* idElement
= GetIdElement();
757 nsGenericHTMLElement::ShouldExposeIdAsHTMLDocumentProperty(idElement
);
760 size_t IdentifierMapEntry::SizeOfExcludingThis(
761 MallocSizeOf aMallocSizeOf
) const {
762 return mKey
.mString
.SizeOfExcludingThisIfUnshared(aMallocSizeOf
);
765 // Helper structs for the content->subdoc map
767 class SubDocMapEntry
: public PLDHashEntryHdr
{
769 // Both of these are strong references
770 dom::Element
* mKey
; // must be first, to look like PLDHashEntryStub
771 dom::Document
* mSubDocument
;
774 class OnloadBlocker final
: public nsIRequest
{
776 OnloadBlocker() = default;
782 ~OnloadBlocker() = default;
785 NS_IMPL_ISUPPORTS(OnloadBlocker
, nsIRequest
)
788 OnloadBlocker::GetName(nsACString
& aResult
) {
789 aResult
.AssignLiteral("about:document-onload-blocker");
794 OnloadBlocker::IsPending(bool* _retval
) {
800 OnloadBlocker::GetStatus(nsresult
* status
) {
805 NS_IMETHODIMP
OnloadBlocker::SetCanceledReason(const nsACString
& aReason
) {
806 return SetCanceledReasonImpl(aReason
);
809 NS_IMETHODIMP
OnloadBlocker::GetCanceledReason(nsACString
& aReason
) {
810 return GetCanceledReasonImpl(aReason
);
813 NS_IMETHODIMP
OnloadBlocker::CancelWithReason(nsresult aStatus
,
814 const nsACString
& aReason
) {
815 return CancelWithReasonImpl(aStatus
, aReason
);
818 OnloadBlocker::Cancel(nsresult status
) { return NS_OK
; }
820 OnloadBlocker::Suspend(void) { return NS_OK
; }
822 OnloadBlocker::Resume(void) { return NS_OK
; }
825 OnloadBlocker::GetLoadGroup(nsILoadGroup
** aLoadGroup
) {
826 *aLoadGroup
= nullptr;
831 OnloadBlocker::SetLoadGroup(nsILoadGroup
* aLoadGroup
) { return NS_OK
; }
834 OnloadBlocker::GetLoadFlags(nsLoadFlags
* aLoadFlags
) {
835 *aLoadFlags
= nsIRequest::LOAD_NORMAL
;
840 OnloadBlocker::GetTRRMode(nsIRequest::TRRMode
* aTRRMode
) {
841 return GetTRRModeImpl(aTRRMode
);
845 OnloadBlocker::SetTRRMode(nsIRequest::TRRMode aTRRMode
) {
846 return SetTRRModeImpl(aTRRMode
);
850 OnloadBlocker::SetLoadFlags(nsLoadFlags aLoadFlags
) { return NS_OK
; }
852 // ==================================================================
856 ExternalResourceMap::ExternalResourceMap() : mHaveShutDown(false) {}
858 Document
* ExternalResourceMap::RequestResource(
859 nsIURI
* aURI
, nsIReferrerInfo
* aReferrerInfo
, nsINode
* aRequestingNode
,
860 Document
* aDisplayDocument
, ExternalResourceLoad
** aPendingLoad
) {
861 // If we ever start allowing non-same-origin loads here, we might need to do
862 // something interesting with aRequestingPrincipal even for the hashtable
864 MOZ_ASSERT(aURI
, "Must have a URI");
865 MOZ_ASSERT(aRequestingNode
, "Must have a node");
866 MOZ_ASSERT(aReferrerInfo
, "Must have a referrerInfo");
867 *aPendingLoad
= nullptr;
872 // First, make sure we strip the ref from aURI.
873 nsCOMPtr
<nsIURI
> clone
;
874 nsresult rv
= NS_GetURIWithoutRef(aURI
, getter_AddRefs(clone
));
875 if (NS_FAILED(rv
) || !clone
) {
879 ExternalResource
* resource
;
880 mMap
.Get(clone
, &resource
);
882 return resource
->mDocument
;
885 bool loadStartSucceeded
=
886 mPendingLoads
.WithEntryHandle(clone
, [&](auto&& loadEntry
) {
888 loadEntry
.Insert(MakeRefPtr
<PendingLoad
>(aDisplayDocument
));
890 if (NS_FAILED(loadEntry
.Data()->StartLoad(clone
, aReferrerInfo
,
896 RefPtr
<PendingLoad
> load(loadEntry
.Data());
897 load
.forget(aPendingLoad
);
900 if (!loadStartSucceeded
) {
901 // Make sure we don't thrash things by trying this load again, since
902 // chances are it failed for good reasons (security check, etc).
903 // This must be done outside the WithEntryHandle functor, as it accesses
905 AddExternalResource(clone
, nullptr, nullptr, aDisplayDocument
);
911 void ExternalResourceMap::EnumerateResources(SubDocEnumFunc aCallback
) {
912 nsTArray
<RefPtr
<Document
>> docs(mMap
.Count());
913 for (const auto& entry
: mMap
.Values()) {
914 if (Document
* doc
= entry
->mDocument
) {
915 docs
.AppendElement(doc
);
919 for (auto& doc
: docs
) {
920 if (aCallback(*doc
) == CallState::Stop
) {
926 void ExternalResourceMap::Traverse(
927 nsCycleCollectionTraversalCallback
* aCallback
) const {
928 // mPendingLoads will get cleared out as the requests complete, so
929 // no need to worry about those here.
930 for (const auto& entry
: mMap
) {
931 ExternalResourceMap::ExternalResource
* resource
= entry
.GetWeak();
933 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*aCallback
,
934 "mExternalResourceMap.mMap entry"
936 aCallback
->NoteXPCOMChild(ToSupports(resource
->mDocument
));
938 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*aCallback
,
939 "mExternalResourceMap.mMap entry"
941 aCallback
->NoteXPCOMChild(resource
->mViewer
);
943 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*aCallback
,
944 "mExternalResourceMap.mMap entry"
946 aCallback
->NoteXPCOMChild(resource
->mLoadGroup
);
950 void ExternalResourceMap::HideViewers() {
951 for (const auto& entry
: mMap
) {
952 nsCOMPtr
<nsIDocumentViewer
> viewer
= entry
.GetData()->mViewer
;
959 void ExternalResourceMap::ShowViewers() {
960 for (const auto& entry
: mMap
) {
961 nsCOMPtr
<nsIDocumentViewer
> viewer
= entry
.GetData()->mViewer
;
968 void TransferShowingState(Document
* aFromDoc
, Document
* aToDoc
) {
969 MOZ_ASSERT(aFromDoc
&& aToDoc
, "transferring showing state from/to null doc");
971 if (aFromDoc
->IsShowing()) {
972 aToDoc
->OnPageShow(true, nullptr);
976 nsresult
ExternalResourceMap::AddExternalResource(nsIURI
* aURI
,
977 nsIDocumentViewer
* aViewer
,
978 nsILoadGroup
* aLoadGroup
,
979 Document
* aDisplayDocument
) {
980 MOZ_ASSERT(aURI
, "Unexpected call");
981 MOZ_ASSERT((aViewer
&& aLoadGroup
) || (!aViewer
&& !aLoadGroup
),
982 "Must have both or neither");
984 RefPtr
<PendingLoad
> load
;
985 mPendingLoads
.Remove(aURI
, getter_AddRefs(load
));
989 nsCOMPtr
<Document
> doc
;
991 doc
= aViewer
->GetDocument();
992 NS_ASSERTION(doc
, "Must have a document");
994 doc
->SetDisplayDocument(aDisplayDocument
);
996 // Make sure that hiding our viewer will tear down its presentation.
997 aViewer
->SetSticky(false);
999 rv
= aViewer
->Init(nullptr, LayoutDeviceIntRect(), nullptr);
1000 if (NS_SUCCEEDED(rv
)) {
1001 rv
= aViewer
->Open(nullptr, nullptr);
1004 if (NS_FAILED(rv
)) {
1007 aLoadGroup
= nullptr;
1011 ExternalResource
* newResource
=
1012 mMap
.InsertOrUpdate(aURI
, MakeUnique
<ExternalResource
>()).get();
1014 newResource
->mDocument
= doc
;
1015 newResource
->mViewer
= aViewer
;
1016 newResource
->mLoadGroup
= aLoadGroup
;
1018 if (nsPresContext
* pc
= doc
->GetPresContext()) {
1019 pc
->RecomputeBrowsingContextDependentData();
1021 TransferShowingState(aDisplayDocument
, doc
);
1024 const nsTArray
<nsCOMPtr
<nsIObserver
>>& obs
= load
->Observers();
1025 for (uint32_t i
= 0; i
< obs
.Length(); ++i
) {
1026 obs
[i
]->Observe(ToSupports(doc
), "external-resource-document-created",
1033 NS_IMPL_ISUPPORTS(ExternalResourceMap::PendingLoad
, nsIStreamListener
,
1037 ExternalResourceMap::PendingLoad::OnStartRequest(nsIRequest
* aRequest
) {
1038 ExternalResourceMap
& map
= mDisplayDocument
->ExternalResourceMap();
1039 if (map
.HaveShutDown()) {
1040 return NS_BINDING_ABORTED
;
1043 nsCOMPtr
<nsIDocumentViewer
> viewer
;
1044 nsCOMPtr
<nsILoadGroup
> loadGroup
;
1046 SetupViewer(aRequest
, getter_AddRefs(viewer
), getter_AddRefs(loadGroup
));
1048 // Make sure to do this no matter what
1050 map
.AddExternalResource(mURI
, viewer
, loadGroup
, mDisplayDocument
);
1051 if (NS_FAILED(rv
)) {
1054 if (NS_FAILED(rv2
)) {
1055 mTargetListener
= nullptr;
1059 return mTargetListener
->OnStartRequest(aRequest
);
1062 nsresult
ExternalResourceMap::PendingLoad::SetupViewer(
1063 nsIRequest
* aRequest
, nsIDocumentViewer
** aViewer
,
1064 nsILoadGroup
** aLoadGroup
) {
1065 MOZ_ASSERT(!mTargetListener
, "Unexpected call to OnStartRequest");
1067 *aLoadGroup
= nullptr;
1069 nsCOMPtr
<nsIChannel
> chan(do_QueryInterface(aRequest
));
1070 NS_ENSURE_TRUE(chan
, NS_ERROR_UNEXPECTED
);
1072 nsCOMPtr
<nsIHttpChannel
> httpChannel(do_QueryInterface(aRequest
));
1074 bool requestSucceeded
;
1075 if (NS_FAILED(httpChannel
->GetRequestSucceeded(&requestSucceeded
)) ||
1076 !requestSucceeded
) {
1077 // Bail out on this load, since it looks like we have an HTTP error page
1078 return NS_BINDING_ABORTED
;
1083 chan
->GetContentType(type
);
1085 nsCOMPtr
<nsILoadGroup
> loadGroup
;
1086 chan
->GetLoadGroup(getter_AddRefs(loadGroup
));
1088 // Give this document its own loadgroup
1089 nsCOMPtr
<nsILoadGroup
> newLoadGroup
=
1090 do_CreateInstance(NS_LOADGROUP_CONTRACTID
);
1091 NS_ENSURE_TRUE(newLoadGroup
, NS_ERROR_OUT_OF_MEMORY
);
1092 newLoadGroup
->SetLoadGroup(loadGroup
);
1094 nsCOMPtr
<nsIInterfaceRequestor
> callbacks
;
1095 loadGroup
->GetNotificationCallbacks(getter_AddRefs(callbacks
));
1097 nsCOMPtr
<nsIInterfaceRequestor
> newCallbacks
=
1098 new LoadgroupCallbacks(callbacks
);
1099 newLoadGroup
->SetNotificationCallbacks(newCallbacks
);
1101 nsCOMPtr
<nsIDocumentLoaderFactory
> docLoaderFactory
=
1102 nsContentUtils::FindInternalDocumentViewer(type
);
1103 NS_ENSURE_TRUE(docLoaderFactory
, NS_ERROR_NOT_AVAILABLE
);
1105 nsCOMPtr
<nsIDocumentViewer
> viewer
;
1106 nsCOMPtr
<nsIStreamListener
> listener
;
1107 nsresult rv
= docLoaderFactory
->CreateInstance(
1108 "external-resource", chan
, newLoadGroup
, type
, nullptr, nullptr,
1109 getter_AddRefs(listener
), getter_AddRefs(viewer
));
1110 NS_ENSURE_SUCCESS(rv
, rv
);
1111 NS_ENSURE_TRUE(viewer
, NS_ERROR_UNEXPECTED
);
1113 nsCOMPtr
<nsIParser
> parser
= do_QueryInterface(listener
);
1115 /// We don't want to deal with the various fake documents yet
1116 return NS_ERROR_NOT_IMPLEMENTED
;
1119 // We can't handle HTML and other weird things here yet.
1120 nsIContentSink
* sink
= parser
->GetContentSink();
1121 nsCOMPtr
<nsIXMLContentSink
> xmlSink
= do_QueryInterface(sink
);
1123 return NS_ERROR_NOT_IMPLEMENTED
;
1126 listener
.swap(mTargetListener
);
1127 viewer
.forget(aViewer
);
1128 newLoadGroup
.forget(aLoadGroup
);
1133 ExternalResourceMap::PendingLoad::OnDataAvailable(nsIRequest
* aRequest
,
1134 nsIInputStream
* aStream
,
1137 // mTargetListener might be null if SetupViewer or AddExternalResource failed.
1138 NS_ENSURE_TRUE(mTargetListener
, NS_ERROR_FAILURE
);
1139 if (mDisplayDocument
->ExternalResourceMap().HaveShutDown()) {
1140 return NS_BINDING_ABORTED
;
1142 return mTargetListener
->OnDataAvailable(aRequest
, aStream
, aOffset
, aCount
);
1146 ExternalResourceMap::PendingLoad::OnStopRequest(nsIRequest
* aRequest
,
1148 // mTargetListener might be null if SetupViewer or AddExternalResource failed
1149 if (mTargetListener
) {
1150 nsCOMPtr
<nsIStreamListener
> listener
;
1151 mTargetListener
.swap(listener
);
1152 return listener
->OnStopRequest(aRequest
, aStatus
);
1158 nsresult
ExternalResourceMap::PendingLoad::StartLoad(
1159 nsIURI
* aURI
, nsIReferrerInfo
* aReferrerInfo
, nsINode
* aRequestingNode
) {
1160 MOZ_ASSERT(aURI
, "Must have a URI");
1161 MOZ_ASSERT(aRequestingNode
, "Must have a node");
1162 MOZ_ASSERT(aReferrerInfo
, "Must have a referrerInfo");
1164 nsCOMPtr
<nsILoadGroup
> loadGroup
=
1165 aRequestingNode
->OwnerDoc()->GetDocumentLoadGroup();
1167 nsresult rv
= NS_OK
;
1168 nsCOMPtr
<nsIChannel
> channel
;
1169 rv
= NS_NewChannel(getter_AddRefs(channel
), aURI
, aRequestingNode
,
1170 nsILoadInfo::SEC_REQUIRE_SAME_ORIGIN_INHERITS_SEC_CONTEXT
,
1171 nsIContentPolicy::TYPE_INTERNAL_EXTERNAL_RESOURCE
,
1172 nullptr, // aPerformanceStorage
1174 NS_ENSURE_SUCCESS(rv
, rv
);
1176 nsCOMPtr
<nsIHttpChannel
> httpChannel(do_QueryInterface(channel
));
1178 rv
= httpChannel
->SetReferrerInfo(aReferrerInfo
);
1179 Unused
<< NS_WARN_IF(NS_FAILED(rv
));
1184 return channel
->AsyncOpen(this);
1187 NS_IMPL_ISUPPORTS(ExternalResourceMap::LoadgroupCallbacks
,
1188 nsIInterfaceRequestor
)
1190 #define IMPL_SHIM(_i) \
1191 NS_IMPL_ISUPPORTS(ExternalResourceMap::LoadgroupCallbacks::_i##Shim, _i)
1193 IMPL_SHIM(nsILoadContext
)
1194 IMPL_SHIM(nsIProgressEventSink
)
1195 IMPL_SHIM(nsIChannelEventSink
)
1199 #define IID_IS(_i) aIID.Equals(NS_GET_IID(_i))
1201 #define TRY_SHIM(_i) \
1204 nsCOMPtr<_i> real = do_GetInterface(mCallbacks); \
1206 return NS_NOINTERFACE; \
1208 nsCOMPtr<_i> shim = new _i##Shim(this, real); \
1209 shim.forget(aSink); \
1215 ExternalResourceMap::LoadgroupCallbacks::GetInterface(const nsIID
& aIID
,
1217 if (mCallbacks
&& (IID_IS(nsIPrompt
) || IID_IS(nsIAuthPrompt
) ||
1218 IID_IS(nsIAuthPrompt2
) || IID_IS(nsIBrowserChild
))) {
1219 return mCallbacks
->GetInterface(aIID
, aSink
);
1224 TRY_SHIM(nsILoadContext
);
1225 TRY_SHIM(nsIProgressEventSink
);
1226 TRY_SHIM(nsIChannelEventSink
);
1228 return NS_NOINTERFACE
;
1234 ExternalResourceMap::ExternalResource::~ExternalResource() {
1236 mViewer
->Close(nullptr);
1241 // ==================================================================
1243 // ==================================================================
1245 // If we ever have an nsIDocumentObserver notification for stylesheet title
1246 // changes we should update the list from that instead of overriding
1248 class DOMStyleSheetSetList final
: public DOMStringList
{
1250 explicit DOMStyleSheetSetList(Document
* aDocument
);
1252 void Disconnect() { mDocument
= nullptr; }
1254 virtual void EnsureFresh() override
;
1257 Document
* mDocument
; // Our document; weak ref. It'll let us know if it
1261 DOMStyleSheetSetList::DOMStyleSheetSetList(Document
* aDocument
)
1262 : mDocument(aDocument
) {
1263 NS_ASSERTION(mDocument
, "Must have document!");
1266 void DOMStyleSheetSetList::EnsureFresh() {
1267 MOZ_ASSERT(NS_IsMainThread());
1272 return; // Spec says "no exceptions", and we have no style sets if we have
1273 // no document, for sure
1276 size_t count
= mDocument
->SheetCount();
1278 for (size_t index
= 0; index
< count
; index
++) {
1279 StyleSheet
* sheet
= mDocument
->SheetAt(index
);
1280 NS_ASSERTION(sheet
, "Null sheet in sheet list!");
1281 sheet
->GetTitle(title
);
1282 if (!title
.IsEmpty() && !mNames
.Contains(title
) && !Add(title
)) {
1288 Document::PendingFrameStaticClone::~PendingFrameStaticClone() = default;
1290 // ==================================================================
1292 // ==================================================================
1294 Document::InternalCommandDataHashtable
*
1295 Document::sInternalCommandDataHashtable
= nullptr;
1298 void Document::Shutdown() {
1299 if (sInternalCommandDataHashtable
) {
1300 sInternalCommandDataHashtable
->Clear();
1301 delete sInternalCommandDataHashtable
;
1302 sInternalCommandDataHashtable
= nullptr;
1306 Document::Document(const char* aContentType
)
1308 DocumentOrShadowRoot(this),
1309 mCharacterSet(WINDOWS_1252_ENCODING
),
1310 mCharacterSetSource(0),
1311 mParentDocument(nullptr),
1312 mCachedRootElement(nullptr),
1313 mNodeInfoManager(nullptr),
1315 mStyledLinksCleared(false),
1317 mCachedStateObjectValid(false),
1318 mBlockAllMixedContent(false),
1319 mBlockAllMixedContentPreloads(false),
1320 mUpgradeInsecureRequests(false),
1321 mUpgradeInsecurePreloads(false),
1322 mDevToolsWatchingDOMMutations(false),
1323 mBidiEnabled(false),
1324 mMayNeedFontPrefsUpdate(true),
1325 mMathMLEnabled(false),
1326 mIsInitialDocumentInWindow(false),
1327 mIsEverInitialDocumentInWindow(false),
1328 mIgnoreDocGroupMismatches(false),
1329 mLoadedAsData(false),
1330 mAddedToMemoryReportingAsDataDocument(false),
1331 mMayStartLayout(true),
1332 mHaveFiredTitleChange(false),
1335 mRemovedFromDocShell(false),
1336 // mAllowDNSPrefetch starts true, so that we can always reliably && it
1337 // with various values that might disable it. Since we never prefetch
1338 // unless we get a window, and in that case the docshell value will get
1339 // &&-ed in, this is safe.
1340 mAllowDNSPrefetch(true),
1341 mIsStaticDocument(false),
1342 mCreatingStaticClone(false),
1343 mHasPrintCallbacks(false),
1344 mInUnlinkOrDeletion(false),
1345 mHasHadScriptHandlingObject(false),
1346 mIsBeingUsedAsImage(false),
1347 mChromeRulesEnabled(false),
1348 mInChromeDocShell(false),
1349 mIsSyntheticDocument(false),
1350 mHasLinksToUpdateRunnable(false),
1351 mFlushingPendingLinkUpdates(false),
1352 mMayHaveDOMMutationObservers(false),
1353 mMayHaveAnimationObservers(false),
1354 mHasCSPDeliveredThroughHeader(false),
1355 mBFCacheDisallowed(false),
1356 mHasHadDefaultView(false),
1357 mStyleSheetChangeEventsEnabled(false),
1358 mDevToolsAnonymousAndShadowEventsEnabled(false),
1359 mIsSrcdocDocument(false),
1360 mHasDisplayDocument(false),
1361 mFontFaceSetDirty(true),
1362 mDidFireDOMContentLoaded(true),
1363 mIsTopLevelContentDocument(false),
1364 mIsContentDocument(false),
1365 mDidCallBeginLoad(false),
1366 mEncodingMenuDisabled(false),
1367 mLinksEnabled(true),
1368 mIsSVGGlyphsDocument(false),
1369 mInDestructor(false),
1370 mIsGoingAway(false),
1371 mStyleSetFilled(false),
1372 mQuirkSheetAdded(false),
1373 mMayHaveTitleElement(false),
1374 mDOMLoadingSet(false),
1375 mDOMInteractiveSet(false),
1376 mDOMCompleteSet(false),
1377 mAutoFocusFired(false),
1378 mScrolledToRefAlready(false),
1379 mChangeScrollPosWhenScrollingToRef(false),
1380 mDelayFrameLoaderInitialization(false),
1381 mSynchronousDOMContentLoaded(false),
1382 mMaybeServiceWorkerControlled(false),
1384 mValidScaleFloat(false),
1385 mValidMinScale(false),
1386 mValidMaxScale(false),
1387 mWidthStrEmpty(false),
1388 mParserAborted(false),
1389 mReportedDocumentUseCounters(false),
1390 mHasReportedShadowDOMUsage(false),
1391 mHasDelayedRefreshEvent(false),
1392 mLoadEventFiring(false),
1393 mSkipLoadEventAfterClose(false),
1394 mDisableCookieAccess(false),
1395 mDisableDocWrite(false),
1396 mTooDeepWriteRecursion(false),
1397 mPendingMaybeEditingStateChanged(false),
1398 mHasBeenEditable(false),
1399 mIsRunningExecCommandByContent(false),
1400 mIsRunningExecCommandByChromeOrAddon(false),
1401 mSetCompleteAfterDOMContentLoaded(false),
1402 mDidHitCompleteSheetCache(false),
1403 mUseCountersInitialized(false),
1404 mShouldReportUseCounters(false),
1405 mShouldSendPageUseCounters(false),
1406 mUserHasInteracted(false),
1407 mHasUserInteractionTimerScheduled(false),
1408 mShouldResistFingerprinting(false),
1409 mIsInPrivateBrowsing(false),
1410 mCloningForSVGUse(false),
1411 mAllowDeclarativeShadowRoots(false),
1412 mSuspendDOMNotifications(false),
1413 mForceLoadAtTop(false),
1414 mFireMutationEvents(true),
1415 mHasPolicyWithRequireTrustedTypesForDirective(false),
1416 mClipboardCopyTriggered(false),
1417 mXMLDeclarationBits(0),
1418 mOnloadBlockCount(0),
1420 mContentEditableCount(0),
1421 mEditingState(EditingState::eOff
),
1422 mCompatMode(eCompatibility_FullStandards
),
1423 mReadyState(ReadyState::READYSTATE_UNINITIALIZED
),
1424 mAncestorIsLoading(false),
1425 mVisibilityState(dom::VisibilityState::Hidden
),
1427 mDefaultElementType(0),
1428 mAllowXULXBL(eTriUnset
),
1429 mSkipDTDSecurityChecks(false),
1430 mBidiOptions(IBMBIDI_DEFAULT_BIDI_OPTIONS
),
1433 mMarkedCCGeneration(0),
1434 mPresShell(nullptr),
1435 mSubtreeModifiedDepth(0),
1436 mPreloadPictureDepth(0),
1437 mEventsSuppressed(0),
1438 mIgnoreDestructiveWritesCounter(0),
1439 mStaticCloneCount(0),
1441 mBFCacheEntry(nullptr),
1442 mInSyncOperationCount(0),
1443 mBlockDOMContentLoaded(0),
1444 mUpdateNestLevel(0),
1445 mHttpsOnlyStatus(nsILoadInfo::HTTPS_ONLY_UNINITIALIZED
),
1446 mViewportType(Unknown
),
1447 mViewportFit(ViewportFitType::Auto
),
1448 mInteractiveWidgetMode(
1449 InteractiveWidgetUtils::DefaultInteractiveWidgetMode()),
1450 mHeaderData(nullptr),
1451 mServoRestyleRootDirtyBits(0),
1452 mThrowOnDynamicMarkupInsertionCounter(0),
1453 mIgnoreOpensDuringUnloadCounter(0),
1454 mSavedResolution(1.0f
),
1456 mCachedTabSizeGeneration(0),
1458 mNextControlNumber(0),
1459 mPreloadService(this),
1460 mShouldNotifyFetchSuccess(false),
1461 mShouldNotifyFormOrPasswordRemoved(false) {
1462 MOZ_LOG(gDocumentLeakPRLog
, LogLevel::Debug
, ("DOCUMENT %p created", this));
1465 SetIsConnected(true);
1467 // Create these unconditionally, they will be used to warn about the `zoom`
1468 // property, even if use counters are disabled.
1469 mStyleUseCounters
.reset(Servo_UseCounters_Create());
1471 SetContentType(nsDependentCString(aContentType
));
1473 // Start out mLastStyleSheetSet as null, per spec
1474 SetDOMStringToNull(mLastStyleSheetSet
);
1476 // void state used to differentiate an empty source from an unselected source
1477 mPreloadPictureFoundSource
.SetIsVoid(true);
1479 RecomputeLanguageFromCharset();
1481 mPreloadReferrerInfo
= new dom::ReferrerInfo(nullptr);
1482 mReferrerInfo
= new dom::ReferrerInfo(nullptr);
1486 // unused by GeckoView
1487 static bool IsAboutErrorPage(nsGlobalWindowInner
* aWin
, const char* aSpec
) {
1488 if (NS_WARN_IF(!aWin
)) {
1492 nsIURI
* uri
= aWin
->GetDocumentURI();
1493 if (NS_WARN_IF(!uri
)) {
1496 // getSpec is an expensive operation, hence we first check the scheme
1497 // to see if the caller is actually an about: page.
1498 if (!uri
->SchemeIs("about")) {
1502 nsAutoCString aboutSpec
;
1503 nsresult rv
= NS_GetAboutModuleName(uri
, aboutSpec
);
1504 NS_ENSURE_SUCCESS(rv
, false);
1506 return aboutSpec
.EqualsASCII(aSpec
);
1510 bool Document::CallerIsTrustedAboutNetError(JSContext
* aCx
, JSObject
* aObject
) {
1511 nsGlobalWindowInner
* win
= xpc::WindowOrNull(aObject
);
1513 // GeckoView uses data URLs for error pages, so for now just check for any
1515 return win
&& win
->GetDocument() && win
->GetDocument()->IsErrorPage();
1517 return win
&& IsAboutErrorPage(win
, "neterror");
1521 bool Document::CallerIsTrustedAboutHttpsOnlyError(JSContext
* aCx
,
1522 JSObject
* aObject
) {
1523 nsGlobalWindowInner
* win
= xpc::WindowOrNull(aObject
);
1525 // GeckoView uses data URLs for error pages, so for now just check for any
1527 return win
&& win
->GetDocument() && win
->GetDocument()->IsErrorPage();
1529 return win
&& IsAboutErrorPage(win
, "httpsonlyerror");
1533 already_AddRefed
<mozilla::dom::Promise
> Document::AddCertException(
1534 bool aIsTemporary
, ErrorResult
& aError
) {
1535 RefPtr
<Promise
> promise
= Promise::Create(GetScopeObject(), aError
,
1536 Promise::ePropagateUserInteraction
);
1537 if (aError
.Failed()) {
1541 nsresult rv
= NS_OK
;
1542 if (NS_WARN_IF(!mFailedChannel
)) {
1543 promise
->MaybeReject(NS_ERROR_DOM_INVALID_STATE_ERR
);
1544 return promise
.forget();
1547 nsCOMPtr
<nsIURI
> failedChannelURI
;
1548 NS_GetFinalChannelURI(mFailedChannel
, getter_AddRefs(failedChannelURI
));
1549 if (!failedChannelURI
) {
1550 promise
->MaybeReject(NS_ERROR_DOM_INVALID_STATE_ERR
);
1551 return promise
.forget();
1554 nsCOMPtr
<nsIURI
> innerURI
= NS_GetInnermostURI(failedChannelURI
);
1556 promise
->MaybeReject(NS_ERROR_DOM_INVALID_STATE_ERR
);
1557 return promise
.forget();
1561 innerURI
->GetAsciiHost(host
);
1563 innerURI
->GetPort(&port
);
1565 nsCOMPtr
<nsITransportSecurityInfo
> tsi
;
1566 rv
= mFailedChannel
->GetSecurityInfo(getter_AddRefs(tsi
));
1567 if (NS_WARN_IF(NS_FAILED(rv
))) {
1568 promise
->MaybeReject(rv
);
1569 return promise
.forget();
1571 if (NS_WARN_IF(!tsi
)) {
1572 promise
->MaybeReject(NS_ERROR_DOM_INVALID_STATE_ERR
);
1573 return promise
.forget();
1576 nsCOMPtr
<nsIX509Cert
> cert
;
1577 rv
= tsi
->GetServerCert(getter_AddRefs(cert
));
1578 if (NS_WARN_IF(NS_FAILED(rv
))) {
1579 promise
->MaybeReject(rv
);
1580 return promise
.forget();
1582 if (NS_WARN_IF(!cert
)) {
1583 promise
->MaybeReject(NS_ERROR_DOM_INVALID_STATE_ERR
);
1584 return promise
.forget();
1587 if (XRE_IsContentProcess()) {
1588 ContentChild
* cc
= ContentChild::GetSingleton();
1590 OriginAttributes
const& attrs
= NodePrincipal()->OriginAttributesRef();
1591 cc
->SendAddCertException(cert
, host
, port
, attrs
, aIsTemporary
)
1592 ->Then(GetCurrentSerialEventTarget(), __func__
,
1593 [promise
](const mozilla::MozPromise
<
1594 nsresult
, mozilla::ipc::ResponseRejectReason
,
1595 true>::ResolveOrRejectValue
& aValue
) {
1596 if (aValue
.IsResolve()) {
1597 promise
->MaybeResolve(aValue
.ResolveValue());
1599 promise
->MaybeRejectWithUndefined();
1602 return promise
.forget();
1605 if (XRE_IsParentProcess()) {
1606 nsCOMPtr
<nsICertOverrideService
> overrideService
=
1607 do_GetService(NS_CERTOVERRIDE_CONTRACTID
);
1608 if (!overrideService
) {
1609 promise
->MaybeReject(NS_ERROR_FAILURE
);
1610 return promise
.forget();
1613 OriginAttributes
const& attrs
= NodePrincipal()->OriginAttributesRef();
1614 rv
= overrideService
->RememberValidityOverride(host
, port
, attrs
, cert
,
1616 if (NS_WARN_IF(NS_FAILED(rv
))) {
1617 promise
->MaybeReject(rv
);
1618 return promise
.forget();
1621 promise
->MaybeResolveWithUndefined();
1622 return promise
.forget();
1625 promise
->MaybeReject(NS_ERROR_FAILURE
);
1626 return promise
.forget();
1629 void Document::ReloadWithHttpsOnlyException() {
1630 if (WindowGlobalChild
* wgc
= GetWindowGlobalChild()) {
1631 wgc
->SendReloadWithHttpsOnlyException();
1635 // Given an nsresult that is assumed to be synthesized by PSM and describes a
1636 // certificate or TLS error, attempts to convert it into a string
1637 // representation of the underlying NSS error.
1638 // `aErrorCodeString` will be an empty string if `aResult` is not an error from
1639 // PSM or it does not represent a valid NSS error.
1640 void GetErrorCodeStringFromNSResult(nsresult aResult
,
1641 nsAString
& aErrorCodeString
) {
1642 aErrorCodeString
.Truncate();
1644 if (NS_ERROR_GET_MODULE(aResult
) != NS_ERROR_MODULE_SECURITY
||
1645 NS_ERROR_GET_SEVERITY(aResult
) != NS_ERROR_SEVERITY_ERROR
) {
1649 PRErrorCode errorCode
= -1 * NS_ERROR_GET_CODE(aResult
);
1650 if (!mozilla::psm::IsNSSErrorCode(errorCode
)) {
1654 const char* errorCodeString
= PR_ErrorToName(errorCode
);
1655 if (!errorCodeString
) {
1659 aErrorCodeString
.AssignASCII(errorCodeString
);
1662 void Document::GetNetErrorInfo(NetErrorInfo
& aInfo
, ErrorResult
& aRv
) {
1663 nsresult rv
= NS_OK
;
1664 if (NS_WARN_IF(!mFailedChannel
)) {
1665 aRv
.Throw(NS_ERROR_DOM_INVALID_STATE_ERR
);
1669 nsCOMPtr
<nsIHttpChannel
> httpChannel(do_QueryInterface(mFailedChannel
));
1671 // We don't throw even if httpChannel is null, we just keep responseStatus and
1672 // responseStatusText empty
1674 uint32_t responseStatus
;
1675 nsAutoCString responseStatusText
;
1676 rv
= httpChannel
->GetResponseStatus(&responseStatus
);
1677 if (NS_SUCCEEDED(rv
)) {
1678 aInfo
.mResponseStatus
= responseStatus
;
1681 rv
= httpChannel
->GetResponseStatusText(responseStatusText
);
1682 if (NS_SUCCEEDED(rv
)) {
1683 aInfo
.mResponseStatusText
.AssignASCII(responseStatusText
);
1687 nsCOMPtr
<nsITransportSecurityInfo
> tsi
;
1688 rv
= mFailedChannel
->GetSecurityInfo(getter_AddRefs(tsi
));
1689 if (NS_WARN_IF(NS_FAILED(rv
))) {
1694 nsresult channelStatus
;
1695 rv
= mFailedChannel
->GetStatus(&channelStatus
);
1696 if (NS_WARN_IF(NS_FAILED(rv
))) {
1700 aInfo
.mChannelStatus
= static_cast<uint32_t>(channelStatus
);
1702 // If nsITransportSecurityInfo is not set, simply keep the remaining fields
1703 // empty (to make responseStatus and responseStatusText accessible).
1708 // TransportSecurityInfo::GetErrorCodeString always returns NS_OK
1709 (void)tsi
->GetErrorCodeString(aInfo
.mErrorCodeString
);
1710 if (aInfo
.mErrorCodeString
.IsEmpty()) {
1711 GetErrorCodeStringFromNSResult(channelStatus
, aInfo
.mErrorCodeString
);
1715 bool Document::CallerIsTrustedAboutCertError(JSContext
* aCx
,
1716 JSObject
* aObject
) {
1717 nsGlobalWindowInner
* win
= xpc::WindowOrNull(aObject
);
1719 // GeckoView uses data URLs for error pages, so for now just check for any
1721 return win
&& win
->GetDocument() && win
->GetDocument()->IsErrorPage();
1723 return win
&& IsAboutErrorPage(win
, "certerror");
1727 bool Document::CallerCanAccessPrivilegeSSA(JSContext
* aCx
, JSObject
* aObject
) {
1728 RefPtr
<BasePrincipal
> principal
=
1729 BasePrincipal::Cast(nsContentUtils::SubjectPrincipal(aCx
));
1735 // We allow the privilege SSA to be called from system principal.
1736 if (principal
->IsSystemPrincipal()) {
1740 // We only allow calling the privilege SSA from the content script of the
1741 // webcompat extension.
1742 if (auto* policy
= principal
->ContentScriptAddonPolicy()) {
1743 nsAutoString addonID
;
1744 policy
->GetId(addonID
);
1746 return addonID
.EqualsLiteral("webcompat@mozilla.org");
1752 bool Document::IsErrorPage() const {
1753 nsCOMPtr
<nsILoadInfo
> loadInfo
= mChannel
? mChannel
->LoadInfo() : nullptr;
1754 return loadInfo
&& loadInfo
->GetLoadErrorPage();
1757 void Document::GetFailedCertSecurityInfo(FailedCertSecurityInfo
& aInfo
,
1759 nsresult rv
= NS_OK
;
1760 if (NS_WARN_IF(!mFailedChannel
)) {
1761 aRv
.Throw(NS_ERROR_DOM_INVALID_STATE_ERR
);
1765 nsCOMPtr
<nsITransportSecurityInfo
> tsi
;
1766 rv
= mFailedChannel
->GetSecurityInfo(getter_AddRefs(tsi
));
1767 if (NS_WARN_IF(NS_FAILED(rv
))) {
1771 if (NS_WARN_IF(!tsi
)) {
1772 aRv
.Throw(NS_ERROR_DOM_INVALID_STATE_ERR
);
1776 nsresult channelStatus
;
1777 rv
= mFailedChannel
->GetStatus(&channelStatus
);
1778 if (NS_WARN_IF(NS_FAILED(rv
))) {
1782 aInfo
.mChannelStatus
= static_cast<uint32_t>(channelStatus
);
1784 // TransportSecurityInfo::GetErrorCodeString always returns NS_OK
1785 (void)tsi
->GetErrorCodeString(aInfo
.mErrorCodeString
);
1786 if (aInfo
.mErrorCodeString
.IsEmpty()) {
1787 GetErrorCodeStringFromNSResult(channelStatus
, aInfo
.mErrorCodeString
);
1790 nsITransportSecurityInfo::OverridableErrorCategory errorCategory
;
1791 rv
= tsi
->GetOverridableErrorCategory(&errorCategory
);
1792 if (NS_WARN_IF(NS_FAILED(rv
))) {
1796 switch (errorCategory
) {
1797 case nsITransportSecurityInfo::OverridableErrorCategory::ERROR_TRUST
:
1798 aInfo
.mOverridableErrorCategory
=
1799 dom::OverridableErrorCategory::Trust_error
;
1801 case nsITransportSecurityInfo::OverridableErrorCategory::ERROR_DOMAIN
:
1802 aInfo
.mOverridableErrorCategory
=
1803 dom::OverridableErrorCategory::Domain_mismatch
;
1805 case nsITransportSecurityInfo::OverridableErrorCategory::ERROR_TIME
:
1806 aInfo
.mOverridableErrorCategory
=
1807 dom::OverridableErrorCategory::Expired_or_not_yet_valid
;
1810 aInfo
.mOverridableErrorCategory
= dom::OverridableErrorCategory::Unset
;
1814 nsCOMPtr
<nsIX509Cert
> cert
;
1815 nsCOMPtr
<nsIX509CertValidity
> validity
;
1816 rv
= tsi
->GetServerCert(getter_AddRefs(cert
));
1817 if (NS_WARN_IF(NS_FAILED(rv
))) {
1821 if (NS_WARN_IF(!cert
)) {
1822 aRv
.Throw(NS_ERROR_DOM_INVALID_STATE_ERR
);
1826 rv
= cert
->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 PRTime validityResult
;
1837 rv
= validity
->GetNotBefore(&validityResult
);
1838 if (NS_WARN_IF(NS_FAILED(rv
))) {
1842 aInfo
.mValidNotBefore
= DOMTimeStamp(validityResult
/ PR_USEC_PER_MSEC
);
1844 rv
= validity
->GetNotAfter(&validityResult
);
1845 if (NS_WARN_IF(NS_FAILED(rv
))) {
1849 aInfo
.mValidNotAfter
= DOMTimeStamp(validityResult
/ PR_USEC_PER_MSEC
);
1851 nsAutoString issuerCommonName
;
1852 nsAutoString certChainPEMString
;
1853 Sequence
<nsString
>& certChainStrings
= aInfo
.mCertChainStrings
.Construct();
1854 int64_t maxValidity
= std::numeric_limits
<int64_t>::max();
1855 int64_t minValidity
= 0;
1856 PRTime notBefore
, notAfter
;
1857 nsTArray
<RefPtr
<nsIX509Cert
>> failedCertArray
;
1858 rv
= tsi
->GetFailedCertChain(failedCertArray
);
1859 if (NS_WARN_IF(NS_FAILED(rv
))) {
1864 if (NS_WARN_IF(failedCertArray
.IsEmpty())) {
1865 aRv
.Throw(NS_ERROR_DOM_INVALID_STATE_ERR
);
1869 for (const auto& certificate
: failedCertArray
) {
1870 rv
= certificate
->GetIssuerCommonName(issuerCommonName
);
1871 if (NS_WARN_IF(NS_FAILED(rv
))) {
1876 rv
= certificate
->GetValidity(getter_AddRefs(validity
));
1877 if (NS_WARN_IF(NS_FAILED(rv
))) {
1881 if (NS_WARN_IF(!validity
)) {
1882 aRv
.Throw(NS_ERROR_DOM_INVALID_STATE_ERR
);
1886 rv
= validity
->GetNotBefore(¬Before
);
1887 if (NS_WARN_IF(NS_FAILED(rv
))) {
1892 rv
= validity
->GetNotAfter(¬After
);
1893 if (NS_WARN_IF(NS_FAILED(rv
))) {
1898 notBefore
= std::max(minValidity
, notBefore
);
1899 notAfter
= std::min(maxValidity
, notAfter
);
1900 nsTArray
<uint8_t> certArray
;
1901 rv
= certificate
->GetRawDER(certArray
);
1902 if (NS_WARN_IF(NS_FAILED(rv
))) {
1908 rv
= Base64Encode(reinterpret_cast<const char*>(certArray
.Elements()),
1909 certArray
.Length(), der64
);
1910 if (NS_WARN_IF(NS_FAILED(rv
))) {
1914 if (!certChainStrings
.AppendElement(der64
, fallible
)) {
1915 aRv
.Throw(NS_ERROR_OUT_OF_MEMORY
);
1920 aInfo
.mIssuerCommonName
.Assign(issuerCommonName
);
1921 aInfo
.mCertValidityRangeNotAfter
= DOMTimeStamp(notAfter
/ PR_USEC_PER_MSEC
);
1922 aInfo
.mCertValidityRangeNotBefore
=
1923 DOMTimeStamp(notBefore
/ PR_USEC_PER_MSEC
);
1926 rv
= tsi
->GetErrorCode(&errorCode
);
1927 if (NS_WARN_IF(NS_FAILED(rv
))) {
1932 nsCOMPtr
<nsINSSErrorsService
> nsserr
=
1933 do_GetService("@mozilla.org/nss_errors_service;1");
1934 if (NS_WARN_IF(!nsserr
)) {
1935 aRv
.Throw(NS_ERROR_DOM_INVALID_STATE_ERR
);
1939 rv
= nsserr
->GetXPCOMFromNSSError(errorCode
, &res
);
1940 if (NS_WARN_IF(NS_FAILED(rv
))) {
1944 rv
= nsserr
->GetErrorMessage(res
, aInfo
.mErrorMessage
);
1945 if (NS_WARN_IF(NS_FAILED(rv
))) {
1950 OriginAttributes attrs
;
1951 StoragePrincipalHelper::GetRegularPrincipalOriginAttributes(this, attrs
);
1952 nsCOMPtr
<nsIURI
> aURI
;
1953 mFailedChannel
->GetURI(getter_AddRefs(aURI
));
1954 if (XRE_IsContentProcess()) {
1955 ContentChild
* cc
= ContentChild::GetSingleton();
1957 cc
->SendIsSecureURI(aURI
, attrs
, &aInfo
.mHasHSTS
);
1959 nsCOMPtr
<nsISiteSecurityService
> sss
=
1960 do_GetService(NS_SSSERVICE_CONTRACTID
);
1961 if (NS_WARN_IF(!sss
)) {
1964 Unused
<< NS_WARN_IF(
1965 NS_FAILED(sss
->IsSecureURI(aURI
, attrs
, &aInfo
.mHasHSTS
)));
1967 nsCOMPtr
<nsIPublicKeyPinningService
> pkps
=
1968 do_GetService(NS_PKPSERVICE_CONTRACTID
);
1969 if (NS_WARN_IF(!pkps
)) {
1972 Unused
<< NS_WARN_IF(NS_FAILED(pkps
->HostHasPins(aURI
, &aInfo
.mHasHPKP
)));
1975 bool Document::IsAboutPage() const {
1976 return NodePrincipal()->SchemeIs("about");
1979 void Document::ConstructUbiNode(void* storage
) {
1980 JS::ubi::Concrete
<Document
>::construct(storage
, this);
1983 void Document::LoadEventFired() {
1984 // Object used to collect some telemetry data so we don't need to query for it
1986 glean::perf::PageLoadExtra pageLoadEventData
;
1988 // Accumulate timing data located in each document's realm and report to
1990 AccumulateJSTelemetry(pageLoadEventData
);
1992 // Collect page load timings
1993 AccumulatePageLoadTelemetry(pageLoadEventData
);
1995 // Record page load event
1996 RecordPageLoadEventTelemetry(pageLoadEventData
);
1998 // Release the JS bytecode cache from its wait on the load event, and
1999 // potentially dispatch the encoding of the bytecode.
2000 if (ScriptLoader()) {
2001 ScriptLoader()->LoadEventFired();
2005 void Document::RecordPageLoadEventTelemetry(
2006 glean::perf::PageLoadExtra
& aEventTelemetryData
) {
2007 // If the page load time is empty, then the content wasn't something we want
2008 // to report (i.e. not a top level document).
2009 if (!aEventTelemetryData
.loadTime
) {
2012 MOZ_ASSERT(IsTopLevelContentDocument());
2014 nsPIDOMWindowOuter
* window
= GetWindow();
2019 nsIDocShell
* docshell
= window
->GetDocShell();
2024 nsAutoCString loadTypeStr
;
2025 switch (docshell
->GetLoadType()) {
2027 case LOAD_NORMAL_REPLACE
:
2028 case LOAD_NORMAL_BYPASS_CACHE
:
2029 case LOAD_NORMAL_BYPASS_PROXY
:
2030 case LOAD_NORMAL_BYPASS_PROXY_AND_CACHE
:
2031 loadTypeStr
.Append("NORMAL");
2034 loadTypeStr
.Append("HISTORY");
2036 case LOAD_RELOAD_NORMAL
:
2037 case LOAD_RELOAD_BYPASS_CACHE
:
2038 case LOAD_RELOAD_BYPASS_PROXY
:
2039 case LOAD_RELOAD_BYPASS_PROXY_AND_CACHE
:
2041 case LOAD_REFRESH_REPLACE
:
2042 case LOAD_RELOAD_CHARSET_CHANGE
:
2043 case LOAD_RELOAD_CHARSET_CHANGE_BYPASS_PROXY_AND_CACHE
:
2044 case LOAD_RELOAD_CHARSET_CHANGE_BYPASS_CACHE
:
2045 loadTypeStr
.Append("RELOAD");
2048 loadTypeStr
.Append("LINK");
2050 case LOAD_STOP_CONTENT
:
2051 case LOAD_STOP_CONTENT_AND_REPLACE
:
2052 loadTypeStr
.Append("STOP");
2054 case LOAD_ERROR_PAGE
:
2055 loadTypeStr
.Append("ERROR");
2058 loadTypeStr
.Append("OTHER");
2062 nsCOMPtr
<nsIEffectiveTLDService
> tldService
=
2063 mozilla::components::EffectiveTLD::Service();
2064 if (tldService
&& mReferrerInfo
&&
2065 (docshell
->GetLoadType() & nsIDocShell::LOAD_CMD_NORMAL
)) {
2066 nsAutoCString currentBaseDomain
, referrerBaseDomain
;
2067 nsCOMPtr
<nsIURI
> referrerURI
= mReferrerInfo
->GetComputedReferrer();
2069 auto result
= NS_SUCCEEDED(
2070 tldService
->GetBaseDomain(referrerURI
, 0, referrerBaseDomain
));
2072 bool sameOrigin
= false;
2073 NodePrincipal()->IsSameOrigin(referrerURI
, &sameOrigin
);
2074 aEventTelemetryData
.sameOriginNav
= mozilla::Some(sameOrigin
);
2079 aEventTelemetryData
.loadType
= mozilla::Some(loadTypeStr
);
2081 // Sending a glean ping must be done on the parent process.
2082 if (ContentChild
* cc
= ContentChild::GetSingleton()) {
2083 cc
->SendRecordPageLoadEvent(aEventTelemetryData
);
2088 static void AccumulateHttp3FcpGleanPref(const nsCString
& http3Key
,
2089 const TimeDuration
& duration
) {
2090 if (http3Key
== "http3"_ns
) {
2091 glean::performance_pageload::http3_fcp_http3
.AccumulateRawDuration(
2093 } else if (http3Key
== "supports_http3"_ns
) {
2094 glean::performance_pageload::http3_fcp_supports_http3
.AccumulateRawDuration(
2097 MOZ_ASSERT_UNREACHABLE("Unknown value for http3Key");
2101 static void AccumulatePriorityFcpGleanPref(
2102 const nsCString
& http3WithPriorityKey
, const TimeDuration
& duration
) {
2103 if (http3WithPriorityKey
== "with_priority"_ns
) {
2104 glean::performance_pageload::h3p_fcp_with_priority
.AccumulateRawDuration(
2106 } else if (http3WithPriorityKey
== "without_priority"_ns
) {
2107 glean::performance_pageload::http3_fcp_without_priority
2108 .AccumulateRawDuration(duration
);
2110 MOZ_ASSERT_UNREACHABLE("Unknown value for http3WithPriorityKey");
2115 void Document::AccumulatePageLoadTelemetry(
2116 glean::perf::PageLoadExtra
& aEventTelemetryDataOut
) {
2117 // Interested only in top level documents for real websites that are in the
2119 if (!ShouldIncludeInTelemetry() || !IsTopLevelContentDocument() ||
2120 !GetNavigationTiming() ||
2121 !GetNavigationTiming()->DocShellHasBeenActiveSinceNavigationStart()) {
2125 if (!GetChannel()) {
2129 nsCOMPtr
<nsITimedChannel
> timedChannel(do_QueryInterface(GetChannel()));
2130 if (!timedChannel
) {
2134 // Default duration is 0, use this to check for bogus negative values.
2135 const TimeDuration zeroDuration
;
2137 TimeStamp responseStart
;
2138 timedChannel
->GetResponseStart(&responseStart
);
2140 TimeStamp redirectStart
, redirectEnd
;
2141 timedChannel
->GetRedirectStart(&redirectStart
);
2142 timedChannel
->GetRedirectEnd(&redirectEnd
);
2144 uint8_t redirectCount
;
2145 timedChannel
->GetRedirectCount(&redirectCount
);
2146 if (redirectCount
) {
2147 aEventTelemetryDataOut
.redirectCount
=
2148 mozilla::Some(static_cast<uint32_t>(redirectCount
));
2151 if (!redirectStart
.IsNull() && !redirectEnd
.IsNull()) {
2152 TimeDuration redirectTime
= redirectEnd
- redirectStart
;
2153 if (redirectTime
> zeroDuration
) {
2154 aEventTelemetryDataOut
.redirectTime
=
2155 mozilla::Some(static_cast<uint32_t>(redirectTime
.ToMilliseconds()));
2159 TimeStamp dnsLookupStart
, dnsLookupEnd
;
2160 timedChannel
->GetDomainLookupStart(&dnsLookupStart
);
2161 timedChannel
->GetDomainLookupEnd(&dnsLookupEnd
);
2163 if (!dnsLookupStart
.IsNull() && !dnsLookupEnd
.IsNull()) {
2164 TimeDuration dnsLookupTime
= dnsLookupEnd
- dnsLookupStart
;
2165 if (dnsLookupTime
> zeroDuration
) {
2166 aEventTelemetryDataOut
.dnsLookupTime
=
2167 mozilla::Some(static_cast<uint32_t>(dnsLookupTime
.ToMilliseconds()));
2171 TimeStamp navigationStart
=
2172 GetNavigationTiming()->GetNavigationStartTimeStamp();
2174 if (!responseStart
|| !navigationStart
) {
2178 nsAutoCString
dnsKey("Native");
2179 nsAutoCString http3Key
;
2180 nsAutoCString http3WithPriorityKey
;
2181 nsAutoCString earlyHintKey
;
2182 nsCOMPtr
<nsIHttpChannelInternal
> httpChannel
=
2183 do_QueryInterface(GetChannel());
2185 bool resolvedByTRR
= false;
2186 Unused
<< httpChannel
->GetIsResolvedByTRR(&resolvedByTRR
);
2187 if (resolvedByTRR
) {
2188 if (nsCOMPtr
<nsIDNSService
> dns
=
2189 do_GetService(NS_DNSSERVICE_CONTRACTID
)) {
2190 dns
->GetTRRDomainKey(dnsKey
);
2192 // Failed to get the DNS service.
2193 dnsKey
= "(fail)"_ns
;
2195 aEventTelemetryDataOut
.trrDomain
= mozilla::Some(dnsKey
);
2200 if (NS_SUCCEEDED(httpChannel
->GetResponseVersion(&major
, &minor
))) {
2202 http3Key
= "http3"_ns
;
2203 nsCOMPtr
<nsIHttpChannel
> httpChannel2
= do_QueryInterface(GetChannel());
2207 httpChannel2
->GetResponseHeader("priority"_ns
, header
)) &&
2208 !header
.IsEmpty()) {
2209 http3WithPriorityKey
= "with_priority"_ns
;
2211 http3WithPriorityKey
= "without_priority"_ns
;
2213 } else if (major
== 2) {
2214 bool supportHttp3
= false;
2215 if (NS_FAILED(httpChannel
->GetSupportsHTTP3(&supportHttp3
))) {
2216 supportHttp3
= false;
2219 http3Key
= "supports_http3"_ns
;
2223 aEventTelemetryDataOut
.httpVer
= mozilla::Some(major
);
2226 uint32_t earlyHintType
= 0;
2227 Unused
<< httpChannel
->GetEarlyHintLinkType(&earlyHintType
);
2228 if (earlyHintType
& LinkStyle::ePRECONNECT
) {
2229 earlyHintKey
.Append("preconnect_"_ns
);
2231 if (earlyHintType
& LinkStyle::ePRELOAD
) {
2232 earlyHintKey
.Append("preload_"_ns
);
2233 earlyHintKey
.Append(mPreloadService
.GetEarlyHintUsed() ? "1"_ns
: "0"_ns
);
2237 TimeStamp asyncOpen
;
2238 timedChannel
->GetAsyncOpen(&asyncOpen
);
2240 Telemetry::AccumulateTimeDelta(Telemetry::DNS_PERF_FIRST_BYTE_MS
, dnsKey
,
2241 asyncOpen
, responseStart
);
2244 // First Contentful Composite
2245 if (TimeStamp firstContentfulComposite
=
2246 GetNavigationTiming()->GetFirstContentfulCompositeTimeStamp()) {
2247 glean::performance_pageload::fcp
.AccumulateRawDuration(
2248 firstContentfulComposite
- navigationStart
);
2250 if (!http3Key
.IsEmpty()) {
2251 Telemetry::AccumulateTimeDelta(
2252 Telemetry::HTTP3_PERF_FIRST_CONTENTFUL_PAINT_MS
, http3Key
,
2253 navigationStart
, firstContentfulComposite
);
2255 AccumulateHttp3FcpGleanPref(http3Key
,
2256 firstContentfulComposite
- navigationStart
);
2260 if (!http3WithPriorityKey
.IsEmpty()) {
2261 Telemetry::AccumulateTimeDelta(
2262 Telemetry::H3P_PERF_FIRST_CONTENTFUL_PAINT_MS
, http3WithPriorityKey
,
2263 navigationStart
, firstContentfulComposite
);
2265 AccumulatePriorityFcpGleanPref(
2266 http3WithPriorityKey
, firstContentfulComposite
- navigationStart
);
2270 Telemetry::AccumulateTimeDelta(
2271 Telemetry::DNS_PERF_FIRST_CONTENTFUL_PAINT_MS
, dnsKey
, navigationStart
,
2272 firstContentfulComposite
);
2274 glean::performance_pageload::fcp_responsestart
.AccumulateRawDuration(
2275 firstContentfulComposite
- responseStart
);
2277 TimeDuration fcpTime
= firstContentfulComposite
- navigationStart
;
2278 if (fcpTime
> zeroDuration
) {
2279 aEventTelemetryDataOut
.fcpTime
=
2280 mozilla::Some(static_cast<uint32_t>(fcpTime
.ToMilliseconds()));
2284 // Report the most up to date LCP time. For our histogram we actually report
2285 // this on page unload.
2286 if (TimeStamp lcpTime
=
2287 GetNavigationTiming()->GetLargestContentfulRenderTimeStamp()) {
2288 aEventTelemetryDataOut
.lcpTime
= mozilla::Some(
2289 static_cast<uint32_t>((lcpTime
- navigationStart
).ToMilliseconds()));
2293 if (TimeStamp loadEventStart
=
2294 GetNavigationTiming()->GetLoadEventStartTimeStamp()) {
2295 glean::performance_pageload::load_time
.AccumulateRawDuration(
2296 loadEventStart
- navigationStart
);
2297 if (!http3Key
.IsEmpty()) {
2298 Telemetry::AccumulateTimeDelta(Telemetry::HTTP3_PERF_PAGE_LOAD_TIME_MS
,
2299 http3Key
, navigationStart
, loadEventStart
);
2302 if (!http3WithPriorityKey
.IsEmpty()) {
2303 Telemetry::AccumulateTimeDelta(Telemetry::H3P_PERF_PAGE_LOAD_TIME_MS
,
2304 http3WithPriorityKey
, navigationStart
,
2308 glean::performance_pageload::load_time_responsestart
.AccumulateRawDuration(
2309 loadEventStart
- responseStart
);
2311 TimeDuration responseTime
= responseStart
- navigationStart
;
2312 if (responseTime
> zeroDuration
) {
2313 aEventTelemetryDataOut
.responseTime
=
2314 mozilla::Some(static_cast<uint32_t>(responseTime
.ToMilliseconds()));
2317 TimeDuration loadTime
= loadEventStart
- navigationStart
;
2318 if (loadTime
> zeroDuration
) {
2319 aEventTelemetryDataOut
.loadTime
=
2320 mozilla::Some(static_cast<uint32_t>(loadTime
.ToMilliseconds()));
2323 TimeStamp requestStart
;
2324 timedChannel
->GetRequestStart(&requestStart
);
2326 TimeDuration timeToRequestStart
= requestStart
- navigationStart
;
2327 if (timeToRequestStart
> zeroDuration
) {
2328 aEventTelemetryDataOut
.timeToRequestStart
= mozilla::Some(
2329 static_cast<uint32_t>(timeToRequestStart
.ToMilliseconds()));
2334 aEventTelemetryDataOut
.features
= mozilla::Some(mPageloadEventFeatures
);
2337 void Document::AccumulateJSTelemetry(
2338 glean::perf::PageLoadExtra
& aEventTelemetryDataOut
) {
2339 if (!IsTopLevelContentDocument() || !ShouldIncludeInTelemetry()) {
2343 if (!GetScopeObject() || !GetScopeObject()->GetGlobalJSObject()) {
2348 JSObject
* globalObject
= GetScopeObject()->GetGlobalJSObject();
2349 JSAutoRealm
ar(cx
, globalObject
);
2350 JS::JSTimers timers
= JS::GetJSTimers(cx
);
2352 if (!timers
.executionTime
.IsZero()) {
2353 glean::javascript_pageload::execution_time
.AccumulateRawDuration(
2354 timers
.executionTime
);
2355 aEventTelemetryDataOut
.jsExecTime
= mozilla::Some(
2356 static_cast<uint32_t>(timers
.executionTime
.ToMilliseconds()));
2359 if (!timers
.delazificationTime
.IsZero()) {
2360 glean::javascript_pageload::delazification_time
.AccumulateRawDuration(
2361 timers
.delazificationTime
);
2364 if (!timers
.xdrEncodingTime
.IsZero()) {
2365 glean::javascript_pageload::xdr_encode_time
.AccumulateRawDuration(
2366 timers
.xdrEncodingTime
);
2369 if (!timers
.baselineCompileTime
.IsZero()) {
2370 glean::javascript_pageload::baseline_compile_time
.AccumulateRawDuration(
2371 timers
.baselineCompileTime
);
2374 if (!timers
.gcTime
.IsZero()) {
2375 glean::javascript_pageload::gc_time
.AccumulateRawDuration(timers
.gcTime
);
2378 if (!timers
.protectTime
.IsZero()) {
2379 glean::javascript_pageload::protect_time
.AccumulateRawDuration(
2380 timers
.protectTime
);
2384 Document::~Document() {
2385 MOZ_LOG(gDocumentLeakPRLog
, LogLevel::Debug
, ("DOCUMENT %p destroyed", this));
2386 MOZ_ASSERT(!IsTopLevelContentDocument() || !IsResourceDoc(),
2387 "Can't be top-level and a resource doc at the same time");
2389 NS_ASSERTION(!mIsShowing
, "Destroying a currently-showing document");
2391 if (IsTopLevelContentDocument()) {
2392 RemoveToplevelLoadingDocument(this);
2394 // don't report for about: pages
2395 if (!IsAboutPage()) {
2396 if (MOZ_UNLIKELY(mMathMLEnabled
)) {
2397 glean::mathml::doc_count
.Add(1);
2402 mInDestructor
= true;
2403 mInUnlinkOrDeletion
= true;
2405 mozilla::DropJSObjects(this);
2407 // Clear mObservers to keep it in sync with the mutationobserver list
2410 mIntersectionObservers
.Clear();
2412 if (mStyleSheetSetList
) {
2413 mStyleSheetSetList
->Disconnect();
2416 if (mAnimationController
) {
2417 mAnimationController
->Disconnect();
2420 MOZ_ASSERT(mTimelines
.isEmpty());
2422 mParentDocument
= nullptr;
2424 // Kill the subdocument map, doing this will release its strong
2425 // references, if any.
2426 mSubDocuments
= nullptr;
2428 nsAutoScriptBlocker scriptBlocker
;
2430 // Destroy link map now so we don't waste time removing
2432 DestroyElementMaps();
2434 // Invalidate cached array of child nodes
2435 InvalidateChildNodes();
2437 // We should not have child nodes when destructor is called,
2438 // since child nodes keep their owner document alive.
2439 MOZ_ASSERT(!HasChildren());
2441 mCachedRootElement
= nullptr;
2443 for (auto& sheets
: mAdditionalSheets
) {
2444 UnlinkStyleSheets(sheets
);
2447 if (mAttributeStyles
) {
2448 mAttributeStyles
->SetOwningDocument(nullptr);
2451 if (mListenerManager
) {
2452 mListenerManager
->Disconnect();
2453 UnsetFlags(NODE_HAS_LISTENERMANAGER
);
2456 if (mScriptLoader
) {
2457 mScriptLoader
->DropDocumentReference();
2461 // Could be null here if Init() failed or if we have been unlinked.
2462 mCSSLoader
->DropDocumentReference();
2465 if (mStyleImageLoader
) {
2466 mStyleImageLoader
->DropDocumentReference();
2469 if (mXULBroadcastManager
) {
2470 mXULBroadcastManager
->DropDocumentReference();
2474 mXULPersist
->DropDocumentReference();
2477 if (mPermissionDelegateHandler
) {
2478 mPermissionDelegateHandler
->DropDocumentReference();
2481 mHeaderData
= nullptr;
2483 mPendingTitleChangeEvent
.Revoke();
2485 MOZ_ASSERT(mDOMMediaQueryLists
.isEmpty(),
2486 "must not have media query lists left");
2488 if (mNodeInfoManager
) {
2489 mNodeInfoManager
->DropDocumentReference();
2493 MOZ_ASSERT(mDocGroup
->GetBrowsingContextGroup());
2494 mDocGroup
->GetBrowsingContextGroup()->RemoveDocument(this, mDocGroup
);
2497 UnlinkOriginalDocumentIfStatic();
2499 UnregisterFromMemoryReportingForDataDocument();
2502 void Document::DropStyleSet() { mStyleSet
= nullptr; }
2504 NS_INTERFACE_TABLE_HEAD(Document
)
2505 NS_WRAPPERCACHE_INTERFACE_TABLE_ENTRY
2506 NS_INTERFACE_TABLE_BEGIN
2507 NS_INTERFACE_TABLE_ENTRY_AMBIGUOUS(Document
, nsISupports
, nsINode
)
2508 NS_INTERFACE_TABLE_ENTRY(Document
, nsINode
)
2509 NS_INTERFACE_TABLE_ENTRY(Document
, Document
)
2510 NS_INTERFACE_TABLE_ENTRY(Document
, nsIScriptObjectPrincipal
)
2511 NS_INTERFACE_TABLE_ENTRY(Document
, EventTarget
)
2512 NS_INTERFACE_TABLE_ENTRY(Document
, nsISupportsWeakReference
)
2513 NS_INTERFACE_TABLE_END
2514 NS_INTERFACE_TABLE_TO_MAP_SEGUE_CYCLE_COLLECTION(Document
)
2515 NS_INTERFACE_MAP_END
2517 NS_IMPL_CYCLE_COLLECTING_ADDREF(Document
)
2518 NS_IMPL_CYCLE_COLLECTING_RELEASE_WITH_LAST_RELEASE(Document
, LastRelease())
2520 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_BEGIN(Document
)
2521 if (Element::CanSkip(tmp
, aRemovingAllowed
)) {
2522 EventListenerManager
* elm
= tmp
->GetExistingListenerManager();
2528 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_END
2530 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_BEGIN(Document
)
2531 return Element::CanSkipInCC(tmp
);
2532 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_END
2534 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_BEGIN(Document
)
2535 return Element::CanSkipThis(tmp
);
2536 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_END
2538 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INTERNAL(Document
)
2539 if (MOZ_UNLIKELY(cb
.WantDebugInfo())) {
2541 nsAutoCString loadedAsData
;
2542 if (tmp
->IsLoadedAsData()) {
2543 loadedAsData
.AssignLiteral("data");
2545 loadedAsData
.AssignLiteral("normal");
2547 uint32_t nsid
= tmp
->GetDefaultNamespaceID();
2549 if (tmp
->mDocumentURI
) uri
= tmp
->mDocumentURI
->GetSpecOrDefault();
2550 static const char* kNSURIs
[] = {"([none])", "(xmlns)", "(xml)",
2551 "(xhtml)", "(XLink)", "(XSLT)",
2552 "(MathML)", "(RDF)", "(XUL)"};
2553 if (nsid
< std::size(kNSURIs
)) {
2554 SprintfLiteral(name
, "Document %s %s %s", loadedAsData
.get(),
2555 kNSURIs
[nsid
], uri
.get());
2557 SprintfLiteral(name
, "Document %s %s", loadedAsData
.get(), uri
.get());
2559 cb
.DescribeRefCountedNode(tmp
->mRefCnt
.get(), name
);
2561 NS_IMPL_CYCLE_COLLECTION_DESCRIBE(Document
, tmp
->mRefCnt
.get())
2564 if (!nsINode::Traverse(tmp
, cb
)) {
2565 return NS_SUCCESS_INTERRUPTED_TRAVERSE
;
2568 tmp
->mExternalResourceMap
.Traverse(&cb
);
2570 // Traverse all Document pointer members.
2571 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSecurityInfo
)
2572 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDisplayDocument
)
2573 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFontFaceSet
)
2574 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mReadyForIdle
)
2575 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocumentL10n
)
2576 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFragmentDirective
)
2577 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mHighlightRegistry
)
2579 // Traverse all Document nsCOMPtrs.
2580 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mParser
)
2581 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mScriptGlobalObject
)
2582 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mListenerManager
)
2583 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mStyleSheetSetList
)
2584 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mScriptLoader
)
2586 DocumentOrShadowRoot::Traverse(tmp
, cb
);
2588 if (tmp
->mRadioGroupContainer
) {
2589 RadioGroupContainer::Traverse(tmp
->mRadioGroupContainer
.get(), cb
);
2592 for (auto& sheets
: tmp
->mAdditionalSheets
) {
2593 tmp
->TraverseStyleSheets(sheets
, "mAdditionalSheets[<origin>][i]", cb
);
2596 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mOnloadBlocker
)
2597 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mLazyLoadObserver
)
2598 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mElementsObservedForLastRememberedSize
)
2599 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDOMImplementation
)
2600 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mImageMaps
)
2601 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mOrientationPendingPromise
)
2602 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mOriginalDocument
)
2603 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCachedEncoder
)
2604 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocumentTimeline
)
2605 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mScrollTimelineAnimationTracker
)
2606 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mTemplateContentsOwner
)
2607 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mChildrenCollection
)
2608 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mImages
);
2609 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mEmbeds
);
2610 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mLinks
);
2611 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mForms
);
2612 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mScripts
);
2613 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mApplets
);
2614 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mAnchors
);
2615 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mAnonymousContents
)
2616 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCommandDispatcher
)
2617 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFeaturePolicy
)
2618 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPermissionDelegateHandler
)
2619 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSuppressedEventListener
)
2620 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPrototypeDocument
)
2621 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mMidasCommandManager
)
2622 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mAll
)
2623 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mActiveViewTransition
)
2624 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocGroup
)
2625 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFrameRequestManager
)
2627 // Traverse all our nsCOMArrays.
2628 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPreloadingImages
)
2630 // Traverse animation components
2631 if (tmp
->mAnimationController
) {
2632 tmp
->mAnimationController
->Traverse(&cb
);
2635 if (tmp
->mSubDocuments
) {
2636 for (auto iter
= tmp
->mSubDocuments
->Iter(); !iter
.Done(); iter
.Next()) {
2637 auto entry
= static_cast<SubDocMapEntry
*>(iter
.Get());
2639 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb
, "mSubDocuments entry->mKey");
2640 cb
.NoteXPCOMChild(entry
->mKey
);
2641 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb
,
2642 "mSubDocuments entry->mSubDocument");
2643 cb
.NoteXPCOMChild(ToSupports(entry
->mSubDocument
));
2647 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCSSLoader
)
2649 // We own only the items in mDOMMediaQueryLists that have listeners;
2650 // this reference is managed by their AddListener and RemoveListener
2652 for (MediaQueryList
* mql
= tmp
->mDOMMediaQueryLists
.getFirst(); mql
;
2653 mql
= static_cast<LinkedListElement
<MediaQueryList
>*>(mql
)->getNext()) {
2654 if (mql
->HasListeners() &&
2655 NS_SUCCEEDED(mql
->CheckCurrentGlobalCorrectness())) {
2656 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb
, "mDOMMediaQueryLists item");
2657 cb
.NoteXPCOMChild(static_cast<EventTarget
*>(mql
));
2661 // XXX: This should be not needed once bug 1569185 lands.
2662 for (const auto& entry
: tmp
->mL10nProtoElements
) {
2663 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb
, "mL10nProtoElements key");
2664 cb
.NoteXPCOMChild(entry
.GetKey());
2665 CycleCollectionNoteChild(cb
, entry
.GetWeak(), "mL10nProtoElements value");
2668 for (size_t i
= 0; i
< tmp
->mPendingFrameStaticClones
.Length(); ++i
) {
2669 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPendingFrameStaticClones
[i
].mElement
);
2670 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(
2671 mPendingFrameStaticClones
[i
].mStaticCloneOf
);
2674 for (auto& tableEntry
: tmp
->mActiveLocks
) {
2675 ImplCycleCollectionTraverse(cb
, *tableEntry
.GetModifiableData(),
2676 "mActiveLocks entry", 0);
2678 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
2680 NS_IMPL_CYCLE_COLLECTION_CLASS(Document
)
2682 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(Document
)
2683 NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER
2684 NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mCachedStateObject
)
2685 NS_IMPL_CYCLE_COLLECTION_TRACE_END
2687 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(Document
)
2688 tmp
->mInUnlinkOrDeletion
= true;
2690 tmp
->SetStateObject(nullptr);
2692 // Clear out our external resources
2693 tmp
->mExternalResourceMap
.Shutdown();
2695 nsAutoScriptBlocker scriptBlocker
;
2697 nsINode::Unlink(tmp
);
2699 while (tmp
->HasChildren()) {
2700 // Hold a strong ref to the node when we remove it, because we may be
2701 // the last reference to it.
2702 // If this code changes, change the corresponding code in Document's
2703 // unlink impl and ContentUnbinder::UnbindSubtree.
2704 nsCOMPtr
<nsIContent
> child
= tmp
->GetLastChild();
2705 tmp
->DisconnectChild(child
);
2706 child
->UnbindFromTree();
2709 tmp
->UnlinkOriginalDocumentIfStatic();
2711 tmp
->mCachedRootElement
= nullptr; // Avoid a dangling pointer
2713 tmp
->SetScriptGlobalObject(nullptr);
2715 for (auto& sheets
: tmp
->mAdditionalSheets
) {
2716 tmp
->UnlinkStyleSheets(sheets
);
2719 NS_IMPL_CYCLE_COLLECTION_UNLINK(mSecurityInfo
)
2720 NS_IMPL_CYCLE_COLLECTION_UNLINK(mDisplayDocument
)
2721 NS_IMPL_CYCLE_COLLECTION_UNLINK(mLazyLoadObserver
)
2722 NS_IMPL_CYCLE_COLLECTION_UNLINK(mElementsObservedForLastRememberedSize
);
2723 NS_IMPL_CYCLE_COLLECTION_UNLINK(mFontFaceSet
)
2724 NS_IMPL_CYCLE_COLLECTION_UNLINK(mReadyForIdle
)
2725 NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocumentL10n
)
2726 NS_IMPL_CYCLE_COLLECTION_UNLINK(mFragmentDirective
)
2727 NS_IMPL_CYCLE_COLLECTION_UNLINK(mHighlightRegistry
)
2728 NS_IMPL_CYCLE_COLLECTION_UNLINK(mParser
)
2729 NS_IMPL_CYCLE_COLLECTION_UNLINK(mOnloadBlocker
)
2730 NS_IMPL_CYCLE_COLLECTION_UNLINK(mDOMImplementation
)
2731 NS_IMPL_CYCLE_COLLECTION_UNLINK(mImageMaps
)
2732 NS_IMPL_CYCLE_COLLECTION_UNLINK(mOrientationPendingPromise
)
2733 NS_IMPL_CYCLE_COLLECTION_UNLINK(mOriginalDocument
)
2734 NS_IMPL_CYCLE_COLLECTION_UNLINK(mCachedEncoder
)
2735 NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocumentTimeline
)
2736 NS_IMPL_CYCLE_COLLECTION_UNLINK(mScrollTimelineAnimationTracker
)
2737 NS_IMPL_CYCLE_COLLECTION_UNLINK(mTemplateContentsOwner
)
2738 NS_IMPL_CYCLE_COLLECTION_UNLINK(mChildrenCollection
)
2739 NS_IMPL_CYCLE_COLLECTION_UNLINK(mImages
);
2740 NS_IMPL_CYCLE_COLLECTION_UNLINK(mEmbeds
);
2741 NS_IMPL_CYCLE_COLLECTION_UNLINK(mLinks
);
2742 NS_IMPL_CYCLE_COLLECTION_UNLINK(mForms
);
2743 NS_IMPL_CYCLE_COLLECTION_UNLINK(mScripts
);
2744 NS_IMPL_CYCLE_COLLECTION_UNLINK(mApplets
);
2745 NS_IMPL_CYCLE_COLLECTION_UNLINK(mAnchors
);
2746 NS_IMPL_CYCLE_COLLECTION_UNLINK(mAnonymousContents
)
2747 NS_IMPL_CYCLE_COLLECTION_UNLINK(mCommandDispatcher
)
2748 NS_IMPL_CYCLE_COLLECTION_UNLINK(mFeaturePolicy
)
2749 NS_IMPL_CYCLE_COLLECTION_UNLINK(mPermissionDelegateHandler
)
2750 NS_IMPL_CYCLE_COLLECTION_UNLINK(mSuppressedEventListener
)
2751 NS_IMPL_CYCLE_COLLECTION_UNLINK(mPrototypeDocument
)
2752 NS_IMPL_CYCLE_COLLECTION_UNLINK(mMidasCommandManager
)
2753 NS_IMPL_CYCLE_COLLECTION_UNLINK(mAll
)
2754 NS_IMPL_CYCLE_COLLECTION_UNLINK(mActiveViewTransition
)
2755 NS_IMPL_CYCLE_COLLECTION_UNLINK(mReferrerInfo
)
2756 NS_IMPL_CYCLE_COLLECTION_UNLINK(mPreloadReferrerInfo
)
2758 if (tmp
->mDocGroup
&& tmp
->mDocGroup
->GetBrowsingContextGroup()) {
2759 tmp
->mDocGroup
->GetBrowsingContextGroup()->RemoveDocument(tmp
,
2762 tmp
->mDocGroup
= nullptr;
2764 if (tmp
->IsTopLevelContentDocument()) {
2765 RemoveToplevelLoadingDocument(tmp
);
2768 tmp
->mParentDocument
= nullptr;
2770 NS_IMPL_CYCLE_COLLECTION_UNLINK(mPreloadingImages
)
2772 NS_IMPL_CYCLE_COLLECTION_UNLINK(mIntersectionObservers
)
2774 if (tmp
->mListenerManager
) {
2775 tmp
->mListenerManager
->Disconnect();
2776 tmp
->UnsetFlags(NODE_HAS_LISTENERMANAGER
);
2777 tmp
->mListenerManager
= nullptr;
2780 if (tmp
->mStyleSheetSetList
) {
2781 tmp
->mStyleSheetSetList
->Disconnect();
2782 tmp
->mStyleSheetSetList
= nullptr;
2785 tmp
->mSubDocuments
= nullptr;
2787 NS_IMPL_CYCLE_COLLECTION_UNLINK(mFrameRequestManager
)
2789 DocumentOrShadowRoot::Unlink(tmp
);
2791 tmp
->mRadioGroupContainer
= nullptr;
2793 // Document has a pretty complex destructor, so we're going to
2794 // assume that *most* cycles you actually want to break somewhere
2795 // else, and not unlink an awful lot here.
2797 tmp
->mExpandoAndGeneration
.OwnerUnlinked();
2799 if (tmp
->mAnimationController
) {
2800 tmp
->mAnimationController
->Unlink();
2803 tmp
->mPendingTitleChangeEvent
.Revoke();
2805 if (tmp
->mCSSLoader
) {
2806 tmp
->mCSSLoader
->DropDocumentReference();
2807 NS_IMPL_CYCLE_COLLECTION_UNLINK(mCSSLoader
)
2810 if (tmp
->mScriptLoader
) {
2811 tmp
->mScriptLoader
->DropDocumentReference();
2812 NS_IMPL_CYCLE_COLLECTION_UNLINK(mScriptLoader
)
2815 // We own only the items in mDOMMediaQueryLists that have listeners;
2816 // this reference is managed by their AddListener and RemoveListener
2818 for (MediaQueryList
* mql
= tmp
->mDOMMediaQueryLists
.getFirst(); mql
;) {
2819 MediaQueryList
* next
=
2820 static_cast<LinkedListElement
<MediaQueryList
>*>(mql
)->getNext();
2825 tmp
->mPendingFrameStaticClones
.Clear();
2827 tmp
->mActiveLocks
.Clear();
2829 tmp
->mInUnlinkOrDeletion
= false;
2831 tmp
->UnregisterFromMemoryReportingForDataDocument();
2833 NS_IMPL_CYCLE_COLLECTION_UNLINK(mL10nProtoElements
)
2834 NS_IMPL_CYCLE_COLLECTION_UNLINK_WEAK_PTR
2835 NS_IMPL_CYCLE_COLLECTION_UNLINK_WEAK_REFERENCE
2836 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
2838 nsresult
Document::Init(nsIPrincipal
* aPrincipal
,
2839 nsIPrincipal
* aPartitionedPrincipal
) {
2840 if (mCSSLoader
|| mStyleImageLoader
|| mNodeInfoManager
|| mScriptLoader
) {
2841 return NS_ERROR_ALREADY_INITIALIZED
;
2844 // Force initialization.
2845 mOnloadBlocker
= new OnloadBlocker();
2846 mStyleImageLoader
= new css::ImageLoader(this);
2848 mNodeInfoManager
= new nsNodeInfoManager(this, aPrincipal
);
2850 // mNodeInfo keeps NodeInfoManager alive!
2851 mNodeInfo
= mNodeInfoManager
->GetDocumentNodeInfo();
2852 NS_ENSURE_TRUE(mNodeInfo
, NS_ERROR_OUT_OF_MEMORY
);
2853 MOZ_ASSERT(mNodeInfo
->NodeType() == DOCUMENT_NODE
,
2854 "Bad NodeType in aNodeInfo");
2856 NS_ASSERTION(OwnerDoc() == this, "Our nodeinfo is busted!");
2858 mCSSLoader
= new css::Loader(this);
2859 // Assume we're not quirky, until we know otherwise
2860 mCSSLoader
->SetCompatibilityMode(eCompatibility_FullStandards
);
2862 // If after creation the owner js global is not set for a document
2863 // we use the default compartment for this document, instead of creating
2864 // wrapper in some random compartment when the document is exposed to js
2866 nsCOMPtr
<nsIGlobalObject
> global
=
2867 xpc::NativeGlobal(xpc::PrivilegedJunkScope());
2868 NS_ENSURE_TRUE(global
, NS_ERROR_FAILURE
);
2869 mScopeObject
= do_GetWeakReference(global
);
2870 MOZ_ASSERT(mScopeObject
);
2872 mScriptLoader
= new dom::ScriptLoader(this);
2874 // we need to create a policy here so getting the policy within
2875 // ::Policy() can *always* return a non null policy
2876 mFeaturePolicy
= new dom::FeaturePolicy(this);
2877 mFeaturePolicy
->SetDefaultOrigin(NodePrincipal());
2880 SetPrincipals(aPrincipal
, aPartitionedPrincipal
);
2882 RecomputeResistFingerprinting();
2888 void Document::RemoveAllProperties() { PropertyTable().RemoveAllProperties(); }
2890 void Document::RemoveAllPropertiesFor(nsINode
* aNode
) {
2891 PropertyTable().RemoveAllPropertiesFor(aNode
);
2894 void Document::Reset(nsIChannel
* aChannel
, nsILoadGroup
* aLoadGroup
) {
2895 nsCOMPtr
<nsIURI
> uri
;
2896 nsCOMPtr
<nsIPrincipal
> principal
;
2897 nsCOMPtr
<nsIPrincipal
> partitionedPrincipal
;
2899 mIsInPrivateBrowsing
= NS_UsePrivateBrowsing(aChannel
);
2901 // Note: this code is duplicated in PrototypeDocumentContentSink::Init and
2902 // nsScriptSecurityManager::GetChannelResultPrincipals.
2903 // Note: this should match the uri used for the OnNewURI call in
2904 // nsDocShell::CreateDocumentViewer.
2905 NS_GetFinalChannelURI(aChannel
, getter_AddRefs(uri
));
2907 nsIScriptSecurityManager
* securityManager
=
2908 nsContentUtils::GetSecurityManager();
2909 if (securityManager
) {
2910 securityManager
->GetChannelResultPrincipals(
2911 aChannel
, getter_AddRefs(principal
),
2912 getter_AddRefs(partitionedPrincipal
));
2916 bool equal
= principal
->Equals(partitionedPrincipal
);
2918 principal
= MaybeDowngradePrincipal(principal
);
2920 partitionedPrincipal
= principal
;
2922 partitionedPrincipal
= MaybeDowngradePrincipal(partitionedPrincipal
);
2925 ResetToURI(uri
, aLoadGroup
, principal
, partitionedPrincipal
);
2927 // Note that, since mTiming does not change during a reset, the
2928 // navigationStart time remains unchanged and therefore any future new
2929 // timeline will have the same global clock time as the old one.
2930 mDocumentTimeline
= nullptr;
2932 if (nsCOMPtr
<nsIPropertyBag2
> bag
= do_QueryInterface(aChannel
)) {
2933 if (nsCOMPtr
<nsIURI
> baseURI
= do_GetProperty(bag
, u
"baseURI"_ns
)) {
2934 mDocumentBaseURI
= baseURI
.forget();
2935 mChromeXHRDocBaseURI
= nullptr;
2939 mChannel
= aChannel
;
2940 RecomputeResistFingerprinting();
2941 MaybeRecomputePartitionKey();
2944 void Document::DisconnectNodeTree() {
2945 // Delete references to sub-documents and kill the subdocument map,
2946 // if any. This is not strictly needed, but makes the node tree
2947 // teardown a bit faster.
2948 mSubDocuments
= nullptr;
2950 bool oldVal
= mInUnlinkOrDeletion
;
2951 mInUnlinkOrDeletion
= true;
2952 { // Scope for update
2953 MOZ_AUTO_DOC_UPDATE(this, true);
2955 // Destroy link map now so we don't waste time removing
2957 DestroyElementMaps();
2959 // Invalidate cached array of child nodes
2960 InvalidateChildNodes();
2962 while (nsCOMPtr
<nsIContent
> content
= GetLastChild()) {
2963 nsMutationGuard::DidMutate();
2964 MutationObservers::NotifyContentWillBeRemoved(this, content
, nullptr);
2965 DisconnectChild(content
);
2966 if (content
== mCachedRootElement
) {
2967 // Immediately clear mCachedRootElement, now that it's been removed
2968 // from mChildren, so that GetRootElement() will stop returning this
2970 mCachedRootElement
= nullptr;
2972 content
->UnbindFromTree();
2974 MOZ_ASSERT(!mCachedRootElement
,
2975 "After removing all children, there should be no root elem");
2977 mInUnlinkOrDeletion
= oldVal
;
2980 void Document::ResetToURI(nsIURI
* aURI
, nsILoadGroup
* aLoadGroup
,
2981 nsIPrincipal
* aPrincipal
,
2982 nsIPrincipal
* aPartitionedPrincipal
) {
2983 MOZ_ASSERT(aURI
, "Null URI passed to ResetToURI");
2984 MOZ_ASSERT(!!aPrincipal
== !!aPartitionedPrincipal
);
2986 MOZ_LOG(gDocumentLeakPRLog
, LogLevel::Debug
,
2987 ("DOCUMENT %p ResetToURI %s", this, aURI
->GetSpecOrDefault().get()));
2989 mSecurityInfo
= nullptr;
2991 nsCOMPtr
<nsILoadGroup
> group
= do_QueryReferent(mDocumentLoadGroup
);
2992 if (!aLoadGroup
|| group
!= aLoadGroup
) {
2993 mDocumentLoadGroup
= nullptr;
2996 DisconnectNodeTree();
2998 // Reset our stylesheets
2999 ResetStylesheetsToURI(aURI
);
3001 // Release the listener manager
3002 if (mListenerManager
) {
3003 mListenerManager
->Disconnect();
3004 mListenerManager
= nullptr;
3007 // Release the stylesheets list.
3008 mDOMStyleSheets
= nullptr;
3010 // Release our principal after tearing down the document, rather than before.
3011 // This ensures that, during teardown, the document and the dying window
3012 // (which already nulled out its document pointer and cached the principal)
3013 // have matching principals.
3014 SetPrincipals(nullptr, nullptr);
3016 // Clear the original URI so SetDocumentURI sets it.
3017 mOriginalURI
= nullptr;
3019 SetDocumentURI(aURI
);
3020 mChromeXHRDocURI
= nullptr;
3021 // If mDocumentBaseURI is null, Document::GetBaseURI() returns
3023 mDocumentBaseURI
= nullptr;
3024 mChromeXHRDocBaseURI
= nullptr;
3027 nsCOMPtr
<nsIInterfaceRequestor
> callbacks
;
3028 aLoadGroup
->GetNotificationCallbacks(getter_AddRefs(callbacks
));
3030 nsCOMPtr
<nsILoadContext
> loadContext
= do_GetInterface(callbacks
);
3032 // This is asserting that if we previously set mIsInPrivateBrowsing
3033 // to true from the channel in Document::Reset, that the loadContext
3034 // also believes it to be true.
3035 MOZ_ASSERT(!mIsInPrivateBrowsing
||
3036 mIsInPrivateBrowsing
== loadContext
->UsePrivateBrowsing());
3037 mIsInPrivateBrowsing
= loadContext
->UsePrivateBrowsing();
3041 mDocumentLoadGroup
= do_GetWeakReference(aLoadGroup
);
3042 // there was an assertion here that aLoadGroup was not null. This
3043 // is no longer valid: nsDocShell::SetDocument does not create a
3044 // load group, and it works just fine
3046 // XXXbz what does "just fine" mean exactly? And given that there
3047 // is no nsDocShell::SetDocument, what is this talking about?
3049 if (IsContentDocument()) {
3050 // Inform the associated request context about this load start so
3051 // any of its internal load progress flags gets reset.
3052 nsCOMPtr
<nsIRequestContextService
> rcsvc
=
3053 net::RequestContextService::GetOrCreate();
3055 nsCOMPtr
<nsIRequestContext
> rc
;
3056 rcsvc
->GetRequestContextFromLoadGroup(aLoadGroup
, getter_AddRefs(rc
));
3064 mLastModified
.Truncate();
3065 // XXXbz I guess we're assuming that the caller will either pass in
3066 // a channel with a useful type or call SetContentType?
3067 SetContentType(""_ns
);
3068 mContentLanguage
= nullptr;
3069 mBaseTarget
.Truncate();
3071 mXMLDeclarationBits
= 0;
3073 // Now get our new principal
3075 SetPrincipals(aPrincipal
, aPartitionedPrincipal
);
3077 nsIScriptSecurityManager
* securityManager
=
3078 nsContentUtils::GetSecurityManager();
3079 if (securityManager
) {
3080 nsCOMPtr
<nsILoadContext
> loadContext(mDocumentContainer
);
3082 if (!loadContext
&& aLoadGroup
) {
3083 nsCOMPtr
<nsIInterfaceRequestor
> cbs
;
3084 aLoadGroup
->GetNotificationCallbacks(getter_AddRefs(cbs
));
3085 loadContext
= do_GetInterface(cbs
);
3088 MOZ_ASSERT(loadContext
,
3089 "must have a load context or pass in an explicit principal");
3091 nsCOMPtr
<nsIPrincipal
> principal
;
3092 nsresult rv
= securityManager
->GetLoadContextContentPrincipal(
3093 mDocumentURI
, loadContext
, getter_AddRefs(principal
));
3094 if (NS_SUCCEEDED(rv
)) {
3095 SetPrincipals(principal
, principal
);
3101 mFontFaceSet
->RefreshStandardFontLoadPrincipal();
3104 // Refresh the principal on the realm.
3105 if (nsPIDOMWindowInner
* win
= GetInnerWindow()) {
3106 nsGlobalWindowInner::Cast(win
)->RefreshRealmPrincipal();
3110 already_AddRefed
<nsIPrincipal
> Document::MaybeDowngradePrincipal(
3111 nsIPrincipal
* aPrincipal
) {
3116 // We can't load a document with an expanded principal. If we're given one,
3117 // automatically downgrade it to the last principal it subsumes (which is the
3118 // extension principal, in the case of extension content scripts).
3119 auto* basePrin
= BasePrincipal::Cast(aPrincipal
);
3120 if (basePrin
->Is
<ExpandedPrincipal
>()) {
3121 MOZ_DIAGNOSTIC_CRASH(
3122 "Should never try to create a document with "
3123 "an expanded principal");
3125 auto* expanded
= basePrin
->As
<ExpandedPrincipal
>();
3126 return do_AddRef(expanded
->AllowList().LastElement());
3129 if (aPrincipal
->IsSystemPrincipal() && mDocumentContainer
) {
3130 // We basically want the parent document here, but because this is very
3131 // early in the load, GetInProcessParentDocument() returns null, so we use
3132 // the docshell hierarchy to get this information instead.
3133 if (RefPtr
<BrowsingContext
> parent
=
3134 mDocumentContainer
->GetBrowsingContext()->GetParent()) {
3135 auto* parentWin
= nsGlobalWindowOuter::Cast(parent
->GetDOMWindow());
3136 if (!parentWin
|| !parentWin
->GetPrincipal()->IsSystemPrincipal()) {
3137 nsCOMPtr
<nsIPrincipal
> nullPrincipal
=
3138 NullPrincipal::CreateWithoutOriginAttributes();
3139 return nullPrincipal
.forget();
3143 nsCOMPtr
<nsIPrincipal
> principal(aPrincipal
);
3144 return principal
.forget();
3147 size_t Document::FindDocStyleSheetInsertionPoint(const StyleSheet
& aSheet
) {
3148 nsStyleSheetService
* sheetService
= nsStyleSheetService::GetInstance();
3149 ServoStyleSet
& styleSet
= EnsureStyleSet();
3151 // lowest index first
3152 int32_t newDocIndex
= StyleOrderIndexOfSheet(aSheet
);
3154 size_t count
= styleSet
.SheetCount(StyleOrigin::Author
);
3156 for (; index
< count
; index
++) {
3157 auto* sheet
= styleSet
.SheetAt(StyleOrigin::Author
, index
);
3159 int32_t sheetDocIndex
= StyleOrderIndexOfSheet(*sheet
);
3160 if (sheetDocIndex
> newDocIndex
) {
3164 // If the sheet is not owned by the document it can be an author
3165 // sheet registered at nsStyleSheetService or an additional author
3166 // sheet on the document, which means the new
3167 // doc sheet should end up before it.
3168 if (sheetDocIndex
< 0) {
3170 auto& authorSheets
= *sheetService
->AuthorStyleSheets();
3171 if (authorSheets
.IndexOf(sheet
) != authorSheets
.NoIndex
) {
3175 if (sheet
== GetFirstAdditionalAuthorSheet()) {
3184 void Document::ResetStylesheetsToURI(nsIURI
* aURI
) {
3187 ClearAdoptedStyleSheets();
3188 ServoStyleSet
& styleSet
= EnsureStyleSet();
3190 auto ClearSheetList
= [&](nsTArray
<RefPtr
<StyleSheet
>>& aSheetList
) {
3191 for (auto& sheet
: Reversed(aSheetList
)) {
3192 sheet
->ClearAssociatedDocumentOrShadowRoot();
3193 if (mStyleSetFilled
) {
3194 styleSet
.RemoveStyleSheet(*sheet
);
3199 ClearSheetList(mStyleSheets
);
3200 for (auto& sheets
: mAdditionalSheets
) {
3201 ClearSheetList(sheets
);
3203 if (mStyleSetFilled
) {
3204 if (auto* ss
= nsStyleSheetService::GetInstance()) {
3205 for (auto& sheet
: Reversed(*ss
->AuthorStyleSheets())) {
3206 MOZ_ASSERT(!sheet
->GetAssociatedDocumentOrShadowRoot());
3207 if (sheet
->IsApplicable()) {
3208 styleSet
.RemoveStyleSheet(*sheet
);
3214 // Now reset our inline style and attribute sheets.
3215 if (mAttributeStyles
) {
3216 mAttributeStyles
->Reset();
3217 mAttributeStyles
->SetOwningDocument(this);
3219 mAttributeStyles
= new AttributeStyles(this);
3222 if (mStyleSetFilled
) {
3223 FillStyleSetDocumentSheets();
3225 if (styleSet
.StyleSheetsHaveChanged()) {
3226 ApplicableStylesChanged();
3231 static void AppendSheetsToStyleSet(
3232 ServoStyleSet
* aStyleSet
, const nsTArray
<RefPtr
<StyleSheet
>>& aSheets
) {
3233 for (StyleSheet
* sheet
: Reversed(aSheets
)) {
3234 aStyleSet
->AppendStyleSheet(*sheet
);
3238 void Document::FillStyleSetUserAndUASheets() {
3239 // Make sure this does the same thing as PresShell::Add{User,Agent}Sheet wrt
3242 // The document will fill in the document sheets when we create the presshell
3243 auto* cache
= GlobalStyleSheetCache::Singleton();
3245 nsStyleSheetService
* sheetService
= nsStyleSheetService::GetInstance();
3246 MOZ_ASSERT(sheetService
,
3247 "should never be creating a StyleSet after the style sheet "
3248 "service has gone");
3250 ServoStyleSet
& styleSet
= EnsureStyleSet();
3251 for (StyleSheet
* sheet
: *sheetService
->UserStyleSheets()) {
3252 styleSet
.AppendStyleSheet(*sheet
);
3255 StyleSheet
* sheet
= IsInChromeDocShell() ? cache
->GetUserChromeSheet()
3256 : cache
->GetUserContentSheet();
3258 styleSet
.AppendStyleSheet(*sheet
);
3261 styleSet
.AppendStyleSheet(*cache
->UASheet());
3263 if (MOZ_LIKELY(NodeInfoManager()->MathMLEnabled())) {
3264 styleSet
.AppendStyleSheet(*cache
->MathMLSheet());
3267 if (MOZ_LIKELY(NodeInfoManager()->SVGEnabled())) {
3268 styleSet
.AppendStyleSheet(*cache
->SVGSheet());
3271 styleSet
.AppendStyleSheet(*cache
->HTMLSheet());
3273 if (nsLayoutUtils::ShouldUseNoFramesSheet(this)) {
3274 styleSet
.AppendStyleSheet(*cache
->NoFramesSheet());
3277 styleSet
.AppendStyleSheet(*cache
->CounterStylesSheet());
3279 // Only load the full XUL sheet if we'll need it.
3280 if (LoadsFullXULStyleSheetUpFront()) {
3281 styleSet
.AppendStyleSheet(*cache
->XULSheet());
3284 styleSet
.AppendStyleSheet(*cache
->FormsSheet());
3285 styleSet
.AppendStyleSheet(*cache
->ScrollbarsSheet());
3287 for (StyleSheet
* sheet
: *sheetService
->AgentStyleSheets()) {
3288 styleSet
.AppendStyleSheet(*sheet
);
3291 MOZ_ASSERT(!mQuirkSheetAdded
);
3292 if (NeedsQuirksSheet()) {
3293 styleSet
.AppendStyleSheet(*cache
->QuirkSheet());
3294 mQuirkSheetAdded
= true;
3298 void Document::FillStyleSet() {
3299 MOZ_ASSERT(!mStyleSetFilled
);
3300 FillStyleSetUserAndUASheets();
3301 FillStyleSetDocumentSheets();
3302 mStyleSetFilled
= true;
3305 void Document::FillStyleSetDocumentSheets() {
3306 ServoStyleSet
& styleSet
= EnsureStyleSet();
3307 MOZ_ASSERT(styleSet
.SheetCount(StyleOrigin::Author
) == 0,
3308 "Style set already has document sheets?");
3310 // Sheets are added in reverse order to avoid worst-case time complexity when
3311 // looking up the index of a sheet.
3313 // Note that usually appending is faster (rebuilds less stuff in the
3314 // styleset), but in this case it doesn't matter since we're filling the
3315 // styleset from scratch anyway.
3316 for (StyleSheet
* sheet
: Reversed(mStyleSheets
)) {
3317 if (sheet
->IsApplicable()) {
3318 styleSet
.AddDocStyleSheet(*sheet
);
3322 EnumerateUniqueAdoptedStyleSheetsBackToFront([&](StyleSheet
& aSheet
) {
3323 if (aSheet
.IsApplicable()) {
3324 styleSet
.AddDocStyleSheet(aSheet
);
3328 nsStyleSheetService
* sheetService
= nsStyleSheetService::GetInstance();
3329 for (StyleSheet
* sheet
: *sheetService
->AuthorStyleSheets()) {
3330 styleSet
.AppendStyleSheet(*sheet
);
3333 AppendSheetsToStyleSet(&styleSet
, mAdditionalSheets
[eAgentSheet
]);
3334 AppendSheetsToStyleSet(&styleSet
, mAdditionalSheets
[eUserSheet
]);
3335 AppendSheetsToStyleSet(&styleSet
, mAdditionalSheets
[eAuthorSheet
]);
3338 void Document::CompatibilityModeChanged() {
3339 MOZ_ASSERT(IsHTMLOrXHTML());
3340 CSSLoader()->SetCompatibilityMode(mCompatMode
);
3343 mStyleSet
->CompatibilityModeChanged();
3345 if (!mStyleSetFilled
) {
3346 MOZ_ASSERT(!mQuirkSheetAdded
);
3350 MOZ_ASSERT(mStyleSet
);
3351 if (PresShell
* presShell
= GetPresShell()) {
3352 // Selectors may have become case-sensitive / case-insensitive, the stylist
3353 // has already performed the relevant invalidation.
3354 presShell
->EnsureStyleFlush();
3356 if (mQuirkSheetAdded
== NeedsQuirksSheet()) {
3359 auto* cache
= GlobalStyleSheetCache::Singleton();
3360 StyleSheet
* sheet
= cache
->QuirkSheet();
3361 if (mQuirkSheetAdded
) {
3362 mStyleSet
->RemoveStyleSheet(*sheet
);
3364 mStyleSet
->AppendStyleSheet(*sheet
);
3366 mQuirkSheetAdded
= !mQuirkSheetAdded
;
3367 ApplicableStylesChanged();
3370 void Document::SetCompatibilityMode(nsCompatibility aMode
) {
3371 NS_ASSERTION(IsHTMLDocument() || aMode
== eCompatibility_FullStandards
,
3372 "Bad compat mode for XHTML document!");
3374 if (mCompatMode
== aMode
) {
3377 mCompatMode
= aMode
;
3378 CompatibilityModeChanged();
3379 // Trigger recomputation of the nsViewportInfo the next time it's queried.
3380 mViewportType
= Unknown
;
3383 static void WarnIfSandboxIneffective(nsIDocShell
* aDocShell
,
3384 uint32_t aSandboxFlags
,
3385 nsIChannel
* aChannel
) {
3386 // If the document permits allow-top-navigation and
3387 // allow-top-navigation-by-user-activation this will permit all top
3389 if (aSandboxFlags
!= SANDBOXED_NONE
&&
3390 !(aSandboxFlags
& SANDBOXED_TOPLEVEL_NAVIGATION
) &&
3391 !(aSandboxFlags
& SANDBOXED_TOPLEVEL_NAVIGATION_USER_ACTIVATION
)) {
3392 nsContentUtils::ReportToConsole(
3393 nsIScriptError::warningFlag
, "Iframe Sandbox"_ns
,
3394 aDocShell
->GetDocument(), nsContentUtils::eSECURITY_PROPERTIES
,
3395 "BothAllowTopNavigationAndUserActivationPresent");
3397 // If the document is sandboxed (via the HTML5 iframe sandbox
3398 // attribute) and both the allow-scripts and allow-same-origin
3399 // keywords are supplied, the sandboxed document can call into its
3400 // parent document and remove its sandboxing entirely - we print a
3401 // warning to the web console in this case.
3402 if (aSandboxFlags
& SANDBOXED_NAVIGATION
&&
3403 !(aSandboxFlags
& SANDBOXED_SCRIPTS
) &&
3404 !(aSandboxFlags
& SANDBOXED_ORIGIN
)) {
3405 RefPtr
<BrowsingContext
> bc
= aDocShell
->GetBrowsingContext();
3406 MOZ_ASSERT(bc
->IsInProcess());
3408 RefPtr
<BrowsingContext
> parentBC
= bc
->GetParent();
3409 if (!parentBC
|| !parentBC
->IsInProcess()) {
3410 // If parent document is not in process, then by construction it
3411 // cannot be same origin.
3415 // Don't warn if our parent is not the top-level document.
3416 if (!parentBC
->IsTopContent()) {
3420 nsCOMPtr
<nsIDocShell
> parentDocShell
= parentBC
->GetDocShell();
3421 MOZ_ASSERT(parentDocShell
);
3423 nsCOMPtr
<nsIChannel
> parentChannel
;
3424 parentDocShell
->GetCurrentDocumentChannel(getter_AddRefs(parentChannel
));
3425 if (!parentChannel
) {
3428 nsresult rv
= nsContentUtils::CheckSameOrigin(aChannel
, parentChannel
);
3429 if (NS_FAILED(rv
)) {
3433 nsCOMPtr
<Document
> parentDocument
= parentDocShell
->GetDocument();
3434 nsCOMPtr
<nsIURI
> iframeUri
;
3435 parentChannel
->GetURI(getter_AddRefs(iframeUri
));
3436 nsContentUtils::ReportToConsole(
3437 nsIScriptError::warningFlag
, "Iframe Sandbox"_ns
, parentDocument
,
3438 nsContentUtils::eSECURITY_PROPERTIES
,
3439 "BothAllowScriptsAndSameOriginPresent", nsTArray
<nsString
>(),
3440 SourceLocation(iframeUri
.get()));
3444 bool Document::IsSynthesized() {
3445 nsCOMPtr
<nsILoadInfo
> loadInfo
= mChannel
? mChannel
->LoadInfo() : nullptr;
3446 return loadInfo
&& loadInfo
->GetServiceWorkerTaintingSynthesized();
3450 bool Document::IsCallerChromeOrAddon(JSContext
* aCx
, JSObject
* aObject
) {
3451 nsIPrincipal
* principal
= nsContentUtils::SubjectPrincipal(aCx
);
3452 return principal
&& (principal
->IsSystemPrincipal() ||
3453 principal
->GetIsAddonOrExpandedAddonPrincipal());
3456 static void CheckIsBadPolicy(nsILoadInfo::CrossOriginOpenerPolicy aPolicy
,
3457 BrowsingContext
* aContext
, nsIChannel
* aChannel
) {
3458 #if defined(EARLY_BETA_OR_EARLIER)
3460 nsILoadInfo::OPENER_POLICY_SAME_ORIGIN_EMBEDDER_POLICY_REQUIRE_CORP
;
3462 if (aContext
->GetOpenerPolicy() == aPolicy
||
3463 (aContext
->GetOpenerPolicy() != requireCORP
&& aPolicy
!= requireCORP
)) {
3467 nsCOMPtr
<nsIURI
> uri
;
3468 bool hasURI
= NS_SUCCEEDED(aChannel
->GetOriginalURI(getter_AddRefs(uri
)));
3470 bool isViewSource
= hasURI
&& uri
->SchemeIs("view-source");
3472 nsCString contentType
;
3473 nsCOMPtr
<nsIPropertyBag2
> bag
= do_QueryInterface(aChannel
);
3474 bool isPDFJS
= bag
&&
3475 NS_SUCCEEDED(bag
->GetPropertyAsACString(u
"contentType"_ns
,
3477 contentType
.EqualsLiteral(APPLICATION_PDF
);
3479 MOZ_DIAGNOSTIC_ASSERT(!isViewSource
,
3480 "Bug 1834864: Assert due to view-source.");
3481 MOZ_DIAGNOSTIC_ASSERT(!isPDFJS
, "Bug 1834864: Assert due to pdfjs.");
3482 MOZ_DIAGNOSTIC_ASSERT(aPolicy
== requireCORP
,
3483 "Assert due to clearing REQUIRE_CORP.");
3484 MOZ_DIAGNOSTIC_ASSERT(aContext
->GetOpenerPolicy() == requireCORP
,
3485 "Assert due to setting REQUIRE_CORP.");
3486 #endif // defined(EARLY_BETA_OR_EARLIER)
3489 nsresult
Document::StartDocumentLoad(const char* aCommand
, nsIChannel
* aChannel
,
3490 nsILoadGroup
* aLoadGroup
,
3491 nsISupports
* aContainer
,
3492 nsIStreamListener
** aDocListener
,
3494 if (MOZ_LOG_TEST(gDocumentLeakPRLog
, LogLevel::Debug
)) {
3495 nsCOMPtr
<nsIURI
> uri
;
3496 aChannel
->GetURI(getter_AddRefs(uri
));
3497 MOZ_LOG(gDocumentLeakPRLog
, LogLevel::Debug
,
3498 ("DOCUMENT %p StartDocumentLoad %s", this,
3499 uri
? uri
->GetSpecOrDefault().get() : ""));
3502 MOZ_ASSERT(GetReadyStateEnum() == Document::READYSTATE_UNINITIALIZED
,
3504 SetReadyStateInternal(READYSTATE_LOADING
);
3506 if (nsCRT::strcmp(kLoadAsData
, aCommand
) == 0) {
3507 mLoadedAsData
= true;
3508 SetLoadedAsData(true, /* aConsiderForMemoryReporting */ true);
3509 // We need to disable script & style loading in this case.
3510 // We leave them disabled even in EndLoad(), and let anyone
3511 // who puts the document on display to worry about enabling.
3513 // Do not load/process scripts when loading as data
3514 ScriptLoader()->SetEnabled(false);
3517 CSSLoader()->SetEnabled(
3518 false); // Do not load/process styles when loading as data
3519 } else if (nsCRT::strcmp("external-resource", aCommand
) == 0) {
3520 // Allow CSS, but not scripts
3521 ScriptLoader()->SetEnabled(false);
3524 mMayStartLayout
= false;
3525 MOZ_ASSERT(!mReadyForIdle
,
3526 "We should never hit DOMContentLoaded before this point");
3529 Reset(aChannel
, aLoadGroup
);
3532 nsAutoCString contentType
;
3533 nsCOMPtr
<nsIPropertyBag2
> bag
= do_QueryInterface(aChannel
);
3534 if ((bag
&& NS_SUCCEEDED(bag
->GetPropertyAsACString(u
"contentType"_ns
,
3536 NS_SUCCEEDED(aChannel
->GetContentType(contentType
))) {
3537 // XXX this is only necessary for viewsource:
3538 nsACString::const_iterator start
, end
, semicolon
;
3539 contentType
.BeginReading(start
);
3540 contentType
.EndReading(end
);
3542 FindCharInReadable(';', semicolon
, end
);
3543 SetContentType(Substring(start
, semicolon
));
3546 RetrieveRelevantHeaders(aChannel
);
3548 mChannel
= aChannel
;
3549 RecomputeResistFingerprinting();
3550 nsCOMPtr
<nsIInputStreamChannel
> inStrmChan
= do_QueryInterface(mChannel
);
3552 bool isSrcdocChannel
;
3553 inStrmChan
->GetIsSrcdocChannel(&isSrcdocChannel
);
3554 if (isSrcdocChannel
) {
3555 mIsSrcdocDocument
= true;
3560 nsLoadFlags loadFlags
;
3561 mChannel
->GetLoadFlags(&loadFlags
);
3562 bool isDocument
= false;
3563 mChannel
->GetIsDocument(&isDocument
);
3564 if (loadFlags
& nsIRequest::LOAD_DOCUMENT_NEEDS_COOKIE
&& isDocument
&&
3565 IsSynthesized() && XRE_IsContentProcess()) {
3566 ContentChild::UpdateCookieStatus(mChannel
);
3569 // Store the security info for future use.
3570 mChannel
->GetSecurityInfo(getter_AddRefs(mSecurityInfo
));
3573 // If this document is being loaded by a docshell, copy its sandbox flags
3574 // to the document, and store the fullscreen enabled flag. These are
3575 // immutable after being set here.
3576 nsCOMPtr
<nsIDocShell
> docShell
= do_QueryInterface(aContainer
);
3578 // If this is an error page, don't inherit sandbox flags
3579 nsCOMPtr
<nsILoadInfo
> loadInfo
= aChannel
->LoadInfo();
3580 if (docShell
&& !loadInfo
->GetLoadErrorPage()) {
3581 mSandboxFlags
= loadInfo
->GetSandboxFlags();
3582 WarnIfSandboxIneffective(docShell
, mSandboxFlags
, GetChannel());
3585 // Set the opener policy for the top level content document.
3586 nsCOMPtr
<nsIHttpChannelInternal
> httpChan
= do_QueryInterface(mChannel
);
3587 nsILoadInfo::CrossOriginOpenerPolicy policy
=
3588 nsILoadInfo::OPENER_POLICY_UNSAFE_NONE
;
3589 if (IsTopLevelContentDocument() && httpChan
&&
3590 NS_SUCCEEDED(httpChan
->GetCrossOriginOpenerPolicy(&policy
)) && docShell
&&
3591 docShell
->GetBrowsingContext()) {
3592 CheckIsBadPolicy(policy
, docShell
->GetBrowsingContext(), aChannel
);
3594 // Setting the opener policy on a discarded context has no effect.
3595 Unused
<< docShell
->GetBrowsingContext()->SetOpenerPolicy(policy
);
3598 // The CSP directives upgrade-insecure-requests as well as
3599 // block-all-mixed-content not only apply to the toplevel document,
3600 // but also to nested documents. The loadInfo of a subdocument
3601 // load already holds the correct flag, so let's just set it here
3602 // on the document. Please note that we set the appropriate preload
3603 // bits just for the sake of completeness here, because the preloader
3604 // does not reach into subdocuments.
3605 mUpgradeInsecureRequests
= loadInfo
->GetUpgradeInsecureRequests();
3606 mUpgradeInsecurePreloads
= mUpgradeInsecureRequests
;
3607 mBlockAllMixedContent
= loadInfo
->GetBlockAllMixedContent();
3608 mBlockAllMixedContentPreloads
= mBlockAllMixedContent
;
3610 // HTTPS-Only Mode flags
3611 // The HTTPS_ONLY_EXEMPT flag of the HTTPS-Only state gets propagated to all
3612 // sub-resources and sub-documents.
3613 mHttpsOnlyStatus
= loadInfo
->GetHttpsOnlyStatus();
3615 nsresult rv
= InitReferrerInfo(aChannel
);
3616 NS_ENSURE_SUCCESS(rv
, rv
);
3618 rv
= InitCOEP(aChannel
);
3619 NS_ENSURE_SUCCESS(rv
, rv
);
3621 // HACK: Calling EnsureIPCPoliciesRead() here will parse the CSP using the
3622 // context's current mSelfURI (which is still the previous mSelfURI),
3623 // bypassing some internal bugs with 'self' and iframe inheritance.
3624 // Not calling it here results in the mSelfURI being the current mSelfURI and
3625 // not the previous which breaks said inheritance.
3626 // https://bugzilla.mozilla.org/show_bug.cgi?id=1793560#ch-8
3627 nsCOMPtr
<nsIContentSecurityPolicy
> cspToInherit
= loadInfo
->GetCspToInherit();
3629 cspToInherit
->EnsureIPCPoliciesRead();
3632 rv
= InitCSP(aChannel
);
3633 NS_ENSURE_SUCCESS(rv
, rv
);
3635 rv
= InitDocPolicy(aChannel
);
3636 NS_ENSURE_SUCCESS(rv
, rv
);
3638 // Initialize FeaturePolicy
3639 rv
= InitFeaturePolicy(aChannel
);
3640 NS_ENSURE_SUCCESS(rv
, rv
);
3642 rv
= loadInfo
->GetCookieJarSettings(getter_AddRefs(mCookieJarSettings
));
3643 NS_ENSURE_SUCCESS(rv
, rv
);
3645 MaybeRecomputePartitionKey();
3647 // Generally XFO and CSP frame-ancestors is handled within
3648 // DocumentLoadListener. However, the DocumentLoadListener can not handle
3649 // object and embed. Until then we have to enforce it here (See Bug 1646899).
3650 nsContentPolicyType internalContentType
=
3651 loadInfo
->InternalContentPolicyType();
3652 if (internalContentType
== nsIContentPolicy::TYPE_INTERNAL_OBJECT
||
3653 internalContentType
== nsIContentPolicy::TYPE_INTERNAL_EMBED
) {
3654 nsContentSecurityUtils::PerformCSPFrameAncestorAndXFOCheck(aChannel
);
3657 aChannel
->GetStatus(&status
);
3658 if (status
== NS_ERROR_XFO_VIOLATION
) {
3659 // stop! ERROR page!
3660 // But before we have to reset the principal of the document
3661 // because the onload() event fires before the error page
3662 // is displayed and we do not want the enclosing document
3663 // to access the contentDocument.
3664 RefPtr
<NullPrincipal
> nullPrincipal
=
3665 NullPrincipal::CreateWithInheritedAttributes(NodePrincipal());
3666 // Before calling SetPrincipals() we should ensure that mFontFaceSet
3667 // and also GetInnerWindow() is still null at this point, before
3668 // we can fix Bug 1614735: Evaluate calls to SetPrincipal
3669 // within Document.cpp
3670 MOZ_ASSERT(!mFontFaceSet
&& !GetInnerWindow());
3671 SetPrincipals(nullPrincipal
, nullPrincipal
);
3678 void Document::SetLoadedAsData(bool aLoadedAsData
,
3679 bool aConsiderForMemoryReporting
) {
3680 mLoadedAsData
= aLoadedAsData
;
3681 if (aConsiderForMemoryReporting
) {
3682 nsIGlobalObject
* global
= GetScopeObject();
3684 if (nsPIDOMWindowInner
* window
= global
->GetAsInnerWindow()) {
3685 nsGlobalWindowInner::Cast(window
)
3686 ->RegisterDataDocumentForMemoryReporting(this);
3692 nsIContentSecurityPolicy
* Document::GetCsp() const { return mCSP
; }
3694 void Document::SetCsp(nsIContentSecurityPolicy
* aCSP
) {
3696 mHasPolicyWithRequireTrustedTypesForDirective
=
3697 aCSP
&& aCSP
->GetRequireTrustedTypesForDirectiveState() !=
3698 RequireTrustedTypesForDirectiveState::NONE
;
3701 nsIContentSecurityPolicy
* Document::GetPreloadCsp() const {
3705 void Document::SetPreloadCsp(nsIContentSecurityPolicy
* aPreloadCSP
) {
3706 mPreloadCSP
= aPreloadCSP
;
3709 void Document::GetCspJSON(nsString
& aJSON
) {
3713 dom::CSPPolicies jsonPolicies
;
3714 jsonPolicies
.ToJSON(aJSON
);
3717 mCSP
->ToJSON(aJSON
);
3720 void Document::SendToConsole(nsCOMArray
<nsISecurityConsoleMessage
>& aMessages
) {
3721 for (uint32_t i
= 0; i
< aMessages
.Length(); ++i
) {
3722 nsAutoString messageTag
;
3723 aMessages
[i
]->GetTag(messageTag
);
3725 nsAutoString category
;
3726 aMessages
[i
]->GetCategory(category
);
3728 nsContentUtils::ReportToConsole(nsIScriptError::warningFlag
,
3729 NS_ConvertUTF16toUTF8(category
), this,
3730 nsContentUtils::eSECURITY_PROPERTIES
,
3731 NS_ConvertUTF16toUTF8(messageTag
).get());
3735 void Document::ApplySettingsFromCSP(bool aSpeculative
) {
3736 nsresult rv
= NS_OK
;
3737 if (!aSpeculative
) {
3738 // 1) apply settings from regular CSP
3740 // Set up 'block-all-mixed-content' if not already inherited
3741 // from the parent context or set by any other CSP.
3742 if (!mBlockAllMixedContent
) {
3744 rv
= mCSP
->GetBlockAllMixedContent(&block
);
3745 NS_ENSURE_SUCCESS_VOID(rv
);
3746 mBlockAllMixedContent
= block
;
3748 if (!mBlockAllMixedContentPreloads
) {
3749 mBlockAllMixedContentPreloads
= mBlockAllMixedContent
;
3752 // Set up 'upgrade-insecure-requests' if not already inherited
3753 // from the parent context or set by any other CSP.
3754 if (!mUpgradeInsecureRequests
) {
3755 bool upgrade
= false;
3756 rv
= mCSP
->GetUpgradeInsecureRequests(&upgrade
);
3757 NS_ENSURE_SUCCESS_VOID(rv
);
3758 mUpgradeInsecureRequests
= upgrade
;
3760 if (!mUpgradeInsecurePreloads
) {
3761 mUpgradeInsecurePreloads
= mUpgradeInsecureRequests
;
3763 // Update csp settings in the parent process
3764 if (auto* wgc
= GetWindowGlobalChild()) {
3765 wgc
->SendUpdateDocumentCspSettings(mBlockAllMixedContent
,
3766 mUpgradeInsecureRequests
);
3772 // 2) apply settings from speculative csp
3774 if (!mBlockAllMixedContentPreloads
) {
3776 rv
= mPreloadCSP
->GetBlockAllMixedContent(&block
);
3777 NS_ENSURE_SUCCESS_VOID(rv
);
3778 mBlockAllMixedContent
= block
;
3780 if (!mUpgradeInsecurePreloads
) {
3781 bool upgrade
= false;
3782 rv
= mPreloadCSP
->GetUpgradeInsecureRequests(&upgrade
);
3783 NS_ENSURE_SUCCESS_VOID(rv
);
3784 mUpgradeInsecurePreloads
= upgrade
;
3789 nsresult
Document::InitCSP(nsIChannel
* aChannel
) {
3790 MOZ_ASSERT(!mScriptGlobalObject
,
3791 "CSP must be initialized before mScriptGlobalObject is set!");
3793 // If this is a data document - no need to set CSP.
3794 if (mLoadedAsData
) {
3798 // If this is an image, no need to set a CSP. Otherwise SVG images
3799 // served with a CSP might block internally applied inline styles.
3800 nsCOMPtr
<nsILoadInfo
> loadInfo
= aChannel
->LoadInfo();
3801 if (loadInfo
->GetExternalContentPolicyType() ==
3802 ExtContentPolicy::TYPE_IMAGE
||
3803 loadInfo
->GetExternalContentPolicyType() ==
3804 ExtContentPolicy::TYPE_IMAGESET
) {
3808 MOZ_ASSERT(!mCSP
, "where did mCSP get set if not here?");
3810 // If there is a CSP that needs to be inherited from whatever
3811 // global is considered the client of the document fetch then
3812 // we query it here from the loadinfo in case the newly created
3813 // document needs to inherit the CSP. See:
3814 // https://w3c.github.io/webappsec-csp/#initialize-document-csp
3815 bool inheritedCSP
= CSP_ShouldResponseInheritCSP(aChannel
);
3817 mCSP
= loadInfo
->GetCspToInherit();
3820 // If there is no CSP to inherit, then we create a new CSP here so
3821 // that history entries always have the right reference in case a
3822 // Meta CSP gets dynamically added after the history entry has
3823 // already been created.
3825 mCSP
= new nsCSPContext();
3826 mHasPolicyWithRequireTrustedTypesForDirective
= false;
3828 mHasPolicyWithRequireTrustedTypesForDirective
=
3829 mCSP
->GetRequireTrustedTypesForDirectiveState() !=
3830 RequireTrustedTypesForDirectiveState::NONE
;
3833 // Always overwrite the requesting context of the CSP so that any new
3834 // 'self' keyword added to an inherited CSP translates correctly.
3835 nsresult rv
= mCSP
->SetRequestContextWithDocument(this);
3836 if (NS_WARN_IF(NS_FAILED(rv
))) {
3840 nsAutoCString tCspHeaderValue
, tCspROHeaderValue
;
3842 nsCOMPtr
<nsIHttpChannel
> httpChannel
;
3843 rv
= GetHttpChannelHelper(aChannel
, getter_AddRefs(httpChannel
));
3844 if (NS_WARN_IF(NS_FAILED(rv
))) {
3849 Unused
<< httpChannel
->GetResponseHeader("content-security-policy"_ns
,
3852 Unused
<< httpChannel
->GetResponseHeader(
3853 "content-security-policy-report-only"_ns
, tCspROHeaderValue
);
3855 NS_ConvertASCIItoUTF16
cspHeaderValue(tCspHeaderValue
);
3856 NS_ConvertASCIItoUTF16
cspROHeaderValue(tCspROHeaderValue
);
3858 // Check if this is a document from a WebExtension.
3859 nsCOMPtr
<nsIPrincipal
> principal
= NodePrincipal();
3860 auto addonPolicy
= BasePrincipal::Cast(principal
)->AddonPolicy();
3862 // If there's no CSP to apply, go ahead and return early
3863 if (!inheritedCSP
&& !addonPolicy
&& cspHeaderValue
.IsEmpty() &&
3864 cspROHeaderValue
.IsEmpty()) {
3865 if (MOZ_LOG_TEST(gCspPRLog
, LogLevel::Debug
)) {
3866 nsCOMPtr
<nsIURI
> chanURI
;
3867 aChannel
->GetURI(getter_AddRefs(chanURI
));
3868 nsAutoCString aspec
;
3869 chanURI
->GetAsciiSpec(aspec
);
3870 MOZ_LOG(gCspPRLog
, LogLevel::Debug
,
3871 ("no CSP for document, %s", aspec
.get()));
3877 MOZ_LOG(gCspPRLog
, LogLevel::Debug
,
3878 ("Document is an add-on or CSP header specified %p", this));
3880 // ----- if the doc is an addon, apply its CSP.
3882 mCSP
->AppendPolicy(addonPolicy
->BaseCSP(), false, false);
3884 mCSP
->AppendPolicy(addonPolicy
->ExtensionPageCSP(), false, false);
3885 // Bug 1548468: Move CSP off ExpandedPrincipal
3886 // Currently the LoadInfo holds the source of truth for every resource load
3887 // because LoadInfo::GetCsp() queries the CSP from an ExpandedPrincipal
3888 // (and not from the Client) if the load was triggered by an extension.
3889 auto* basePrin
= BasePrincipal::Cast(principal
);
3890 if (basePrin
->Is
<ExpandedPrincipal
>()) {
3891 basePrin
->As
<ExpandedPrincipal
>()->SetCsp(mCSP
);
3895 // ----- if there's a full-strength CSP header, apply it.
3896 if (!cspHeaderValue
.IsEmpty()) {
3897 mHasCSPDeliveredThroughHeader
= true;
3898 rv
= CSP_AppendCSPFromHeader(mCSP
, cspHeaderValue
, false);
3899 NS_ENSURE_SUCCESS(rv
, rv
);
3902 // ----- if there's a report-only CSP header, apply it.
3903 if (!cspROHeaderValue
.IsEmpty()) {
3904 rv
= CSP_AppendCSPFromHeader(mCSP
, cspROHeaderValue
, true);
3905 NS_ENSURE_SUCCESS(rv
, rv
);
3908 // ----- Enforce sandbox policy if supplied in CSP header
3909 // The document may already have some sandbox flags set (e.g. if the document
3910 // is an iframe with the sandbox attribute set). If we have a CSP sandbox
3911 // directive, intersect the CSP sandbox flags with the existing flags. This
3912 // corresponds to the _least_ permissive policy.
3913 uint32_t cspSandboxFlags
= SANDBOXED_NONE
;
3914 rv
= mCSP
->GetCSPSandboxFlags(&cspSandboxFlags
);
3915 NS_ENSURE_SUCCESS(rv
, rv
);
3917 // Probably the iframe sandbox attribute already caused the creation of a
3918 // new NullPrincipal. Only create a new NullPrincipal if CSP requires so
3919 // and no one has been created yet.
3920 bool needNewNullPrincipal
= (cspSandboxFlags
& SANDBOXED_ORIGIN
) &&
3921 !(mSandboxFlags
& SANDBOXED_ORIGIN
);
3923 mSandboxFlags
|= cspSandboxFlags
;
3925 if (needNewNullPrincipal
) {
3926 principal
= NullPrincipal::CreateWithInheritedAttributes(principal
);
3927 // Skip setting the content blocking allowlist principal to NullPrincipal.
3928 // The principal is only used to enable/disable trackingprotection via
3929 // permission and can be shared with the top level sandboxed site.
3931 SetPrincipals(principal
, principal
);
3934 ApplySettingsFromCSP(false);
3938 static FeaturePolicy
* GetFeaturePolicyFromElement(Element
* aElement
) {
3939 if (auto* iframe
= HTMLIFrameElement::FromNodeOrNull(aElement
)) {
3940 return iframe
->FeaturePolicy();
3943 if (!HTMLObjectElement::FromNodeOrNull(aElement
) &&
3944 !HTMLEmbedElement::FromNodeOrNull(aElement
)) {
3948 return aElement
->OwnerDoc()->FeaturePolicy();
3951 nsresult
Document::InitDocPolicy(nsIChannel
* aChannel
) {
3952 // We only use document policy to implement the text fragments spec, so leave
3953 // everything at the default value if it isn't enabled. This includes the
3954 // behavior for element fragments.
3955 if (!StaticPrefs::dom_text_fragments_enabled()) {
3959 nsCOMPtr
<nsIHttpChannel
> httpChannel
;
3960 nsresult rv
= GetHttpChannelHelper(aChannel
, getter_AddRefs(httpChannel
));
3961 if (NS_WARN_IF(NS_FAILED(rv
))) {
3965 nsAutoCString docPolicyString
;
3967 Unused
<< httpChannel
->GetResponseHeader("Document-Policy"_ns
,
3971 if (docPolicyString
.IsEmpty()) {
3975 mForceLoadAtTop
= NS_GetForceLoadAtTopFromHeader(docPolicyString
);
3980 void Document::InitFeaturePolicy(
3981 const Variant
<Nothing
, FeaturePolicyInfo
, Element
*>&
3982 aContainerFeaturePolicy
) {
3983 MOZ_ASSERT(mFeaturePolicy
, "we should have FeaturePolicy created");
3985 mFeaturePolicy
->ResetDeclaredPolicy();
3987 mFeaturePolicy
->SetDefaultOrigin(NodePrincipal());
3989 RefPtr
<dom::FeaturePolicy
> featurePolicy
= mFeaturePolicy
;
3990 aContainerFeaturePolicy
.match(
3991 [](const Nothing
&) {},
3992 [featurePolicy
](const FeaturePolicyInfo
& aContainerFeaturePolicy
) {
3993 // Let's inherit the policy from the possibly cross-origin container.
3994 featurePolicy
->InheritPolicy(aContainerFeaturePolicy
);
3995 featurePolicy
->SetSrcOrigin(aContainerFeaturePolicy
.mSrcOrigin
);
3997 [featurePolicy
](Element
* aContainer
) {
3998 // Let's inherit the policy from the parent container element if it
4000 if (RefPtr
<dom::FeaturePolicy
> containerFeaturePolicy
=
4001 GetFeaturePolicyFromElement(aContainer
)) {
4002 featurePolicy
->InheritPolicy(containerFeaturePolicy
);
4003 featurePolicy
->SetSrcOrigin(containerFeaturePolicy
->GetSrcOrigin());
4008 Element
* GetEmbedderElementFrom(BrowsingContext
* aBrowsingContext
) {
4009 if (!aBrowsingContext
) {
4012 if (!aBrowsingContext
->IsContentSubframe()) {
4016 return aBrowsingContext
->GetEmbedderElement();
4019 nsresult
Document::InitFeaturePolicy(nsIChannel
* aChannel
) {
4020 nsCOMPtr
<nsILoadInfo
> loadInfo
= aChannel
->LoadInfo();
4021 if (Element
* embedderElement
= GetEmbedderElementFrom(GetBrowsingContext())) {
4022 InitFeaturePolicy(AsVariant(embedderElement
));
4023 } else if (Maybe
<FeaturePolicyInfo
> featurePolicyContainer
=
4024 loadInfo
->GetContainerFeaturePolicyInfo()) {
4025 InitFeaturePolicy(AsVariant(*featurePolicyContainer
));
4027 InitFeaturePolicy(AsVariant(Nothing
{}));
4030 // We don't want to parse the http Feature-Policy header if this pref is off.
4031 if (!StaticPrefs::dom_security_featurePolicy_header_enabled()) {
4035 nsCOMPtr
<nsIHttpChannel
> httpChannel
;
4036 nsresult rv
= GetHttpChannelHelper(aChannel
, getter_AddRefs(httpChannel
));
4037 if (NS_WARN_IF(NS_FAILED(rv
))) {
4045 // query the policy from the header
4046 nsAutoCString value
;
4047 rv
= httpChannel
->GetResponseHeader("Feature-Policy"_ns
, value
);
4048 if (NS_SUCCEEDED(rv
)) {
4049 mFeaturePolicy
->SetDeclaredPolicy(this, NS_ConvertUTF8toUTF16(value
),
4050 NodePrincipal(), nullptr);
4056 void Document::EnsureNotEnteringAndExitFullscreen() {
4057 Document::ClearPendingFullscreenRequests(this);
4058 if (GetFullscreenElement()) {
4059 Document::AsyncExitFullscreen(this);
4063 void Document::SetReferrerInfo(nsIReferrerInfo
* aReferrerInfo
) {
4064 mReferrerInfo
= aReferrerInfo
;
4065 mCachedReferrerInfoForInternalCSSAndSVGResources
= nullptr;
4066 mCachedURLData
= nullptr;
4069 nsresult
Document::InitReferrerInfo(nsIChannel
* aChannel
) {
4070 MOZ_ASSERT(mReferrerInfo
);
4071 MOZ_ASSERT(mPreloadReferrerInfo
);
4073 if (ReferrerInfo::ShouldResponseInheritReferrerInfo(aChannel
)) {
4074 // The channel is loading `about:srcdoc`. Srcdoc loads should respond with
4075 // their parent's ReferrerInfo when asked for their ReferrerInfo, unless
4076 // they have an opaque origin.
4077 // https://w3c.github.io/webappsec-referrer-policy/#determine-requests-referrer
4078 if (BrowsingContext
* bc
= GetBrowsingContext()) {
4079 // At this point the document is not fully created and mParentDocument has
4080 // not been set yet,
4081 Document
* parentDoc
= bc
->GetEmbedderElement()
4082 ? bc
->GetEmbedderElement()->OwnerDoc()
4085 SetReferrerInfo(parentDoc
->GetReferrerInfo());
4086 mPreloadReferrerInfo
= mReferrerInfo
;
4090 MOZ_ASSERT(bc
->IsInProcess() || NodePrincipal()->GetIsNullPrincipal(),
4091 "srcdoc without null principal as toplevel!");
4095 nsCOMPtr
<nsIHttpChannel
> httpChannel
;
4096 nsresult rv
= GetHttpChannelHelper(aChannel
, getter_AddRefs(httpChannel
));
4097 if (NS_WARN_IF(NS_FAILED(rv
))) {
4105 if (nsCOMPtr
<nsIReferrerInfo
> referrerInfo
= httpChannel
->GetReferrerInfo()) {
4106 SetReferrerInfo(referrerInfo
);
4109 // Override policy if we get one from Referrerr-Policy header
4110 mozilla::dom::ReferrerPolicy policy
=
4111 nsContentUtils::GetReferrerPolicyFromChannel(aChannel
);
4112 nsCOMPtr
<nsIReferrerInfo
> clone
=
4113 static_cast<dom::ReferrerInfo
*>(mReferrerInfo
.get())
4114 ->CloneWithNewPolicy(policy
);
4115 SetReferrerInfo(clone
);
4116 mPreloadReferrerInfo
= mReferrerInfo
;
4120 nsresult
Document::InitCOEP(nsIChannel
* aChannel
) {
4121 nsCOMPtr
<nsIHttpChannel
> httpChannel
;
4122 nsresult rv
= GetHttpChannelHelper(aChannel
, getter_AddRefs(httpChannel
));
4123 if (NS_FAILED(rv
)) {
4127 nsCOMPtr
<nsIHttpChannelInternal
> intChannel
= do_QueryInterface(httpChannel
);
4133 nsILoadInfo::CrossOriginEmbedderPolicy policy
=
4134 nsILoadInfo::EMBEDDER_POLICY_NULL
;
4135 if (NS_SUCCEEDED(intChannel
->GetResponseEmbedderPolicy(
4136 mTrials
.IsEnabled(OriginTrial::CoepCredentialless
), &policy
))) {
4137 mEmbedderPolicy
= Some(policy
);
4143 void Document::StopDocumentLoad() {
4145 mParserAborted
= true;
4146 mParser
->Terminate();
4150 void Document::SetDocumentURI(nsIURI
* aURI
) {
4151 nsCOMPtr
<nsIURI
> oldBase
= GetDocBaseURI();
4152 mDocumentURI
= aURI
;
4153 // This loosely implements §3.4.1 of Text Fragments
4154 // https://wicg.github.io/scroll-to-text-fragment/#invoking-text-directives
4155 // Unlike specified in the spec, the fragment directive is not stripped from
4156 // the URL in the session history entry. Instead it is removed when the URL is
4157 // set in the `Document`. Also, instead of storing the `uninvokedDirective` in
4158 // `Document` as mentioned in the spec, the extracted directives are moved to
4159 // the `FragmentDirective` object which deals with finding the ranges to
4160 // highlight in `ScrollToRef()`.
4161 // XXX(:jjaschke): This is only a temporary solution.
4162 // https://bugzil.la/1881429 is filed for revisiting this.
4163 nsTArray
<TextDirective
> textDirectives
;
4164 FragmentDirective::ParseAndRemoveFragmentDirectiveFromFragment(
4165 mDocumentURI
, &textDirectives
);
4166 FragmentDirective()->SetTextDirectives(std::move(textDirectives
));
4168 nsIURI
* newBase
= GetDocBaseURI();
4170 mChromeRulesEnabled
= URLExtraData::ChromeRulesEnabled(aURI
);
4172 bool equalBases
= false;
4173 // Changing just the ref of a URI does not change how relative URIs would
4174 // resolve wrt to it, so we can treat the bases as equal as long as they're
4175 // equal ignoring the ref.
4176 if (oldBase
&& newBase
) {
4177 oldBase
->EqualsExceptRef(newBase
, &equalBases
);
4179 equalBases
= !oldBase
&& !newBase
;
4182 // If this is the first time we're setting the document's URI, set the
4183 // document's original URI.
4184 if (!mOriginalURI
) mOriginalURI
= mDocumentURI
;
4186 // If changing the document's URI changed the base URI of the document, we
4187 // need to refresh the hrefs of all the links on the page.
4189 mCachedURLData
= nullptr;
4193 // Recalculate our base domain
4194 mBaseDomain
.Truncate();
4195 ThirdPartyUtil
* thirdPartyUtil
= ThirdPartyUtil::GetInstance();
4196 if (thirdPartyUtil
) {
4197 Unused
<< thirdPartyUtil
->GetBaseDomain(mDocumentURI
, mBaseDomain
);
4200 // Tell our WindowGlobalParent that the document's URI has been changed.
4201 if (WindowGlobalChild
* wgc
= GetWindowGlobalChild()) {
4202 wgc
->SetDocumentURI(mDocumentURI
);
4206 static void GetFormattedTimeString(PRTime aTime
, bool aUniversal
,
4207 nsAString
& aFormattedTimeString
) {
4208 PRExplodedTime prtime
;
4209 PR_ExplodeTime(aTime
, aUniversal
? PR_GMTParameters
: PR_LocalTimeParameters
,
4211 // "MM/DD/YYYY hh:mm:ss"
4212 char formatedTime
[24];
4213 if (SprintfLiteral(formatedTime
, "%02d/%02d/%04d %02d:%02d:%02d",
4214 prtime
.tm_month
+ 1, prtime
.tm_mday
, int(prtime
.tm_year
),
4215 prtime
.tm_hour
, prtime
.tm_min
, prtime
.tm_sec
)) {
4216 CopyASCIItoUTF16(nsDependentCString(formatedTime
), aFormattedTimeString
);
4218 // If we for whatever reason failed to find the last modified time
4219 // (or even the current time), fall back to what NS4.x returned.
4220 aFormattedTimeString
.AssignLiteral(u
"01/01/1970 00:00:00");
4224 void Document::GetLastModified(nsAString
& aLastModified
) const {
4225 if (!mLastModified
.IsEmpty()) {
4226 aLastModified
.Assign(mLastModified
);
4228 GetFormattedTimeString(PR_Now(),
4229 ShouldResistFingerprinting(RFPTarget::JSDateTimeUTC
),
4234 static void IncrementExpandoGeneration(Document
& aDoc
) {
4235 ++aDoc
.mExpandoAndGeneration
.generation
;
4238 void Document::AddToNameTable(Element
* aElement
, nsAtom
* aName
) {
4240 nsGenericHTMLElement::ShouldExposeNameAsHTMLDocumentProperty(aElement
),
4241 "Only put elements that need to be exposed as document['name'] in "
4242 "the named table.");
4244 IdentifierMapEntry
* entry
= mIdentifierMap
.PutEntry(aName
);
4246 // Null for out-of-memory
4248 if (!entry
->HasNameElement() &&
4249 !entry
->HasIdElementExposedAsHTMLDocumentProperty()) {
4250 IncrementExpandoGeneration(*this);
4252 entry
->AddNameElement(this, aElement
);
4256 void Document::RemoveFromNameTable(Element
* aElement
, nsAtom
* aName
) {
4257 // Speed up document teardown
4258 if (mIdentifierMap
.Count() == 0) return;
4260 IdentifierMapEntry
* entry
= mIdentifierMap
.GetEntry(aName
);
4261 if (!entry
) // Could be false if the element was anonymous, hence never added
4264 entry
->RemoveNameElement(aElement
);
4265 if (!entry
->HasNameElement() &&
4266 !entry
->HasIdElementExposedAsHTMLDocumentProperty()) {
4267 IncrementExpandoGeneration(*this);
4271 void Document::AddToIdTable(Element
* aElement
, nsAtom
* aId
) {
4272 IdentifierMapEntry
* entry
= mIdentifierMap
.PutEntry(aId
);
4274 if (entry
) { /* True except on OOM */
4275 if (nsGenericHTMLElement::ShouldExposeIdAsHTMLDocumentProperty(aElement
) &&
4276 !entry
->HasNameElement() &&
4277 !entry
->HasIdElementExposedAsHTMLDocumentProperty()) {
4278 IncrementExpandoGeneration(*this);
4280 entry
->AddIdElement(aElement
);
4284 void Document::RemoveFromIdTable(Element
* aElement
, nsAtom
* aId
) {
4285 NS_ASSERTION(aId
, "huhwhatnow?");
4287 // Speed up document teardown
4288 if (mIdentifierMap
.Count() == 0) {
4292 IdentifierMapEntry
* entry
= mIdentifierMap
.GetEntry(aId
);
4293 if (!entry
) // Can be null for XML elements with changing ids.
4296 entry
->RemoveIdElement(aElement
);
4297 if (nsGenericHTMLElement::ShouldExposeIdAsHTMLDocumentProperty(aElement
) &&
4298 !entry
->HasNameElement() &&
4299 !entry
->HasIdElementExposedAsHTMLDocumentProperty()) {
4300 IncrementExpandoGeneration(*this);
4302 if (entry
->IsEmpty()) {
4303 mIdentifierMap
.RemoveEntry(entry
);
4307 void Document::UpdateReferrerInfoFromMeta(const nsAString
& aMetaReferrer
,
4309 ReferrerPolicyEnum policy
=
4310 ReferrerInfo::ReferrerPolicyFromMetaString(aMetaReferrer
);
4311 // The empty string "" corresponds to no referrer policy, causing a fallback
4312 // to a referrer policy defined elsewhere.
4313 if (policy
== ReferrerPolicy::_empty
) {
4317 MOZ_ASSERT(mReferrerInfo
);
4318 MOZ_ASSERT(mPreloadReferrerInfo
);
4321 mPreloadReferrerInfo
=
4322 static_cast<mozilla::dom::ReferrerInfo
*>((mPreloadReferrerInfo
).get())
4323 ->CloneWithNewPolicy(policy
);
4325 nsCOMPtr
<nsIReferrerInfo
> clone
=
4326 static_cast<mozilla::dom::ReferrerInfo
*>((mReferrerInfo
).get())
4327 ->CloneWithNewPolicy(policy
);
4328 SetReferrerInfo(clone
);
4332 void Document::SetPrincipals(nsIPrincipal
* aNewPrincipal
,
4333 nsIPrincipal
* aNewPartitionedPrincipal
) {
4334 MOZ_ASSERT(!!aNewPrincipal
== !!aNewPartitionedPrincipal
);
4335 if (aNewPrincipal
&& mAllowDNSPrefetch
&&
4336 StaticPrefs::network_dns_disablePrefetchFromHTTPS()) {
4337 if (aNewPrincipal
->SchemeIs("https")) {
4338 mAllowDNSPrefetch
= false;
4342 mScriptLoader
->DeregisterFromCache();
4343 mCSSLoader
->DeregisterFromSheetCache();
4345 mNodeInfoManager
->SetDocumentPrincipal(aNewPrincipal
);
4346 mPartitionedPrincipal
= aNewPartitionedPrincipal
;
4348 mCachedURLData
= nullptr;
4350 mCSSLoader
->RegisterInSheetCache();
4351 mScriptLoader
->RegisterToCache();
4353 RecomputeResistFingerprinting();
4356 // Validate that the docgroup is set correctly by calling its getter and
4357 // triggering its sanity check.
4359 // If we're setting the principal to null, we don't want to perform the check,
4360 // as the document is entering an intermediate state where it does not have a
4361 // principal. It will be given another real principal shortly which we will
4362 // check. It's not unsafe to have a document which has a null principal in the
4363 // same docgroup as another document, so this should not be a problem.
4364 if (aNewPrincipal
) {
4371 void Document::AssertDocGroupMatchesKey() const {
4372 // Sanity check that we have an up-to-date and accurate docgroup
4373 // We only check if the principal when we can get the browsing context.
4375 // Note that we can be invoked during cycle collection, so we need to handle
4376 // the browsingcontext being partially unlinked - normally you shouldn't
4377 // null-check `Group()` as it shouldn't return nullptr.
4378 if (!GetBrowsingContext() || !GetBrowsingContext()->Group()) {
4382 if (mDocGroup
&& mDocGroup
->GetBrowsingContextGroup()) {
4383 MOZ_ASSERT(mDocGroup
->GetBrowsingContextGroup() ==
4384 GetBrowsingContext()->Group());
4386 // GetKey() can fail, e.g. after the TLD service has shut down.
4387 nsAutoCString docGroupKey
;
4388 nsresult rv
= mozilla::dom::DocGroup::GetKey(
4390 GetBrowsingContext()->Group()->IsPotentiallyCrossOriginIsolated(),
4392 if (NS_SUCCEEDED(rv
)) {
4393 MOZ_ASSERT(mDocGroup
->MatchesKey(docGroupKey
));
4399 nsresult
Document::Dispatch(already_AddRefed
<nsIRunnable
>&& aRunnable
) const {
4400 return SchedulerGroup::Dispatch(std::move(aRunnable
));
4403 void Document::NoteScriptTrackingStatus(const nsACString
& aURL
,
4406 mTrackingScripts
.Insert(aURL
);
4408 // Ideally, whether a given script is tracking or not should be consistent,
4409 // but there is a race so that it is not, when loading real sites in debug
4410 // builds. See bug 1925286.
4411 // MOZ_ASSERT_IF(!aIsTracking, !mTrackingScripts.Contains(aURL));
4414 bool Document::IsScriptTracking(JSContext
* aCx
) const {
4415 JS::AutoFilename filename
;
4416 if (!JS::DescribeScriptedCaller(&filename
, aCx
)) {
4419 return mTrackingScripts
.Contains(nsDependentCString(filename
.get()));
4422 void Document::GetContentType(nsAString
& aContentType
) {
4423 CopyUTF8toUTF16(GetContentTypeInternal(), aContentType
);
4426 void Document::SetContentType(const nsACString
& aContentType
) {
4427 if (!IsHTMLOrXHTML() && mDefaultElementType
== kNameSpaceID_None
&&
4428 aContentType
.EqualsLiteral("application/xhtml+xml")) {
4429 mDefaultElementType
= kNameSpaceID_XHTML
;
4432 mCachedEncoder
= nullptr;
4433 mContentType
= aContentType
;
4436 bool Document::HasPendingInitialTranslation() {
4437 return mDocumentL10n
&& mDocumentL10n
->GetState() != DocumentL10nState::Ready
;
4440 bool Document::HasPendingL10nMutations() const {
4441 return mDocumentL10n
&& mDocumentL10n
->HasPendingMutations();
4444 bool Document::DocumentSupportsL10n(JSContext
* aCx
, JSObject
* aObject
) {
4445 JS::Rooted
<JSObject
*> object(aCx
, aObject
);
4446 nsCOMPtr
<nsIPrincipal
> callerPrincipal
=
4447 nsContentUtils::SubjectPrincipal(aCx
);
4448 nsGlobalWindowInner
* win
= xpc::WindowOrNull(object
);
4449 bool allowed
= false;
4450 callerPrincipal
->IsL10nAllowed(win
? win
->GetDocumentURI() : nullptr,
4455 void Document::LocalizationLinkAdded(Element
* aLinkElement
) {
4456 if (!AllowsL10n()) {
4461 aLinkElement
->GetAttr(nsGkAtoms::href
, href
);
4463 if (!mDocumentL10n
) {
4464 Element
* elem
= GetDocumentElement();
4465 MOZ_DIAGNOSTIC_ASSERT(elem
);
4467 bool isSync
= elem
->HasAttr(nsGkAtoms::datal10nsync
);
4468 mDocumentL10n
= DocumentL10n::Create(this, isSync
);
4469 if (NS_WARN_IF(!mDocumentL10n
)) {
4474 mDocumentL10n
->AddResourceId(NS_ConvertUTF16toUTF8(href
));
4476 if (mReadyState
>= READYSTATE_INTERACTIVE
) {
4477 nsContentUtils::AddScriptRunner(NewRunnableMethod(
4478 "DocumentL10n::TriggerInitialTranslation()", mDocumentL10n
,
4479 &DocumentL10n::TriggerInitialTranslation
));
4481 if (!mDocumentL10n
->mBlockingLayout
) {
4482 // Our initial translation is going to block layout start. Make sure
4483 // we don't fire the load event until after that stops happening and
4484 // layout has a chance to start.
4486 mDocumentL10n
->mBlockingLayout
= true;
4491 void Document::LocalizationLinkRemoved(Element
* aLinkElement
) {
4492 if (!AllowsL10n()) {
4496 if (mDocumentL10n
) {
4498 aLinkElement
->GetAttr(nsGkAtoms::href
, href
);
4499 uint32_t remaining
=
4500 mDocumentL10n
->RemoveResourceId(NS_ConvertUTF16toUTF8(href
));
4501 if (remaining
== 0) {
4502 if (mDocumentL10n
->mBlockingLayout
) {
4503 mDocumentL10n
->mBlockingLayout
= false;
4504 UnblockOnload(/* aFireSync = */ false);
4506 mDocumentL10n
= nullptr;
4512 * This method should be called once the end of the l10n
4513 * resource container has been parsed.
4515 * In XUL this is the end of the first </linkset>,
4516 * In XHTML/HTML this is the end of </head>.
4518 * This milestone is used to allow for batch
4519 * localization context I/O and building done
4520 * once when all resources in the document have been
4523 void Document::OnL10nResourceContainerParsed() {
4524 // XXX: This is a scaffolding for where we might inject prefetch
4528 void Document::OnParsingCompleted() {
4529 // Let's call it again, in case the resource
4530 // container has not been closed, and only
4531 // now we're closing the document.
4532 OnL10nResourceContainerParsed();
4534 if (mDocumentL10n
) {
4535 RefPtr
<DocumentL10n
> l10n
= mDocumentL10n
;
4536 l10n
->TriggerInitialTranslation();
4540 void Document::InitialTranslationCompleted(bool aL10nCached
) {
4541 if (mDocumentL10n
&& mDocumentL10n
->mBlockingLayout
) {
4542 // This means we blocked the load event in LocalizationLinkAdded. It's
4543 // important that the load blocker removal here be async, because our caller
4544 // will notify the content sink after us, and we want the content sync's
4545 // work to happen before the load event fires.
4546 mDocumentL10n
->mBlockingLayout
= false;
4547 UnblockOnload(/* aFireSync = */ false);
4550 mL10nProtoElements
.Clear();
4552 nsXULPrototypeDocument
* proto
= GetPrototype();
4554 proto
->SetIsL10nCached(aL10nCached
);
4558 bool Document::AllowsL10n() const {
4559 if (IsStaticDocument()) {
4560 // We don't allow l10n on static documents, because the nodes are already
4561 // cloned translated, and static docs don't get parsed so we never
4562 // TriggerInitialTranslation, etc, so a load blocker would keep hanging
4566 bool allowed
= false;
4567 NodePrincipal()->IsL10nAllowed(GetDocumentURI(), &allowed
);
4571 DocumentTimeline
* Document::Timeline() {
4572 if (!mDocumentTimeline
) {
4573 mDocumentTimeline
= new DocumentTimeline(this, TimeDuration(0));
4576 return mDocumentTimeline
;
4579 SVGSVGElement
* Document::GetSVGRootElement() const {
4580 Element
* root
= GetRootElement();
4581 if (!root
|| !root
->IsSVGElement(nsGkAtoms::svg
)) {
4584 return static_cast<SVGSVGElement
*>(root
);
4587 /* Return true if the document is in the focused top-level window, and is an
4588 * ancestor of the focused DOMWindow. */
4589 bool Document::HasFocus(ErrorResult
& rv
) const {
4590 nsFocusManager
* fm
= nsFocusManager::GetFocusManager();
4592 rv
.Throw(NS_ERROR_NOT_AVAILABLE
);
4596 BrowsingContext
* bc
= GetBrowsingContext();
4601 if (!fm
->IsInActiveWindow(bc
)) {
4605 return fm
->IsSameOrAncestor(bc
, fm
->GetFocusedBrowsingContext());
4608 bool Document::ThisDocumentHasFocus() const {
4609 nsFocusManager
* fm
= nsFocusManager::GetFocusManager();
4610 return fm
&& fm
->GetFocusedWindow() &&
4611 fm
->GetFocusedWindow()->GetExtantDoc() == this;
4614 void Document::GetDesignMode(nsAString
& aDesignMode
) {
4615 if (IsInDesignMode()) {
4616 aDesignMode
.AssignLiteral("on");
4618 aDesignMode
.AssignLiteral("off");
4622 void Document::SetDesignMode(const nsAString
& aDesignMode
,
4623 nsIPrincipal
& aSubjectPrincipal
, ErrorResult
& rv
) {
4624 SetDesignMode(aDesignMode
, Some(&aSubjectPrincipal
), rv
);
4627 static void NotifyEditableStateChange(Document
& aDoc
) {
4628 #ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
4631 for (nsIContent
* node
= aDoc
.GetNextNode(&aDoc
); node
;
4632 node
= node
->GetNextNode(&aDoc
)) {
4633 if (auto* element
= Element::FromNode(node
)) {
4634 element
->UpdateEditableState(true);
4637 MOZ_DIAGNOSTIC_ASSERT(!g
.Mutated(0));
4640 void Document::SetDocumentEditableFlag(bool aEditable
) {
4641 if (HasFlag(NODE_IS_EDITABLE
) == aEditable
) {
4644 SetEditableFlag(aEditable
);
4645 // Changing the NODE_IS_EDITABLE flags on document changes the intrinsic
4646 // state of all descendant elements of it. Update that now.
4647 NotifyEditableStateChange(*this);
4650 void Document::SetDesignMode(const nsAString
& aDesignMode
,
4651 const Maybe
<nsIPrincipal
*>& aSubjectPrincipal
,
4653 if (aSubjectPrincipal
.isSome() &&
4654 !aSubjectPrincipal
.value()->Subsumes(NodePrincipal())) {
4655 rv
.Throw(NS_ERROR_DOM_PROP_ACCESS_DENIED
);
4658 const bool editableMode
= IsInDesignMode();
4659 if (aDesignMode
.LowerCaseEqualsASCII(editableMode
? "off" : "on")) {
4660 SetDocumentEditableFlag(!editableMode
);
4661 rv
= EditingStateChanged();
4665 nsCommandManager
* Document::GetMidasCommandManager() {
4666 // check if we have it cached
4667 if (mMidasCommandManager
) {
4668 return mMidasCommandManager
;
4671 nsPIDOMWindowOuter
* window
= GetWindow();
4676 nsIDocShell
* docshell
= window
->GetDocShell();
4681 mMidasCommandManager
= docshell
->GetCommandManager();
4682 return mMidasCommandManager
;
4686 void Document::EnsureInitializeInternalCommandDataHashtable() {
4687 if (sInternalCommandDataHashtable
) {
4690 using CommandOnTextEditor
= InternalCommandData::CommandOnTextEditor
;
4691 sInternalCommandDataHashtable
= new InternalCommandDataHashtable();
4693 sInternalCommandDataHashtable
->InsertOrUpdate(
4695 InternalCommandData(
4697 Command::FormatBold
,
4698 ExecCommandParam::Ignore
,
4699 StyleUpdatingCommand::GetInstance
,
4700 CommandOnTextEditor::Disabled
));
4701 sInternalCommandDataHashtable
->InsertOrUpdate(
4703 InternalCommandData(
4705 Command::FormatItalic
,
4706 ExecCommandParam::Ignore
,
4707 StyleUpdatingCommand::GetInstance
,
4708 CommandOnTextEditor::Disabled
));
4709 sInternalCommandDataHashtable
->InsertOrUpdate(
4711 InternalCommandData(
4713 Command::FormatUnderline
,
4714 ExecCommandParam::Ignore
,
4715 StyleUpdatingCommand::GetInstance
,
4716 CommandOnTextEditor::Disabled
));
4717 sInternalCommandDataHashtable
->InsertOrUpdate(
4718 u
"strikethrough"_ns
,
4719 InternalCommandData(
4720 "cmd_strikethrough",
4721 Command::FormatStrikeThrough
,
4722 ExecCommandParam::Ignore
,
4723 StyleUpdatingCommand::GetInstance
,
4724 CommandOnTextEditor::Disabled
));
4725 sInternalCommandDataHashtable
->InsertOrUpdate(
4727 InternalCommandData(
4729 Command::FormatSubscript
,
4730 ExecCommandParam::Ignore
,
4731 StyleUpdatingCommand::GetInstance
,
4732 CommandOnTextEditor::Disabled
));
4733 sInternalCommandDataHashtable
->InsertOrUpdate(
4735 InternalCommandData(
4737 Command::FormatSuperscript
,
4738 ExecCommandParam::Ignore
,
4739 StyleUpdatingCommand::GetInstance
,
4740 CommandOnTextEditor::Disabled
));
4741 sInternalCommandDataHashtable
->InsertOrUpdate(
4743 InternalCommandData(
4746 ExecCommandParam::Ignore
,
4747 CutCommand::GetInstance
,
4748 CommandOnTextEditor::Enabled
));
4749 sInternalCommandDataHashtable
->InsertOrUpdate(
4751 InternalCommandData(
4754 ExecCommandParam::Ignore
,
4755 CopyCommand::GetInstance
,
4756 CommandOnTextEditor::Enabled
));
4757 sInternalCommandDataHashtable
->InsertOrUpdate(
4759 InternalCommandData(
4762 ExecCommandParam::Ignore
,
4763 PasteCommand::GetInstance
,
4764 CommandOnTextEditor::Enabled
));
4765 sInternalCommandDataHashtable
->InsertOrUpdate(
4767 InternalCommandData(
4768 "cmd_deleteCharBackward",
4769 Command::DeleteCharBackward
,
4770 ExecCommandParam::Ignore
,
4771 DeleteCommand::GetInstance
,
4772 CommandOnTextEditor::Enabled
));
4773 sInternalCommandDataHashtable
->InsertOrUpdate(
4774 u
"forwarddelete"_ns
,
4775 InternalCommandData(
4776 "cmd_deleteCharForward",
4777 Command::DeleteCharForward
,
4778 ExecCommandParam::Ignore
,
4779 DeleteCommand::GetInstance
,
4780 CommandOnTextEditor::Enabled
));
4781 sInternalCommandDataHashtable
->InsertOrUpdate(
4783 InternalCommandData(
4786 ExecCommandParam::Ignore
,
4787 SelectAllCommand::GetInstance
,
4788 CommandOnTextEditor::Enabled
));
4789 sInternalCommandDataHashtable
->InsertOrUpdate(
4791 InternalCommandData(
4793 Command::HistoryUndo
,
4794 ExecCommandParam::Ignore
,
4795 UndoCommand::GetInstance
,
4796 CommandOnTextEditor::Enabled
));
4797 sInternalCommandDataHashtable
->InsertOrUpdate(
4799 InternalCommandData(
4801 Command::HistoryRedo
,
4802 ExecCommandParam::Ignore
,
4803 RedoCommand::GetInstance
,
4804 CommandOnTextEditor::Enabled
));
4805 sInternalCommandDataHashtable
->InsertOrUpdate(
4807 InternalCommandData("cmd_indent",
4808 Command::FormatIndent
,
4809 ExecCommandParam::Ignore
,
4810 IndentCommand::GetInstance
,
4811 CommandOnTextEditor::Disabled
));
4812 sInternalCommandDataHashtable
->InsertOrUpdate(
4814 InternalCommandData(
4816 Command::FormatOutdent
,
4817 ExecCommandParam::Ignore
,
4818 OutdentCommand::GetInstance
,
4819 CommandOnTextEditor::Disabled
));
4820 sInternalCommandDataHashtable
->InsertOrUpdate(
4822 InternalCommandData(
4824 Command::FormatBackColor
,
4825 ExecCommandParam::String
,
4826 HighlightColorStateCommand::GetInstance
,
4827 CommandOnTextEditor::Disabled
));
4828 sInternalCommandDataHashtable
->InsertOrUpdate(
4830 InternalCommandData(
4832 Command::FormatBackColor
,
4833 ExecCommandParam::String
,
4834 HighlightColorStateCommand::GetInstance
,
4835 CommandOnTextEditor::Disabled
));
4836 sInternalCommandDataHashtable
->InsertOrUpdate(
4838 InternalCommandData(
4840 Command::FormatFontColor
,
4841 ExecCommandParam::String
,
4842 FontColorStateCommand::GetInstance
,
4843 CommandOnTextEditor::Disabled
));
4844 sInternalCommandDataHashtable
->InsertOrUpdate(
4846 InternalCommandData(
4848 Command::FormatFontName
,
4849 ExecCommandParam::String
,
4850 FontFaceStateCommand::GetInstance
,
4851 CommandOnTextEditor::Disabled
));
4852 sInternalCommandDataHashtable
->InsertOrUpdate(
4854 InternalCommandData(
4856 Command::FormatFontSize
,
4857 ExecCommandParam::String
,
4858 FontSizeStateCommand::GetInstance
,
4859 CommandOnTextEditor::Disabled
));
4860 sInternalCommandDataHashtable
->InsertOrUpdate(
4861 u
"inserthorizontalrule"_ns
,
4862 InternalCommandData(
4864 Command::InsertHorizontalRule
,
4865 ExecCommandParam::Ignore
,
4866 InsertTagCommand::GetInstance
,
4867 CommandOnTextEditor::Disabled
));
4868 sInternalCommandDataHashtable
->InsertOrUpdate(
4870 InternalCommandData(
4871 "cmd_insertLinkNoUI",
4872 Command::InsertLink
,
4873 ExecCommandParam::String
,
4874 InsertTagCommand::GetInstance
,
4875 CommandOnTextEditor::Disabled
));
4876 sInternalCommandDataHashtable
->InsertOrUpdate(
4878 InternalCommandData(
4879 "cmd_insertImageNoUI",
4880 Command::InsertImage
,
4881 ExecCommandParam::String
,
4882 InsertTagCommand::GetInstance
,
4883 CommandOnTextEditor::Disabled
));
4884 sInternalCommandDataHashtable
->InsertOrUpdate(
4886 InternalCommandData(
4888 Command::InsertHTML
,
4889 ExecCommandParam::String
,
4890 InsertHTMLCommand::GetInstance
,
4891 // TODO: Chromium inserts text content of the document fragment
4892 // created from the param.
4893 // https://source.chromium.org/chromium/chromium/src/+/master:third_party/blink/renderer/core/editing/commands/insert_commands.cc;l=105;drc=a4708b724062f17824815b896c3aaa43825128f8
4894 CommandOnTextEditor::Disabled
));
4895 sInternalCommandDataHashtable
->InsertOrUpdate(
4897 InternalCommandData(
4899 Command::InsertText
,
4900 ExecCommandParam::String
,
4901 InsertPlaintextCommand::GetInstance
,
4902 CommandOnTextEditor::Enabled
));
4903 sInternalCommandDataHashtable
->InsertOrUpdate(
4905 InternalCommandData(
4907 Command::FormatJustifyLeft
,
4908 ExecCommandParam::Ignore
, // Will be set to "left"
4909 AlignCommand::GetInstance
,
4910 CommandOnTextEditor::Disabled
));
4911 sInternalCommandDataHashtable
->InsertOrUpdate(
4913 InternalCommandData(
4915 Command::FormatJustifyRight
,
4916 ExecCommandParam::Ignore
, // Will be set to "right"
4917 AlignCommand::GetInstance
,
4918 CommandOnTextEditor::Disabled
));
4919 sInternalCommandDataHashtable
->InsertOrUpdate(
4920 u
"justifycenter"_ns
,
4921 InternalCommandData(
4923 Command::FormatJustifyCenter
,
4924 ExecCommandParam::Ignore
, // Will be set to "center"
4925 AlignCommand::GetInstance
,
4926 CommandOnTextEditor::Disabled
));
4927 sInternalCommandDataHashtable
->InsertOrUpdate(
4929 InternalCommandData(
4931 Command::FormatJustifyFull
,
4932 ExecCommandParam::Ignore
, // Will be set to "justify"
4933 AlignCommand::GetInstance
,
4934 CommandOnTextEditor::Disabled
));
4935 sInternalCommandDataHashtable
->InsertOrUpdate(
4937 InternalCommandData(
4939 Command::FormatRemove
,
4940 ExecCommandParam::Ignore
,
4941 RemoveStylesCommand::GetInstance
,
4942 CommandOnTextEditor::Disabled
));
4943 sInternalCommandDataHashtable
->InsertOrUpdate(
4945 InternalCommandData(
4947 Command::FormatRemoveLink
,
4948 ExecCommandParam::Ignore
,
4949 StyleUpdatingCommand::GetInstance
,
4950 CommandOnTextEditor::Disabled
));
4951 sInternalCommandDataHashtable
->InsertOrUpdate(
4952 u
"insertorderedlist"_ns
,
4953 InternalCommandData(
4955 Command::InsertOrderedList
,
4956 ExecCommandParam::Ignore
,
4957 ListCommand::GetInstance
,
4958 CommandOnTextEditor::Disabled
));
4959 sInternalCommandDataHashtable
->InsertOrUpdate(
4960 u
"insertunorderedlist"_ns
,
4961 InternalCommandData(
4963 Command::InsertUnorderedList
,
4964 ExecCommandParam::Ignore
,
4965 ListCommand::GetInstance
,
4966 CommandOnTextEditor::Disabled
));
4967 sInternalCommandDataHashtable
->InsertOrUpdate(
4968 u
"insertparagraph"_ns
,
4969 InternalCommandData(
4970 "cmd_insertParagraph",
4971 Command::InsertParagraph
,
4972 ExecCommandParam::Ignore
,
4973 InsertParagraphCommand::GetInstance
,
4974 CommandOnTextEditor::Enabled
));
4975 sInternalCommandDataHashtable
->InsertOrUpdate(
4976 u
"insertlinebreak"_ns
,
4977 InternalCommandData(
4978 "cmd_insertLineBreak",
4979 Command::InsertLineBreak
,
4980 ExecCommandParam::Ignore
,
4981 InsertLineBreakCommand::GetInstance
,
4982 CommandOnTextEditor::Enabled
));
4983 sInternalCommandDataHashtable
->InsertOrUpdate(
4985 InternalCommandData(
4987 Command::FormatBlock
,
4988 ExecCommandParam::String
,
4989 FormatBlockStateCommand::GetInstance
,
4990 CommandOnTextEditor::Disabled
));
4991 sInternalCommandDataHashtable
->InsertOrUpdate(
4993 InternalCommandData(
4994 "cmd_setDocumentUseCSS",
4995 Command::SetDocumentUseCSS
,
4996 ExecCommandParam::Boolean
,
4997 SetDocumentStateCommand::GetInstance
,
4998 CommandOnTextEditor::FallThrough
));
4999 sInternalCommandDataHashtable
->InsertOrUpdate(
5000 u
"usecss"_ns
, // Legacy command
5001 InternalCommandData(
5002 "cmd_setDocumentUseCSS",
5003 Command::SetDocumentUseCSS
,
5004 ExecCommandParam::InvertedBoolean
,
5005 SetDocumentStateCommand::GetInstance
,
5006 CommandOnTextEditor::FallThrough
));
5007 sInternalCommandDataHashtable
->InsertOrUpdate(
5008 u
"contentReadOnly"_ns
,
5009 InternalCommandData(
5010 "cmd_setDocumentReadOnly",
5011 Command::SetDocumentReadOnly
,
5012 ExecCommandParam::Boolean
,
5013 SetDocumentStateCommand::GetInstance
,
5014 CommandOnTextEditor::Enabled
));
5015 sInternalCommandDataHashtable
->InsertOrUpdate(
5016 u
"insertBrOnReturn"_ns
,
5017 InternalCommandData(
5018 "cmd_insertBrOnReturn",
5019 Command::SetDocumentInsertBROnEnterKeyPress
,
5020 ExecCommandParam::Boolean
,
5021 SetDocumentStateCommand::GetInstance
,
5022 CommandOnTextEditor::FallThrough
));
5023 sInternalCommandDataHashtable
->InsertOrUpdate(
5024 u
"defaultParagraphSeparator"_ns
,
5025 InternalCommandData(
5026 "cmd_defaultParagraphSeparator",
5027 Command::SetDocumentDefaultParagraphSeparator
,
5028 ExecCommandParam::String
,
5029 SetDocumentStateCommand::GetInstance
,
5030 CommandOnTextEditor::FallThrough
));
5031 sInternalCommandDataHashtable
->InsertOrUpdate(
5032 u
"enableObjectResizing"_ns
,
5033 InternalCommandData(
5034 "cmd_enableObjectResizing",
5035 Command::ToggleObjectResizers
,
5036 ExecCommandParam::Boolean
,
5037 SetDocumentStateCommand::GetInstance
,
5038 CommandOnTextEditor::FallThrough
));
5039 sInternalCommandDataHashtable
->InsertOrUpdate(
5040 u
"enableInlineTableEditing"_ns
,
5041 InternalCommandData(
5042 "cmd_enableInlineTableEditing",
5043 Command::ToggleInlineTableEditor
,
5044 ExecCommandParam::Boolean
,
5045 SetDocumentStateCommand::GetInstance
,
5046 CommandOnTextEditor::FallThrough
));
5047 sInternalCommandDataHashtable
->InsertOrUpdate(
5048 u
"enableAbsolutePositionEditing"_ns
,
5049 InternalCommandData(
5050 "cmd_enableAbsolutePositionEditing",
5051 Command::ToggleAbsolutePositionEditor
,
5052 ExecCommandParam::Boolean
,
5053 SetDocumentStateCommand::GetInstance
,
5054 CommandOnTextEditor::FallThrough
));
5055 sInternalCommandDataHashtable
->InsertOrUpdate(
5056 u
"enableCompatibleJoinSplitDirection"_ns
,
5057 InternalCommandData("cmd_enableCompatibleJoinSplitNodeDirection",
5058 Command::EnableCompatibleJoinSplitNodeDirection
,
5059 ExecCommandParam::Boolean
,
5060 SetDocumentStateCommand::GetInstance
,
5061 CommandOnTextEditor::FallThrough
));
5063 // with empty string
5064 sInternalCommandDataHashtable
->InsertOrUpdate(
5066 InternalCommandData(
5069 ExecCommandParam::Ignore
,
5071 CommandOnTextEditor::Disabled
)); // Not implemented yet.
5072 // REQUIRED SPECIAL REVIEW special review
5073 sInternalCommandDataHashtable
->InsertOrUpdate(
5075 InternalCommandData(
5078 ExecCommandParam::Boolean
,
5080 CommandOnTextEditor::FallThrough
)); // Not implemented yet.
5081 // REQUIRED SPECIAL REVIEW special review
5082 sInternalCommandDataHashtable
->InsertOrUpdate(
5084 InternalCommandData(
5087 ExecCommandParam::Boolean
,
5089 CommandOnTextEditor::FallThrough
)); // Not implemented yet.
5094 Document::InternalCommandData
Document::ConvertToInternalCommand(
5095 const nsAString
& aHTMLCommandName
,
5096 const TrustedHTMLOrString
* aValue
/* = nullptr */,
5097 ErrorResult
* aRv
/* = nullptr */,
5098 nsAString
* aAdjustedValue
/* = nullptr */) {
5099 MOZ_ASSERT(!aAdjustedValue
|| aAdjustedValue
->IsEmpty());
5100 EnsureInitializeInternalCommandDataHashtable();
5101 InternalCommandData commandData
;
5102 if (!sInternalCommandDataHashtable
->Get(aHTMLCommandName
, &commandData
)) {
5103 return InternalCommandData();
5105 // Ignore if the command is disabled by a corresponding pref due to Gecko
5107 switch (commandData
.mCommand
) {
5108 case Command::SetDocumentReadOnly
:
5109 if (!StaticPrefs::dom_document_edit_command_contentReadOnly_enabled() &&
5110 aHTMLCommandName
.LowerCaseEqualsLiteral("contentreadonly")) {
5111 return InternalCommandData();
5114 case Command::SetDocumentInsertBROnEnterKeyPress
:
5115 MOZ_DIAGNOSTIC_ASSERT(
5116 aHTMLCommandName
.LowerCaseEqualsLiteral("insertbronreturn"));
5117 if (!StaticPrefs::dom_document_edit_command_insertBrOnReturn_enabled()) {
5118 return InternalCommandData();
5124 if (!aAdjustedValue
) {
5125 // No further work to do
5130 Maybe
<nsAutoString
> compliantStringHolder
;
5131 const nsAString
* compliantString
= nullptr;
5132 if (commandData
.mCommand
== Command::InsertHTML
) {
5133 constexpr nsLiteralString sink
= u
"Document execCommand"_ns
;
5134 compliantString
= TrustedTypeUtils::GetTrustedTypesCompliantString(
5135 *aValue
, sink
, kTrustedTypesOnlySinkGroup
, *this, compliantStringHolder
,
5137 if (aRv
->Failed()) {
5138 return InternalCommandData();
5141 compliantString
= aValue
->IsString() ? &aValue
->GetAsString()
5142 : &aValue
->GetAsTrustedHTML().mData
;
5145 switch (commandData
.mExecCommandParam
) {
5146 case ExecCommandParam::Ignore
:
5147 // Just have to copy it, no checking
5148 switch (commandData
.mCommand
) {
5149 case Command::FormatJustifyLeft
:
5150 aAdjustedValue
->AssignLiteral("left");
5152 case Command::FormatJustifyRight
:
5153 aAdjustedValue
->AssignLiteral("right");
5155 case Command::FormatJustifyCenter
:
5156 aAdjustedValue
->AssignLiteral("center");
5158 case Command::FormatJustifyFull
:
5159 aAdjustedValue
->AssignLiteral("justify");
5162 MOZ_ASSERT(EditorCommand::GetParamType(commandData
.mCommand
) ==
5163 EditorCommandParamType::None
);
5168 case ExecCommandParam::Boolean
:
5169 MOZ_ASSERT(!!(EditorCommand::GetParamType(commandData
.mCommand
) &
5170 EditorCommandParamType::Bool
));
5171 // If this is a boolean value and it's not explicitly false (e.g. no
5172 // value). We default to "true" (see bug 301490).
5173 if (!compliantString
->LowerCaseEqualsLiteral("false")) {
5174 aAdjustedValue
->AssignLiteral("true");
5176 aAdjustedValue
->AssignLiteral("false");
5180 case ExecCommandParam::InvertedBoolean
:
5181 MOZ_ASSERT(!!(EditorCommand::GetParamType(commandData
.mCommand
) &
5182 EditorCommandParamType::Bool
));
5183 // For old backwards commands we invert the check.
5184 if (compliantString
->LowerCaseEqualsLiteral("false")) {
5185 aAdjustedValue
->AssignLiteral("true");
5187 aAdjustedValue
->AssignLiteral("false");
5191 case ExecCommandParam::String
:
5193 EditorCommand::GetParamType(commandData
.mCommand
) &
5194 (EditorCommandParamType::String
| EditorCommandParamType::CString
)));
5195 switch (commandData
.mCommand
) {
5196 case Command::FormatBlock
: {
5197 const char16_t
* start
= compliantString
->BeginReading();
5198 const char16_t
* end
= compliantString
->EndReading();
5199 if (start
!= end
&& *start
== '<' && *(end
- 1) == '>') {
5203 // XXX Should we reorder this array with actual usage?
5204 static const nsStaticAtom
* kFormattableBlockTags
[] = {
5209 nsGkAtoms::blockquote
,
5230 nsAutoString
value(nsDependentSubstring(start
, end
));
5232 const nsStaticAtom
* valueAtom
= NS_GetStaticAtom(value
);
5233 for (const nsStaticAtom
* kTag
: kFormattableBlockTags
) {
5234 if (valueAtom
== kTag
) {
5235 kTag
->ToString(*aAdjustedValue
);
5239 return InternalCommandData();
5241 case Command::FormatFontSize
: {
5242 // Per editing spec as of April 23, 2012, we need to reject the value
5243 // if it's not a valid floating-point number surrounded by optional
5244 // whitespace. Otherwise, we parse it as a legacy font size. For
5245 // now, we just parse as a legacy font size regardless (matching
5246 // WebKit) -- bug 747879.
5247 int32_t size
= nsContentUtils::ParseLegacyFontSize(*compliantString
);
5249 return InternalCommandData();
5251 MOZ_ASSERT(aAdjustedValue
->IsEmpty());
5252 aAdjustedValue
->AppendInt(size
);
5255 case Command::InsertImage
:
5256 case Command::InsertLink
:
5257 if (compliantString
->IsEmpty()) {
5258 // Invalid value, return false
5259 return InternalCommandData();
5261 aAdjustedValue
->Assign(*compliantString
);
5263 case Command::SetDocumentDefaultParagraphSeparator
:
5264 if (!compliantString
->LowerCaseEqualsLiteral("div") &&
5265 !compliantString
->LowerCaseEqualsLiteral("p") &&
5266 !compliantString
->LowerCaseEqualsLiteral("br")) {
5268 return InternalCommandData();
5270 aAdjustedValue
->Assign(*compliantString
);
5273 aAdjustedValue
->Assign(*compliantString
);
5278 MOZ_ASSERT_UNREACHABLE("New ExecCommandParam value hasn't been handled");
5279 return InternalCommandData();
5283 Document::AutoEditorCommandTarget::AutoEditorCommandTarget(
5284 Document
& aDocument
, const InternalCommandData
& aCommandData
)
5285 : mCommandData(aCommandData
) {
5286 // We'll retrieve an editor with current DOM tree and layout information.
5287 // However, JS may have already hidden or remove exposed root content of
5288 // the editor. Therefore, we need the latest layout information here.
5289 aDocument
.FlushPendingNotifications(FlushType::Layout
);
5290 if (!aDocument
.GetPresShell() || aDocument
.GetPresShell()->IsDestroying()) {
5295 if (nsPresContext
* presContext
= aDocument
.GetPresContext()) {
5296 // Consider context of command handling which is automatically resolved
5297 // by order of controllers in `nsCommandManager::GetControllerForCommand()`.
5299 // 1. TextEditor if there is an active element and it has TextEditor like
5300 // <input type="text"> or <textarea>.
5301 // 2. HTMLEditor for the document, if there is.
5302 // 3. Retarget to the DocShell or nsCommandManager as what we've done.
5303 if (aCommandData
.IsCutOrCopyCommand()) {
5304 // Note that we used to use DocShell to handle `cut` and `copy` command
5305 // for dispatching corresponding events for making possible web apps to
5306 // implement their own editor without editable elements but supports
5307 // standard shortcut keys, etc. In this case, we prefer to use active
5308 // element's editor to keep same behavior.
5309 mActiveEditor
= nsContentUtils::GetActiveEditor(presContext
);
5311 mActiveEditor
= nsContentUtils::GetActiveEditor(presContext
);
5312 mHTMLEditor
= nsContentUtils::GetHTMLEditor(presContext
);
5313 if (!mActiveEditor
) {
5314 mActiveEditor
= mHTMLEditor
;
5319 // Then, retrieve editor command class instance which should handle it
5320 // and can handle it now.
5321 if (!mActiveEditor
) {
5322 // If the command is available without editor, we should redirect the
5323 // command to focused descendant with DocShell.
5324 if (aCommandData
.IsAvailableOnlyWhenEditable()) {
5331 // Otherwise, we should use EditorCommand instance (which is singleton
5332 // instance) when it's enabled.
5333 mEditorCommand
= aCommandData
.mGetEditorCommandFunc
5334 ? aCommandData
.mGetEditorCommandFunc()
5336 if (!mEditorCommand
) {
5338 mActiveEditor
= nullptr;
5339 mHTMLEditor
= nullptr;
5343 if (IsCommandEnabled()) {
5347 // If the EditorCommand instance is disabled, we should do nothing if
5348 // the command requires an editor.
5349 if (aCommandData
.IsAvailableOnlyWhenEditable()) {
5350 // Do nothing if editor specific commands is disabled (bug 760052).
5355 // Otherwise, we should redirect it to focused descendant with DocShell.
5356 mEditorCommand
= nullptr;
5357 mActiveEditor
= nullptr;
5358 mHTMLEditor
= nullptr;
5361 EditorBase
* Document::AutoEditorCommandTarget::GetTargetEditor() const {
5362 using CommandOnTextEditor
= InternalCommandData::CommandOnTextEditor
;
5363 switch (mCommandData
.mCommandOnTextEditor
) {
5364 case CommandOnTextEditor::Enabled
:
5365 return mActiveEditor
;
5366 case CommandOnTextEditor::Disabled
:
5367 return mActiveEditor
&& mActiveEditor
->IsTextEditor()
5369 : mActiveEditor
.get();
5370 case CommandOnTextEditor::FallThrough
:
5376 bool Document::AutoEditorCommandTarget::IsEditable(Document
* aDocument
) const {
5377 if (RefPtr
<Document
> doc
= aDocument
->GetInProcessParentDocument()) {
5378 // Make sure frames are up to date, since that can affect whether
5380 doc
->FlushPendingNotifications(FlushType::Frames
);
5382 EditorBase
* targetEditor
= GetTargetEditor();
5383 if (targetEditor
&& targetEditor
->IsTextEditor()) {
5384 // FYI: When `disabled` attribute is set, `TextEditor` treats it as
5386 return !targetEditor
->IsReadonly();
5388 return aDocument
->IsEditingOn();
5391 bool Document::AutoEditorCommandTarget::IsCommandEnabled() const {
5392 EditorBase
* targetEditor
= GetTargetEditor();
5393 if (!targetEditor
) {
5396 MOZ_ASSERT(targetEditor
== mActiveEditor
|| targetEditor
== mHTMLEditor
);
5397 return MOZ_KnownLive(mEditorCommand
)
5398 ->IsCommandEnabled(mCommandData
.mCommand
, MOZ_KnownLive(targetEditor
));
5401 nsresult
Document::AutoEditorCommandTarget::DoCommand(
5402 nsIPrincipal
* aPrincipal
) const {
5403 MOZ_ASSERT(!DoNothing());
5404 MOZ_ASSERT(mEditorCommand
);
5405 EditorBase
* targetEditor
= GetTargetEditor();
5406 if (!targetEditor
) {
5407 return NS_SUCCESS_DOM_NO_OPERATION
;
5409 MOZ_ASSERT(targetEditor
== mActiveEditor
|| targetEditor
== mHTMLEditor
);
5410 return MOZ_KnownLive(mEditorCommand
)
5411 ->DoCommand(mCommandData
.mCommand
, MOZ_KnownLive(*targetEditor
),
5415 template <typename ParamType
>
5416 nsresult
Document::AutoEditorCommandTarget::DoCommandParam(
5417 const ParamType
& aParam
, nsIPrincipal
* aPrincipal
) const {
5418 MOZ_ASSERT(!DoNothing());
5419 MOZ_ASSERT(mEditorCommand
);
5420 EditorBase
* targetEditor
= GetTargetEditor();
5421 if (!targetEditor
) {
5422 return NS_SUCCESS_DOM_NO_OPERATION
;
5424 MOZ_ASSERT(targetEditor
== mActiveEditor
|| targetEditor
== mHTMLEditor
);
5425 return MOZ_KnownLive(mEditorCommand
)
5426 ->DoCommandParam(mCommandData
.mCommand
, aParam
,
5427 MOZ_KnownLive(*targetEditor
), aPrincipal
);
5430 nsresult
Document::AutoEditorCommandTarget::GetCommandStateParams(
5431 nsCommandParams
& aParams
) const {
5432 MOZ_ASSERT(mEditorCommand
);
5433 EditorBase
* targetEditor
= GetTargetEditor();
5434 if (!targetEditor
) {
5437 MOZ_ASSERT(targetEditor
== mActiveEditor
|| targetEditor
== mHTMLEditor
);
5438 return MOZ_KnownLive(mEditorCommand
)
5439 ->GetCommandStateParams(mCommandData
.mCommand
, MOZ_KnownLive(aParams
),
5440 MOZ_KnownLive(targetEditor
), nullptr);
5443 Document::AutoRunningExecCommandMarker::AutoRunningExecCommandMarker(
5444 Document
& aDocument
, nsIPrincipal
* aPrincipal
)
5445 : mDocument(aDocument
),
5446 mTreatAsUserInput(EditorBase::TreatAsUserInput(aPrincipal
)),
5447 mHasBeenRunningByContent(aDocument
.mIsRunningExecCommandByContent
),
5448 mHasBeenRunningByChromeOrAddon(
5449 aDocument
.mIsRunningExecCommandByChromeOrAddon
) {
5450 if (mTreatAsUserInput
) {
5451 aDocument
.mIsRunningExecCommandByChromeOrAddon
= true;
5453 aDocument
.mIsRunningExecCommandByContent
= true;
5457 bool Document::ExecCommand(const nsAString
& aHTMLCommandName
, bool aShowUI
,
5458 const TrustedHTMLOrString
& aValue
,
5459 nsIPrincipal
& aSubjectPrincipal
, ErrorResult
& aRv
) {
5460 // Only allow on HTML documents.
5461 if (!IsHTMLOrXHTML()) {
5462 aRv
.ThrowInvalidStateError(
5463 "execCommand is only supported on HTML documents");
5466 // Otherwise, don't throw exception for compatibility with Chrome.
5468 // if they are requesting UI from us, let's fail since we have no UI
5473 // for optional parameters see dom/src/base/nsHistory.cpp: HistoryImpl::Go()
5474 // this might add some ugly JS dependencies?
5476 nsAutoString adjustedValue
;
5477 InternalCommandData commandData
=
5478 ConvertToInternalCommand(aHTMLCommandName
, &aValue
, &aRv
, &adjustedValue
);
5479 switch (commandData
.mCommand
) {
5480 case Command::DoNothing
:
5482 case Command::SetDocumentReadOnly
:
5483 SetUseCounter(eUseCounter_custom_DocumentExecCommandContentReadOnly
);
5485 case Command::EnableCompatibleJoinSplitNodeDirection
:
5486 // We didn't allow to enable the legacy behavior once we've enabled the
5487 // new behavior by default. For keeping the behavior at supporting both
5488 // mode, we should keep returning `false` if the web app to enable the
5489 // legacy mode. Additionally, we don't support the legacy direction
5490 // anymore. Therefore, we can return `false` here even if the caller is
5491 // an addon or chrome script.
5492 if (!adjustedValue
.EqualsLiteral("true")) {
5500 AutoRunningExecCommandMarker
markRunningExecCommand(*this,
5501 &aSubjectPrincipal
);
5503 // If we're running an execCommand, we should just return false.
5504 // https://github.com/w3c/editing/issues/200#issuecomment-575241816
5505 if (!markRunningExecCommand
.IsSafeToRun()) {
5509 // Do security check first.
5510 if (commandData
.IsCutOrCopyCommand()) {
5511 if (!nsContentUtils::IsCutCopyAllowed(this, aSubjectPrincipal
)) {
5512 // We have rejected the event due to it not being performed in an
5513 // input-driven context therefore, we report the error to the console.
5514 nsContentUtils::ReportToConsole(nsIScriptError::warningFlag
, "DOM"_ns
,
5515 this, nsContentUtils::eDOM_PROPERTIES
,
5516 "ExecCommandCutCopyDeniedNotInputDriven");
5519 } else if (commandData
.IsPasteCommand()) {
5520 if (!nsContentUtils::PrincipalHasPermission(aSubjectPrincipal
,
5521 nsGkAtoms::clipboardRead
)) {
5526 // Next, consider context of command handling which is automatically resolved
5527 // by order of controllers in `nsCommandManager::GetControllerForCommand()`.
5528 AutoEditorCommandTarget
editCommandTarget(*this, commandData
);
5529 if (commandData
.IsAvailableOnlyWhenEditable()) {
5530 if (!editCommandTarget
.IsEditable(this)) {
5533 // If currently the editor cannot dispatch `input` events, it means that the
5534 // editor value is being set and that caused unexpected composition events.
5535 // In this case, the value will be updated to the setting value soon and
5536 // Chromium does not dispatch any events during the sequence but we dispatch
5537 // `compositionupdate` and `compositionend` events to conform to the UI
5538 // Events spec. Therefore, this execCommand must be called accidentally.
5539 EditorBase
* targetEditor
= editCommandTarget
.GetTargetEditor();
5540 if (targetEditor
&& targetEditor
->IsSuppressingDispatchingInputEvent()) {
5545 if (editCommandTarget
.DoNothing()) {
5549 // If we cannot use EditorCommand instance directly, we need to handle the
5550 // command with traditional path (i.e., with DocShell or nsCommandManager).
5551 if (!editCommandTarget
.IsEditor()) {
5552 MOZ_ASSERT(!commandData
.IsAvailableOnlyWhenEditable());
5554 // Special case clipboard write commands like Command::Cut and
5555 // Command::Copy. For such commands, we need the behaviour from
5556 // nsWindowRoot::GetControllers() which is to look at the focused element,
5557 // and defer to a focused textbox's controller. The code past taken by
5558 // other commands in ExecCommand() always uses the window directly, rather
5559 // than deferring to the textbox, which is desireable for most editor
5560 // commands, but not these commands (as those should allow copying out of
5561 // embedded editors). This behaviour is invoked if we call DoCommand()
5562 // directly on the docShell.
5563 // XXX This means that we allow web app to pick up selected content in
5564 // descendant document and write it into the clipboard when a
5565 // descendant document has focus. However, Chromium does not allow
5566 // this and this seems that it's not good behavior from point of view
5567 // of security. We should treat this issue in another bug.
5568 if (commandData
.IsCutOrCopyCommand()) {
5569 nsCOMPtr
<nsIDocShell
> docShell(mDocumentContainer
);
5573 nsresult rv
= docShell
->DoCommand(commandData
.mXULCommandName
);
5574 if (rv
== NS_SUCCESS_DOM_NO_OPERATION
) {
5577 return NS_SUCCEEDED(rv
);
5580 // Otherwise (currently, only clipboard read commands like Command::Paste),
5581 // we don't need to redirect the command to focused subdocument.
5582 // Therefore, we should handle it with nsCommandManager as used to be.
5583 // It may dispatch only preceding event of editing on non-editable element
5584 // to make web apps possible to handle standard shortcut key, etc in
5585 // their own editor.
5586 RefPtr
<nsCommandManager
> commandManager
= GetMidasCommandManager();
5587 if (!commandManager
) {
5591 nsCOMPtr
<nsPIDOMWindowOuter
> window
= GetWindow();
5596 // Return false for disabled commands (bug 760052)
5597 if (!commandManager
->IsCommandEnabled(
5598 nsDependentCString(commandData
.mXULCommandName
), window
)) {
5602 MOZ_ASSERT(commandData
.IsPasteCommand() ||
5603 commandData
.mCommand
== Command::SelectAll
);
5605 commandManager
->DoCommand(commandData
.mXULCommandName
, nullptr, window
);
5606 return NS_SUCCEEDED(rv
) && rv
!= NS_SUCCESS_DOM_NO_OPERATION
;
5609 // Now, our target is fixed to the editor. So, we can use EditorCommand
5610 // in EditorCommandTarget directly.
5612 EditorCommandParamType paramType
=
5613 EditorCommand::GetParamType(commandData
.mCommand
);
5615 // If we don't have meaningful parameter or the EditorCommand does not
5616 // require additional parameter, we can use `DoCommand()`.
5617 if (adjustedValue
.IsEmpty() || paramType
== EditorCommandParamType::None
) {
5618 MOZ_ASSERT(!(paramType
& EditorCommandParamType::Bool
));
5619 nsresult rv
= editCommandTarget
.DoCommand(&aSubjectPrincipal
);
5620 return NS_SUCCEEDED(rv
) && rv
!= NS_SUCCESS_DOM_NO_OPERATION
;
5623 // If the EditorCommand requires `bool` parameter, `adjustedValue` must be
5624 // "true" or "false" here. So, we can use `DoCommandParam()` which takes
5626 if (!!(paramType
& EditorCommandParamType::Bool
)) {
5627 MOZ_ASSERT(adjustedValue
.EqualsLiteral("true") ||
5628 adjustedValue
.EqualsLiteral("false"));
5629 nsresult rv
= editCommandTarget
.DoCommandParam(
5630 Some(adjustedValue
.EqualsLiteral("true")), &aSubjectPrincipal
);
5631 return NS_SUCCEEDED(rv
) && rv
!= NS_SUCCESS_DOM_NO_OPERATION
;
5634 // Now, the EditorCommand requires `nsAString` or `nsACString` parameter
5635 // in this case. However, `paramType` may contain both `String` and
5636 // `CString` but in such case, we should use `DoCommandParam()` which
5637 // takes `nsAString`. So, we should check whether `paramType` contains
5638 // `String` or not first.
5639 if (!!(paramType
& EditorCommandParamType::String
)) {
5640 MOZ_ASSERT(!adjustedValue
.IsVoid());
5642 editCommandTarget
.DoCommandParam(adjustedValue
, &aSubjectPrincipal
);
5643 return NS_SUCCEEDED(rv
) && rv
!= NS_SUCCESS_DOM_NO_OPERATION
;
5646 // Finally, `paramType` should have `CString`. We should use
5647 // `DoCommandParam()` which takes `nsACString`.
5648 if (!!(paramType
& EditorCommandParamType::CString
)) {
5649 NS_ConvertUTF16toUTF8
utf8Value(adjustedValue
);
5650 MOZ_ASSERT(!utf8Value
.IsVoid());
5652 editCommandTarget
.DoCommandParam(utf8Value
, &aSubjectPrincipal
);
5653 return NS_SUCCEEDED(rv
) && rv
!= NS_SUCCESS_DOM_NO_OPERATION
;
5656 MOZ_ASSERT_UNREACHABLE(
5657 "Not yet implemented to handle new EditorCommandParamType");
5661 bool Document::QueryCommandEnabled(const nsAString
& aHTMLCommandName
,
5662 nsIPrincipal
& aSubjectPrincipal
,
5664 // Only allow on HTML documents.
5665 if (!IsHTMLOrXHTML()) {
5666 aRv
.ThrowInvalidStateError(
5667 "queryCommandEnabled is only supported on HTML documents");
5670 // Otherwise, don't throw exception for compatibility with Chrome.
5672 InternalCommandData commandData
= ConvertToInternalCommand(aHTMLCommandName
);
5673 switch (commandData
.mCommand
) {
5674 case Command::DoNothing
:
5676 case Command::SetDocumentReadOnly
:
5678 eUseCounter_custom_DocumentQueryCommandSupportedOrEnabledContentReadOnly
);
5680 case Command::SetDocumentInsertBROnEnterKeyPress
:
5682 eUseCounter_custom_DocumentQueryCommandSupportedOrEnabledInsertBrOnReturn
);
5688 // cut & copy are always allowed
5689 if (commandData
.IsCutOrCopyCommand()) {
5690 return nsContentUtils::IsCutCopyAllowed(this, aSubjectPrincipal
);
5693 // Report false for restricted commands
5694 if (commandData
.IsPasteCommand() && !aSubjectPrincipal
.IsSystemPrincipal()) {
5698 AutoEditorCommandTarget
editCommandTarget(*this, commandData
);
5699 if (commandData
.IsAvailableOnlyWhenEditable() &&
5700 !editCommandTarget
.IsEditable(this)) {
5704 if (editCommandTarget
.IsEditor()) {
5705 return editCommandTarget
.IsCommandEnabled();
5708 // get command manager and dispatch command to our window if it's acceptable
5709 RefPtr
<nsCommandManager
> commandManager
= GetMidasCommandManager();
5710 if (!commandManager
) {
5714 nsPIDOMWindowOuter
* window
= GetWindow();
5719 return commandManager
->IsCommandEnabled(
5720 nsDependentCString(commandData
.mXULCommandName
), window
);
5723 bool Document::QueryCommandIndeterm(const nsAString
& aHTMLCommandName
,
5725 // Only allow on HTML documents.
5726 if (!IsHTMLOrXHTML()) {
5727 aRv
.ThrowInvalidStateError(
5728 "queryCommandIndeterm is only supported on HTML documents");
5731 // Otherwise, don't throw exception for compatibility with Chrome.
5733 InternalCommandData commandData
= ConvertToInternalCommand(aHTMLCommandName
);
5734 if (commandData
.mCommand
== Command::DoNothing
) {
5738 AutoEditorCommandTarget
editCommandTarget(*this, commandData
);
5739 if (commandData
.IsAvailableOnlyWhenEditable() &&
5740 !editCommandTarget
.IsEditable(this)) {
5743 RefPtr
<nsCommandParams
> params
= new nsCommandParams();
5744 if (editCommandTarget
.IsEditor()) {
5745 if (NS_FAILED(editCommandTarget
.GetCommandStateParams(*params
))) {
5749 // get command manager and dispatch command to our window if it's acceptable
5750 RefPtr
<nsCommandManager
> commandManager
= GetMidasCommandManager();
5751 if (!commandManager
) {
5755 nsPIDOMWindowOuter
* window
= GetWindow();
5760 if (NS_FAILED(commandManager
->GetCommandState(commandData
.mXULCommandName
,
5766 // If command does not have a state_mixed value, this call fails and sets
5767 // retval to false. This is fine -- we want to return false in that case
5768 // anyway (bug 738385), so we just don't throw regardless.
5769 return params
->GetBool("state_mixed");
5772 bool Document::QueryCommandState(const nsAString
& aHTMLCommandName
,
5774 // Only allow on HTML documents.
5775 if (!IsHTMLOrXHTML()) {
5776 aRv
.ThrowInvalidStateError(
5777 "queryCommandState is only supported on HTML documents");
5780 // Otherwise, don't throw exception for compatibility with Chrome.
5782 InternalCommandData commandData
= ConvertToInternalCommand(aHTMLCommandName
);
5783 switch (commandData
.mCommand
) {
5784 case Command::DoNothing
:
5786 case Command::SetDocumentReadOnly
:
5788 eUseCounter_custom_DocumentQueryCommandStateOrValueContentReadOnly
);
5790 case Command::SetDocumentInsertBROnEnterKeyPress
:
5792 eUseCounter_custom_DocumentQueryCommandStateOrValueInsertBrOnReturn
);
5798 if (aHTMLCommandName
.LowerCaseEqualsLiteral("usecss")) {
5799 // Per spec, state is supported for styleWithCSS but not useCSS, so we just
5800 // return false always.
5804 AutoEditorCommandTarget
editCommandTarget(*this, commandData
);
5805 if (commandData
.IsAvailableOnlyWhenEditable() &&
5806 !editCommandTarget
.IsEditable(this)) {
5809 RefPtr
<nsCommandParams
> params
= new nsCommandParams();
5810 if (editCommandTarget
.IsEditor()) {
5811 if (NS_FAILED(editCommandTarget
.GetCommandStateParams(*params
))) {
5815 // get command manager and dispatch command to our window if it's acceptable
5816 RefPtr
<nsCommandManager
> commandManager
= GetMidasCommandManager();
5817 if (!commandManager
) {
5821 nsPIDOMWindowOuter
* window
= GetWindow();
5826 if (NS_FAILED(commandManager
->GetCommandState(commandData
.mXULCommandName
,
5832 // handle alignment as a special case (possibly other commands too?)
5833 // Alignment is special because the external api is individual
5834 // commands but internally we use cmd_align with different
5835 // parameters. When getting the state of this command, we need to
5836 // return the boolean for this particular alignment rather than the
5837 // string of 'which alignment is this?'
5838 switch (commandData
.mCommand
) {
5839 case Command::FormatJustifyLeft
: {
5840 nsAutoCString currentValue
;
5841 nsresult rv
= params
->GetCString("state_attribute", currentValue
);
5842 if (NS_FAILED(rv
)) {
5845 return currentValue
.EqualsLiteral("left");
5847 case Command::FormatJustifyRight
: {
5848 nsAutoCString currentValue
;
5849 nsresult rv
= params
->GetCString("state_attribute", currentValue
);
5850 if (NS_FAILED(rv
)) {
5853 return currentValue
.EqualsLiteral("right");
5855 case Command::FormatJustifyCenter
: {
5856 nsAutoCString currentValue
;
5857 nsresult rv
= params
->GetCString("state_attribute", currentValue
);
5858 if (NS_FAILED(rv
)) {
5861 return currentValue
.EqualsLiteral("center");
5863 case Command::FormatJustifyFull
: {
5864 nsAutoCString currentValue
;
5865 nsresult rv
= params
->GetCString("state_attribute", currentValue
);
5866 if (NS_FAILED(rv
)) {
5869 return currentValue
.EqualsLiteral("justify");
5875 // If command does not have a state_all value, this call fails and sets
5876 // retval to false. This is fine -- we want to return false in that case
5877 // anyway (bug 738385), so we just succeed and return false regardless.
5878 return params
->GetBool("state_all");
5881 bool Document::QueryCommandSupported(const nsAString
& aHTMLCommandName
,
5882 CallerType aCallerType
, ErrorResult
& aRv
) {
5883 // Only allow on HTML documents.
5884 if (!IsHTMLOrXHTML()) {
5885 aRv
.ThrowInvalidStateError(
5886 "queryCommandSupported is only supported on HTML documents");
5889 // Otherwise, don't throw exception for compatibility with Chrome.
5891 InternalCommandData commandData
= ConvertToInternalCommand(aHTMLCommandName
);
5892 switch (commandData
.mCommand
) {
5893 case Command::DoNothing
:
5895 case Command::SetDocumentReadOnly
:
5897 eUseCounter_custom_DocumentQueryCommandSupportedOrEnabledContentReadOnly
);
5899 case Command::SetDocumentInsertBROnEnterKeyPress
:
5901 eUseCounter_custom_DocumentQueryCommandSupportedOrEnabledInsertBrOnReturn
);
5907 // Gecko technically supports all the clipboard commands including
5908 // cut/copy/paste, but non-privileged content will be unable to call
5909 // paste, and depending on the pref "dom.allow_cut_copy", cut and copy
5910 // may also be disallowed to be called from non-privileged content.
5911 // For that reason, we report the support status of corresponding
5912 // command accordingly.
5913 if (aCallerType
!= CallerType::System
) {
5914 if (commandData
.IsPasteCommand()) {
5917 if (commandData
.IsCutOrCopyCommand() &&
5918 !StaticPrefs::dom_allow_cut_copy()) {
5919 // XXXbz should we worry about correctly reporting "true" in the
5920 // "restricted, but we're an addon with clipboardWrite permissions" case?
5921 // See also nsContentUtils::IsCutCopyAllowed.
5926 // aHTMLCommandName is supported if it can be converted to a Midas command
5930 void Document::QueryCommandValue(const nsAString
& aHTMLCommandName
,
5931 nsAString
& aValue
, ErrorResult
& aRv
) {
5934 // Only allow on HTML documents.
5935 if (!IsHTMLOrXHTML()) {
5936 aRv
.ThrowInvalidStateError(
5937 "queryCommandValue is only supported on HTML documents");
5940 // Otherwise, don't throw exception for compatibility with Chrome.
5942 InternalCommandData commandData
= ConvertToInternalCommand(aHTMLCommandName
);
5943 switch (commandData
.mCommand
) {
5944 case Command::DoNothing
:
5945 // Return empty string
5947 case Command::SetDocumentReadOnly
:
5949 eUseCounter_custom_DocumentQueryCommandStateOrValueContentReadOnly
);
5951 case Command::SetDocumentInsertBROnEnterKeyPress
:
5953 eUseCounter_custom_DocumentQueryCommandStateOrValueInsertBrOnReturn
);
5959 AutoEditorCommandTarget
editCommandTarget(*this, commandData
);
5960 if (commandData
.IsAvailableOnlyWhenEditable() &&
5961 !editCommandTarget
.IsEditable(this)) {
5964 RefPtr
<nsCommandParams
> params
= new nsCommandParams();
5965 if (editCommandTarget
.IsEditor()) {
5966 if (NS_FAILED(params
->SetCString("state_attribute", ""_ns
))) {
5970 if (NS_FAILED(editCommandTarget
.GetCommandStateParams(*params
))) {
5974 // get command manager and dispatch command to our window if it's acceptable
5975 RefPtr
<nsCommandManager
> commandManager
= GetMidasCommandManager();
5976 if (!commandManager
) {
5980 nsCOMPtr
<nsPIDOMWindowOuter
> window
= GetWindow();
5985 if (NS_FAILED(params
->SetCString("state_attribute", ""_ns
))) {
5989 if (NS_FAILED(commandManager
->GetCommandState(commandData
.mXULCommandName
,
5995 // If command does not have a state_attribute value, this call fails, and
5996 // aValue will wind up being the empty string. This is fine -- we want to
5997 // return "" in that case anyway (bug 738385), so we just return NS_OK
5999 nsAutoCString result
;
6000 params
->GetCString("state_attribute", result
);
6001 CopyUTF8toUTF16(result
, aValue
);
6004 void Document::MaybeEditingStateChanged() {
6005 if (!mPendingMaybeEditingStateChanged
&& mMayStartLayout
&&
6006 mUpdateNestLevel
== 0 && (mContentEditableCount
> 0) != IsEditingOn()) {
6007 if (nsContentUtils::IsSafeToRunScript()) {
6008 EditingStateChanged();
6009 } else if (!mInDestructor
) {
6010 nsContentUtils::AddScriptRunner(
6011 NewRunnableMethod("Document::MaybeEditingStateChanged", this,
6012 &Document::MaybeEditingStateChanged
));
6017 void Document::NotifyFetchOrXHRSuccess() {
6018 if (mShouldNotifyFetchSuccess
) {
6019 nsContentUtils::DispatchEventOnlyToChrome(
6020 this, this, u
"DOMDocFetchSuccess"_ns
, CanBubble::eNo
, Cancelable::eNo
,
6021 /* DefaultAction */ nullptr);
6025 void Document::SetNotifyFetchSuccess(bool aShouldNotify
) {
6026 mShouldNotifyFetchSuccess
= aShouldNotify
;
6029 void Document::SetNotifyFormOrPasswordRemoved(bool aShouldNotify
) {
6030 mShouldNotifyFormOrPasswordRemoved
= aShouldNotify
;
6033 void Document::TearingDownEditor() {
6034 if (IsEditingOn()) {
6035 mEditingState
= EditingState::eTearingDown
;
6039 nsresult
Document::TurnEditingOff() {
6040 NS_ASSERTION(mEditingState
!= EditingState::eOff
, "Editing is already off.");
6042 nsPIDOMWindowOuter
* window
= GetWindow();
6044 return NS_ERROR_FAILURE
;
6047 nsIDocShell
* docshell
= window
->GetDocShell();
6049 return NS_ERROR_FAILURE
;
6052 bool isBeingDestroyed
= false;
6053 docshell
->IsBeingDestroyed(&isBeingDestroyed
);
6054 if (isBeingDestroyed
) {
6055 return NS_ERROR_FAILURE
;
6058 nsCOMPtr
<nsIEditingSession
> editSession
;
6059 nsresult rv
= docshell
->GetEditingSession(getter_AddRefs(editSession
));
6060 NS_ENSURE_SUCCESS(rv
, rv
);
6063 rv
= editSession
->TearDownEditorOnWindow(window
);
6064 NS_ENSURE_SUCCESS(rv
, rv
);
6066 mEditingState
= EditingState::eOff
;
6068 // Editor resets selection since it is being destroyed. But if focus is
6069 // still into editable control, we have to initialize selection again.
6070 if (RefPtr
<TextControlElement
> textControlElement
=
6071 TextControlElement::FromNodeOrNull(
6072 nsFocusManager::GetFocusedElementStatic())) {
6073 if (RefPtr
<TextEditor
> textEditor
= textControlElement
->GetTextEditor()) {
6074 textEditor
->ReinitializeSelection(*textControlElement
);
6081 static bool HasPresShell(nsPIDOMWindowOuter
* aWindow
) {
6082 nsIDocShell
* docShell
= aWindow
->GetDocShell();
6086 return docShell
->GetPresShell() != nullptr;
6089 HTMLEditor
* Document::GetHTMLEditor() const {
6090 nsPIDOMWindowOuter
* window
= GetWindow();
6095 nsIDocShell
* docshell
= window
->GetDocShell();
6100 return docshell
->GetHTMLEditor();
6103 nsresult
Document::EditingStateChanged() {
6104 if (mRemovedFromDocShell
) {
6108 if (mEditingState
== EditingState::eSettingUp
||
6109 mEditingState
== EditingState::eTearingDown
) {
6110 // XXX We shouldn't recurse
6114 const bool designMode
= IsInDesignMode();
6115 const EditingState newState
=
6116 designMode
? EditingState::eDesignMode
6117 : (mContentEditableCount
> 0 ? EditingState::eContentEditable
6118 : EditingState::eOff
);
6119 if (mEditingState
== newState
) {
6120 // No changes in editing mode.
6124 const bool thisDocumentHasFocus
= ThisDocumentHasFocus();
6125 if (newState
== EditingState::eOff
) {
6126 // Editing is being turned off.
6127 nsAutoScriptBlocker scriptBlocker
;
6128 RefPtr
<HTMLEditor
> htmlEditor
= GetHTMLEditor();
6129 nsresult rv
= TurnEditingOff();
6130 // If this document has focus and the editing state of this document
6131 // becomes "off", it means that HTMLEditor won't handle any inputs nor
6132 // modify the DOM tree. However, HTMLEditor may not receive `blur`
6133 // event for this state change since this may occur without focus change.
6134 // Therefore, let's notify HTMLEditor of this editing state change.
6135 // Note that even if focusedElement is an editable text control element,
6136 // it becomes not editable from HTMLEditor point of view since text
6137 // control elements are manged by TextEditor.
6138 RefPtr
<Element
> focusedElement
= nsFocusManager::GetFocusedElementStatic();
6139 DebugOnly
<nsresult
> rvIgnored
=
6140 HTMLEditor::FocusedElementOrDocumentBecomesNotEditable(
6141 htmlEditor
, *this, focusedElement
);
6142 NS_WARNING_ASSERTION(
6143 NS_SUCCEEDED(rvIgnored
),
6144 "HTMLEditor::FocusedElementOrDocumentBecomesNotEditable() failed, but "
6149 const EditingState oldState
= mEditingState
;
6150 MOZ_ASSERT(newState
== EditingState::eDesignMode
||
6151 newState
== EditingState::eContentEditable
);
6152 MOZ_ASSERT_IF(newState
== EditingState::eDesignMode
,
6153 oldState
== EditingState::eContentEditable
||
6154 oldState
== EditingState::eOff
);
6156 newState
== EditingState::eContentEditable
,
6157 oldState
== EditingState::eDesignMode
|| oldState
== EditingState::eOff
);
6159 // Flush out style changes on our _parent_ document, if any, so that
6160 // our check for a presshell won't get stale information.
6161 if (mParentDocument
) {
6162 mParentDocument
->FlushPendingNotifications(FlushType::Style
);
6165 // get editing session, make sure this is a strong reference so the
6166 // window can't get deleted during the rest of this call.
6167 const nsCOMPtr
<nsPIDOMWindowOuter
> window
= GetWindow();
6169 return NS_ERROR_FAILURE
;
6172 nsIDocShell
* docshell
= window
->GetDocShell();
6174 return NS_ERROR_FAILURE
;
6177 // FlushPendingNotifications might destroy our docshell.
6178 bool isBeingDestroyed
= false;
6179 docshell
->IsBeingDestroyed(&isBeingDestroyed
);
6180 if (isBeingDestroyed
) {
6181 return NS_ERROR_FAILURE
;
6184 nsCOMPtr
<nsIEditingSession
> editSession
;
6185 nsresult rv
= docshell
->GetEditingSession(getter_AddRefs(editSession
));
6186 NS_ENSURE_SUCCESS(rv
, rv
);
6188 RefPtr
<HTMLEditor
> htmlEditor
= editSession
->GetHTMLEditorForWindow(window
);
6190 // We might already have an editor if it was set up for mail, let's see
6191 // if this is actually the case.
6193 htmlEditor
->GetFlags(&flags
);
6194 if (flags
& nsIEditor::eEditorMailMask
) {
6195 // We already have a mail editor, then we should not attempt to create
6201 if (!HasPresShell(window
)) {
6202 // We should not make the window editable or setup its editor.
6203 // It's probably style=display:none.
6207 bool makeWindowEditable
= mEditingState
== EditingState::eOff
;
6208 bool spellRecheckAll
= false;
6209 bool putOffToRemoveScriptBlockerUntilModifyingEditingState
= false;
6210 htmlEditor
= nullptr;
6213 nsAutoEditingState
push(this, EditingState::eSettingUp
);
6215 RefPtr
<PresShell
> presShell
= GetPresShell();
6216 NS_ENSURE_TRUE(presShell
, NS_ERROR_FAILURE
);
6218 // If we're entering the design mode from non-editable state, put the
6219 // selection at the beginning of the document for compatibility reasons.
6220 bool collapseSelectionAtBeginningOfDocument
=
6221 designMode
&& oldState
== EditingState::eOff
;
6222 // However, mEditingState may be eOff even if there is some
6223 // `contenteditable` area and selection has been initialized for it because
6224 // mEditingState for `contenteditable` may have been scheduled to modify
6225 // when safe. In such case, we should not reinitialize selection.
6226 if (collapseSelectionAtBeginningOfDocument
&& mContentEditableCount
) {
6227 Selection
* selection
=
6228 presShell
->GetSelection(nsISelectionController::SELECTION_NORMAL
);
6229 NS_WARNING_ASSERTION(selection
, "Why don't we have Selection?");
6230 if (selection
&& selection
->RangeCount()) {
6231 // Perhaps, we don't need to check whether the selection is in
6232 // an editing host or not because all contents will be editable
6233 // in designMode. (And we don't want to make this code so complicated
6234 // because of legacy API.)
6235 collapseSelectionAtBeginningOfDocument
= false;
6239 MOZ_ASSERT(mStyleSetFilled
);
6242 // designMode is being turned on (overrides contentEditable).
6243 spellRecheckAll
= oldState
== EditingState::eContentEditable
;
6246 // Adjust focused element with new style but blur event shouldn't be fired
6247 // until mEditingState is modified with newState.
6248 nsAutoScriptBlocker scriptBlocker
;
6250 nsCOMPtr
<nsPIDOMWindowOuter
> focusedWindow
;
6251 nsIContent
* focusedContent
= nsFocusManager::GetFocusedDescendant(
6252 window
, nsFocusManager::eOnlyCurrentWindow
,
6253 getter_AddRefs(focusedWindow
));
6254 if (focusedContent
) {
6255 nsIFrame
* focusedFrame
= focusedContent
->GetPrimaryFrame();
6256 bool clearFocus
= focusedFrame
6257 ? !focusedFrame
->IsFocusable()
6258 : !focusedContent
->IsFocusableWithoutStyle();
6260 if (RefPtr
<nsFocusManager
> fm
= nsFocusManager::GetFocusManager()) {
6261 fm
->ClearFocus(window
);
6262 // If we need to dispatch blur event, we should put off after
6263 // modifying mEditingState since blur event handler may change
6264 // designMode state again.
6265 putOffToRemoveScriptBlockerUntilModifyingEditingState
= true;
6271 if (makeWindowEditable
) {
6272 // Editing is being turned on (through designMode or contentEditable)
6274 // XXX This can cause flushing which can change the editing state, so make
6275 // sure to avoid recursing.
6276 rv
= editSession
->MakeWindowEditable(window
, "html", false, false, true);
6277 NS_ENSURE_SUCCESS(rv
, rv
);
6280 // XXX Need to call TearDownEditorOnWindow for all failures.
6281 htmlEditor
= docshell
->GetHTMLEditor();
6283 // Return NS_OK even though we've failed to create an editor here. This
6284 // is so that the setter of designMode on non-HTML documents does not
6286 // This is OK to do because in nsEditingSession::SetupEditorOnWindow() we
6287 // would detect that we can't support the mimetype if appropriate and
6288 // would fall onto the eEditorErrorCantEditMimeType path.
6292 if (collapseSelectionAtBeginningOfDocument
) {
6293 htmlEditor
->BeginningOfDocument();
6296 if (putOffToRemoveScriptBlockerUntilModifyingEditingState
) {
6297 nsContentUtils::AddScriptBlocker();
6301 mEditingState
= newState
;
6302 if (putOffToRemoveScriptBlockerUntilModifyingEditingState
) {
6303 nsContentUtils::RemoveScriptBlocker();
6304 // If mEditingState is overwritten by another call and already disabled
6305 // the editing, we shouldn't keep making window editable.
6306 if (mEditingState
== EditingState::eOff
) {
6311 if (makeWindowEditable
) {
6312 // TODO: We should do this earlier in this method.
6313 // Previously, we called `ExecCommand` with `insertBrOnReturn` command
6314 // whose argument is false here. Then, if it returns error, we
6315 // stopped making it editable. However, after bug 1697078 fixed,
6316 // `ExecCommand` returns error only when the document is not XHTML's
6317 // nor HTML's. Therefore, we use same error handling for now.
6318 if (MOZ_UNLIKELY(NS_WARN_IF(!IsHTMLOrXHTML()))) {
6319 // Editor setup failed. Editing is not on after all.
6320 // XXX Should we reset the editable flag on nodes?
6321 editSession
->TearDownEditorOnWindow(window
);
6322 mEditingState
= EditingState::eOff
;
6323 return NS_ERROR_DOM_INVALID_STATE_ERR
;
6325 // Set the editor to not insert <br> elements on return when in <p> elements
6327 htmlEditor
->SetReturnInParagraphCreatesNewParagraph(true);
6330 // Resync the editor's spellcheck state.
6331 if (spellRecheckAll
) {
6332 nsCOMPtr
<nsISelectionController
> selectionController
=
6333 htmlEditor
->GetSelectionController();
6334 if (NS_WARN_IF(!selectionController
)) {
6335 return NS_ERROR_FAILURE
;
6338 RefPtr
<Selection
> spellCheckSelection
= selectionController
->GetSelection(
6339 nsISelectionController::SELECTION_SPELLCHECK
);
6340 if (spellCheckSelection
) {
6341 spellCheckSelection
->RemoveAllRanges(IgnoreErrors());
6344 htmlEditor
->SyncRealTimeSpell();
6346 MaybeDispatchCheckKeyPressEventModelEvent();
6348 // If this document keeps having focus, the HTMLEditor may not receive `focus`
6349 // event for this editing state change since this may occur without a focus
6350 // change. Therefore, let's notify HTMLEditor of this editing state change.
6351 if (thisDocumentHasFocus
&& ThisDocumentHasFocus()) {
6352 RefPtr
<Element
> focusedElement
= nsFocusManager::GetFocusedElementStatic();
6353 MOZ_ASSERT_IF(focusedElement
, focusedElement
->GetComposedDoc() == this);
6354 if ((focusedElement
&& focusedElement
->IsEditable() &&
6355 (!focusedElement
->IsTextControlElement() ||
6356 !TextControlElement::FromNode(focusedElement
)
6357 ->IsSingleLineTextControlOrTextArea())) ||
6358 (!focusedElement
&& IsInDesignMode())) {
6359 DebugOnly
<nsresult
> rvIgnored
=
6360 htmlEditor
->FocusedElementOrDocumentBecomesEditable(*this,
6362 NS_WARNING_ASSERTION(
6363 NS_SUCCEEDED(rvIgnored
),
6364 "HTMLEditor::FocusedElementOrDocumentBecomesEditable() failed, but "
6366 } else if (htmlEditor
->HasFocus()) {
6367 DebugOnly
<nsresult
> rvIgnored
=
6368 HTMLEditor::FocusedElementOrDocumentBecomesNotEditable(
6369 htmlEditor
, *this, focusedElement
);
6370 NS_WARNING_ASSERTION(
6371 NS_SUCCEEDED(rvIgnored
),
6372 "HTMLEditor::FocusedElementOrDocumentBecomesNotEditable() failed, "
6380 // Helper class, used below in ChangeContentEditableCount().
6381 class DeferredContentEditableCountChangeEvent
: public Runnable
{
6383 DeferredContentEditableCountChangeEvent(Document
* aDoc
, Element
* aElement
)
6384 : mozilla::Runnable("DeferredContentEditableCountChangeEvent"),
6386 mElement(aElement
) {}
6388 MOZ_CAN_RUN_SCRIPT_BOUNDARY NS_IMETHOD
Run() override
{
6389 if (mElement
&& mElement
->OwnerDoc() == mDoc
) {
6390 RefPtr
<Document
> doc
= std::move(mDoc
);
6391 RefPtr
<Element
> element
= std::move(mElement
);
6392 doc
->DeferredContentEditableCountChange(element
);
6398 RefPtr
<Document
> mDoc
;
6399 RefPtr
<Element
> mElement
;
6402 void Document::ChangeContentEditableCount(Element
* aElement
, int32_t aChange
) {
6403 NS_ASSERTION(int32_t(mContentEditableCount
) + aChange
>= 0,
6404 "Trying to decrement too much.");
6406 mContentEditableCount
+= aChange
;
6409 nsContentUtils::AddScriptRunner(
6410 new DeferredContentEditableCountChangeEvent(this, aElement
));
6414 void Document::DeferredContentEditableCountChange(Element
* aElement
) {
6415 const bool elementHasFocus
=
6416 aElement
&& nsFocusManager::GetFocusedElementStatic() == aElement
;
6417 if (elementHasFocus
) {
6418 MOZ_ASSERT(nsContentUtils::IsSafeToRunScript());
6419 // When contenteditable of aElement is changed and HTMLEditor works with it
6420 // or needs to start working with it, HTMLEditor may not receive `focus`
6421 // event nor `blur` event because this may occur without a focus change.
6422 // Therefore, we need to notify HTMLEditor of this contenteditable attribute
6424 RefPtr
<HTMLEditor
> htmlEditor
= GetHTMLEditor();
6425 if (aElement
->HasFlag(NODE_IS_EDITABLE
)) {
6427 DebugOnly
<nsresult
> rvIgnored
=
6428 htmlEditor
->FocusedElementOrDocumentBecomesEditable(*this,
6430 NS_WARNING_ASSERTION(
6431 NS_SUCCEEDED(rvIgnored
),
6432 "HTMLEditor::FocusedElementOrDocumentBecomesEditable() failed, but "
6436 DebugOnly
<nsresult
> rvIgnored
=
6437 HTMLEditor::FocusedElementOrDocumentBecomesNotEditable(
6438 htmlEditor
, *this, aElement
);
6439 NS_WARNING_ASSERTION(
6440 NS_SUCCEEDED(rvIgnored
),
6441 "HTMLEditor::FocusedElementOrDocumentBecomesNotEditable() failed, "
6447 (mUpdateNestLevel
> 0 && (mContentEditableCount
> 0) != IsEditingOn())) {
6451 EditingState oldState
= mEditingState
;
6453 nsresult rv
= EditingStateChanged();
6454 NS_ENSURE_SUCCESS_VOID(rv
);
6456 if (oldState
== mEditingState
&&
6457 mEditingState
== EditingState::eContentEditable
) {
6458 // We just changed the contentEditable state of a node, we need to reset
6459 // the spellchecking state of that node.
6461 if (RefPtr
<HTMLEditor
> htmlEditor
= GetHTMLEditor()) {
6462 nsCOMPtr
<nsIInlineSpellChecker
> spellChecker
;
6463 DebugOnly
<nsresult
> rvIgnored
= htmlEditor
->GetInlineSpellChecker(
6464 false, getter_AddRefs(spellChecker
));
6465 NS_WARNING_ASSERTION(
6466 NS_SUCCEEDED(rvIgnored
),
6467 "EditorBase::GetInlineSpellChecker() failed, but ignored");
6470 aElement
->InclusiveDescendantMayNeedSpellchecking(htmlEditor
)) {
6471 RefPtr
<nsRange
> range
= nsRange::Create(aElement
);
6472 IgnoredErrorResult res
;
6473 range
->SelectNodeContents(*aElement
, res
);
6475 // The node might be detached from the document at this point,
6476 // which would cause this call to fail. In this case, we can
6477 // safely ignore the contenteditable count change.
6481 rv
= spellChecker
->SpellCheckRange(range
);
6482 NS_ENSURE_SUCCESS_VOID(rv
);
6488 // aElement causes creating new HTMLEditor and the element had and keep
6489 // having focus, the HTMLEditor won't receive `focus` event. Therefore, we
6490 // need to notify HTMLEditor of it becomes editable.
6491 if (elementHasFocus
&& aElement
->HasFlag(NODE_IS_EDITABLE
) &&
6492 nsFocusManager::GetFocusedElementStatic() == aElement
) {
6493 if (RefPtr
<HTMLEditor
> htmlEditor
= GetHTMLEditor()) {
6494 DebugOnly
<nsresult
> rvIgnored
=
6495 htmlEditor
->FocusedElementOrDocumentBecomesEditable(*this, aElement
);
6496 NS_WARNING_ASSERTION(
6497 NS_SUCCEEDED(rvIgnored
),
6498 "HTMLEditor::FocusedElementOrDocumentBecomesEditable() failed, but "
6504 void Document::MaybeDispatchCheckKeyPressEventModelEvent() {
6505 // Currently, we need to check only when we're becoming editable for
6507 if (mEditingState
!= EditingState::eContentEditable
) {
6511 if (mHasBeenEditable
) {
6514 mHasBeenEditable
= true;
6516 // Dispatch "CheckKeyPressEventModel" event. That is handled only by
6517 // KeyPressEventModelCheckerChild. Then, it calls SetKeyPressEventModel()
6518 // with proper keypress event for the active web app.
6519 WidgetEvent
checkEvent(true, eUnidentifiedEvent
);
6520 checkEvent
.mSpecifiedEventType
= nsGkAtoms::onCheckKeyPressEventModel
;
6521 checkEvent
.mFlags
.mCancelable
= false;
6522 checkEvent
.mFlags
.mBubbles
= false;
6523 checkEvent
.mFlags
.mOnlySystemGroupDispatch
= true;
6524 // Post the event rather than dispatching it synchronously because we need
6525 // a call of SetKeyPressEventModel() before first key input. Therefore, we
6526 // can avoid paying unnecessary runtime cost for most web apps.
6527 (new AsyncEventDispatcher(this, checkEvent
))->PostDOMEvent();
6530 void Document::SetKeyPressEventModel(uint16_t aKeyPressEventModel
) {
6531 PresShell
* presShell
= GetPresShell();
6535 presShell
->SetKeyPressEventModel(aKeyPressEventModel
);
6538 TimeStamp
Document::LastFocusTime() const { return mLastFocusTime
; }
6540 void Document::SetLastFocusTime(const TimeStamp
& aFocusTime
) {
6541 MOZ_DIAGNOSTIC_ASSERT(!aFocusTime
.IsNull());
6542 MOZ_DIAGNOSTIC_ASSERT(mLastFocusTime
.IsNull() ||
6543 aFocusTime
>= mLastFocusTime
);
6544 mLastFocusTime
= aFocusTime
;
6547 void Document::GetReferrer(nsACString
& aReferrer
) const {
6548 aReferrer
.Truncate();
6549 if (!mReferrerInfo
) {
6553 nsCOMPtr
<nsIURI
> referrer
= mReferrerInfo
->GetComputedReferrer();
6558 URLDecorationStripper::StripTrackingIdentifiers(referrer
, aReferrer
);
6561 void Document::GetCookie(nsAString
& aCookie
, ErrorResult
& aRv
) {
6562 aCookie
.Truncate(); // clear current cookie in case service fails;
6563 // no cookie isn't an error condition.
6565 nsCOMPtr
<nsIPrincipal
> cookiePrincipal
;
6566 nsCOMPtr
<nsIPrincipal
> cookiePartitionedPrincipal
;
6568 CookieCommons::SecurityChecksResult checkResult
=
6569 CookieCommons::CheckGlobalAndRetrieveCookiePrincipals(
6570 this, getter_AddRefs(cookiePrincipal
),
6571 getter_AddRefs(cookiePartitionedPrincipal
));
6572 switch (checkResult
) {
6573 case CookieCommons::SecurityChecksResult::eSandboxedError
:
6574 aRv
.ThrowSecurityError(
6575 "Forbidden in a sandboxed document without the 'allow-same-origin' "
6579 case CookieCommons::SecurityChecksResult::eSecurityError
:
6582 case CookieCommons::SecurityChecksResult::eDoNotContinue
:
6585 case CookieCommons::SecurityChecksResult::eContinue
:
6589 bool thirdParty
= true;
6590 nsPIDOMWindowInner
* innerWindow
= GetInnerWindow();
6591 // in gtests we don't have a window, let's consider those requests as 3rd
6594 ThirdPartyUtil
* thirdPartyUtil
= ThirdPartyUtil::GetInstance();
6596 if (thirdPartyUtil
) {
6597 Unused
<< thirdPartyUtil
->IsThirdPartyWindow(
6598 innerWindow
->GetOuterWindow(), nullptr, &thirdParty
);
6602 nsTArray
<nsCOMPtr
<nsIPrincipal
>> principals
;
6604 MOZ_ASSERT(cookiePrincipal
);
6605 principals
.AppendElement(cookiePrincipal
);
6607 if (cookiePartitionedPrincipal
) {
6608 principals
.AppendElement(cookiePartitionedPrincipal
);
6611 nsTArray
<RefPtr
<Cookie
>> cookieList
;
6613 int64_t currentTimeInUsec
= PR_Now();
6614 int64_t currentTime
= currentTimeInUsec
/ PR_USEC_PER_SEC
;
6616 // not having a cookie service isn't an error
6617 nsCOMPtr
<nsICookieService
> service
=
6618 do_GetService(NS_COOKIESERVICE_CONTRACTID
);
6623 nsCOMPtr
<nsILoadInfo
> loadInfo
=
6624 GetChannel() ? GetChannel()->LoadInfo() : nullptr;
6625 bool on3pcbException
= loadInfo
&& loadInfo
->GetIsOn3PCBExceptionList();
6627 for (auto& principal
: principals
) {
6628 nsAutoCString baseDomain
;
6629 nsresult rv
= CookieCommons::GetBaseDomain(principal
, baseDomain
);
6630 if (NS_WARN_IF(NS_FAILED(rv
))) {
6634 nsAutoCString hostFromURI
;
6635 rv
= nsContentUtils::GetHostOrIPv6WithBrackets(principal
, hostFromURI
);
6636 if (NS_WARN_IF(NS_FAILED(rv
))) {
6640 nsAutoCString pathFromURI
;
6641 rv
= principal
->GetFilePath(pathFromURI
);
6642 if (NS_WARN_IF(NS_FAILED(rv
))) {
6646 nsTArray
<RefPtr
<Cookie
>> cookies
;
6647 service
->GetCookiesFromHost(baseDomain
, principal
->OriginAttributesRef(),
6649 if (cookies
.IsEmpty()) {
6653 // check if the nsIPrincipal is using an https secure protocol.
6654 // if it isn't, then we can't send a secure cookie over the connection.
6655 bool potentiallyTrustworthy
=
6656 principal
->GetIsOriginPotentiallyTrustworthy();
6658 // iterate the cookies!
6659 for (Cookie
* cookie
: cookies
) {
6660 // check the host, since the base domain lookup is conservative.
6661 if (!CookieCommons::DomainMatches(cookie
, hostFromURI
)) {
6665 // if the cookie is httpOnly and it's not going directly to the HTTP
6666 // connection, don't send it
6667 if (cookie
->IsHttpOnly()) {
6672 !CookieCommons::ShouldIncludeCrossSiteCookie(
6673 cookie
, CookieJarSettings()->GetPartitionForeign(),
6674 IsInPrivateBrowsing(), UsingStorageAccess(), on3pcbException
)) {
6678 // if the cookie is secure and the host scheme isn't, we can't send it
6679 if (cookie
->IsSecure() && !potentiallyTrustworthy
) {
6683 // if the nsIURI path doesn't match the cookie path, don't send it back
6684 if (!CookieCommons::PathMatches(cookie
, pathFromURI
)) {
6688 // check if the cookie has expired
6689 if (cookie
->Expiry() <= currentTime
) {
6693 // all checks passed - add to list and check if lastAccessed stamp needs
6695 cookieList
.AppendElement(cookie
);
6696 if (cookie
->IsStale()) {
6702 if (cookieList
.IsEmpty()) {
6706 // update lastAccessed timestamps. we only do this if the timestamp is stale
6707 // by a certain amount, to avoid thrashing the db during pageload.
6709 service
->StaleCookies(cookieList
, currentTimeInUsec
);
6712 // return cookies in order of path length; longest to shortest.
6713 // this is required per RFC2109. if cookies match in length,
6714 // then sort by creation time (see bug 236772).
6715 cookieList
.Sort(CompareCookiesForSending());
6717 nsAutoCString cookieString
;
6718 CookieCommons::ComposeCookieString(cookieList
, cookieString
);
6720 // CopyUTF8toUTF16 doesn't handle error
6721 // because it assumes that the input is valid.
6722 UTF_8_ENCODING
->DecodeWithoutBOMHandling(cookieString
, aCookie
);
6725 void Document::SetCookie(const nsAString
& aCookieString
, ErrorResult
& aRv
) {
6726 nsCOMPtr
<nsIPrincipal
> cookiePrincipal
;
6728 CookieCommons::SecurityChecksResult checkResult
=
6729 CookieCommons::CheckGlobalAndRetrieveCookiePrincipals(
6730 this, getter_AddRefs(cookiePrincipal
), nullptr);
6731 switch (checkResult
) {
6732 case CookieCommons::SecurityChecksResult::eSandboxedError
:
6733 aRv
.ThrowSecurityError(
6734 "Forbidden in a sandboxed document without the 'allow-same-origin' "
6738 case CookieCommons::SecurityChecksResult::eSecurityError
:
6741 case CookieCommons::SecurityChecksResult::eDoNotContinue
:
6744 case CookieCommons::SecurityChecksResult::eContinue
:
6748 if (!mDocumentURI
) {
6752 // not having a cookie service isn't an error
6753 nsCOMPtr
<nsICookieService
> service
=
6754 do_GetService(NS_COOKIESERVICE_CONTRACTID
);
6759 NS_ConvertUTF16toUTF8
cookieString(aCookieString
);
6761 nsCOMPtr
<nsIURI
> documentURI
;
6762 nsAutoCString baseDomain
;
6763 OriginAttributes attrs
;
6765 int64_t currentTimeInUsec
= PR_Now();
6767 auto* basePrincipal
= BasePrincipal::Cast(NodePrincipal());
6768 basePrincipal
->GetURI(getter_AddRefs(documentURI
));
6769 if (NS_WARN_IF(!documentURI
)) {
6770 // Document's principal is not a content or null (may be system), so
6771 // can't set cookies
6775 // Console report takes care of the correct reporting at the exit of this
6777 RefPtr
<ConsoleReportCollector
> crc
= new ConsoleReportCollector();
6778 auto scopeExit
= MakeScopeExit([&] { crc
->FlushConsoleReports(this); });
6780 CookieParser
cookieParser(crc
, documentURI
);
6782 ThirdPartyUtil
* thirdPartyUtil
= ThirdPartyUtil::GetInstance();
6783 if (!thirdPartyUtil
) {
6787 nsCOMPtr
<nsIEffectiveTLDService
> tldService
=
6788 mozilla::components::EffectiveTLD::Service();
6793 RefPtr
<Cookie
> cookie
= CookieCommons::CreateCookieFromDocument(
6794 cookieParser
, this, cookieString
, currentTimeInUsec
, tldService
,
6795 thirdPartyUtil
, baseDomain
, attrs
);
6800 bool thirdParty
= true;
6801 nsPIDOMWindowInner
* innerWindow
= GetInnerWindow();
6802 // in gtests we don't have a window, let's consider those requests as 3rd
6805 Unused
<< thirdPartyUtil
->IsThirdPartyWindow(innerWindow
->GetOuterWindow(),
6806 nullptr, &thirdParty
);
6809 nsCOMPtr
<nsILoadInfo
> loadInfo
=
6810 GetChannel() ? GetChannel()->LoadInfo() : nullptr;
6811 bool on3pcbException
= loadInfo
&& loadInfo
->GetIsOn3PCBExceptionList();
6814 !CookieCommons::ShouldIncludeCrossSiteCookie(
6815 cookie
, CookieJarSettings()->GetPartitionForeign(),
6816 IsInPrivateBrowsing(), UsingStorageAccess(), on3pcbException
)) {
6820 // add the cookie to the list. AddCookieFromDocument() takes care of logging.
6821 service
->AddCookieFromDocument(cookieParser
, baseDomain
, attrs
, *cookie
,
6822 currentTimeInUsec
, documentURI
, thirdParty
,
6825 nsCOMPtr
<nsIObserverService
> observerService
=
6826 mozilla::services::GetObserverService();
6827 if (observerService
) {
6828 observerService
->NotifyObservers(ToSupports(this), "document-set-cookie",
6829 nsString(aCookieString
).get());
6833 ReferrerPolicy
Document::GetReferrerPolicy() const {
6834 return mReferrerInfo
? mReferrerInfo
->ReferrerPolicy()
6835 : ReferrerPolicy::_empty
;
6838 void Document::GetAlinkColor(nsAString
& aAlinkColor
) {
6839 aAlinkColor
.Truncate();
6841 HTMLBodyElement
* body
= GetBodyElement();
6843 body
->GetALink(aAlinkColor
);
6847 void Document::SetAlinkColor(const nsAString
& aAlinkColor
) {
6848 HTMLBodyElement
* body
= GetBodyElement();
6850 body
->SetALink(aAlinkColor
);
6854 void Document::GetLinkColor(nsAString
& aLinkColor
) {
6855 aLinkColor
.Truncate();
6857 HTMLBodyElement
* body
= GetBodyElement();
6859 body
->GetLink(aLinkColor
);
6863 void Document::SetLinkColor(const nsAString
& aLinkColor
) {
6864 HTMLBodyElement
* body
= GetBodyElement();
6866 body
->SetLink(aLinkColor
);
6870 void Document::GetVlinkColor(nsAString
& aVlinkColor
) {
6871 aVlinkColor
.Truncate();
6873 HTMLBodyElement
* body
= GetBodyElement();
6875 body
->GetVLink(aVlinkColor
);
6879 void Document::SetVlinkColor(const nsAString
& aVlinkColor
) {
6880 HTMLBodyElement
* body
= GetBodyElement();
6882 body
->SetVLink(aVlinkColor
);
6886 void Document::GetBgColor(nsAString
& aBgColor
) {
6887 aBgColor
.Truncate();
6889 HTMLBodyElement
* body
= GetBodyElement();
6891 body
->GetBgColor(aBgColor
);
6895 void Document::SetBgColor(const nsAString
& aBgColor
) {
6896 HTMLBodyElement
* body
= GetBodyElement();
6898 body
->SetBgColor(aBgColor
);
6902 void Document::GetFgColor(nsAString
& aFgColor
) {
6903 aFgColor
.Truncate();
6905 HTMLBodyElement
* body
= GetBodyElement();
6907 body
->GetText(aFgColor
);
6911 void Document::SetFgColor(const nsAString
& aFgColor
) {
6912 HTMLBodyElement
* body
= GetBodyElement();
6914 body
->SetText(aFgColor
);
6918 void Document::CaptureEvents() {
6919 WarnOnceAbout(DeprecatedOperations::eUseOfCaptureEvents
);
6922 void Document::ReleaseEvents() {
6923 WarnOnceAbout(DeprecatedOperations::eUseOfReleaseEvents
);
6926 HTMLAllCollection
* Document::All() {
6928 mAll
= new HTMLAllCollection(this);
6933 nsresult
Document::GetSrcdocData(nsAString
& aSrcdocData
) {
6934 if (mIsSrcdocDocument
) {
6935 nsCOMPtr
<nsIInputStreamChannel
> inStrmChan
= do_QueryInterface(mChannel
);
6937 return inStrmChan
->GetSrcdocData(aSrcdocData
);
6940 aSrcdocData
= VoidString();
6944 Nullable
<WindowProxyHolder
> Document::GetDefaultView() const {
6945 nsPIDOMWindowOuter
* win
= GetWindow();
6949 return WindowProxyHolder(win
->GetBrowsingContext());
6952 nsIContent
* Document::GetUnretargetedFocusedContent(
6953 IncludeChromeOnly aIncludeChromeOnly
) const {
6954 nsCOMPtr
<nsPIDOMWindowOuter
> window
= GetWindow();
6958 nsCOMPtr
<nsPIDOMWindowOuter
> focusedWindow
;
6959 nsIContent
* focusedContent
= nsFocusManager::GetFocusedDescendant(
6960 window
, nsFocusManager::eOnlyCurrentWindow
,
6961 getter_AddRefs(focusedWindow
));
6962 if (!focusedContent
) {
6965 // be safe and make sure the element is from this document
6966 if (focusedContent
->OwnerDoc() != this) {
6969 if (focusedContent
->ChromeOnlyAccess() &&
6970 aIncludeChromeOnly
== IncludeChromeOnly::No
) {
6971 return focusedContent
->FindFirstNonChromeOnlyAccessContent();
6973 return focusedContent
;
6976 Element
* Document::GetActiveElement() {
6977 // Get the focused element.
6978 Element
* focusedElement
= GetRetargetedFocusedElement();
6979 if (focusedElement
) {
6980 return focusedElement
;
6983 // No focused element anywhere in this document. Try to get the BODY.
6984 if (IsHTMLOrXHTML()) {
6985 Element
* bodyElement
= AsHTMLDocument()->GetBody();
6989 // Special case to handle the transition to XHTML from XUL documents
6990 // where there currently isn't a body element, but we need to match the
6991 // XUL behavior. This should be removed when bug 1540278 is resolved.
6992 if (nsContentUtils::IsChromeDoc(this)) {
6993 Element
* docElement
= GetDocumentElement();
6994 if (docElement
&& docElement
->IsXULElement()) {
6998 // Because of IE compatibility, return null when html document doesn't have
7003 // If we couldn't get a BODY, return the root element.
7004 return GetDocumentElement();
7007 Element
* Document::GetCurrentScript() {
7008 nsCOMPtr
<Element
> el(do_QueryInterface(ScriptLoader()->GetCurrentScript()));
7012 void Document::ReleaseCapture() const {
7013 // only release the capture if the caller can access it. This prevents a
7014 // page from stopping a scrollbar grab for example.
7015 nsCOMPtr
<nsINode
> node
= PresShell::GetCapturingContent();
7016 if (node
&& nsContentUtils::CanCallerAccess(node
)) {
7017 PresShell::ReleaseCapturingContent();
7021 nsIURI
* Document::GetBaseURI(bool aTryUseXHRDocBaseURI
) const {
7022 if (aTryUseXHRDocBaseURI
&& mChromeXHRDocBaseURI
) {
7023 return mChromeXHRDocBaseURI
;
7026 return GetDocBaseURI();
7029 void Document::SetBaseURI(nsIURI
* aURI
) {
7030 if (!aURI
&& !mDocumentBaseURI
) {
7034 // Don't do anything if the URI wasn't actually changed.
7035 if (aURI
&& mDocumentBaseURI
) {
7036 bool equalBases
= false;
7037 mDocumentBaseURI
->Equals(aURI
, &equalBases
);
7043 mDocumentBaseURI
= aURI
;
7044 mCachedURLData
= nullptr;
7048 Result
<OwningNonNull
<nsIURI
>, nsresult
> Document::ResolveWithBaseURI(
7049 const nsAString
& aURI
) {
7050 RefPtr
<nsIURI
> resolvedURI
;
7052 NS_NewURI(getter_AddRefs(resolvedURI
), aURI
, nullptr, GetDocBaseURI()));
7053 return OwningNonNull
<nsIURI
>(std::move(resolvedURI
));
7056 nsIReferrerInfo
* Document::ReferrerInfoForInternalCSSAndSVGResources() {
7057 if (!mCachedReferrerInfoForInternalCSSAndSVGResources
) {
7058 mCachedReferrerInfoForInternalCSSAndSVGResources
=
7059 ReferrerInfo::CreateForInternalCSSAndSVGResources(this);
7061 return mCachedReferrerInfoForInternalCSSAndSVGResources
;
7064 URLExtraData
* Document::DefaultStyleAttrURLData() {
7065 MOZ_ASSERT(NS_IsMainThread());
7066 if (!mCachedURLData
) {
7067 mCachedURLData
= new URLExtraData(
7068 GetDocBaseURI(), ReferrerInfoForInternalCSSAndSVGResources(),
7071 return mCachedURLData
;
7074 void Document::SetDocumentCharacterSet(NotNull
<const Encoding
*> aEncoding
) {
7075 if (mCharacterSet
!= aEncoding
) {
7076 mCharacterSet
= aEncoding
;
7077 mEncodingMenuDisabled
= aEncoding
== UTF_8_ENCODING
;
7078 RecomputeLanguageFromCharset();
7080 if (nsPresContext
* context
= GetPresContext()) {
7081 context
->DocumentCharSetChanged(aEncoding
);
7086 void Document::GetSandboxFlagsAsString(nsAString
& aFlags
) {
7087 nsContentUtils::SandboxFlagsToString(mSandboxFlags
, aFlags
);
7090 void Document::GetHeaderData(nsAtom
* aHeaderField
, nsAString
& aData
) const {
7092 const HeaderData
* data
= mHeaderData
.get();
7094 if (data
->mField
== aHeaderField
) {
7095 aData
= data
->mData
;
7098 data
= data
->mNext
.get();
7102 void Document::SetHeaderData(nsAtom
* aHeaderField
, const nsAString
& aData
) {
7103 if (!aHeaderField
) {
7104 NS_ERROR("null headerField");
7109 if (!aData
.IsEmpty()) { // don't bother storing empty string
7110 mHeaderData
= MakeUnique
<HeaderData
>(aHeaderField
, aData
);
7113 HeaderData
* data
= mHeaderData
.get();
7114 UniquePtr
<HeaderData
>* lastPtr
= &mHeaderData
;
7116 do { // look for existing and replace
7117 if (data
->mField
== aHeaderField
) {
7118 if (!aData
.IsEmpty()) {
7119 data
->mData
.Assign(aData
);
7120 } else { // don't store empty string
7121 // Note that data->mNext is moved to a temporary before the old value
7122 // of *lastPtr is deleted.
7123 *lastPtr
= std::move(data
->mNext
);
7129 lastPtr
= &data
->mNext
;
7130 data
= lastPtr
->get();
7133 if (!aData
.IsEmpty() && !found
) {
7134 // didn't find, append
7135 *lastPtr
= MakeUnique
<HeaderData
>(aHeaderField
, aData
);
7139 if (aHeaderField
== nsGkAtoms::headerContentLanguage
) {
7140 if (aData
.IsEmpty()) {
7141 mContentLanguage
= nullptr;
7143 mContentLanguage
= NS_AtomizeMainThread(aData
);
7145 mMayNeedFontPrefsUpdate
= true;
7146 if (auto* presContext
= GetPresContext()) {
7147 presContext
->ContentLanguageChanged();
7151 if (aHeaderField
== nsGkAtoms::origin_trial
) {
7152 mTrials
.UpdateFromToken(aData
, NodePrincipal());
7153 if (mTrials
.IsEnabled(OriginTrial::CoepCredentialless
)) {
7156 // If we still don't have a WindowContext, WindowContext::OnNewDocument
7157 // will take care of this.
7158 if (WindowContext
* ctx
= GetWindowContext()) {
7159 if (mEmbedderPolicy
) {
7160 Unused
<< ctx
->SetEmbedderPolicy(mEmbedderPolicy
.value());
7166 if (aHeaderField
== nsGkAtoms::headerDefaultStyle
) {
7167 SetPreferredStyleSheetSet(aData
);
7170 if (aHeaderField
== nsGkAtoms::refresh
&& !IsStaticDocument()) {
7171 // We get into this code before we have a script global yet, so get to our
7172 // container via mDocumentContainer.
7173 if (mDocumentContainer
) {
7174 // Note: using mDocumentURI instead of mBaseURI here, for consistency
7175 // (used to just use the current URI of our webnavigation, but that
7176 // should really be the same thing). Note that this code can run
7177 // before the current URI of the webnavigation has been updated, so we
7178 // can't assert equality here.
7179 mDocumentContainer
->SetupRefreshURIFromHeader(this, aData
);
7183 if (aHeaderField
== nsGkAtoms::headerDNSPrefetchControl
&&
7184 mAllowDNSPrefetch
) {
7185 // Chromium treats any value other than 'on' (case insensitive) as 'off'.
7186 mAllowDNSPrefetch
= aData
.IsEmpty() || aData
.LowerCaseEqualsLiteral("on");
7189 if (aHeaderField
== nsGkAtoms::handheldFriendly
) {
7190 mViewportType
= Unknown
;
7194 void Document::SetEarlyHints(
7195 nsTArray
<net::EarlyHintConnectArgs
>&& aEarlyHints
) {
7196 mEarlyHints
= std::move(aEarlyHints
);
7199 void Document::TryChannelCharset(nsIChannel
* aChannel
, int32_t& aCharsetSource
,
7200 NotNull
<const Encoding
*>& aEncoding
,
7201 nsHtml5TreeOpExecutor
* aExecutor
) {
7203 nsAutoCString charsetVal
;
7204 nsresult rv
= aChannel
->GetContentCharset(charsetVal
);
7205 if (NS_SUCCEEDED(rv
)) {
7206 const Encoding
* preferred
= Encoding::ForLabel(charsetVal
);
7208 if (aExecutor
&& preferred
== REPLACEMENT_ENCODING
) {
7209 aExecutor
->ComplainAboutBogusProtocolCharset(this, false);
7211 aEncoding
= WrapNotNull(preferred
);
7212 aCharsetSource
= kCharsetFromChannel
;
7214 } else if (aExecutor
&& !charsetVal
.IsEmpty()) {
7215 aExecutor
->ComplainAboutBogusProtocolCharset(this, true);
7221 static inline void AssertNoStaleServoDataIn(nsINode
& aSubtreeRoot
) {
7223 for (nsINode
* node
: ShadowIncludingTreeIterator(aSubtreeRoot
)) {
7224 const Element
* element
= Element::FromNode(node
);
7228 MOZ_ASSERT(!element
->HasServoData());
7233 already_AddRefed
<PresShell
> Document::CreatePresShell(
7234 nsPresContext
* aContext
, nsViewManager
* aViewManager
) {
7235 MOZ_DIAGNOSTIC_ASSERT(!mPresShell
, "We have a presshell already!");
7237 NS_ENSURE_FALSE(GetBFCacheEntry(), nullptr);
7239 AssertNoStaleServoDataIn(*this);
7241 RefPtr
<PresShell
> presShell
= new PresShell(this);
7242 // Note: we don't hold a ref to the shell (it holds a ref to us)
7243 mPresShell
= presShell
;
7245 if (!mStyleSetFilled
) {
7249 presShell
->Init(aContext
, aViewManager
);
7250 if (RefPtr
<class HighlightRegistry
> highlightRegistry
= mHighlightRegistry
) {
7251 highlightRegistry
->AddHighlightSelectionsToFrameSelection();
7253 // Gaining a shell causes changes in how media queries are evaluated, so
7255 aContext
->MediaFeatureValuesChanged(
7256 {MediaFeatureChange::kAllChanges
},
7257 MediaFeatureChangePropagation::JustThisDocument
);
7259 // Make sure to never paint if we belong to an invisible DocShell.
7260 nsCOMPtr
<nsIDocShell
> docShell(mDocumentContainer
);
7261 if (docShell
&& docShell
->IsInvisible()) {
7262 presShell
->SetNeverPainting(true);
7265 MOZ_LOG(gDocumentLeakPRLog
, LogLevel::Debug
,
7266 ("DOCUMENT %p with PressShell %p and DocShell %p", this,
7267 presShell
.get(), docShell
.get()));
7269 mExternalResourceMap
.ShowViewers();
7271 MaybeScheduleFrameRequestCallbacks();
7273 if (mDocumentL10n
) {
7274 // In case we already accumulated mutations,
7275 // we'll trigger the refresh driver now.
7276 mDocumentL10n
->OnCreatePresShell();
7279 if (HasAutoFocusCandidates()) {
7280 ScheduleFlushAutoFocusCandidates();
7282 // Now that we have a shell, we might have @font-face rules (the presence of a
7283 // shell may change which rules apply to us). We don't need to do anything
7284 // like EnsureStyleFlush or such, there's nothing to update yet and when stuff
7285 // is ready to update we'll flush the font set.
7286 MarkUserFontSetDirty();
7288 // Take the author style disabled state from the top browsing cvontext.
7289 // (PageStyleChild.sys.mjs ensures this is up to date.)
7290 if (BrowsingContext
* bc
= GetBrowsingContext()) {
7291 presShell
->SetAuthorStyleDisabled(bc
->Top()->AuthorStyleDisabledDefault());
7294 return presShell
.forget();
7297 void Document::MaybeScheduleFrameRequestCallbacks() {
7298 if (!HasFrameRequestCallbacks() || !ShouldFireFrameRequestCallbacks()) {
7301 MOZ_ASSERT(mPresShell
);
7302 nsRefreshDriver
* rd
= mPresShell
->GetPresContext()->RefreshDriver();
7303 rd
->EnsureFrameRequestCallbacksHappen();
7306 void Document::TakeVideoFrameRequestCallbacks(
7307 nsTArray
<RefPtr
<HTMLVideoElement
>>& aVideoCallbacks
) {
7308 MOZ_ASSERT(aVideoCallbacks
.IsEmpty());
7309 mFrameRequestManager
.Take(aVideoCallbacks
);
7312 void Document::TakeFrameRequestCallbacks(nsTArray
<FrameRequest
>& aCallbacks
) {
7313 MOZ_ASSERT(aCallbacks
.IsEmpty());
7314 mFrameRequestManager
.Take(aCallbacks
);
7317 bool Document::ShouldThrottleFrameRequests() const {
7318 if (mStaticCloneCount
> 0) {
7319 // Even if we're not visible, a static clone may be, so run at full speed.
7323 if (Hidden() && !StaticPrefs::layout_testing_top_level_always_active()) {
7324 // We're not visible (probably in a background tab or the bf cache).
7329 // Can't do anything smarter. We don't run frame requests in documents
7330 // without a pres shell anyways.
7334 if (!mPresShell
->IsActive()) {
7335 // The pres shell is not active (we're an invisible OOP iframe or such), so
7340 if (mPresShell
->IsPaintingSuppressed()) {
7341 // Historically we have throttled frame requests until we've painted at
7342 // least once, so keep doing that.
7346 if (mPresShell
->IsUnderHiddenEmbedderElement()) {
7347 // For display: none and visibility: hidden we always throttle, for
7348 // consistency with OOP iframes.
7352 Element
* el
= GetEmbedderElement();
7354 // If we're not in-process, our refresh driver is throttled separately (via
7355 // PresShell::SetIsActive, so not much more we can do here.
7359 if (!StaticPrefs::layout_throttle_in_process_iframes()) {
7363 // Note that because we have to scroll this document into view at least once
7364 // to un-throttle it, we will drop one requestAnimationFrame frame when a
7365 // document that previously wasn't visible scrolls into view. This is
7366 // acceptable / unlikely to be human-perceivable, though we could improve on
7367 // it if needed by adding an intersection margin or something of that sort.
7368 auto margin
= DOMIntersectionObserver::LazyLoadingRootMargin();
7369 const IntersectionInput input
= DOMIntersectionObserver::ComputeInput(
7370 *el
->OwnerDoc(), /* aRoot = */ nullptr, &margin
);
7371 const IntersectionOutput output
= DOMIntersectionObserver::Intersect(
7372 input
, *el
, DOMIntersectionObserver::BoxToUse::Content
);
7373 return !output
.Intersects();
7376 void Document::DeletePresShell() {
7377 mExternalResourceMap
.HideViewers();
7378 if (nsPresContext
* presContext
= mPresShell
->GetPresContext()) {
7379 presContext
->RefreshDriver()->CancelPendingFullscreenEvents(this);
7380 presContext
->RefreshDriver()->CancelFlushAutoFocus(this);
7383 // When our shell goes away, request that all our images be immediately
7384 // discarded, so we don't carry around decoded image data for a document we
7385 // no longer intend to paint.
7386 ImageTracker()->RequestDiscardAll();
7388 // Now that we no longer have a shell, we need to forget about any FontFace
7389 // objects for @font-face rules that came from the style set. There's no need
7390 // to call EnsureStyleFlush either, the shell is going away anyway, so there's
7392 mFontFaceSetDirty
= true;
7394 if (IsEditingOn()) {
7398 mPresShell
= nullptr;
7400 ClearStaleServoData();
7401 AssertNoStaleServoDataIn(*this);
7403 mStyleSet
->ShellDetachedFromDocument();
7404 mStyleSetFilled
= false;
7405 mQuirkSheetAdded
= false;
7408 void Document::DisallowBFCaching(uint32_t aStatus
) {
7409 NS_ASSERTION(!mBFCacheEntry
, "We're already in the bfcache!");
7410 if (!mBFCacheDisallowed
) {
7411 if (WindowGlobalChild
* wgc
= GetWindowGlobalChild()) {
7412 wgc
->SendUpdateBFCacheStatus(aStatus
, 0);
7415 mBFCacheDisallowed
= true;
7418 void Document::SetBFCacheEntry(nsIBFCacheEntry
* aEntry
) {
7419 MOZ_ASSERT(IsBFCachingAllowed() || !aEntry
, "You should have checked!");
7423 mPresShell
->StopObservingRefreshDriver();
7424 } else if (mBFCacheEntry
) {
7425 mPresShell
->StartObservingRefreshDriver();
7428 mBFCacheEntry
= aEntry
;
7431 bool Document::RemoveFromBFCacheSync() {
7432 bool removed
= false;
7433 if (nsCOMPtr
<nsIBFCacheEntry
> entry
= GetBFCacheEntry()) {
7434 entry
->RemoveFromBFCacheSync();
7436 } else if (!IsCurrentActiveDocument()) {
7437 // In the old bfcache implementation while the new page is loading, but
7438 // before nsIDocumentViewer.show() has been called, the previous page
7439 // doesn't yet have nsIBFCacheEntry. However, the previous page isn't the
7440 // current active document anymore.
7441 DisallowBFCaching();
7445 if (mozilla::SessionHistoryInParent() && XRE_IsContentProcess()) {
7446 if (BrowsingContext
* bc
= GetBrowsingContext()) {
7447 if (bc
->IsInBFCache()) {
7448 ContentChild
* cc
= ContentChild::GetSingleton();
7449 // IPC is asynchronous but the caller is supposed to check the return
7450 // value. The reason for 'Sync' in the method name is that the old
7451 // implementation may run scripts. There is Async variant in
7452 // the old session history implementation for the cases where
7453 // synchronous operation isn't safe.
7454 cc
->SendRemoveFromBFCache(bc
->Top());
7462 static void SubDocClearEntry(PLDHashTable
* table
, PLDHashEntryHdr
* entry
) {
7463 SubDocMapEntry
* e
= static_cast<SubDocMapEntry
*>(entry
);
7465 NS_RELEASE(e
->mKey
);
7466 if (e
->mSubDocument
) {
7467 e
->mSubDocument
->SetParentDocument(nullptr);
7468 NS_RELEASE(e
->mSubDocument
);
7472 static void SubDocInitEntry(PLDHashEntryHdr
* entry
, const void* key
) {
7474 const_cast<SubDocMapEntry
*>(static_cast<const SubDocMapEntry
*>(entry
));
7476 e
->mKey
= const_cast<Element
*>(static_cast<const Element
*>(key
));
7479 e
->mSubDocument
= nullptr;
7482 nsresult
Document::SetSubDocumentFor(Element
* aElement
, Document
* aSubDoc
) {
7483 NS_ENSURE_TRUE(aElement
, NS_ERROR_UNEXPECTED
);
7486 // aSubDoc is nullptr, remove the mapping
7488 if (mSubDocuments
) {
7489 mSubDocuments
->Remove(aElement
);
7492 if (!mSubDocuments
) {
7493 // Create a new hashtable
7495 static const PLDHashTableOps hash_table_ops
= {
7496 PLDHashTable::HashVoidPtrKeyStub
, PLDHashTable::MatchEntryStub
,
7497 PLDHashTable::MoveEntryStub
, SubDocClearEntry
, SubDocInitEntry
};
7500 MakeUnique
<PLDHashTable
>(&hash_table_ops
, sizeof(SubDocMapEntry
));
7503 // Add a mapping to the hash table
7505 static_cast<SubDocMapEntry
*>(mSubDocuments
->Add(aElement
, fallible
));
7508 return NS_ERROR_OUT_OF_MEMORY
;
7511 if (entry
->mSubDocument
) {
7512 entry
->mSubDocument
->SetParentDocument(nullptr);
7514 // Release the old sub document
7515 NS_RELEASE(entry
->mSubDocument
);
7518 entry
->mSubDocument
= aSubDoc
;
7519 NS_ADDREF(entry
->mSubDocument
);
7521 aSubDoc
->SetParentDocument(this);
7527 Document
* Document::GetSubDocumentFor(nsIContent
* aContent
) const {
7528 if (mSubDocuments
&& aContent
->IsElement()) {
7529 auto entry
= static_cast<SubDocMapEntry
*>(
7530 mSubDocuments
->Search(aContent
->AsElement()));
7533 return entry
->mSubDocument
;
7540 Element
* Document::GetEmbedderElement() const {
7541 // We check if we're the active document in our BrowsingContext
7542 // by comparing against its document, rather than checking if the
7543 // WindowContext is cached, since mWindow may be null when we're
7544 // called (such as in nsPresContext::Init).
7545 if (BrowsingContext
* bc
= GetBrowsingContext()) {
7546 return bc
->GetExtantDocument() == this ? bc
->GetEmbedderElement() : nullptr;
7552 Element
* Document::GetRootElement() const {
7553 return (mCachedRootElement
&& mCachedRootElement
->GetParentNode() == this)
7554 ? mCachedRootElement
7555 : GetRootElementInternal();
7558 Element
* Document::GetUnfocusedKeyEventTarget() { return GetRootElement(); }
7560 Element
* Document::GetRootElementInternal() const {
7561 // We invoke GetRootElement() immediately before the servo traversal, so we
7562 // should always have a cache hit from Servo.
7563 MOZ_ASSERT(NS_IsMainThread());
7565 // Loop backwards because any non-elements, such as doctypes and PIs
7566 // are likely to appear before the root element.
7567 for (nsIContent
* child
= GetLastChild(); child
;
7568 child
= child
->GetPreviousSibling()) {
7569 if (Element
* element
= Element::FromNode(child
)) {
7570 const_cast<Document
*>(this)->mCachedRootElement
= element
;
7575 const_cast<Document
*>(this)->mCachedRootElement
= nullptr;
7579 void Document::InsertChildBefore(nsIContent
* aKid
, nsIContent
* aBeforeThis
,
7580 bool aNotify
, ErrorResult
& aRv
) {
7581 if (aKid
->IsElement() && GetRootElement()) {
7582 NS_WARNING("Inserting root element when we already have one");
7583 aRv
.ThrowHierarchyRequestError("There is already a root element.");
7587 nsINode::InsertChildBefore(aKid
, aBeforeThis
, aNotify
, aRv
);
7590 void Document::RemoveChildNode(nsIContent
* aKid
, bool aNotify
,
7591 const BatchRemovalState
* aState
) {
7592 Maybe
<mozAutoDocUpdate
> updateBatch
;
7593 const bool removingRoot
= aKid
->IsElement();
7595 updateBatch
.emplace(this, aNotify
);
7596 // Destroy the link map up front before we mess with the child list.
7597 DestroyElementMaps();
7599 // Notify early so that we can clear the cached element after notifying,
7600 // without having to slow down nsINode::RemoveChildNode.
7602 MutationObservers::NotifyContentWillBeRemoved(this, aKid
, aState
);
7606 // Preemptively clear mCachedRootElement, since we are about to remove it
7607 // from our child list, and we don't want to return this maybe-obsolete
7608 // value from any GetRootElement() calls that happen inside of
7609 // RemoveChildNode().
7610 // (NOTE: for this to be useful, RemoveChildNode() must NOT trigger any
7611 // GetRootElement() calls until after it's removed the child from mChildren.
7612 // Any call before that point would restore this soon-to-be-obsolete cached
7613 // answer, and our clearing here would be fruitless.)
7614 mCachedRootElement
= nullptr;
7617 nsINode::RemoveChildNode(aKid
, aNotify
);
7618 MOZ_ASSERT(mCachedRootElement
!= aKid
,
7619 "Stale pointer in mCachedRootElement, after we tried to clear it "
7620 "(maybe somebody called GetRootElement() too early?)");
7623 void Document::AddStyleSheetToStyleSets(StyleSheet
& aSheet
) {
7624 if (mStyleSetFilled
) {
7625 EnsureStyleSet().AddDocStyleSheet(aSheet
);
7626 ApplicableStylesChanged();
7630 void Document::RecordShadowStyleChange(ShadowRoot
& aShadowRoot
) {
7631 EnsureStyleSet().RecordShadowStyleChange(aShadowRoot
);
7632 ApplicableStylesChanged(/* aKnownInShadowTree= */ true);
7635 void Document::ApplicableStylesChanged(bool aKnownInShadowTree
) {
7636 // TODO(emilio): if we decide to resolve style in display: none iframes, then
7637 // we need to always track style changes and remove the mStyleSetFilled.
7638 if (!mStyleSetFilled
) {
7641 if (!aKnownInShadowTree
) {
7642 MarkUserFontSetDirty();
7644 PresShell
* ps
= GetPresShell();
7649 ps
->EnsureStyleFlush();
7650 nsPresContext
* pc
= ps
->GetPresContext();
7655 if (!aKnownInShadowTree
) {
7656 pc
->MarkCounterStylesDirty();
7657 pc
->MarkFontFeatureValuesDirty();
7658 pc
->MarkFontPaletteValuesDirty();
7660 pc
->RestyleManager()->NextRestyleIsForCSSRuleChanges();
7663 void Document::RemoveStyleSheetFromStyleSets(StyleSheet
& aSheet
) {
7664 if (mStyleSetFilled
) {
7665 mStyleSet
->RemoveStyleSheet(aSheet
);
7666 ApplicableStylesChanged();
7670 void Document::InsertSheetAt(size_t aIndex
, StyleSheet
& aSheet
) {
7671 DocumentOrShadowRoot::InsertSheetAt(aIndex
, aSheet
);
7673 if (aSheet
.IsApplicable()) {
7674 AddStyleSheetToStyleSets(aSheet
);
7678 void Document::StyleSheetApplicableStateChanged(StyleSheet
& aSheet
) {
7679 const bool applicable
= aSheet
.IsApplicable();
7680 // If we're actually in the document style sheet list
7681 if (StyleOrderIndexOfSheet(aSheet
) >= 0) {
7683 AddStyleSheetToStyleSets(aSheet
);
7685 RemoveStyleSheetFromStyleSets(aSheet
);
7690 void Document::PostStyleSheetApplicableStateChangeEvent(StyleSheet
& aSheet
) {
7691 if (!StyleSheetChangeEventsEnabled()) {
7695 StyleSheetApplicableStateChangeEventInit init
;
7696 init
.mBubbles
= true;
7697 init
.mCancelable
= true;
7698 init
.mStylesheet
= &aSheet
;
7699 init
.mApplicable
= aSheet
.IsApplicable();
7701 RefPtr
<StyleSheetApplicableStateChangeEvent
> event
=
7702 StyleSheetApplicableStateChangeEvent::Constructor(
7703 this, u
"StyleSheetApplicableStateChanged"_ns
, init
);
7704 event
->SetTrusted(true);
7705 event
->SetTarget(this);
7706 RefPtr
<AsyncEventDispatcher
> asyncDispatcher
=
7707 new AsyncEventDispatcher(this, event
.forget(), ChromeOnlyDispatch::eYes
);
7708 asyncDispatcher
->PostDOMEvent();
7711 void Document::PostStyleSheetRemovedEvent(StyleSheet
& aSheet
) {
7712 if (!StyleSheetChangeEventsEnabled()) {
7716 StyleSheetRemovedEventInit init
;
7717 init
.mBubbles
= true;
7718 init
.mCancelable
= false;
7719 init
.mStylesheet
= &aSheet
;
7721 RefPtr
<StyleSheetRemovedEvent
> event
=
7722 StyleSheetRemovedEvent::Constructor(this, u
"StyleSheetRemoved"_ns
, init
);
7723 event
->SetTrusted(true);
7724 event
->SetTarget(this);
7725 RefPtr
<AsyncEventDispatcher
> asyncDispatcher
=
7726 new AsyncEventDispatcher(this, event
.forget(), ChromeOnlyDispatch::eYes
);
7727 asyncDispatcher
->PostDOMEvent();
7730 void Document::PostCustomPropertyRegistered(
7731 const PropertyDefinition
& aDefinition
) {
7732 if (!StyleSheetChangeEventsEnabled()) {
7736 CSSCustomPropertyRegisteredEventInit init
;
7737 init
.mBubbles
= true;
7738 init
.mCancelable
= false;
7740 InspectorCSSPropertyDefinition property
;
7742 property
.mName
.Append(aDefinition
.mName
);
7743 property
.mSyntax
.Append(aDefinition
.mSyntax
);
7744 property
.mInherits
= aDefinition
.mInherits
;
7745 if (aDefinition
.mInitialValue
.WasPassed()) {
7746 property
.mInitialValue
.Append(aDefinition
.mInitialValue
.Value());
7748 property
.mInitialValue
.SetIsVoid(true);
7750 property
.mFromJS
= true;
7751 init
.mPropertyDefinition
= property
;
7753 RefPtr
<CSSCustomPropertyRegisteredEvent
> event
=
7754 CSSCustomPropertyRegisteredEvent::Constructor(
7755 this, u
"csscustompropertyregistered"_ns
, init
);
7756 event
->SetTrusted(true);
7757 event
->SetTarget(this);
7758 RefPtr
<AsyncEventDispatcher
> asyncDispatcher
=
7759 new AsyncEventDispatcher(this, event
.forget(), ChromeOnlyDispatch::eYes
);
7760 asyncDispatcher
->PostDOMEvent();
7763 static int32_t FindSheet(const nsTArray
<RefPtr
<StyleSheet
>>& aSheets
,
7764 nsIURI
* aSheetURI
) {
7765 for (int32_t i
= aSheets
.Length() - 1; i
>= 0; i
--) {
7767 nsIURI
* uri
= aSheets
[i
]->GetSheetURI();
7769 if (uri
&& NS_SUCCEEDED(uri
->Equals(aSheetURI
, &bEqual
)) && bEqual
)
7776 nsresult
Document::LoadAdditionalStyleSheet(additionalSheetType aType
,
7777 nsIURI
* aSheetURI
) {
7778 MOZ_ASSERT(aSheetURI
, "null arg");
7780 // Checking if we have loaded this one already.
7781 if (FindSheet(mAdditionalSheets
[aType
], aSheetURI
) >= 0)
7782 return NS_ERROR_INVALID_ARG
;
7784 // Loading the sheet sync.
7785 RefPtr
<css::Loader
> loader
= new css::Loader(GetDocGroup());
7787 css::SheetParsingMode parsingMode
;
7789 case Document::eAgentSheet
:
7790 parsingMode
= css::eAgentSheetFeatures
;
7793 case Document::eUserSheet
:
7794 parsingMode
= css::eUserSheetFeatures
;
7797 case Document::eAuthorSheet
:
7798 parsingMode
= css::eAuthorSheetFeatures
;
7802 MOZ_CRASH("impossible value for aType");
7805 auto result
= loader
->LoadSheetSync(aSheetURI
, parsingMode
,
7806 css::Loader::UseSystemPrincipal::Yes
);
7807 if (result
.isErr()) {
7808 return result
.unwrapErr();
7811 RefPtr
<StyleSheet
> sheet
= result
.unwrap();
7813 sheet
->SetAssociatedDocumentOrShadowRoot(this);
7814 MOZ_ASSERT(sheet
->IsApplicable());
7816 return AddAdditionalStyleSheet(aType
, sheet
);
7819 nsresult
Document::AddAdditionalStyleSheet(additionalSheetType aType
,
7820 StyleSheet
* aSheet
) {
7821 if (mAdditionalSheets
[aType
].Contains(aSheet
)) {
7822 return NS_ERROR_INVALID_ARG
;
7825 if (!aSheet
->IsApplicable()) {
7826 return NS_ERROR_INVALID_ARG
;
7829 mAdditionalSheets
[aType
].AppendElement(aSheet
);
7831 if (mStyleSetFilled
) {
7832 EnsureStyleSet().AppendStyleSheet(*aSheet
);
7833 ApplicableStylesChanged();
7838 void Document::RemoveAdditionalStyleSheet(additionalSheetType aType
,
7839 nsIURI
* aSheetURI
) {
7840 MOZ_ASSERT(aSheetURI
);
7842 nsTArray
<RefPtr
<StyleSheet
>>& sheets
= mAdditionalSheets
[aType
];
7844 int32_t i
= FindSheet(mAdditionalSheets
[aType
], aSheetURI
);
7846 RefPtr
<StyleSheet
> sheetRef
= std::move(sheets
[i
]);
7847 sheets
.RemoveElementAt(i
);
7849 if (!mIsGoingAway
) {
7850 MOZ_ASSERT(sheetRef
->IsApplicable());
7851 if (mStyleSetFilled
) {
7852 EnsureStyleSet().RemoveStyleSheet(*sheetRef
);
7853 ApplicableStylesChanged();
7856 sheetRef
->ClearAssociatedDocumentOrShadowRoot();
7860 nsIGlobalObject
* Document::GetScopeObject() const {
7861 nsCOMPtr
<nsIGlobalObject
> scope(do_QueryReferent(mScopeObject
));
7865 DocGroup
* Document::GetDocGroupOrCreate() {
7866 if (!mDocGroup
&& GetBrowsingContext()) {
7867 BrowsingContextGroup
* group
= GetBrowsingContext()->Group();
7870 nsAutoCString docGroupKey
;
7871 nsresult rv
= mozilla::dom::DocGroup::GetKey(
7872 NodePrincipal(), group
->IsPotentiallyCrossOriginIsolated(),
7874 if (NS_SUCCEEDED(rv
)) {
7875 mDocGroup
= group
->AddDocument(docGroupKey
, this);
7881 void Document::SetScopeObject(nsIGlobalObject
* aGlobal
) {
7882 mScopeObject
= do_GetWeakReference(aGlobal
);
7884 mHasHadScriptHandlingObject
= true;
7886 nsPIDOMWindowInner
* window
= aGlobal
->GetAsInnerWindow();
7891 // Same origin data documents should have the same docGroup as their scope
7893 if (mLoadedAsData
&& window
->GetExtantDoc() &&
7894 window
->GetExtantDoc() != this &&
7895 window
->GetExtantDoc()->NodePrincipal() == NodePrincipal()) {
7896 DocGroup
* docGroup
= window
->GetExtantDoc()->GetDocGroup();
7900 mDocGroup
= docGroup
;
7901 mDocGroup
->AddDocument(this);
7903 MOZ_ASSERT(mDocGroup
== docGroup
,
7904 "Data document has a mismatched doc group?");
7907 AssertDocGroupMatchesKey();
7910 // Update data document's mMutationEventsEnabled early on so that
7911 // we can avoid extra IsURIInPrefList calls.
7912 if (mMutationEventsEnabled
.isNothing()) {
7913 mMutationEventsEnabled
.emplace(
7914 window
->GetExtantDoc()->MutationEventsEnabled());
7920 MOZ_ASSERT_UNREACHABLE(
7921 "Scope window doesn't have DocGroup when creating data document?");
7922 // ... but fall through to be safe.
7925 BrowsingContextGroup
* browsingContextGroup
=
7926 window
->GetBrowsingContextGroup();
7928 // We should already have the principal, and now that we have been added
7929 // to a window, we should be able to join a DocGroup!
7930 nsAutoCString docGroupKey
;
7931 nsresult rv
= mozilla::dom::DocGroup::GetKey(
7933 browsingContextGroup
->IsPotentiallyCrossOriginIsolated(), docGroupKey
);
7935 if (NS_SUCCEEDED(rv
)) {
7936 MOZ_RELEASE_ASSERT(mDocGroup
->MatchesKey(docGroupKey
));
7938 MOZ_RELEASE_ASSERT(mDocGroup
->GetBrowsingContextGroup() ==
7939 browsingContextGroup
);
7941 mDocGroup
= browsingContextGroup
->AddDocument(docGroupKey
, this);
7943 MOZ_ASSERT(mDocGroup
);
7947 mNodeInfoManager
->GetArenaAllocator(),
7948 mNodeInfoManager
->GetArenaAllocator() == mDocGroup
->ArenaAllocator());
7952 bool Document::ContainsEMEContent() {
7953 nsPIDOMWindowInner
* win
= GetInnerWindow();
7954 // Note this case is different from checking just media elements in that
7955 // it covers when we've created MediaKeys but not associated them with a
7957 return win
&& win
->HasActiveMediaKeysInstance();
7960 bool Document::ContainsMSEContent() {
7961 bool containsMSE
= false;
7962 EnumerateActivityObservers([&containsMSE
](nsISupports
* aSupports
) {
7963 nsCOMPtr
<nsIContent
> content(do_QueryInterface(aSupports
));
7964 if (auto* mediaElem
= HTMLMediaElement::FromNodeOrNull(content
)) {
7965 RefPtr
<MediaSource
> ms
= mediaElem
->GetMozMediaSourceObject();
7974 static void NotifyActivityChangedCallback(nsISupports
* aSupports
) {
7975 nsCOMPtr
<nsIContent
> content(do_QueryInterface(aSupports
));
7976 if (auto* mediaElem
= HTMLMediaElement::FromNodeOrNull(content
)) {
7977 mediaElem
->NotifyOwnerDocumentActivityChanged();
7979 nsCOMPtr
<nsIDocumentActivity
> objectDocumentActivity(
7980 do_QueryInterface(aSupports
));
7981 if (objectDocumentActivity
) {
7982 objectDocumentActivity
->NotifyOwnerDocumentActivityChanged();
7984 nsCOMPtr
<nsIImageLoadingContent
> imageLoadingContent(
7985 do_QueryInterface(aSupports
));
7986 if (imageLoadingContent
) {
7988 static_cast<nsImageLoadingContent
*>(imageLoadingContent
.get());
7989 ilc
->NotifyOwnerDocumentActivityChanged();
7994 void Document::NotifyActivityChanged() {
7995 EnumerateActivityObservers(NotifyActivityChangedCallback
);
7996 // https://w3c.github.io/screen-wake-lock/#handling-document-loss-of-full-activity
7998 UnlockAllWakeLocks(WakeLockType::Screen
);
8002 void Document::SetContainer(nsDocShell
* aContainer
) {
8004 mDocumentContainer
= aContainer
;
8006 mDocumentContainer
= WeakPtr
<nsDocShell
>();
8010 aContainer
&& aContainer
->GetBrowsingContext()->IsChrome();
8012 NotifyActivityChanged();
8014 // IsTopLevelWindowInactive depends on the docshell, so
8015 // update the cached value now that it's available.
8016 UpdateDocumentStates(DocumentState::WINDOW_INACTIVE
, false);
8021 BrowsingContext
* context
= aContainer
->GetBrowsingContext();
8022 MOZ_ASSERT_IF(context
&& mDocGroup
,
8023 context
->Group() == mDocGroup
->GetBrowsingContextGroup());
8024 if (context
&& context
->IsContent()) {
8025 SetIsTopLevelContentDocument(context
->IsTopContent());
8026 SetIsContentDocument(true);
8028 SetIsTopLevelContentDocument(false);
8029 SetIsContentDocument(false);
8033 nsISupports
* Document::GetContainer() const {
8034 return static_cast<nsIDocShell
*>(mDocumentContainer
);
8037 void Document::SetScriptGlobalObject(
8038 nsIScriptGlobalObject
* aScriptGlobalObject
) {
8039 MOZ_ASSERT(aScriptGlobalObject
|| !mAnimationController
||
8040 mAnimationController
->IsPausedByType(
8041 SMILTimeContainer::PAUSE_PAGEHIDE
|
8042 SMILTimeContainer::PAUSE_BEGIN
),
8043 "Clearing window pointer while animations are unpaused");
8045 if (mScriptGlobalObject
&& !aScriptGlobalObject
) {
8046 // We're detaching from the window. We need to grab a pointer to
8047 // our layout history state now.
8048 mLayoutHistoryState
= GetLayoutHistoryState();
8050 // Also make sure to remove our onload blocker now if we haven't done it yet
8051 if (mOnloadBlockCount
!= 0) {
8052 nsCOMPtr
<nsILoadGroup
> loadGroup
= GetDocumentLoadGroup();
8054 loadGroup
->RemoveRequest(mOnloadBlocker
, nullptr, NS_OK
);
8058 if (GetController().isSome()) {
8059 if (imgLoader
* loader
= nsContentUtils::GetImgLoaderForDocument(this)) {
8060 loader
->ClearCacheForControlledDocument(this);
8063 // We may become controlled again if this document comes back out
8064 // of bfcache. Clear our state to allow that to happen. Only
8065 // clear this flag if we are actually controlled, though, so pages
8066 // that were force reloaded don't become controlled when they
8067 // come out of bfcache.
8068 mMaybeServiceWorkerControlled
= false;
8071 if (GetWindowContext()) {
8072 // The document is about to lose its window, so this is a good time to
8073 // send our page use counters, while we still have access to our
8076 // (We also do this in nsGlobalWindowInner::FreeInnerObjects(), which
8077 // catches some cases of documents losing their window that don't
8079 SendPageUseCounters();
8083 // BlockOnload() might be called before mScriptGlobalObject is set.
8084 // We may need to add the blocker once mScriptGlobalObject is set.
8085 bool needOnloadBlocker
= !mScriptGlobalObject
&& aScriptGlobalObject
;
8087 mScriptGlobalObject
= aScriptGlobalObject
;
8089 if (needOnloadBlocker
) {
8090 EnsureOnloadBlocker();
8093 // FIXME(emilio): is this really needed?
8094 MaybeScheduleFrameRequestCallbacks();
8096 if (aScriptGlobalObject
) {
8097 // Go back to using the docshell for the layout history state
8098 mLayoutHistoryState
= nullptr;
8099 SetScopeObject(aScriptGlobalObject
);
8100 mHasHadDefaultView
= true;
8102 if (mAllowDNSPrefetch
) {
8103 nsCOMPtr
<nsIDocShell
> docShell(mDocumentContainer
);
8106 nsCOMPtr
<nsIWebNavigation
> webNav
=
8107 do_GetInterface(aScriptGlobalObject
);
8108 NS_ASSERTION(SameCOMIdentity(webNav
, docShell
),
8109 "Unexpected container or script global?");
8111 bool allowDNSPrefetch
;
8112 docShell
->GetAllowDNSPrefetch(&allowDNSPrefetch
);
8113 mAllowDNSPrefetch
= allowDNSPrefetch
;
8117 // If we are set in a window that is already focused we should remember this
8118 // as the time the document gained focus.
8119 if (HasFocus(IgnoreErrors())) {
8120 SetLastFocusTime(TimeStamp::Now());
8124 // Remember the pointer to our window (or lack there of), to avoid
8125 // having to QI every time it's asked for.
8126 nsCOMPtr
<nsPIDOMWindowInner
> window
= do_QueryInterface(mScriptGlobalObject
);
8129 // Now that we know what our window is, we can flush the CSP errors to the
8130 // Web Console. We are flushing all messages that occurred and were stored in
8131 // the queue prior to this point.
8133 static_cast<nsCSPContext
*>(mCSP
.get())->flushConsoleMessages();
8136 nsCOMPtr
<nsIHttpChannelInternal
> internalChannel
=
8137 do_QueryInterface(GetChannel());
8138 if (internalChannel
) {
8139 nsCOMArray
<nsISecurityConsoleMessage
> messages
;
8140 DebugOnly
<nsresult
> rv
= internalChannel
->TakeAllSecurityMessages(messages
);
8141 MOZ_ASSERT(NS_SUCCEEDED(rv
));
8142 SendToConsole(messages
);
8145 // Set our visibility state, but do not fire the event. This is correct
8146 // because either we're coming out of bfcache (in which case IsVisible() will
8147 // still test false at this point and no state change will happen) or we're
8148 // doing the initial document load and don't want to fire the event for this
8151 // When the visibility is changed, notify it to observers.
8152 // Some observers need the notification, for example HTMLMediaElement uses
8153 // it to update internal media resource allocation.
8154 // When video is loaded via VideoDocument, HTMLMediaElement and MediaDecoder
8155 // creation are already done before Document::SetScriptGlobalObject() call.
8156 // MediaDecoder decides whether starting decoding is decided based on
8157 // document's visibility. When the MediaDecoder is created,
8158 // Document::SetScriptGlobalObject() is not yet called and document is
8159 // hidden state. Therefore the MediaDecoder decides that decoding is
8160 // not yet necessary. But soon after Document::SetScriptGlobalObject()
8161 // call, the document becomes not hidden. At the time, MediaDecoder needs
8162 // to know it and needs to start updating decoding.
8163 UpdateVisibilityState(DispatchVisibilityChange::No
);
8165 // The global in the template contents owner document should be the same.
8166 if (mTemplateContentsOwner
&& mTemplateContentsOwner
!= this) {
8167 mTemplateContentsOwner
->SetScriptGlobalObject(aScriptGlobalObject
);
8170 // Tell the script loader about the new global object.
8171 if (mScriptLoader
&& !IsTemplateContentsOwner()) {
8172 mScriptLoader
->SetGlobalObject(mScriptGlobalObject
);
8175 if (!mMaybeServiceWorkerControlled
&& mDocumentContainer
&&
8176 mScriptGlobalObject
&& GetChannel()) {
8177 // If we are shift-reloaded, don't associate with a ServiceWorker.
8178 if (mDocumentContainer
->IsForceReloading()) {
8179 NS_WARNING("Page was shift reloaded, skipping ServiceWorker control");
8183 mMaybeServiceWorkerControlled
= true;
8187 nsIScriptGlobalObject
* Document::GetScriptHandlingObjectInternal() const {
8188 MOZ_ASSERT(!mScriptGlobalObject
,
8189 "Do not call this when mScriptGlobalObject is set!");
8190 if (mHasHadDefaultView
) {
8194 nsCOMPtr
<nsIScriptGlobalObject
> scriptHandlingObject
=
8195 do_QueryReferent(mScopeObject
);
8196 nsCOMPtr
<nsPIDOMWindowInner
> win
= do_QueryInterface(scriptHandlingObject
);
8198 nsPIDOMWindowOuter
* outer
= win
->GetOuterWindow();
8199 if (!outer
|| outer
->GetCurrentInnerWindow() != win
) {
8200 NS_WARNING("Wrong inner/outer window combination!");
8204 return scriptHandlingObject
;
8206 void Document::SetScriptHandlingObject(nsIScriptGlobalObject
* aScriptObject
) {
8207 NS_ASSERTION(!mScriptGlobalObject
|| mScriptGlobalObject
== aScriptObject
,
8208 "Wrong script object!");
8209 if (aScriptObject
) {
8210 SetScopeObject(aScriptObject
);
8211 mHasHadDefaultView
= false;
8215 nsPIDOMWindowOuter
* Document::GetWindowInternal() const {
8216 MOZ_ASSERT(!mWindow
, "This should not be called when mWindow is not null!");
8217 // Let's use mScriptGlobalObject. Even if the document is already removed from
8218 // the docshell, the outer window might be still obtainable from the it.
8219 nsCOMPtr
<nsPIDOMWindowOuter
> win
;
8220 if (mRemovedFromDocShell
) {
8221 // The docshell returns the outer window we are done.
8222 nsCOMPtr
<nsIDocShell
> kungFuDeathGrip(mDocumentContainer
);
8223 if (kungFuDeathGrip
) {
8224 win
= kungFuDeathGrip
->GetWindow();
8227 if (nsCOMPtr
<nsPIDOMWindowInner
> inner
=
8228 do_QueryInterface(mScriptGlobalObject
)) {
8229 // mScriptGlobalObject is always the inner window, let's get the outer.
8230 win
= inner
->GetOuterWindow();
8237 bool Document::InternalAllowXULXBL() {
8238 if (nsContentUtils::AllowXULXBLForPrincipal(NodePrincipal())) {
8239 mAllowXULXBL
= eTriTrue
;
8243 mAllowXULXBL
= eTriFalse
;
8247 // Note: We don't hold a reference to the document observer; we assume
8248 // that it has a live reference to the document.
8249 void Document::AddObserver(nsIDocumentObserver
* aObserver
) {
8250 NS_ASSERTION(mObservers
.IndexOf(aObserver
) == nsTArray
<int>::NoIndex
,
8251 "Observer already in the list");
8252 mObservers
.AppendElement(aObserver
);
8253 AddMutationObserver(aObserver
);
8256 bool Document::RemoveObserver(nsIDocumentObserver
* aObserver
) {
8257 // If we're in the process of destroying the document (and we're
8258 // informing the observers of the destruction), don't remove the
8259 // observers from the list. This is not a big deal, since we
8260 // don't hold a live reference to the observers.
8261 if (!mInDestructor
) {
8262 RemoveMutationObserver(aObserver
);
8263 return mObservers
.RemoveElement(aObserver
);
8266 return mObservers
.Contains(aObserver
);
8269 void Document::BeginUpdate() {
8271 nsContentUtils::AddScriptBlocker();
8272 NS_DOCUMENT_NOTIFY_OBSERVERS(BeginUpdate
, (this));
8275 void Document::EndUpdate() {
8276 const bool reset
= !mPendingMaybeEditingStateChanged
;
8277 mPendingMaybeEditingStateChanged
= true;
8279 NS_DOCUMENT_NOTIFY_OBSERVERS(EndUpdate
, (this));
8283 nsContentUtils::RemoveScriptBlocker();
8285 if (mXULBroadcastManager
) {
8286 mXULBroadcastManager
->MaybeBroadcast();
8290 mPendingMaybeEditingStateChanged
= false;
8292 MaybeEditingStateChanged();
8295 void Document::BeginLoad() {
8296 if (IsEditingOn()) {
8297 // Reset() blows away all event listeners in the document, and our
8298 // editor relies heavily on those. Midas is turned on, to make it
8299 // work, re-initialize it to give it a chance to add its event
8303 EditingStateChanged();
8306 MOZ_ASSERT(!mDidCallBeginLoad
);
8307 mDidCallBeginLoad
= true;
8309 // Block onload here to prevent having to deal with blocking and
8310 // unblocking it while we know the document is loading.
8312 mDidFireDOMContentLoaded
= false;
8313 BlockDOMContentLoaded();
8315 if (mScriptLoader
) {
8316 mScriptLoader
->BeginDeferringScripts();
8319 NS_DOCUMENT_NOTIFY_OBSERVERS(BeginLoad
, (this));
8322 void Document::MozSetImageElement(const nsAString
& aImageElementId
,
8323 Element
* aElement
) {
8324 if (aImageElementId
.IsEmpty()) return;
8326 // Hold a script blocker while calling SetImageElement since that can call
8327 // out to id-observers
8328 nsAutoScriptBlocker scriptBlocker
;
8330 IdentifierMapEntry
* entry
= mIdentifierMap
.PutEntry(aImageElementId
);
8332 entry
->SetImageElement(aElement
);
8333 if (entry
->IsEmpty()) {
8334 mIdentifierMap
.RemoveEntry(entry
);
8339 void Document::DispatchContentLoadedEvents() {
8340 // If you add early returns from this method, make sure you're
8341 // calling UnblockOnload properly.
8343 // Unpin references to preloaded images
8344 mPreloadingImages
.Clear();
8346 // DOM manipulation after content loaded should not care if the element
8347 // came from the preloader.
8348 mPreloadedPreconnects
.Clear();
8351 mTiming
->NotifyDOMContentLoadedStart(Document::GetDocumentURI());
8354 // Dispatch observer notification to notify observers document is interactive.
8355 nsCOMPtr
<nsIObserverService
> os
= mozilla::services::GetObserverService();
8357 nsIPrincipal
* principal
= NodePrincipal();
8358 os
->NotifyObservers(ToSupports(this),
8359 principal
->IsSystemPrincipal()
8360 ? "chrome-document-interactive"
8361 : "content-document-interactive",
8365 // Fire a DOM event notifying listeners that this document has been
8366 // loaded (excluding images and other loads initiated by this
8368 nsContentUtils::DispatchTrustedEvent(this, this, u
"DOMContentLoaded"_ns
,
8369 CanBubble::eYes
, Cancelable::eNo
);
8371 if (auto* const window
= GetInnerWindow()) {
8372 const RefPtr
<ServiceWorkerContainer
> serviceWorker
=
8373 window
->Navigator()->ServiceWorker();
8375 // This could cause queued messages from a service worker to get
8376 // dispatched on serviceWorker.
8377 serviceWorker
->StartMessages();
8380 if (MayStartLayout()) {
8381 MaybeResolveReadyForIdle();
8385 mTiming
->NotifyDOMContentLoadedEnd(Document::GetDocumentURI());
8388 // If this document is a [i]frame, fire a DOMFrameContentLoaded
8389 // event on all parent documents notifying that the HTML (excluding
8390 // other external files such as images and stylesheets) in a frame
8391 // has finished loading.
8393 // target_frame is the [i]frame element that will be used as the
8394 // target for the event. It's the [i]frame whose content is done
8396 nsCOMPtr
<Element
> target_frame
= GetEmbedderElement();
8398 if (target_frame
&& target_frame
->IsInComposedDoc()) {
8399 nsCOMPtr
<Document
> parent
= target_frame
->OwnerDoc();
8401 RefPtr
<Event
> event
;
8403 IgnoredErrorResult ignored
;
8404 event
= parent
->CreateEvent(u
"Events"_ns
, CallerType::System
, ignored
);
8408 event
->InitEvent(u
"DOMFrameContentLoaded"_ns
, true, true);
8410 event
->SetTarget(target_frame
);
8411 event
->SetTrusted(true);
8413 // To dispatch this event we must manually call
8414 // EventDispatcher::Dispatch() on the ancestor document since the
8415 // target is not in the same document, so the event would never reach
8416 // the ancestor document if we used the normal event
8417 // dispatching code.
8419 WidgetEvent
* innerEvent
= event
->WidgetEventPtr();
8421 nsEventStatus status
= nsEventStatus_eIgnore
;
8423 if (RefPtr
<nsPresContext
> context
= parent
->GetPresContext()) {
8424 EventDispatcher::Dispatch(parent
, context
, innerEvent
, event
,
8430 parent
= parent
->GetInProcessParentDocument();
8434 nsPIDOMWindowInner
* inner
= GetInnerWindow();
8436 inner
->NoteDOMContentLoaded();
8440 if (mMaybeServiceWorkerControlled
) {
8441 using mozilla::dom::ServiceWorkerManager
;
8442 RefPtr
<ServiceWorkerManager
> swm
= ServiceWorkerManager::GetInstance();
8444 Maybe
<ClientInfo
> clientInfo
= GetClientInfo();
8445 if (clientInfo
.isSome()) {
8446 swm
->MaybeCheckNavigationUpdate(clientInfo
.ref());
8451 if (mSetCompleteAfterDOMContentLoaded
) {
8452 SetReadyStateInternal(ReadyState::READYSTATE_COMPLETE
);
8453 mSetCompleteAfterDOMContentLoaded
= false;
8456 UnblockOnload(true);
8459 void Document::EndLoad() {
8460 bool turnOnEditing
=
8461 mParser
&& (IsInDesignMode() || mContentEditableCount
> 0);
8464 // only assert if nothing stopped the load on purpose
8465 if (!mParserAborted
) {
8466 nsContentSecurityUtils::AssertAboutPageHasCSP(this);
8470 // EndLoad may have been called without a matching call to BeginLoad, in the
8471 // case of a failed parse (for example, due to timeout). In such a case, we
8472 // still want to execute part of this code to do appropriate cleanup, but we
8473 // gate part of it because it is intended to match 1-for-1 with calls to
8474 // BeginLoad. We have an explicit flag bit for this purpose, since it's
8475 // complicated and error prone to derive this condition from other related
8476 // flags that can be manipulated outside of a BeginLoad/EndLoad pair.
8478 // Part 1: Code that always executes to cleanup end of parsing, whether
8479 // that parsing was successful or not.
8481 // Drop the ref to our parser, if any, but keep hold of the sink so that we
8482 // can flush it from FlushPendingNotifications as needed. We might have to
8483 // do that to get a StartLayout() to happen.
8485 mWeakSink
= do_GetWeakReference(mParser
->GetContentSink());
8489 // Update the attributes on the PerformanceNavigationTiming before notifying
8490 // the onload observers.
8491 if (nsPIDOMWindowInner
* window
= GetInnerWindow()) {
8492 if (RefPtr
<Performance
> performance
= window
->GetPerformance()) {
8493 performance
->UpdateNavigationTimingEntry();
8497 NS_DOCUMENT_NOTIFY_OBSERVERS(EndLoad
, (this));
8499 // Part 2: Code that only executes when this EndLoad matches a BeginLoad.
8501 if (!mDidCallBeginLoad
) {
8504 mDidCallBeginLoad
= false;
8506 UnblockDOMContentLoaded();
8508 if (turnOnEditing
) {
8509 EditingStateChanged();
8513 // This is a document that's not in a window. For example, this could be an
8514 // XMLHttpRequest responseXML document, or a document created via DOMParser
8515 // or DOMImplementation. We don't reach this code normally for such
8516 // documents (which is not obviously correct), but can reach it via
8517 // document.open()/document.close().
8519 // Such documents don't fire load events, but per spec should set their
8520 // readyState to "complete" when parsing and all loading of subresources is
8521 // done. Parsing is done now, and documents not in a window don't load
8522 // subresources, so just go ahead and mark ourselves as complete.
8523 SetReadyStateInternal(Document::READYSTATE_COMPLETE
,
8524 /* updateTimingInformation = */ false);
8526 // Reset mSkipLoadEventAfterClose just in case.
8527 mSkipLoadEventAfterClose
= false;
8531 void Document::UnblockDOMContentLoaded() {
8532 MOZ_ASSERT(mBlockDOMContentLoaded
);
8533 if (--mBlockDOMContentLoaded
!= 0 || mDidFireDOMContentLoaded
) {
8537 MOZ_LOG(gDocumentLeakPRLog
, LogLevel::Debug
,
8538 ("DOCUMENT %p UnblockDOMContentLoaded", this));
8540 mDidFireDOMContentLoaded
= true;
8542 MOZ_ASSERT(mReadyState
== READYSTATE_INTERACTIVE
);
8543 if (!mSynchronousDOMContentLoaded
) {
8544 MOZ_RELEASE_ASSERT(NS_IsMainThread());
8545 nsCOMPtr
<nsIRunnable
> ev
=
8546 NewRunnableMethod("Document::DispatchContentLoadedEvents", this,
8547 &Document::DispatchContentLoadedEvents
);
8548 Dispatch(ev
.forget());
8550 DispatchContentLoadedEvents();
8554 void Document::ElementStateChanged(Element
* aElement
, ElementState aStateMask
) {
8555 MOZ_ASSERT(!nsContentUtils::IsSafeToRunScript(),
8556 "Someone forgot a scriptblocker");
8557 NS_DOCUMENT_NOTIFY_OBSERVERS(ElementStateChanged
,
8558 (this, aElement
, aStateMask
));
8561 void Document::RuleChanged(StyleSheet
& aSheet
, css::Rule
*,
8562 const StyleRuleChange
&) {
8563 if (aSheet
.IsApplicable()) {
8564 ApplicableStylesChanged();
8568 void Document::RuleAdded(StyleSheet
& aSheet
, css::Rule
& aRule
) {
8569 if (aRule
.IsIncompleteImportRule()) {
8573 if (aSheet
.IsApplicable()) {
8574 ApplicableStylesChanged();
8578 void Document::ImportRuleLoaded(StyleSheet
& aSheet
) {
8579 if (aSheet
.IsApplicable()) {
8580 ApplicableStylesChanged();
8584 void Document::RuleRemoved(StyleSheet
& aSheet
, css::Rule
& aRule
) {
8585 if (aSheet
.IsApplicable()) {
8586 ApplicableStylesChanged();
8590 static Element
* GetCustomContentContainer(PresShell
* aPresShell
) {
8591 if (!aPresShell
|| !aPresShell
->GetCanvasFrame()) {
8595 return aPresShell
->GetCanvasFrame()->GetCustomContentContainer();
8598 already_AddRefed
<AnonymousContent
> Document::InsertAnonymousContent(
8599 bool aForce
, ErrorResult
& aRv
) {
8600 RefPtr
<PresShell
> shell
= GetPresShell();
8601 if (aForce
&& !GetCustomContentContainer(shell
)) {
8602 FlushPendingNotifications(FlushType::Layout
);
8603 shell
= GetPresShell();
8606 nsAutoScriptBlocker scriptBlocker
;
8608 RefPtr
<AnonymousContent
> anonContent
= AnonymousContent::Create(*this);
8610 aRv
.Throw(NS_ERROR_OUT_OF_MEMORY
);
8614 mAnonymousContents
.AppendElement(anonContent
);
8616 if (RefPtr
<Element
> container
= GetCustomContentContainer(shell
)) {
8617 // If the container is empty and we have other anon content we should be
8618 // about to show all the other anonymous content nodes.
8619 if (container
->HasChildren() || mAnonymousContents
.Length() == 1) {
8620 container
->AppendChildTo(anonContent
->Host(), true, IgnoreErrors());
8621 if (auto* canvasFrame
= shell
->GetCanvasFrame()) {
8622 canvasFrame
->ShowCustomContentContainer();
8627 return anonContent
.forget();
8630 static void RemoveAnonContentFromCanvas(AnonymousContent
& aAnonContent
,
8631 PresShell
* aPresShell
) {
8632 RefPtr
<Element
> container
= GetCustomContentContainer(aPresShell
);
8636 container
->RemoveChild(*aAnonContent
.Host(), IgnoreErrors());
8639 void Document::RemoveAnonymousContent(AnonymousContent
& aContent
) {
8640 nsAutoScriptBlocker scriptBlocker
;
8642 auto index
= mAnonymousContents
.IndexOf(&aContent
);
8643 if (index
== mAnonymousContents
.NoIndex
) {
8647 mAnonymousContents
.RemoveElementAt(index
);
8648 RemoveAnonContentFromCanvas(aContent
, GetPresShell());
8650 if (mAnonymousContents
.IsEmpty() &&
8651 GetCustomContentContainer(GetPresShell())) {
8652 GetPresShell()->GetCanvasFrame()->HideCustomContentContainer();
8656 Element
* Document::GetAnonRootIfInAnonymousContentContainer(
8657 nsINode
* aNode
) const {
8658 if (!aNode
->IsInNativeAnonymousSubtree()) {
8662 PresShell
* presShell
= GetPresShell();
8663 if (!presShell
|| !presShell
->GetCanvasFrame()) {
8667 nsAutoScriptBlocker scriptBlocker
;
8668 nsCOMPtr
<Element
> customContainer
=
8669 presShell
->GetCanvasFrame()->GetCustomContentContainer();
8670 if (!customContainer
) {
8674 // An arbitrary number of elements can be inserted as children of the custom
8675 // container frame. We want the one that was added that contains aNode, so
8676 // we need to keep track of the last child separately using |child| here.
8677 nsINode
* child
= aNode
;
8678 nsINode
* parent
= aNode
->GetParentNode();
8679 while (parent
&& parent
->IsInNativeAnonymousSubtree()) {
8680 if (parent
== customContainer
) {
8681 return Element::FromNode(child
);
8684 parent
= child
->GetParentNode();
8689 Maybe
<ClientInfo
> Document::GetClientInfo() const {
8690 if (const Document
* orig
= GetOriginalDocument()) {
8691 if (Maybe
<ClientInfo
> info
= orig
->GetClientInfo()) {
8696 if (nsPIDOMWindowInner
* inner
= GetInnerWindow()) {
8697 return inner
->GetClientInfo();
8700 return Maybe
<ClientInfo
>();
8703 Maybe
<ClientState
> Document::GetClientState() const {
8704 if (const Document
* orig
= GetOriginalDocument()) {
8705 if (Maybe
<ClientState
> state
= orig
->GetClientState()) {
8710 if (nsPIDOMWindowInner
* inner
= GetInnerWindow()) {
8711 return inner
->GetClientState();
8714 return Maybe
<ClientState
>();
8717 Maybe
<ServiceWorkerDescriptor
> Document::GetController() const {
8718 if (const Document
* orig
= GetOriginalDocument()) {
8719 if (Maybe
<ServiceWorkerDescriptor
> controller
= orig
->GetController()) {
8724 if (nsPIDOMWindowInner
* inner
= GetInnerWindow()) {
8725 return inner
->GetController();
8728 return Maybe
<ServiceWorkerDescriptor
>();
8732 // Document interface
8734 DocumentType
* Document::GetDoctype() const {
8735 for (nsIContent
* child
= GetFirstChild(); child
;
8736 child
= child
->GetNextSibling()) {
8737 if (child
->NodeType() == DOCUMENT_TYPE_NODE
) {
8738 return static_cast<DocumentType
*>(child
);
8744 DOMImplementation
* Document::GetImplementation(ErrorResult
& rv
) {
8745 if (!mDOMImplementation
) {
8746 nsCOMPtr
<nsIURI
> uri
;
8747 NS_NewURI(getter_AddRefs(uri
), "about:blank");
8749 rv
.Throw(NS_ERROR_OUT_OF_MEMORY
);
8752 bool hasHadScriptObject
= true;
8753 nsIScriptGlobalObject
* scriptObject
=
8754 GetScriptHandlingObject(hasHadScriptObject
);
8755 if (!scriptObject
&& hasHadScriptObject
) {
8756 rv
.Throw(NS_ERROR_UNEXPECTED
);
8759 mDOMImplementation
= new DOMImplementation(
8760 this, scriptObject
? scriptObject
: GetScopeObject(), uri
, uri
);
8763 return mDOMImplementation
;
8766 bool IsLowercaseASCII(const nsAString
& aValue
) {
8767 int32_t len
= aValue
.Length();
8768 for (int32_t i
= 0; i
< len
; ++i
) {
8769 char16_t c
= aValue
[i
];
8770 if (!(0x0061 <= (c
) && ((c
) <= 0x007a))) {
8777 already_AddRefed
<Element
> Document::CreateElement(
8778 const nsAString
& aTagName
, const ElementCreationOptionsOrString
& aOptions
,
8780 rv
= nsContentUtils::CheckQName(aTagName
, false);
8785 bool needsLowercase
= IsHTMLDocument() && !IsLowercaseASCII(aTagName
);
8786 nsAutoString lcTagName
;
8787 if (needsLowercase
) {
8788 nsContentUtils::ASCIIToLower(aTagName
, lcTagName
);
8791 const nsString
* is
= nullptr;
8792 PseudoStyleType pseudoType
= PseudoStyleType::NotPseudo
;
8793 if (aOptions
.IsElementCreationOptions()) {
8794 const ElementCreationOptions
& options
=
8795 aOptions
.GetAsElementCreationOptions();
8797 if (options
.mIs
.WasPassed()) {
8798 is
= &options
.mIs
.Value();
8801 // Check 'pseudo' and throw an exception if it's not one allowed
8802 // with CSS_PSEUDO_ELEMENT_IS_JS_CREATED_NAC.
8803 if (options
.mPseudo
.WasPassed()) {
8804 Maybe
<PseudoStyleRequest
> request
=
8805 nsCSSPseudoElements::ParsePseudoElement(options
.mPseudo
.Value());
8806 if (!request
|| request
->IsNotPseudo() ||
8807 !nsCSSPseudoElements::PseudoElementIsJSCreatedNAC(request
->mType
)) {
8808 rv
.ThrowNotSupportedError("Invalid pseudo-element");
8811 pseudoType
= request
->mType
;
8815 RefPtr
<Element
> elem
= CreateElem(needsLowercase
? lcTagName
: aTagName
,
8816 nullptr, mDefaultElementType
, is
);
8818 if (pseudoType
!= PseudoStyleType::NotPseudo
) {
8819 elem
->SetPseudoElementType(pseudoType
);
8822 return elem
.forget();
8825 already_AddRefed
<Element
> Document::CreateElementNS(
8826 const nsAString
& aNamespaceURI
, const nsAString
& aQualifiedName
,
8827 const ElementCreationOptionsOrString
& aOptions
, ErrorResult
& rv
) {
8828 RefPtr
<mozilla::dom::NodeInfo
> nodeInfo
;
8829 rv
= nsContentUtils::GetNodeInfoFromQName(aNamespaceURI
, aQualifiedName
,
8830 mNodeInfoManager
, ELEMENT_NODE
,
8831 getter_AddRefs(nodeInfo
));
8836 const nsString
* is
= nullptr;
8837 if (aOptions
.IsElementCreationOptions()) {
8838 const ElementCreationOptions
& options
=
8839 aOptions
.GetAsElementCreationOptions();
8840 if (options
.mIs
.WasPassed()) {
8841 is
= &options
.mIs
.Value();
8845 nsCOMPtr
<Element
> element
;
8846 rv
= NS_NewElement(getter_AddRefs(element
), nodeInfo
.forget(),
8847 NOT_FROM_PARSER
, is
);
8852 return element
.forget();
8855 already_AddRefed
<Element
> Document::CreateXULElement(
8856 const nsAString
& aTagName
, const ElementCreationOptionsOrString
& aOptions
,
8858 aRv
= nsContentUtils::CheckQName(aTagName
, false);
8863 const nsString
* is
= nullptr;
8864 if (aOptions
.IsElementCreationOptions()) {
8865 const ElementCreationOptions
& options
=
8866 aOptions
.GetAsElementCreationOptions();
8867 if (options
.mIs
.WasPassed()) {
8868 is
= &options
.mIs
.Value();
8872 RefPtr
<Element
> elem
= CreateElem(aTagName
, nullptr, kNameSpaceID_XUL
, is
);
8874 aRv
.Throw(NS_ERROR_NOT_AVAILABLE
);
8877 return elem
.forget();
8880 already_AddRefed
<nsTextNode
> Document::CreateEmptyTextNode() const {
8881 RefPtr
<nsTextNode
> text
= new (mNodeInfoManager
) nsTextNode(mNodeInfoManager
);
8882 return text
.forget();
8885 already_AddRefed
<nsTextNode
> Document::CreateTextNode(
8886 const nsAString
& aData
) const {
8887 RefPtr
<nsTextNode
> text
= new (mNodeInfoManager
) nsTextNode(mNodeInfoManager
);
8888 // Don't notify; this node is still being created.
8889 text
->SetText(aData
, false);
8890 return text
.forget();
8893 already_AddRefed
<DocumentFragment
> Document::CreateDocumentFragment() const {
8894 RefPtr
<DocumentFragment
> frag
=
8895 new (mNodeInfoManager
) DocumentFragment(mNodeInfoManager
);
8896 return frag
.forget();
8899 // Unfortunately, bareword "Comment" is ambiguous with some Mac system headers.
8900 already_AddRefed
<dom::Comment
> Document::CreateComment(
8901 const nsAString
& aData
) const {
8902 RefPtr
<dom::Comment
> comment
=
8903 new (mNodeInfoManager
) dom::Comment(mNodeInfoManager
);
8905 // Don't notify; this node is still being created.
8906 comment
->SetText(aData
, false);
8907 return comment
.forget();
8910 already_AddRefed
<CDATASection
> Document::CreateCDATASection(
8911 const nsAString
& aData
, ErrorResult
& rv
) {
8912 if (IsHTMLDocument()) {
8913 rv
.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR
);
8917 if (FindInReadable(u
"]]>"_ns
, aData
)) {
8918 rv
.Throw(NS_ERROR_DOM_INVALID_CHARACTER_ERR
);
8922 RefPtr
<CDATASection
> cdata
=
8923 new (mNodeInfoManager
) CDATASection(mNodeInfoManager
);
8925 // Don't notify; this node is still being created.
8926 cdata
->SetText(aData
, false);
8928 return cdata
.forget();
8931 already_AddRefed
<ProcessingInstruction
> Document::CreateProcessingInstruction(
8932 const nsAString
& aTarget
, const nsAString
& aData
, ErrorResult
& rv
) const {
8933 nsresult res
= nsContentUtils::CheckQName(aTarget
, false);
8934 if (NS_FAILED(res
)) {
8939 if (FindInReadable(u
"?>"_ns
, aData
)) {
8940 rv
.Throw(NS_ERROR_DOM_INVALID_CHARACTER_ERR
);
8944 RefPtr
<ProcessingInstruction
> pi
=
8945 NS_NewXMLProcessingInstruction(mNodeInfoManager
, aTarget
, aData
);
8950 already_AddRefed
<Attr
> Document::CreateAttribute(const nsAString
& aName
,
8952 if (!mNodeInfoManager
) {
8953 rv
.Throw(NS_ERROR_NOT_INITIALIZED
);
8957 nsresult res
= nsContentUtils::CheckQName(aName
, false);
8958 if (NS_FAILED(res
)) {
8964 if (IsHTMLDocument()) {
8965 nsContentUtils::ASCIIToLower(aName
, name
);
8970 RefPtr
<mozilla::dom::NodeInfo
> nodeInfo
;
8971 res
= mNodeInfoManager
->GetNodeInfo(name
, nullptr, kNameSpaceID_None
,
8972 ATTRIBUTE_NODE
, getter_AddRefs(nodeInfo
));
8973 if (NS_FAILED(res
)) {
8978 RefPtr
<Attr
> attribute
=
8979 new (mNodeInfoManager
) Attr(nullptr, nodeInfo
.forget(), u
""_ns
);
8980 return attribute
.forget();
8983 already_AddRefed
<Attr
> Document::CreateAttributeNS(
8984 const nsAString
& aNamespaceURI
, const nsAString
& aQualifiedName
,
8986 RefPtr
<mozilla::dom::NodeInfo
> nodeInfo
;
8987 rv
= nsContentUtils::GetNodeInfoFromQName(aNamespaceURI
, aQualifiedName
,
8988 mNodeInfoManager
, ATTRIBUTE_NODE
,
8989 getter_AddRefs(nodeInfo
));
8994 RefPtr
<Attr
> attribute
=
8995 new (mNodeInfoManager
) Attr(nullptr, nodeInfo
.forget(), u
""_ns
);
8996 return attribute
.forget();
8999 void Document::ScheduleForPresAttrEvaluation(Element
* aElement
) {
9000 MOZ_ASSERT(aElement
->IsInComposedDoc());
9001 DebugOnly
<bool> inserted
= mLazyPresElements
.EnsureInserted(aElement
);
9002 MOZ_ASSERT(inserted
);
9003 if (aElement
->HasServoData()) {
9004 // TODO(emilio): RESTYLE_SELF is too strong, there should be no need to
9005 // re-selector-match, but right now this is needed to pick up the new mapped
9006 // attributes. We need something like RESTYLE_STYLE_ATTRIBUTE but for mapped
9008 nsLayoutUtils::PostRestyleEvent(aElement
, RestyleHint::RESTYLE_SELF
,
9010 } else if (auto* presContext
= GetPresContext()) {
9011 presContext
->RestyleManager()->IncrementUndisplayedRestyleGeneration();
9015 void Document::UnscheduleForPresAttrEvaluation(Element
* aElement
) {
9016 mLazyPresElements
.Remove(aElement
);
9019 void Document::DoResolveScheduledPresAttrs() {
9020 MOZ_ASSERT(!mLazyPresElements
.IsEmpty());
9021 for (Element
* el
: mLazyPresElements
) {
9022 MOZ_ASSERT(el
->IsInComposedDoc(),
9023 "Un-schedule when removing from the document");
9024 MOZ_ASSERT(el
->IsPendingMappedAttributeEvaluation());
9025 if (auto* svg
= SVGElement::FromNode(el
)) {
9026 // SVG does its own (very similar) thing, for now at least.
9027 svg
->UpdateMappedDeclarationBlock();
9029 MappedDeclarationsBuilder
builder(*el
, *this,
9030 el
->GetMappedAttributeStyle());
9031 auto function
= el
->GetAttributeMappingFunction();
9033 el
->SetMappedDeclarationBlock(builder
.TakeDeclarationBlock());
9035 MOZ_ASSERT(!el
->IsPendingMappedAttributeEvaluation());
9037 mLazyPresElements
.Clear();
9040 already_AddRefed
<nsSimpleContentList
> Document::BlockedNodesByClassifier()
9042 RefPtr
<nsSimpleContentList
> list
= new nsSimpleContentList(nullptr);
9044 for (const nsWeakPtr
& weakNode
: mBlockedNodesByClassifier
) {
9045 if (nsCOMPtr
<nsIContent
> node
= do_QueryReferent(weakNode
)) {
9046 // Consider only nodes to which we have managed to get strong references.
9047 // Coping with nullptrs since it's expected for nodes to disappear when
9048 // nobody else is referring to them.
9049 list
->AppendElement(node
);
9053 return list
.forget();
9056 void Document::GetSelectedStyleSheetSet(nsAString
& aSheetSet
) {
9057 aSheetSet
.Truncate();
9059 // Look through our sheets, find the selected set title
9060 size_t count
= SheetCount();
9062 for (size_t index
= 0; index
< count
; index
++) {
9063 StyleSheet
* sheet
= SheetAt(index
);
9064 NS_ASSERTION(sheet
, "Null sheet in sheet list!");
9066 if (sheet
->Disabled()) {
9067 // Disabled sheets don't affect the currently selected set
9071 sheet
->GetTitle(title
);
9073 if (aSheetSet
.IsEmpty()) {
9075 } else if (!title
.IsEmpty() && !aSheetSet
.Equals(title
)) {
9076 // Sheets from multiple sets enabled; return null string, per spec.
9077 SetDOMStringToNull(aSheetSet
);
9083 void Document::SetSelectedStyleSheetSet(const nsAString
& aSheetSet
) {
9084 if (DOMStringIsNull(aSheetSet
)) {
9088 // Must update mLastStyleSheetSet before doing anything else with stylesheets
9090 mLastStyleSheetSet
= aSheetSet
;
9091 EnableStyleSheetsForSetInternal(aSheetSet
, true);
9094 void Document::SetPreferredStyleSheetSet(const nsAString
& aSheetSet
) {
9095 mPreferredStyleSheetSet
= aSheetSet
;
9096 // Only mess with our stylesheets if we don't have a lastStyleSheetSet, per
9098 if (DOMStringIsNull(mLastStyleSheetSet
)) {
9099 // Calling EnableStyleSheetsForSetInternal, not SetSelectedStyleSheetSet,
9100 // per spec. The idea here is that we're changing our preferred set and
9101 // that shouldn't change the value of lastStyleSheetSet. Also, we're
9102 // using the Internal version so we can update the CSSLoader and not have
9103 // to worry about null strings.
9104 EnableStyleSheetsForSetInternal(aSheetSet
, true);
9108 DOMStringList
* Document::StyleSheetSets() {
9109 if (!mStyleSheetSetList
) {
9110 mStyleSheetSetList
= new DOMStyleSheetSetList(this);
9112 return mStyleSheetSetList
;
9115 void Document::EnableStyleSheetsForSet(const nsAString
& aSheetSet
) {
9116 // Per spec, passing in null is a no-op.
9117 if (!DOMStringIsNull(aSheetSet
)) {
9118 // Note: must make sure to not change the CSSLoader's preferred sheet --
9119 // that value should be equal to either our lastStyleSheetSet (if that's
9120 // non-null) or to our preferredStyleSheetSet. And this method doesn't
9121 // change either of those.
9122 EnableStyleSheetsForSetInternal(aSheetSet
, false);
9126 void Document::EnableStyleSheetsForSetInternal(const nsAString
& aSheetSet
,
9127 bool aUpdateCSSLoader
) {
9128 size_t count
= SheetCount();
9130 for (size_t index
= 0; index
< count
; index
++) {
9131 StyleSheet
* sheet
= SheetAt(index
);
9132 NS_ASSERTION(sheet
, "Null sheet in sheet list!");
9134 sheet
->GetTitle(title
);
9135 if (!title
.IsEmpty()) {
9136 sheet
->SetEnabled(title
.Equals(aSheetSet
));
9139 if (aUpdateCSSLoader
) {
9140 CSSLoader()->DocumentStyleSheetSetChanged();
9142 if (EnsureStyleSet().StyleSheetsHaveChanged()) {
9143 ApplicableStylesChanged();
9147 void Document::GetCharacterSet(nsAString
& aCharacterSet
) const {
9148 nsAutoCString charset
;
9149 GetDocumentCharacterSet()->Name(charset
);
9150 CopyASCIItoUTF16(charset
, aCharacterSet
);
9153 already_AddRefed
<nsINode
> Document::ImportNode(nsINode
& aNode
, bool aDeep
,
9154 ErrorResult
& rv
) const {
9155 nsINode
* imported
= &aNode
;
9157 switch (imported
->NodeType()) {
9158 case DOCUMENT_NODE
: {
9161 case DOCUMENT_FRAGMENT_NODE
:
9162 case ATTRIBUTE_NODE
:
9164 case PROCESSING_INSTRUCTION_NODE
:
9166 case CDATA_SECTION_NODE
:
9168 case DOCUMENT_TYPE_NODE
: {
9169 return imported
->Clone(aDeep
, mNodeInfoManager
, rv
);
9172 NS_WARNING("Don't know how to clone this nodetype for importNode.");
9176 rv
.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR
);
9180 already_AddRefed
<nsRange
> Document::CreateRange(ErrorResult
& rv
) {
9181 return nsRange::Create(this, 0, this, 0, rv
);
9184 already_AddRefed
<NodeIterator
> Document::CreateNodeIterator(
9185 nsINode
& aRoot
, uint32_t aWhatToShow
, NodeFilter
* aFilter
,
9186 ErrorResult
& rv
) const {
9187 RefPtr
<NodeIterator
> iterator
=
9188 new NodeIterator(&aRoot
, aWhatToShow
, aFilter
);
9189 return iterator
.forget();
9192 already_AddRefed
<TreeWalker
> Document::CreateTreeWalker(nsINode
& aRoot
,
9193 uint32_t aWhatToShow
,
9194 NodeFilter
* aFilter
,
9195 ErrorResult
& rv
) const {
9196 RefPtr
<TreeWalker
> walker
= new TreeWalker(&aRoot
, aWhatToShow
, aFilter
);
9197 return walker
.forget();
9200 already_AddRefed
<Location
> Document::GetLocation() const {
9201 nsCOMPtr
<nsPIDOMWindowInner
> w
= do_QueryInterface(mScriptGlobalObject
);
9207 return do_AddRef(w
->Location());
9210 already_AddRefed
<nsIURI
> Document::GetDomainURI() {
9211 nsIPrincipal
* principal
= NodePrincipal();
9213 nsCOMPtr
<nsIURI
> uri
;
9214 principal
->GetDomain(getter_AddRefs(uri
));
9216 return uri
.forget();
9218 auto* basePrin
= BasePrincipal::Cast(principal
);
9219 basePrin
->GetURI(getter_AddRefs(uri
));
9220 return uri
.forget();
9223 void Document::GetDomain(nsAString
& aDomain
) {
9224 nsCOMPtr
<nsIURI
> uri
= GetDomainURI();
9231 nsAutoCString hostName
;
9232 nsresult rv
= nsContentUtils::GetHostOrIPv6WithBrackets(uri
, hostName
);
9233 if (NS_SUCCEEDED(rv
)) {
9234 CopyUTF8toUTF16(hostName
, aDomain
);
9236 // If we can't get the host from the URI (e.g. about:, javascript:,
9237 // etc), just return an empty string.
9242 void Document::SetDomain(const nsAString
& aDomain
, ErrorResult
& rv
) {
9243 if (!GetBrowsingContext()) {
9244 // If our browsing context is null; disallow setting domain
9245 rv
.Throw(NS_ERROR_DOM_SECURITY_ERR
);
9249 if (mSandboxFlags
& SANDBOXED_DOMAIN
) {
9250 // We're sandboxed; disallow setting domain
9251 rv
.Throw(NS_ERROR_DOM_SECURITY_ERR
);
9255 if (!FeaturePolicyUtils::IsFeatureAllowed(this, u
"document-domain"_ns
)) {
9256 rv
.Throw(NS_ERROR_DOM_SECURITY_ERR
);
9260 if (aDomain
.IsEmpty()) {
9261 rv
.Throw(NS_ERROR_DOM_SECURITY_ERR
);
9265 nsCOMPtr
<nsIURI
> uri
= GetDomainURI();
9267 rv
.Throw(NS_ERROR_FAILURE
);
9271 // Check new domain - must be a superdomain of the current host
9272 // For example, a page from foo.bar.com may set domain to bar.com,
9273 // but not to ar.com, baz.com, or fi.foo.bar.com.
9275 nsCOMPtr
<nsIURI
> newURI
= RegistrableDomainSuffixOfInternal(aDomain
, uri
);
9277 // Error: illegal domain
9278 rv
.Throw(NS_ERROR_DOM_SECURITY_ERR
);
9282 if (GetBrowsingContext()->Group()->IsPotentiallyCrossOriginIsolated()) {
9283 WarnOnceAbout(Document::eDocumentSetDomainNotAllowed
);
9287 MOZ_ALWAYS_SUCCEEDS(NodePrincipal()->SetDomain(newURI
));
9288 MOZ_ALWAYS_SUCCEEDS(PartitionedPrincipal()->SetDomain(newURI
));
9289 if (WindowGlobalChild
* wgc
= GetWindowGlobalChild()) {
9290 wgc
->SendSetDocumentDomain(WrapNotNull(newURI
));
9294 already_AddRefed
<nsIURI
> Document::CreateInheritingURIForHost(
9295 const nsACString
& aHostString
) {
9296 if (aHostString
.IsEmpty()) {
9301 nsCOMPtr
<nsIURI
> uri
= GetDomainURI();
9307 rv
= NS_MutateURI(uri
)
9309 .SetPort(-1) // we want to reset the port number if needed.
9310 .SetHostPort(aHostString
)
9312 if (NS_FAILED(rv
)) {
9316 return uri
.forget();
9319 already_AddRefed
<nsIURI
> Document::RegistrableDomainSuffixOfInternal(
9320 const nsAString
& aNewDomain
, nsIURI
* aOrigHost
) {
9321 if (NS_WARN_IF(!aOrigHost
)) {
9325 nsCOMPtr
<nsIURI
> newURI
=
9326 CreateInheritingURIForHost(NS_ConvertUTF16toUTF8(aNewDomain
));
9328 // Error: failed to parse input domain
9332 if (!IsValidDomain(aOrigHost
, newURI
)) {
9333 // Error: illegal domain
9337 nsAutoCString domain
;
9338 if (NS_FAILED(newURI
->GetAsciiHost(domain
))) {
9342 return CreateInheritingURIForHost(domain
);
9346 bool Document::IsValidDomain(nsIURI
* aOrigHost
, nsIURI
* aNewURI
) {
9347 // Check new domain - must be a superdomain of the current host
9348 // For example, a page from foo.bar.com may set domain to bar.com,
9349 // but not to ar.com, baz.com, or fi.foo.bar.com.
9350 nsAutoCString current
;
9351 nsAutoCString domain
;
9352 if (NS_FAILED(aOrigHost
->GetAsciiHost(current
))) {
9355 if (NS_FAILED(aNewURI
->GetAsciiHost(domain
))) {
9359 bool ok
= current
.Equals(domain
);
9360 if (current
.Length() > domain
.Length() && StringEndsWith(current
, domain
) &&
9361 current
.CharAt(current
.Length() - domain
.Length() - 1) == '.') {
9362 // We're golden if the new domain is the current page's base domain or a
9364 nsCOMPtr
<nsIEffectiveTLDService
> tldService
=
9365 mozilla::components::EffectiveTLD::Service();
9370 nsAutoCString currentBaseDomain
;
9372 tldService
->GetBaseDomain(aOrigHost
, 0, currentBaseDomain
));
9373 NS_ASSERTION(StringEndsWith(domain
, currentBaseDomain
) ==
9374 (domain
.Length() >= currentBaseDomain
.Length()),
9375 "uh-oh! slight optimization wasn't valid somehow!");
9376 ok
= ok
&& domain
.Length() >= currentBaseDomain
.Length();
9382 Element
* Document::GetHtmlElement() const {
9383 Element
* rootElement
= GetRootElement();
9384 if (rootElement
&& rootElement
->IsHTMLElement(nsGkAtoms::html
)) {
9390 Element
* Document::GetHtmlChildElement(
9391 nsAtom
* aTag
, const nsIContent
* aContentToIgnore
) const {
9392 Element
* html
= GetHtmlElement();
9397 // Look for the element with aTag inside html. This needs to run
9398 // forwards to find the first such element.
9399 for (nsIContent
* child
= html
->GetFirstChild(); child
;
9400 child
= child
->GetNextSibling()) {
9401 if (child
->IsHTMLElement(aTag
) && MOZ_LIKELY(child
!= aContentToIgnore
)) {
9402 return child
->AsElement();
9408 nsGenericHTMLElement
* Document::GetBody() const {
9409 Element
* html
= GetHtmlElement();
9414 for (nsIContent
* child
= html
->GetFirstChild(); child
;
9415 child
= child
->GetNextSibling()) {
9416 if (child
->IsAnyOfHTMLElements(nsGkAtoms::body
, nsGkAtoms::frameset
)) {
9417 return static_cast<nsGenericHTMLElement
*>(child
);
9424 void Document::SetBody(nsGenericHTMLElement
* newBody
, ErrorResult
& rv
) {
9425 nsCOMPtr
<Element
> root
= GetRootElement();
9427 // The body element must be either a body tag or a frameset tag. And we must
9428 // have a root element to be able to add kids to it.
9430 !newBody
->IsAnyOfHTMLElements(nsGkAtoms::body
, nsGkAtoms::frameset
)) {
9431 rv
.ThrowHierarchyRequestError(
9432 "The new body must be either a body tag or frameset tag.");
9437 rv
.ThrowHierarchyRequestError("No root element.");
9441 // Use DOM methods so that we pass through the appropriate security checks.
9442 nsCOMPtr
<Element
> currentBody
= GetBody();
9444 root
->ReplaceChild(*newBody
, *currentBody
, rv
);
9446 root
->AppendChild(*newBody
, rv
);
9450 HTMLSharedElement
* Document::GetHead() const {
9451 return static_cast<HTMLSharedElement
*>(GetHeadElement());
9454 Element
* Document::GetTitleElement() {
9455 // mMayHaveTitleElement will have been set to true if any HTML or SVG
9456 // <title> element has been bound to this document. So if it's false,
9457 // we know there is nothing to do here. This avoids us having to search
9458 // the whole DOM if someone calls document.title on a large document
9460 if (!mMayHaveTitleElement
) {
9464 Element
* root
= GetRootElement();
9465 if (root
&& root
->IsSVGElement(nsGkAtoms::svg
)) {
9466 // In SVG, the document's title must be a child
9467 for (nsIContent
* child
= root
->GetFirstChild(); child
;
9468 child
= child
->GetNextSibling()) {
9469 if (child
->IsSVGElement(nsGkAtoms::title
)) {
9470 return child
->AsElement();
9476 // We check the HTML namespace even for non-HTML documents, except SVG. This
9477 // matches the spec and the behavior of all tested browsers.
9478 for (nsINode
* node
= GetFirstChild(); node
; node
= node
->GetNextNode(this)) {
9479 if (node
->IsHTMLElement(nsGkAtoms::title
)) {
9480 return node
->AsElement();
9486 void Document::GetTitle(nsAString
& aTitle
) {
9489 Element
* rootElement
= GetRootElement();
9494 if (rootElement
->IsXULElement()) {
9495 rootElement
->GetAttr(nsGkAtoms::title
, aTitle
);
9496 } else if (Element
* title
= GetTitleElement()) {
9497 nsContentUtils::GetNodeTextContent(title
, false, aTitle
);
9502 aTitle
.CompressWhitespace();
9505 void Document::SetTitle(const nsAString
& aTitle
, ErrorResult
& aRv
) {
9506 Element
* rootElement
= GetRootElement();
9511 if (rootElement
->IsXULElement()) {
9513 rootElement
->SetAttr(kNameSpaceID_None
, nsGkAtoms::title
, aTitle
, true);
9517 Maybe
<mozAutoDocUpdate
> updateBatch
;
9518 nsCOMPtr
<Element
> title
= GetTitleElement();
9519 if (rootElement
->IsSVGElement(nsGkAtoms::svg
)) {
9521 // Batch updates so that mutation events don't change "the title
9522 // element" under us
9523 updateBatch
.emplace(this, true);
9524 RefPtr
<mozilla::dom::NodeInfo
> titleInfo
= mNodeInfoManager
->GetNodeInfo(
9525 nsGkAtoms::title
, nullptr, kNameSpaceID_SVG
, ELEMENT_NODE
);
9526 NS_NewSVGElement(getter_AddRefs(title
), titleInfo
.forget(),
9531 rootElement
->InsertChildBefore(title
, rootElement
->GetFirstChild(), true,
9534 } else if (rootElement
->IsHTMLElement()) {
9536 // Batch updates so that mutation events don't change "the title
9537 // element" under us
9538 updateBatch
.emplace(this, true);
9539 Element
* head
= GetHeadElement();
9544 RefPtr
<mozilla::dom::NodeInfo
> titleInfo
;
9545 titleInfo
= mNodeInfoManager
->GetNodeInfo(
9546 nsGkAtoms::title
, nullptr, kNameSpaceID_XHTML
, ELEMENT_NODE
);
9547 title
= NS_NewHTMLTitleElement(titleInfo
.forget());
9552 head
->AppendChildTo(title
, true, IgnoreErrors());
9558 aRv
= nsContentUtils::SetNodeTextContent(title
, aTitle
, false);
9561 class Document::TitleChangeEvent final
: public Runnable
{
9563 explicit TitleChangeEvent(Document
* aDoc
)
9564 : Runnable("Document::TitleChangeEvent"),
9566 mBlockOnload(aDoc
->IsInChromeDocShell()) {
9568 mDoc
->BlockOnload();
9572 NS_IMETHOD
Run() final
{
9576 const RefPtr
<Document
> doc
= mDoc
;
9577 const bool blockOnload
= mBlockOnload
;
9579 doc
->DoNotifyPossibleTitleChange();
9581 doc
->UnblockOnload(/* aFireSync = */ true);
9589 mDoc
->UnblockOnload(/* aFireSync = */ false);
9596 // Weak, caller is responsible for calling Revoke() when needed.
9598 // title changes should block the load event on chrome docshells, so that the
9599 // window title is consistently set by the time the top window is displayed.
9600 // Otherwise, some window manager integrations don't work properly,
9602 const bool mBlockOnload
= false;
9605 void Document::NotifyPossibleTitleChange(bool aBoundTitleElement
) {
9606 NS_ASSERTION(!mInUnlinkOrDeletion
|| !aBoundTitleElement
,
9607 "Setting a title while unlinking or destroying the element?");
9608 if (mInUnlinkOrDeletion
) {
9612 if (aBoundTitleElement
) {
9613 mMayHaveTitleElement
= true;
9616 if (mPendingTitleChangeEvent
.IsPending()) {
9620 MOZ_RELEASE_ASSERT(NS_IsMainThread());
9621 RefPtr
<TitleChangeEvent
> event
= new TitleChangeEvent(this);
9622 if (NS_WARN_IF(NS_FAILED(Dispatch(do_AddRef(event
))))) {
9626 mPendingTitleChangeEvent
= std::move(event
);
9629 void Document::DoNotifyPossibleTitleChange() {
9630 if (!mPendingTitleChangeEvent
.IsPending()) {
9633 // Make sure the pending runnable method is cleared.
9634 mPendingTitleChangeEvent
.Revoke();
9635 mHaveFiredTitleChange
= true;
9640 if (RefPtr
<PresShell
> presShell
= GetPresShell()) {
9641 nsCOMPtr
<nsISupports
> container
=
9642 presShell
->GetPresContext()->GetContainerWeak();
9644 if (nsCOMPtr
<nsIBaseWindow
> docShellWin
= do_QueryInterface(container
)) {
9645 docShellWin
->SetTitle(title
);
9650 if (WindowGlobalChild
* child
= GetWindowGlobalChild()) {
9651 child
->SendUpdateDocumentTitle(title
);
9654 // Fire a DOM event for the title change.
9655 nsContentUtils::DispatchChromeEvent(this, this, u
"DOMTitleChanged"_ns
,
9656 CanBubble::eYes
, Cancelable::eYes
);
9658 nsCOMPtr
<nsIObserverService
> obs
= mozilla::services::GetObserverService();
9660 obs
->NotifyObservers(ToSupports(this), "document-title-changed", nullptr);
9664 already_AddRefed
<MediaQueryList
> Document::MatchMedia(
9665 const nsACString
& aMediaQueryList
, CallerType aCallerType
) {
9666 RefPtr
<MediaQueryList
> result
=
9667 new MediaQueryList(this, aMediaQueryList
, aCallerType
);
9669 mDOMMediaQueryLists
.insertBack(result
);
9671 return result
.forget();
9674 void Document::SetMayStartLayout(bool aMayStartLayout
) {
9675 mMayStartLayout
= aMayStartLayout
;
9676 if (MayStartLayout()) {
9677 // Before starting layout, check whether we're a toplevel chrome
9678 // window. If we are, setup some state so that we don't have to restyle
9679 // the whole tree after StartLayout.
9680 if (nsCOMPtr
<nsIAppWindow
> win
= GetAppWindowIfToplevelChrome()) {
9681 // We're the chrome document!
9682 win
->BeforeStartLayout();
9684 ReadyState state
= GetReadyStateEnum();
9685 if (state
>= READYSTATE_INTERACTIVE
) {
9686 // DOMContentLoaded has fired already.
9687 MaybeResolveReadyForIdle();
9691 MaybeEditingStateChanged();
9694 nsresult
Document::InitializeFrameLoader(nsFrameLoader
* aLoader
) {
9695 mInitializableFrameLoaders
.RemoveElement(aLoader
);
9696 // Don't even try to initialize.
9697 if (mInDestructor
) {
9699 "Trying to initialize a frame loader while"
9700 "document is being deleted");
9701 return NS_ERROR_FAILURE
;
9704 mInitializableFrameLoaders
.AppendElement(aLoader
);
9705 if (!mFrameLoaderRunner
) {
9706 mFrameLoaderRunner
=
9707 NewRunnableMethod("Document::MaybeInitializeFinalizeFrameLoaders", this,
9708 &Document::MaybeInitializeFinalizeFrameLoaders
);
9709 NS_ENSURE_TRUE(mFrameLoaderRunner
, NS_ERROR_OUT_OF_MEMORY
);
9710 nsContentUtils::AddScriptRunner(mFrameLoaderRunner
);
9715 nsresult
Document::FinalizeFrameLoader(nsFrameLoader
* aLoader
,
9716 nsIRunnable
* aFinalizer
) {
9717 mInitializableFrameLoaders
.RemoveElement(aLoader
);
9718 if (mInDestructor
) {
9719 return NS_ERROR_FAILURE
;
9722 LogRunnable::LogDispatch(aFinalizer
);
9723 mFrameLoaderFinalizers
.AppendElement(aFinalizer
);
9724 if (!mFrameLoaderRunner
) {
9725 mFrameLoaderRunner
=
9726 NewRunnableMethod("Document::MaybeInitializeFinalizeFrameLoaders", this,
9727 &Document::MaybeInitializeFinalizeFrameLoaders
);
9728 NS_ENSURE_TRUE(mFrameLoaderRunner
, NS_ERROR_OUT_OF_MEMORY
);
9729 nsContentUtils::AddScriptRunner(mFrameLoaderRunner
);
9734 void Document::MaybeInitializeFinalizeFrameLoaders() {
9735 if (mDelayFrameLoaderInitialization
) {
9736 // This method will be recalled when !mDelayFrameLoaderInitialization.
9737 mFrameLoaderRunner
= nullptr;
9741 // We're not in an update, but it is not safe to run scripts, so
9742 // postpone frameloader initialization and finalization.
9743 if (!nsContentUtils::IsSafeToRunScript()) {
9744 if (!mInDestructor
&& !mFrameLoaderRunner
&&
9745 (mInitializableFrameLoaders
.Length() ||
9746 mFrameLoaderFinalizers
.Length())) {
9747 mFrameLoaderRunner
= NewRunnableMethod(
9748 "Document::MaybeInitializeFinalizeFrameLoaders", this,
9749 &Document::MaybeInitializeFinalizeFrameLoaders
);
9750 nsContentUtils::AddScriptRunner(mFrameLoaderRunner
);
9754 mFrameLoaderRunner
= nullptr;
9756 // Don't use a temporary array for mInitializableFrameLoaders, because
9757 // loading a frame may cause some other frameloader to be removed from the
9758 // array. But be careful to keep the loader alive when starting the load!
9759 while (mInitializableFrameLoaders
.Length()) {
9760 RefPtr
<nsFrameLoader
> loader
= mInitializableFrameLoaders
[0];
9761 mInitializableFrameLoaders
.RemoveElementAt(0);
9762 NS_ASSERTION(loader
, "null frameloader in the array?");
9763 loader
->ReallyStartLoading();
9766 uint32_t length
= mFrameLoaderFinalizers
.Length();
9768 nsTArray
<nsCOMPtr
<nsIRunnable
>> finalizers
=
9769 std::move(mFrameLoaderFinalizers
);
9770 for (uint32_t i
= 0; i
< length
; ++i
) {
9771 LogRunnable::Run
run(finalizers
[i
]);
9772 finalizers
[i
]->Run();
9777 void Document::TryCancelFrameLoaderInitialization(nsIDocShell
* aShell
) {
9778 uint32_t length
= mInitializableFrameLoaders
.Length();
9779 for (uint32_t i
= 0; i
< length
; ++i
) {
9780 if (mInitializableFrameLoaders
[i
]->GetExistingDocShell() == aShell
) {
9781 mInitializableFrameLoaders
.RemoveElementAt(i
);
9787 void Document::SetPrototypeDocument(nsXULPrototypeDocument
* aPrototype
) {
9788 mPrototypeDocument
= aPrototype
;
9789 mSynchronousDOMContentLoaded
= true;
9792 nsIPermissionDelegateHandler
* Document::PermDelegateHandler() {
9793 return GetPermissionDelegateHandler();
9796 Document
* Document::RequestExternalResource(
9797 nsIURI
* aURI
, nsIReferrerInfo
* aReferrerInfo
, nsINode
* aRequestingNode
,
9798 ExternalResourceLoad
** aPendingLoad
) {
9799 MOZ_ASSERT(aURI
, "Must have a URI");
9800 MOZ_ASSERT(aRequestingNode
, "Must have a node");
9801 MOZ_ASSERT(aReferrerInfo
, "Must have a referrerInfo");
9802 if (mDisplayDocument
) {
9803 return mDisplayDocument
->RequestExternalResource(
9804 aURI
, aReferrerInfo
, aRequestingNode
, aPendingLoad
);
9807 return mExternalResourceMap
.RequestResource(
9808 aURI
, aReferrerInfo
, aRequestingNode
, this, aPendingLoad
);
9811 void Document::EnumerateExternalResources(SubDocEnumFunc aCallback
) {
9812 mExternalResourceMap
.EnumerateResources(aCallback
);
9815 SMILAnimationController
* Document::GetAnimationController() {
9816 // We create the animation controller lazily because most documents won't want
9817 // one and only SVG documents and the like will call this
9818 if (mAnimationController
) return mAnimationController
;
9819 // Refuse to create an Animation Controller for data documents.
9820 if (mLoadedAsData
) return nullptr;
9822 mAnimationController
= new SMILAnimationController(this);
9824 // If there's a presContext then check the animation mode and pause if
9826 nsPresContext
* context
= GetPresContext();
9827 if (mAnimationController
&& context
&&
9828 context
->ImageAnimationMode() == imgIContainer::kDontAnimMode
) {
9829 mAnimationController
->Pause(SMILTimeContainer::PAUSE_USERPREF
);
9832 // If we're hidden (or being hidden), notify the newly-created animation
9833 // controller. (Skip this check for SVG-as-an-image documents, though,
9834 // because they don't get OnPageShow / OnPageHide calls).
9835 if (!mIsShowing
&& !mIsBeingUsedAsImage
) {
9836 mAnimationController
->OnPageHide();
9839 return mAnimationController
;
9842 ScrollTimelineAnimationTracker
*
9843 Document::GetOrCreateScrollTimelineAnimationTracker() {
9844 if (!mScrollTimelineAnimationTracker
) {
9845 mScrollTimelineAnimationTracker
= new ScrollTimelineAnimationTracker(this);
9848 return mScrollTimelineAnimationTracker
;
9852 * Retrieve the "direction" property of the document.
9856 void Document::GetDir(nsAString
& aDirection
) const {
9857 aDirection
.Truncate();
9858 Element
* rootElement
= GetHtmlElement();
9860 static_cast<nsGenericHTMLElement
*>(rootElement
)->GetDir(aDirection
);
9865 * Set the "direction" property of the document.
9869 void Document::SetDir(const nsAString
& aDirection
) {
9870 Element
* rootElement
= GetHtmlElement();
9872 rootElement
->SetAttr(kNameSpaceID_None
, nsGkAtoms::dir
, aDirection
, true);
9876 nsIHTMLCollection
* Document::Images() {
9878 mImages
= new nsContentList(this, kNameSpaceID_XHTML
, nsGkAtoms::img
,
9884 nsIHTMLCollection
* Document::Embeds() {
9886 mEmbeds
= new nsContentList(this, kNameSpaceID_XHTML
, nsGkAtoms::embed
,
9892 static bool MatchLinks(Element
* aElement
, int32_t aNamespaceID
, nsAtom
* aAtom
,
9894 return aElement
->IsAnyOfHTMLElements(nsGkAtoms::a
, nsGkAtoms::area
) &&
9895 aElement
->HasAttr(nsGkAtoms::href
);
9898 nsIHTMLCollection
* Document::Links() {
9900 mLinks
= new nsContentList(this, MatchLinks
, nullptr, nullptr);
9905 nsIHTMLCollection
* Document::Forms() {
9907 // Please keep this in sync with nsHTMLDocument::GetFormsAndFormControls.
9908 mForms
= new nsContentList(this, kNameSpaceID_XHTML
, nsGkAtoms::form
,
9915 nsIHTMLCollection
* Document::Scripts() {
9917 mScripts
= new nsContentList(this, kNameSpaceID_XHTML
, nsGkAtoms::script
,
9923 nsIHTMLCollection
* Document::Applets() {
9925 mApplets
= new nsEmptyContentList(this);
9930 static bool MatchAnchors(Element
* aElement
, int32_t aNamespaceID
, nsAtom
* aAtom
,
9932 return aElement
->IsHTMLElement(nsGkAtoms::a
) &&
9933 aElement
->HasAttr(nsGkAtoms::name
);
9936 nsIHTMLCollection
* Document::Anchors() {
9938 mAnchors
= new nsContentList(this, MatchAnchors
, nullptr, nullptr);
9943 mozilla::dom::Nullable
<mozilla::dom::WindowProxyHolder
> Document::Open(
9944 const nsAString
& aURL
, const nsAString
& aName
, const nsAString
& aFeatures
,
9946 MOZ_ASSERT(nsContentUtils::CanCallerAccess(this),
9947 "XOW should have caught this!");
9949 nsCOMPtr
<nsPIDOMWindowInner
> window
= GetInnerWindow();
9951 rv
.Throw(NS_ERROR_DOM_INVALID_ACCESS_ERR
);
9954 nsCOMPtr
<nsPIDOMWindowOuter
> outer
=
9955 nsPIDOMWindowOuter::GetFromCurrentInner(window
);
9957 rv
.Throw(NS_ERROR_NOT_INITIALIZED
);
9960 RefPtr
<nsGlobalWindowOuter
> win
= nsGlobalWindowOuter::Cast(outer
);
9961 RefPtr
<BrowsingContext
> newBC
;
9962 rv
= win
->OpenJS(NS_ConvertUTF16toUTF8(aURL
), aName
, aFeatures
,
9963 getter_AddRefs(newBC
));
9967 return WindowProxyHolder(std::move(newBC
));
9970 Document
* Document::Open(const Optional
<nsAString
>& /* unused */,
9971 const Optional
<nsAString
>& /* unused */,
9972 ErrorResult
& aError
) {
9974 // <https://html.spec.whatwg.org/multipage/dynamic-markup-insertion.html#document-open-steps>
9976 MOZ_ASSERT(nsContentUtils::CanCallerAccess(this),
9977 "XOW should have caught this!");
9979 // Step 1 -- throw if we're an XML document.
9980 if (!IsHTMLDocument() || mDisableDocWrite
) {
9981 aError
.Throw(NS_ERROR_DOM_INVALID_STATE_ERR
);
9985 // Step 2 -- throw if dynamic markup insertion should throw.
9986 if (ShouldThrowOnDynamicMarkupInsertion()) {
9987 aError
.Throw(NS_ERROR_DOM_INVALID_STATE_ERR
);
9991 // Step 3 -- get the entry document, so we can use it for security checks.
9992 nsCOMPtr
<Document
> callerDoc
= GetEntryDocument();
9994 if (nsIGlobalObject
* callerGlobal
= GetEntryGlobal()) {
9995 if (callerGlobal
->IsXPCSandbox()) {
9996 if (nsIPrincipal
* principal
= callerGlobal
->PrincipalOrNull()) {
9997 if (principal
->Equals(NodePrincipal())) {
9998 // In case we're being called from some JS sandbox scope,
9999 // pretend that the caller is the document itself.
10007 // If we're called from C++ or in some other way without an originating
10008 // document we can't do a document.open w/o changing the principal of the
10009 // document to something like about:blank (as that's the only sane thing
10010 // to do when we don't know the origin of this call), and since we can't
10011 // change the principals of a document for security reasons we'll have to
10012 // refuse to go ahead with this call.
10014 aError
.Throw(NS_ERROR_DOM_SECURITY_ERR
);
10019 // Step 4 -- make sure we're same-origin (not just same origin-domain) with
10020 // the entry document.
10021 if (!callerDoc
->NodePrincipal()->Equals(NodePrincipal())) {
10022 aError
.Throw(NS_ERROR_DOM_SECURITY_ERR
);
10026 // Step 5 -- if we have an active parser with a nonzero script nesting level,
10028 if ((mParser
&& mParser
->HasNonzeroScriptNestingLevel()) || mParserAborted
) {
10032 // Step 6 -- check for open() during unload. Per spec, this is just a check
10033 // of the ignore-opens-during-unload counter, but our unload event code
10034 // doesn't affect that counter yet (unlike pagehide and beforeunload, which
10035 // do), so we check for unload directly.
10036 if (ShouldIgnoreOpens()) {
10040 RefPtr
<nsDocShell
> shell(mDocumentContainer
);
10043 shell
->GetIsInUnload(&inUnload
);
10049 // At this point we know this is a valid-enough document.open() call
10050 // and not a no-op. Increment our use counter.
10051 SetUseCounter(eUseCounter_custom_DocumentOpen
);
10053 // XXX The spec has changed. There is a new step 7 and step 8 has changed
10056 // Step 8 -- stop existing navigation of our browsing context (and all other
10057 // loads it's doing) if we're the active document of our browsing context.
10058 // Note that we do not want to stop anything if there is no existing
10060 if (shell
&& IsCurrentActiveDocument() &&
10061 shell
->GetIsAttemptingToNavigate()) {
10062 shell
->Stop(nsIWebNavigation::STOP_NETWORK
);
10064 // The Stop call may have cancelled the onload blocker request or
10065 // prevented it from getting added, so we need to make sure it gets added
10066 // to the document again otherwise the document could have a non-zero
10067 // onload block count without the onload blocker request being in the
10069 EnsureOnloadBlocker();
10072 // Step 9 -- clear event listeners out of our DOM tree
10073 for (nsINode
* node
: ShadowIncludingTreeIterator(*this)) {
10074 if (EventListenerManager
* elm
= node
->GetExistingListenerManager()) {
10075 elm
->RemoveAllListeners();
10079 // Step 10 -- clear event listeners from our window, if we have one.
10081 // Note that we explicitly want the inner window, and only if we're its
10082 // document. We want to do this (per spec) even when we're not the "active
10083 // document", so we can't go through GetWindow(), because it might forward to
10084 // the wrong inner.
10085 if (nsPIDOMWindowInner
* win
= GetInnerWindow()) {
10086 if (win
->GetExtantDoc() == this) {
10087 if (EventListenerManager
* elm
=
10088 nsGlobalWindowInner::Cast(win
)->GetExistingListenerManager()) {
10089 elm
->RemoveAllListeners();
10094 // If we have a parser that has a zero script nesting level, we need to
10095 // properly terminate it. We do that after we've removed all the event
10096 // listeners (so termination won't trigger event listeners if it does
10097 // something to the DOM), but before we remove all elements from the document
10098 // (so if termination does modify the DOM in some way we will just blow it
10099 // away immediately. See the similar code in WriteCommon that handles the
10100 // !IsInsertionPointDefined() case and should stay in sync with this code.
10102 MOZ_ASSERT(!mParser
->HasNonzeroScriptNestingLevel(),
10103 "Why didn't we take the early return?");
10104 // Make sure we don't re-enter.
10105 IgnoreOpensDuringUnload
ignoreOpenGuard(this);
10106 mParser
->Terminate();
10107 MOZ_RELEASE_ASSERT(!mParser
, "mParser should have been null'd out");
10110 // Steps 11, 12, 13, 14 --
10111 // remove all our DOM kids without firing any mutation events.
10113 bool oldFlag
= FireMutationEvents();
10114 SetFireMutationEvents(false);
10116 // We want to ignore any recursive calls to Open() that happen while
10117 // disconnecting the node tree. The spec doesn't say to do this, but the
10118 // spec also doesn't envision unload events on subframes firing while we do
10119 // this, while all browsers fire them in practice. See
10120 // <https://github.com/whatwg/html/issues/4611>.
10121 IgnoreOpensDuringUnload
ignoreOpenGuard(this);
10122 DisconnectNodeTree();
10123 SetFireMutationEvents(oldFlag
);
10126 // Step 15 -- if we're the current document in our docshell, do the
10127 // equivalent of pushState() with the new URL we should have.
10128 if (shell
&& IsCurrentActiveDocument()) {
10129 nsCOMPtr
<nsIURI
> newURI
= callerDoc
->GetDocumentURI();
10130 if (callerDoc
!= this) {
10131 nsCOMPtr
<nsIURI
> noFragmentURI
;
10132 nsresult rv
= NS_GetURIWithoutRef(newURI
, getter_AddRefs(noFragmentURI
));
10133 if (NS_WARN_IF(NS_FAILED(rv
))) {
10137 newURI
= std::move(noFragmentURI
);
10140 // UpdateURLAndHistory might do various member-setting, so make sure we're
10141 // holding strong refs to all the refcounted args on the stack. We can
10142 // assume that our caller is holding on to "this" already.
10143 nsCOMPtr
<nsIURI
> currentURI
= GetDocumentURI();
10145 nsresult rv
= currentURI
->Equals(newURI
, &equalURIs
);
10146 if (NS_WARN_IF(NS_FAILED(rv
))) {
10150 nsCOMPtr
<nsIStructuredCloneContainer
> stateContainer(mStateObjectContainer
);
10151 rv
= shell
->UpdateURLAndHistory(this, newURI
, stateContainer
, u
""_ns
,
10152 /* aReplace = */ true, currentURI
,
10154 if (NS_WARN_IF(NS_FAILED(rv
))) {
10159 // And use the security info of the caller document as well, since
10160 // it's the thing providing our data.
10161 mSecurityInfo
= callerDoc
->GetSecurityInfo();
10164 // See <https://github.com/whatwg/html/issues/4299>. Since our
10165 // URL may be changing away from about:blank here, we really want to unset
10166 // this flag no matter what, since only about:blank can be an initial
10168 SetIsInitialDocument(false);
10170 // And let our docloader know that it will need to track our load event.
10171 nsDocShell::Cast(shell
)->SetDocumentOpenedButNotLoaded();
10174 // Per spec nothing happens with our URI in other cases, though note
10175 // <https://github.com/whatwg/html/issues/4286>.
10177 // Note that we don't need to do anything here with base URIs per spec.
10178 // That said, this might be assuming that we implement
10179 // https://html.spec.whatwg.org/multipage/urls-and-fetching.html#fallback-base-url
10180 // correctly, which we don't right now for the about:blank case.
10182 // Step 17, but note <https://github.com/whatwg/html/issues/4292>.
10183 mSkipLoadEventAfterClose
= mLoadEventFiring
;
10185 // Preliminary to steps 18-21. Set our ready state to uninitialized before
10186 // we do anything else, so we can then proceed to later ready state levels.
10187 SetReadyStateInternal(READYSTATE_UNINITIALIZED
,
10188 /* updateTimingInformation = */ false);
10189 // Reset a flag that affects readyState behavior.
10190 mSetCompleteAfterDOMContentLoaded
= false;
10192 // Step 18 -- set our compat mode to standards.
10193 SetCompatibilityMode(eCompatibility_FullStandards
);
10195 // Step 19 -- create a new parser associated with document. This also does
10196 // step 20 implicitly.
10197 mParserAborted
= false;
10198 RefPtr
<nsHtml5Parser
> parser
= nsHtml5Module::NewHtml5Parser();
10200 parser
->Initialize(this, GetDocumentURI(), ToSupports(shell
), nullptr);
10201 nsresult rv
= parser
->StartExecutor();
10202 if (NS_WARN_IF(NS_FAILED(rv
))) {
10207 // Clear out our form control state, because the state of controls
10208 // in the pre-open() document should not affect the state of
10209 // controls that are now going to be written.
10210 mLayoutHistoryState
= nullptr;
10213 // Prepare the docshell and the document viewer for the impending
10214 // out-of-band document.write()
10215 shell
->PrepareForNewContentModel();
10217 nsCOMPtr
<nsIDocumentViewer
> viewer
;
10218 shell
->GetDocViewer(getter_AddRefs(viewer
));
10220 viewer
->LoadStart(this);
10225 SetReadyStateInternal(Document::READYSTATE_LOADING
,
10226 /* updateTimingInformation = */ false);
10232 void Document::Close(ErrorResult
& rv
) {
10233 if (!IsHTMLDocument()) {
10234 // No calling document.close() on XHTML!
10236 rv
.Throw(NS_ERROR_DOM_INVALID_STATE_ERR
);
10240 if (ShouldThrowOnDynamicMarkupInsertion()) {
10241 rv
.Throw(NS_ERROR_DOM_INVALID_STATE_ERR
);
10245 if (!mParser
|| !mParser
->IsScriptCreated()) {
10250 rv
= (static_cast<nsHtml5Parser
*>(mParser
.get()))
10251 ->Parse(u
""_ns
, nullptr, true);
10255 void Document::WriteCommon(const Sequence
<OwningTrustedHTMLOrString
>& aText
,
10256 bool aNewlineTerminate
, mozilla::ErrorResult
& rv
) {
10257 bool isTrusted
= true;
10259 [&isTrusted
](const OwningTrustedHTMLOrString
& aTrustedHTMLOrString
) {
10260 if (aTrustedHTMLOrString
.IsString()) {
10262 return &aTrustedHTMLOrString
.GetAsString();
10264 return &aTrustedHTMLOrString
.GetAsTrustedHTML()->mData
;
10267 // Fast path the common case
10268 if (aText
.Length() == 1) {
10269 WriteCommon(*getAsString(aText
[0]), aNewlineTerminate
,
10270 aText
[0].IsTrustedHTML(), rv
);
10272 // XXXbz it would be nice if we could pass all the strings to the parser
10273 // without having to do all this copying and then ask it to start
10276 for (size_t i
= 0; i
< aText
.Length(); ++i
) {
10277 text
.Append(*getAsString(aText
[i
]));
10279 WriteCommon(text
, aNewlineTerminate
, isTrusted
, rv
);
10283 void Document::WriteCommon(const nsAString
& aText
, bool aNewlineTerminate
,
10284 bool aIsTrusted
, ErrorResult
& aRv
) {
10287 // Assert that we do not use or accidentally introduce doc.write()
10288 // in system privileged context or in any of our about: pages.
10289 nsCOMPtr
<nsIPrincipal
> principal
= NodePrincipal();
10290 bool isAboutOrPrivContext
= principal
->IsSystemPrincipal();
10291 if (!isAboutOrPrivContext
) {
10292 if (principal
->SchemeIs("about")) {
10293 // about:blank inherits the security contetext and this assertion
10294 // is only meant for actual about: pages.
10295 nsAutoCString host
;
10296 principal
->GetHost(host
);
10297 isAboutOrPrivContext
= !host
.EqualsLiteral("blank");
10300 // Some automated tests use an empty string to kick off some parsing
10301 // mechansims, but they do not do any harm since they use an empty string.
10302 MOZ_ASSERT(!isAboutOrPrivContext
|| aText
.IsEmpty(),
10303 "do not use doc.write in privileged context!");
10307 mTooDeepWriteRecursion
=
10308 (mWriteLevel
> NS_MAX_DOCUMENT_WRITE_DEPTH
|| mTooDeepWriteRecursion
);
10309 if (NS_WARN_IF(mTooDeepWriteRecursion
)) {
10310 aRv
.Throw(NS_ERROR_UNEXPECTED
);
10314 if (!IsHTMLDocument() || mDisableDocWrite
) {
10315 // No calling document.write*() on XHTML!
10317 aRv
.Throw(NS_ERROR_DOM_INVALID_STATE_ERR
);
10321 if (ShouldThrowOnDynamicMarkupInsertion()) {
10322 aRv
.Throw(NS_ERROR_DOM_INVALID_STATE_ERR
);
10326 if (mParserAborted
) {
10327 // Hixie says aborting the parser doesn't undefine the insertion point.
10328 // However, since we null out mParser in that case, we track the
10329 // theoretically defined insertion point using mParserAborted.
10333 // Implement Step 4.1 of:
10334 // https://html.spec.whatwg.org/multipage/dynamic-markup-insertion.html#document-write-steps
10335 if (ShouldIgnoreOpens()) {
10339 void* key
= GenerateParserKey();
10340 if (mParser
&& !mParser
->IsInsertionPointDefined()) {
10341 if (mIgnoreDestructiveWritesCounter
) {
10342 // Instead of implying a call to document.open(), ignore the call.
10343 nsContentUtils::ReportToConsole(
10344 nsIScriptError::warningFlag
, "DOM Events"_ns
, this,
10345 nsContentUtils::eDOM_PROPERTIES
, "DocumentWriteIgnored");
10348 // The spec doesn't tell us to ignore opens from here, but we need to
10349 // ensure opens are ignored here. See similar code in Open() that handles
10350 // the case of an existing parser which is not currently running script and
10351 // should stay in sync with this code.
10352 IgnoreOpensDuringUnload
ignoreOpenGuard(this);
10353 mParser
->Terminate();
10354 MOZ_RELEASE_ASSERT(!mParser
, "mParser should have been null'd out");
10358 if (mIgnoreDestructiveWritesCounter
) {
10359 // Instead of implying a call to document.open(), ignore the call.
10360 nsContentUtils::ReportToConsole(
10361 nsIScriptError::warningFlag
, "DOM Events"_ns
, this,
10362 nsContentUtils::eDOM_PROPERTIES
, "DocumentWriteIgnored");
10368 // If Open() fails, or if it didn't create a parser (as it won't
10369 // if the user chose to not discard the current document through
10370 // onbeforeunload), don't write anything.
10371 if (aRv
.Failed() || !mParser
) {
10376 static constexpr auto new_line
= u
"\n"_ns
;
10381 [this, &aNewlineTerminate
, &aRv
, &key
](const nsAString
& aString
)
10382 MOZ_CAN_RUN_SCRIPT_FOR_DEFINITION
-> void {
10383 // This could be done with less code, but for performance reasons it
10384 // makes sense to have the code for two separate Parse() calls here
10385 // since the concatenation of strings costs more than we like. And
10386 // why pay that price when we don't need to?
10387 if (aNewlineTerminate
) {
10388 aRv
= (static_cast<nsHtml5Parser
*>(mParser
.get()))
10389 ->Parse(aString
+ new_line
, key
, false);
10391 aRv
= (static_cast<nsHtml5Parser
*>(mParser
.get()))
10392 ->Parse(aString
, key
, false);
10397 parseString(aText
);
10399 constexpr nsLiteralString sinkWrite
= u
"Document write"_ns
;
10400 constexpr nsLiteralString sinkWriteLn
= u
"Document writeln"_ns
;
10401 Maybe
<nsAutoString
> compliantStringHolder
;
10402 const nsAString
* compliantString
=
10403 TrustedTypeUtils::GetTrustedTypesCompliantStringForTrustedHTML(
10404 aText
, aNewlineTerminate
? sinkWriteLn
: sinkWrite
,
10405 kTrustedTypesOnlySinkGroup
, *this, compliantStringHolder
, aRv
);
10406 if (!aRv
.Failed()) {
10407 parseString(*compliantString
);
10413 mTooDeepWriteRecursion
= (mWriteLevel
!= 0 && mTooDeepWriteRecursion
);
10416 void Document::Write(const Sequence
<OwningTrustedHTMLOrString
>& aText
,
10418 WriteCommon(aText
, false, rv
);
10421 void Document::Writeln(const Sequence
<OwningTrustedHTMLOrString
>& aText
,
10423 WriteCommon(aText
, true, rv
);
10426 void* Document::GenerateParserKey(void) {
10427 if (!mScriptLoader
) {
10428 // If we don't have a script loader, then the parser probably isn't parsing
10429 // anything anyway, so just return null.
10433 // The script loader provides us with the currently executing script element,
10434 // which is guaranteed to be unique per script.
10435 nsIScriptElement
* script
= mScriptLoader
->GetCurrentParserInsertedScript();
10436 if (script
&& mParser
&& mParser
->IsScriptCreated()) {
10437 nsCOMPtr
<nsIParser
> creatorParser
= script
->GetCreatorParser();
10438 if (creatorParser
!= mParser
) {
10439 // Make scripts that aren't inserted by the active parser of this document
10440 // participate in the context of the script that document.open()ed
10449 bool Document::MatchNameAttribute(Element
* aElement
, int32_t aNamespaceID
,
10450 nsAtom
* aAtom
, void* aData
) {
10451 MOZ_ASSERT(aElement
, "Must have element to work with!");
10453 if (!aElement
->HasName()) {
10457 nsString
* elementName
= static_cast<nsString
*>(aData
);
10458 return aElement
->GetNameSpaceID() == kNameSpaceID_XHTML
&&
10459 aElement
->AttrValueIs(kNameSpaceID_None
, nsGkAtoms::name
, *elementName
,
10464 void* Document::UseExistingNameString(nsINode
* aRootNode
,
10465 const nsString
* aName
) {
10466 return const_cast<nsString
*>(aName
);
10469 nsresult
Document::GetDocumentURI(nsString
& aDocumentURI
) const {
10470 if (mDocumentURI
) {
10472 nsresult rv
= mDocumentURI
->GetSpec(uri
);
10473 NS_ENSURE_SUCCESS(rv
, rv
);
10475 CopyUTF8toUTF16(uri
, aDocumentURI
);
10477 aDocumentURI
.Truncate();
10484 nsresult
Document::GetURL(nsString
& aURL
) const { return GetDocumentURI(aURL
); }
10486 void Document::GetDocumentURIFromJS(nsString
& aDocumentURI
,
10487 CallerType aCallerType
,
10488 ErrorResult
& aRv
) const {
10489 if (!mChromeXHRDocURI
|| aCallerType
!= CallerType::System
) {
10490 aRv
= GetDocumentURI(aDocumentURI
);
10495 nsresult res
= mChromeXHRDocURI
->GetSpec(uri
);
10496 if (NS_FAILED(res
)) {
10500 CopyUTF8toUTF16(uri
, aDocumentURI
);
10503 nsIURI
* Document::GetDocumentURIObject() const {
10504 if (!mChromeXHRDocURI
) {
10505 return GetDocumentURI();
10508 return mChromeXHRDocURI
;
10511 void Document::GetCompatMode(nsString
& aCompatMode
) const {
10512 NS_ASSERTION(mCompatMode
== eCompatibility_NavQuirks
||
10513 mCompatMode
== eCompatibility_AlmostStandards
||
10514 mCompatMode
== eCompatibility_FullStandards
,
10515 "mCompatMode is neither quirks nor strict for this document");
10517 if (mCompatMode
== eCompatibility_NavQuirks
) {
10518 aCompatMode
.AssignLiteral("BackCompat");
10520 aCompatMode
.AssignLiteral("CSS1Compat");
10525 } // namespace mozilla
10527 void nsDOMAttributeMap::BlastSubtreeToPieces(nsINode
* aNode
) {
10528 if (Element
* element
= Element::FromNode(aNode
)) {
10529 if (const nsDOMAttributeMap
* map
= element
->GetAttributeMap()) {
10533 // Use an iterator to get an arbitrary attribute from the
10534 // cache. The iterator must be destroyed before any other
10535 // operations on mAttributeCache, to avoid hash table
10537 auto iter
= map
->mAttributeCache
.ConstIter();
10541 attr
= iter
.UserData();
10544 BlastSubtreeToPieces(attr
);
10546 mozilla::DebugOnly
<nsresult
> rv
=
10547 element
->UnsetAttr(attr
->NodeInfo()->NamespaceID(),
10548 attr
->NodeInfo()->NameAtom(), false);
10550 // XXX Should we abort here?
10551 NS_ASSERTION(NS_SUCCEEDED(rv
), "Uh-oh, UnsetAttr shouldn't fail!");
10555 if (mozilla::dom::ShadowRoot
* shadow
= element
->GetShadowRoot()) {
10556 BlastSubtreeToPieces(shadow
);
10557 element
->UnattachShadow();
10561 while (aNode
->HasChildren()) {
10562 nsIContent
* node
= aNode
->GetFirstChild();
10563 BlastSubtreeToPieces(node
);
10564 aNode
->RemoveChildNode(node
, false);
10568 namespace mozilla::dom
{
10570 nsINode
* Document::AdoptNode(nsINode
& aAdoptedNode
, ErrorResult
& rv
,
10571 bool aAcceptShadowRoot
) {
10572 OwningNonNull
<nsINode
> adoptedNode
= aAdoptedNode
;
10573 if (adoptedNode
->IsShadowRoot() && !aAcceptShadowRoot
) {
10574 rv
.ThrowHierarchyRequestError("The adopted node is a shadow root.");
10578 // Scope firing mutation events so that we don't carry any state that
10581 if (nsCOMPtr
<nsINode
> parent
= adoptedNode
->GetParentNode()) {
10582 nsContentUtils::MaybeFireNodeRemoved(adoptedNode
, parent
);
10586 nsAutoScriptBlocker scriptBlocker
;
10588 switch (adoptedNode
->NodeType()) {
10589 case ATTRIBUTE_NODE
: {
10590 // Remove from ownerElement.
10591 OwningNonNull
<Attr
> adoptedAttr
= static_cast<Attr
&>(*adoptedNode
);
10593 nsCOMPtr
<Element
> ownerElement
= adoptedAttr
->GetOwnerElement();
10598 if (ownerElement
) {
10599 OwningNonNull
<Attr
> newAttr
=
10600 ownerElement
->RemoveAttributeNode(*adoptedAttr
, rv
);
10608 case DOCUMENT_FRAGMENT_NODE
:
10610 case PROCESSING_INSTRUCTION_NODE
:
10612 case CDATA_SECTION_NODE
:
10614 case DOCUMENT_TYPE_NODE
: {
10615 // Don't allow adopting a node's anonymous subtree out from under it.
10616 if (adoptedNode
->IsRootOfNativeAnonymousSubtree()) {
10617 rv
.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR
);
10621 // We don't want to adopt an element into its own contentDocument or into
10622 // a descendant contentDocument, so we check if the frameElement of this
10623 // document or any of its parents is the adopted node or one of its
10625 RefPtr
<BrowsingContext
> bc
= GetBrowsingContext();
10627 nsCOMPtr
<nsINode
> node
= bc
->GetEmbedderElement();
10628 if (node
&& node
->IsInclusiveDescendantOf(adoptedNode
)) {
10629 rv
.ThrowHierarchyRequestError(
10630 "Trying to adopt a node into its own contentDocument or a "
10631 "descendant contentDocument.");
10635 if (XRE_IsParentProcess()) {
10636 bc
= bc
->Canonical()->GetParentCrossChromeBoundary();
10638 bc
= bc
->GetParent();
10642 // Remove from parent.
10643 nsCOMPtr
<nsINode
> parent
= adoptedNode
->GetParentNode();
10645 parent
->RemoveChildNode(adoptedNode
->AsContent(), true);
10647 MOZ_ASSERT(!adoptedNode
->IsInUncomposedDoc());
10652 case DOCUMENT_NODE
: {
10653 rv
.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR
);
10657 NS_WARNING("Don't know how to adopt this nodetype for adoptNode.");
10659 rv
.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR
);
10664 nsCOMPtr
<Document
> oldDocument
= adoptedNode
->OwnerDoc();
10665 bool sameDocument
= oldDocument
== this;
10668 JS::Rooted
<JSObject
*> newScope(cx
, nullptr);
10669 if (!sameDocument
) {
10670 newScope
= GetWrapper();
10671 if (!newScope
&& GetScopeObject() && GetScopeObject()->HasJSGlobal()) {
10672 // Make sure cx is in a semi-sane compartment before we call WrapNative.
10673 // It's kind of irrelevant, given that we're passing aAllowWrapping =
10674 // false, and documents should always insist on being wrapped in an
10675 // canonical scope. But we try to pass something sane anyway.
10676 JSObject
* globalObject
= GetScopeObject()->GetGlobalJSObject();
10677 JSAutoRealm
ar(cx
, globalObject
);
10678 JS::Rooted
<JS::Value
> v(cx
);
10679 rv
= nsContentUtils::WrapNative(cx
, ToSupports(this), this, &v
,
10680 /* aAllowWrapping = */ false);
10681 if (rv
.Failed()) return nullptr;
10682 newScope
= &v
.toObject();
10686 adoptedNode
->Adopt(sameDocument
? nullptr : mNodeInfoManager
, newScope
, rv
);
10688 // Disconnect all nodes from their parents, since some have the old document
10689 // as their ownerDocument and some have this as their ownerDocument.
10690 nsDOMAttributeMap::BlastSubtreeToPieces(adoptedNode
);
10694 MOZ_ASSERT(adoptedNode
->OwnerDoc() == this,
10695 "Should still be in the document we just got adopted into");
10697 return adoptedNode
;
10700 bool Document::UseWidthDeviceWidthFallbackViewport() const { return false; }
10702 static Maybe
<LayoutDeviceToScreenScale
> ParseScaleString(
10703 const nsString
& aScaleString
) {
10704 // https://drafts.csswg.org/css-device-adapt/#min-scale-max-scale
10705 if (aScaleString
.EqualsLiteral("device-width") ||
10706 aScaleString
.EqualsLiteral("device-height")) {
10707 return Some(LayoutDeviceToScreenScale(10.0f
));
10708 } else if (aScaleString
.EqualsLiteral("yes")) {
10709 return Some(LayoutDeviceToScreenScale(1.0f
));
10710 } else if (aScaleString
.EqualsLiteral("no")) {
10711 return Some(LayoutDeviceToScreenScale(ViewportMinScale()));
10712 } else if (aScaleString
.IsEmpty()) {
10716 nsresult scaleErrorCode
;
10717 float scale
= aScaleString
.ToFloatAllowTrailingChars(&scaleErrorCode
);
10718 if (NS_FAILED(scaleErrorCode
)) {
10719 return Some(LayoutDeviceToScreenScale(ViewportMinScale()));
10725 return Some(std::clamp(LayoutDeviceToScreenScale(scale
), ViewportMinScale(),
10726 ViewportMaxScale()));
10729 void Document::ParseScalesInViewportMetaData(
10730 const ViewportMetaData
& aViewportMetaData
) {
10731 Maybe
<LayoutDeviceToScreenScale
> scale
;
10733 scale
= ParseScaleString(aViewportMetaData
.mInitialScale
);
10734 mScaleFloat
= scale
.valueOr(LayoutDeviceToScreenScale(0.0f
));
10735 mValidScaleFloat
= scale
.isSome();
10737 scale
= ParseScaleString(aViewportMetaData
.mMaximumScale
);
10738 // Chrome uses '5' for the fallback value of maximum-scale, we might
10739 // consider matching it in future.
10740 // https://cs.chromium.org/chromium/src/third_party/blink/renderer/core/html/html_meta_element.cc?l=452&rcl=65ca4278b42d269ca738fc93ef7ae04a032afeb0
10741 mScaleMaxFloat
= scale
.valueOr(ViewportMaxScale());
10742 mValidMaxScale
= scale
.isSome();
10744 scale
= ParseScaleString(aViewportMetaData
.mMinimumScale
);
10745 mScaleMinFloat
= scale
.valueOr(ViewportMinScale());
10746 mValidMinScale
= scale
.isSome();
10748 // Resolve min-zoom and max-zoom values.
10749 // https://drafts.csswg.org/css-device-adapt/#constraining-min-max-zoom
10750 if (mValidMaxScale
&& mValidMinScale
) {
10751 mScaleMaxFloat
= std::max(mScaleMinFloat
, mScaleMaxFloat
);
10755 void Document::ParseWidthAndHeightInMetaViewport(const nsAString
& aWidthString
,
10756 const nsAString
& aHeightString
,
10757 bool aHasValidScale
) {
10758 // The width and height properties
10759 // https://drafts.csswg.org/css-device-adapt/#width-and-height-properties
10761 // The width and height viewport <META> properties are translated into width
10762 // and height descriptors, setting the min-width/min-height value to
10763 // extend-to-zoom and the max-width/max-height value to the length from the
10764 // viewport <META> property as follows:
10766 // 1. Non-negative number values are translated to pixel lengths, clamped to
10767 // the range: [1px, 10000px]
10768 // 2. Negative number values are dropped
10769 // 3. device-width and device-height translate to 100vw and 100vh respectively
10770 // 4. Other keywords and unknown values are also dropped
10771 mMinWidth
= nsViewportInfo::kAuto
;
10772 mMaxWidth
= nsViewportInfo::kAuto
;
10773 if (!aWidthString
.IsEmpty()) {
10774 mMinWidth
= nsViewportInfo::kExtendToZoom
;
10775 if (aWidthString
.EqualsLiteral("device-width")) {
10776 mMaxWidth
= nsViewportInfo::kDeviceSize
;
10778 nsresult widthErrorCode
;
10779 mMaxWidth
= aWidthString
.ToInteger(&widthErrorCode
);
10780 if (NS_FAILED(widthErrorCode
)) {
10781 mMaxWidth
= nsViewportInfo::kAuto
;
10782 } else if (mMaxWidth
>= 0.0f
) {
10783 mMaxWidth
= std::clamp(mMaxWidth
, CSSCoord(1.0f
), CSSCoord(10000.0f
));
10785 mMaxWidth
= nsViewportInfo::kAuto
;
10788 } else if (aHasValidScale
) {
10789 if (aHeightString
.IsEmpty()) {
10790 mMinWidth
= nsViewportInfo::kExtendToZoom
;
10791 mMaxWidth
= nsViewportInfo::kExtendToZoom
;
10793 } else if (aHeightString
.IsEmpty() && UseWidthDeviceWidthFallbackViewport()) {
10794 mMinWidth
= nsViewportInfo::kExtendToZoom
;
10795 mMaxWidth
= nsViewportInfo::kDeviceSize
;
10798 mMinHeight
= nsViewportInfo::kAuto
;
10799 mMaxHeight
= nsViewportInfo::kAuto
;
10800 if (!aHeightString
.IsEmpty()) {
10801 mMinHeight
= nsViewportInfo::kExtendToZoom
;
10802 if (aHeightString
.EqualsLiteral("device-height")) {
10803 mMaxHeight
= nsViewportInfo::kDeviceSize
;
10805 nsresult heightErrorCode
;
10806 mMaxHeight
= aHeightString
.ToInteger(&heightErrorCode
);
10807 if (NS_FAILED(heightErrorCode
)) {
10808 mMaxHeight
= nsViewportInfo::kAuto
;
10809 } else if (mMaxHeight
>= 0.0f
) {
10810 mMaxHeight
= std::clamp(mMaxHeight
, CSSCoord(1.0f
), CSSCoord(10000.0f
));
10812 mMaxHeight
= nsViewportInfo::kAuto
;
10818 nsViewportInfo
Document::GetViewportInfo(const ScreenIntSize
& aDisplaySize
) {
10819 MOZ_ASSERT(mPresShell
);
10821 // Compute the CSS-to-LayoutDevice pixel scale as the product of the
10822 // widget scale and the full zoom.
10823 nsPresContext
* context
= mPresShell
->GetPresContext();
10824 // When querying the full zoom, get it from the device context rather than
10825 // directly from the pres context, because the device context's value can
10826 // include an adjustment necessary to keep the number of app units per device
10827 // pixel an integer, and we want the adjusted value.
10828 float fullZoom
= context
? context
->DeviceContext()->GetFullZoom() : 1.0;
10829 fullZoom
= (fullZoom
== 0.0) ? 1.0 : fullZoom
;
10830 CSSToLayoutDeviceScale layoutDeviceScale
=
10831 context
? context
->CSSToDevPixelScale() : CSSToLayoutDeviceScale(1);
10833 CSSToScreenScale defaultScale
=
10834 layoutDeviceScale
* LayoutDeviceToScreenScale(1.0);
10836 auto* bc
= GetBrowsingContext();
10837 const bool inRDM
= bc
&& bc
->InRDMPane();
10838 const bool ignoreMetaTag
= [&] {
10839 if (!nsLayoutUtils::ShouldHandleMetaViewport(this)) {
10842 if (Fullscreen()) {
10843 // We ignore viewport meta tag etc when in fullscreen, see bug 1696717.
10846 if (inRDM
&& bc
->ForceDesktopViewport()) {
10847 // We ignore meta viewport when devtools tells us to force desktop
10848 // viewport on RDM.
10854 if (ignoreMetaTag
) {
10855 return nsViewportInfo(aDisplaySize
, defaultScale
,
10856 nsLayoutUtils::AllowZoomingForDocument(this)
10857 ? nsViewportInfo::ZoomFlag::AllowZoom
10858 : nsViewportInfo::ZoomFlag::DisallowZoom
,
10859 StaticPrefs::apz_allow_zooming_out()
10860 ? nsViewportInfo::ZoomBehaviour::Mobile
10861 : nsViewportInfo::ZoomBehaviour::Desktop
);
10864 // Special behaviour for desktop mode, provided we are not on an about: page.
10865 if (bc
&& bc
->ForceDesktopViewport() && !IsAboutPage()) {
10866 CSSCoord viewportWidth
=
10867 StaticPrefs::browser_viewport_desktopWidth() / fullZoom
;
10868 CSSToScreenScale
scaleToFit(aDisplaySize
.width
/ viewportWidth
);
10869 float aspectRatio
= (float)aDisplaySize
.height
/ aDisplaySize
.width
;
10870 CSSSize
viewportSize(viewportWidth
, viewportWidth
* aspectRatio
);
10871 ScreenIntSize fakeDesktopSize
= RoundedToInt(viewportSize
* scaleToFit
);
10872 return nsViewportInfo(fakeDesktopSize
, scaleToFit
,
10873 nsViewportInfo::ZoomFlag::AllowZoom
,
10874 nsViewportInfo::ZoomBehaviour::Mobile
,
10875 nsViewportInfo::AutoScaleFlag::AutoScale
);
10878 // In cases where the width of the CSS viewport is less than or equal to the
10879 // width of the display (i.e. width <= device-width) then we disable
10880 // double-tap-to-zoom behaviour. See bug 941995 for details.
10882 switch (mViewportType
) {
10883 case DisplayWidthHeight
:
10884 return nsViewportInfo(aDisplaySize
, defaultScale
,
10885 nsViewportInfo::ZoomFlag::AllowZoom
,
10886 nsViewportInfo::ZoomBehaviour::Mobile
);
10888 // We might early exit if the viewport is empty. Even if we don't,
10889 // at the end of this case we'll note that it was empty. Later, when
10890 // we're using the cached values, this will trigger alternate code paths.
10891 if (!mLastModifiedViewportMetaData
) {
10892 // If the docType specifies that we are on a site optimized for mobile,
10893 // then we want to return specially crafted defaults for the viewport
10895 if (RefPtr
<DocumentType
> docType
= GetDoctype()) {
10896 nsAutoString docId
;
10897 docType
->GetPublicId(docId
);
10898 if ((docId
.Find(u
"WAP") != -1) || (docId
.Find(u
"Mobile") != -1) ||
10899 (docId
.Find(u
"WML") != -1)) {
10900 // We're making an assumption that the docType can't change here
10901 mViewportType
= DisplayWidthHeight
;
10902 return nsViewportInfo(aDisplaySize
, defaultScale
,
10903 nsViewportInfo::ZoomFlag::AllowZoom
,
10904 nsViewportInfo::ZoomBehaviour::Mobile
);
10908 nsAutoString handheldFriendly
;
10909 GetHeaderData(nsGkAtoms::handheldFriendly
, handheldFriendly
);
10910 if (handheldFriendly
.EqualsLiteral("true")) {
10911 mViewportType
= DisplayWidthHeight
;
10912 return nsViewportInfo(aDisplaySize
, defaultScale
,
10913 nsViewportInfo::ZoomFlag::AllowZoom
,
10914 nsViewportInfo::ZoomBehaviour::Mobile
);
10918 ViewportMetaData metaData
= GetViewportMetaData();
10920 // Parse initial-scale, minimum-scale and maximum-scale.
10921 ParseScalesInViewportMetaData(metaData
);
10923 // Parse width and height properties
10924 // This function sets m{Min,Max}{Width,Height}.
10925 ParseWidthAndHeightInMetaViewport(metaData
.mWidth
, metaData
.mHeight
,
10929 if ((metaData
.mUserScalable
.EqualsLiteral("0")) ||
10930 (metaData
.mUserScalable
.EqualsLiteral("no")) ||
10931 (metaData
.mUserScalable
.EqualsLiteral("false"))) {
10932 mAllowZoom
= false;
10935 // Resolve viewport-fit value.
10936 // https://drafts.csswg.org/css-round-display/#viewport-fit-descriptor
10937 mViewportFit
= ViewportFitType::Auto
;
10938 if (!metaData
.mViewportFit
.IsEmpty()) {
10939 if (metaData
.mViewportFit
.EqualsLiteral("contain")) {
10940 mViewportFit
= ViewportFitType::Contain
;
10941 } else if (metaData
.mViewportFit
.EqualsLiteral("cover")) {
10942 mViewportFit
= ViewportFitType::Cover
;
10946 mWidthStrEmpty
= metaData
.mWidth
.IsEmpty();
10948 mViewportType
= Specified
;
10953 LayoutDeviceToScreenScale effectiveMinScale
= mScaleMinFloat
;
10954 LayoutDeviceToScreenScale effectiveMaxScale
= mScaleMaxFloat
;
10955 bool effectiveValidMaxScale
= mValidMaxScale
;
10957 nsViewportInfo::ZoomFlag effectiveZoomFlag
=
10958 mAllowZoom
? nsViewportInfo::ZoomFlag::AllowZoom
10959 : nsViewportInfo::ZoomFlag::DisallowZoom
;
10960 if (StaticPrefs::browser_ui_zoom_force_user_scalable()) {
10961 // If the pref to force user-scalable is enabled, we ignore the values
10962 // from the meta-viewport tag for these properties and just assume they
10963 // allow the page to be scalable. Note in particular that this code is
10964 // in the "Specified" branch of the enclosing switch statement, so that
10965 // calls to GetViewportInfo always use the latest value of the
10966 // browser_ui_zoom_force_user_scalable pref. Other codepaths that
10967 // return nsViewportInfo instances are all consistent with
10968 // browser_ui_zoom_force_user_scalable() already.
10969 effectiveMinScale
= ViewportMinScale();
10970 effectiveMaxScale
= ViewportMaxScale();
10971 effectiveValidMaxScale
= true;
10972 effectiveZoomFlag
= nsViewportInfo::ZoomFlag::AllowZoom
;
10975 // Returns extend-zoom value which is MIN(mScaleFloat, mScaleMaxFloat).
10976 auto ComputeExtendZoom
= [&]() -> float {
10977 if (mValidScaleFloat
&& effectiveValidMaxScale
) {
10978 return std::min(mScaleFloat
.scale
, effectiveMaxScale
.scale
);
10980 if (mValidScaleFloat
) {
10981 return mScaleFloat
.scale
;
10983 if (effectiveValidMaxScale
) {
10984 return effectiveMaxScale
.scale
;
10986 return nsViewportInfo::kAuto
;
10989 // Resolving 'extend-to-zoom'
10990 // https://drafts.csswg.org/css-device-adapt/#resolve-extend-to-zoom
10991 float extendZoom
= ComputeExtendZoom();
10993 CSSCoord minWidth
= mMinWidth
;
10994 CSSCoord maxWidth
= mMaxWidth
;
10995 CSSCoord minHeight
= mMinHeight
;
10996 CSSCoord maxHeight
= mMaxHeight
;
10998 // aDisplaySize is in screen pixels; convert them to CSS pixels for the
10999 // viewport size. We need to use this scaled size for any clamping of
11000 // width or height.
11001 CSSSize displaySize
= ScreenSize(aDisplaySize
) / defaultScale
;
11003 // Our min and max width and height values are mostly as specified by
11004 // the viewport declaration, but we make an exception for max width.
11005 // Max width, if auto, and if there's no initial-scale, will be set
11006 // to a default size. This is to support legacy site design with no
11007 // viewport declaration, and to do that using the same scheme as
11008 // Chrome does, in order to maintain web compatibility. Since the
11009 // default size has a complicated calculation, we fixup the maxWidth
11010 // value after setting it, above.
11011 if (maxWidth
== nsViewportInfo::kAuto
&& !mValidScaleFloat
) {
11012 maxWidth
= StaticPrefs::browser_viewport_desktopWidth();
11014 bc
->TouchEventsOverride() == TouchEventsOverride::Enabled
) {
11015 // If RDM and touch simulation are active, then use the simulated
11016 // screen width to accommodate for cases where the screen width is
11017 // larger than the desktop viewport default.
11018 maxWidth
= nsViewportInfo::Max(displaySize
.width
, maxWidth
);
11020 // Divide by fullZoom to stretch CSS pixel size of viewport in order
11021 // to keep device pixel size unchanged after full zoom applied.
11023 maxWidth
/= fullZoom
;
11025 // We set minWidth to ExtendToZoom, which will cause our later width
11026 // calculation to expand to maxWidth, if scale restrictions allow it.
11027 minWidth
= nsViewportInfo::kExtendToZoom
;
11030 // Resolve device-width and device-height first.
11031 if (maxWidth
== nsViewportInfo::kDeviceSize
) {
11032 maxWidth
= displaySize
.width
;
11034 if (maxHeight
== nsViewportInfo::kDeviceSize
) {
11035 maxHeight
= displaySize
.height
;
11037 if (extendZoom
== nsViewportInfo::kAuto
) {
11038 if (maxWidth
== nsViewportInfo::kExtendToZoom
) {
11039 maxWidth
= nsViewportInfo::kAuto
;
11041 if (maxHeight
== nsViewportInfo::kExtendToZoom
) {
11042 maxHeight
= nsViewportInfo::kAuto
;
11044 if (minWidth
== nsViewportInfo::kExtendToZoom
) {
11045 minWidth
= maxWidth
;
11047 if (minHeight
== nsViewportInfo::kExtendToZoom
) {
11048 minHeight
= maxHeight
;
11051 CSSSize extendSize
= displaySize
/ extendZoom
;
11052 if (maxWidth
== nsViewportInfo::kExtendToZoom
) {
11053 maxWidth
= extendSize
.width
;
11055 if (maxHeight
== nsViewportInfo::kExtendToZoom
) {
11056 maxHeight
= extendSize
.height
;
11058 if (minWidth
== nsViewportInfo::kExtendToZoom
) {
11059 minWidth
= nsViewportInfo::Max(extendSize
.width
, maxWidth
);
11061 if (minHeight
== nsViewportInfo::kExtendToZoom
) {
11062 minHeight
= nsViewportInfo::Max(extendSize
.height
, maxHeight
);
11066 // Resolve initial width and height from min/max descriptors
11067 // https://drafts.csswg.org/css-device-adapt/#resolve-initial-width-height
11068 CSSCoord width
= nsViewportInfo::kAuto
;
11069 if (minWidth
!= nsViewportInfo::kAuto
||
11070 maxWidth
!= nsViewportInfo::kAuto
) {
11071 width
= nsViewportInfo::Max(
11072 minWidth
, nsViewportInfo::Min(maxWidth
, displaySize
.width
));
11074 CSSCoord height
= nsViewportInfo::kAuto
;
11075 if (minHeight
!= nsViewportInfo::kAuto
||
11076 maxHeight
!= nsViewportInfo::kAuto
) {
11077 height
= nsViewportInfo::Max(
11078 minHeight
, nsViewportInfo::Min(maxHeight
, displaySize
.height
));
11081 // Resolve width value
11082 // https://drafts.csswg.org/css-device-adapt/#resolve-width
11083 if (width
== nsViewportInfo::kAuto
) {
11084 if (height
== nsViewportInfo::kAuto
|| aDisplaySize
.height
== 0) {
11085 width
= displaySize
.width
;
11087 width
= height
* aDisplaySize
.width
/ aDisplaySize
.height
;
11091 // Resolve height value
11092 // https://drafts.csswg.org/css-device-adapt/#resolve-height
11093 if (height
== nsViewportInfo::kAuto
) {
11094 if (aDisplaySize
.width
== 0) {
11095 height
= displaySize
.height
;
11097 height
= width
* aDisplaySize
.height
/ aDisplaySize
.width
;
11100 MOZ_ASSERT(width
!= nsViewportInfo::kAuto
&&
11101 height
!= nsViewportInfo::kAuto
);
11103 CSSSize
size(width
, height
);
11105 CSSToScreenScale scaleFloat
= mScaleFloat
* layoutDeviceScale
;
11106 CSSToScreenScale scaleMinFloat
= effectiveMinScale
* layoutDeviceScale
;
11107 CSSToScreenScale scaleMaxFloat
= effectiveMaxScale
* layoutDeviceScale
;
11109 nsViewportInfo::AutoSizeFlag sizeFlag
=
11110 nsViewportInfo::AutoSizeFlag::FixedSize
;
11111 if (mMaxWidth
== nsViewportInfo::kDeviceSize
||
11112 (mWidthStrEmpty
&& (mMaxHeight
== nsViewportInfo::kDeviceSize
||
11113 mScaleFloat
.scale
== 1.0f
)) ||
11114 (!mWidthStrEmpty
&& mMaxWidth
== nsViewportInfo::kAuto
&&
11116 sizeFlag
= nsViewportInfo::AutoSizeFlag::AutoSize
;
11119 // FIXME: Resolving width and height should be done above 'Resolve width
11120 // value' and 'Resolve height value'.
11121 if (sizeFlag
== nsViewportInfo::AutoSizeFlag::AutoSize
) {
11122 size
= displaySize
;
11125 // The purpose of clamping the viewport width to a minimum size is to
11126 // prevent page authors from setting it to a ridiculously small value.
11127 // If the page is actually being rendered in a very small area (as might
11128 // happen in e.g. Android 8's picture-in-picture mode), we don't want to
11129 // prevent the viewport from taking on that size.
11130 CSSSize effectiveMinSize
= Min(CSSSize(kViewportMinSize
), displaySize
);
11132 size
.width
= std::clamp(size
.width
, effectiveMinSize
.width
,
11133 float(kViewportMaxSize
.width
));
11135 // Also recalculate the default zoom, if it wasn't specified in the
11136 // metadata, and the width is specified.
11137 if (!mValidScaleFloat
&& !mWidthStrEmpty
) {
11138 CSSToScreenScale
bestFitScale(float(aDisplaySize
.width
) / size
.width
);
11139 scaleFloat
= (scaleFloat
> bestFitScale
) ? scaleFloat
: bestFitScale
;
11142 size
.height
= std::clamp(size
.height
, effectiveMinSize
.height
,
11143 float(kViewportMaxSize
.height
));
11145 // In cases of user-scalable=no, if we have a positive scale, clamp it to
11146 // min and max, and then use the clamped value for the scale, the min, and
11147 // the max. If we don't have a positive scale, assert that we are setting
11148 // the auto scale flag.
11149 if (effectiveZoomFlag
== nsViewportInfo::ZoomFlag::DisallowZoom
&&
11150 scaleFloat
> CSSToScreenScale(0.0f
)) {
11151 scaleFloat
= scaleMinFloat
= scaleMaxFloat
=
11152 std::clamp(scaleFloat
, scaleMinFloat
, scaleMaxFloat
);
11155 scaleFloat
> CSSToScreenScale(0.0f
) || !mValidScaleFloat
,
11156 "If we don't have a positive scale, we should be using auto scale.");
11158 // We need to perform a conversion, but only if the initial or maximum
11159 // scale were set explicitly by the user.
11160 if (mValidScaleFloat
&& scaleFloat
>= scaleMinFloat
&&
11161 scaleFloat
<= scaleMaxFloat
) {
11162 CSSSize displaySize
= ScreenSize(aDisplaySize
) / scaleFloat
;
11163 size
.width
= std::max(size
.width
, displaySize
.width
);
11164 size
.height
= std::max(size
.height
, displaySize
.height
);
11165 } else if (effectiveValidMaxScale
) {
11166 CSSSize displaySize
= ScreenSize(aDisplaySize
) / scaleMaxFloat
;
11167 size
.width
= std::max(size
.width
, displaySize
.width
);
11168 size
.height
= std::max(size
.height
, displaySize
.height
);
11171 return nsViewportInfo(
11172 scaleFloat
, scaleMinFloat
, scaleMaxFloat
, size
, sizeFlag
,
11173 mValidScaleFloat
? nsViewportInfo::AutoScaleFlag::FixedScale
11174 : nsViewportInfo::AutoScaleFlag::AutoScale
,
11175 effectiveZoomFlag
, mViewportFit
);
11179 ViewportMetaData
Document::GetViewportMetaData() const {
11180 return mLastModifiedViewportMetaData
? *mLastModifiedViewportMetaData
11181 : ViewportMetaData();
11184 static InteractiveWidget
ParseInteractiveWidget(
11185 const ViewportMetaData
& aViewportMetaData
) {
11186 if (aViewportMetaData
.mInteractiveWidgetMode
.IsEmpty()) {
11187 return InteractiveWidgetUtils::DefaultInteractiveWidgetMode();
11190 if (aViewportMetaData
.mInteractiveWidgetMode
.EqualsIgnoreCase(
11191 "resizes-visual")) {
11192 return InteractiveWidget::ResizesVisual
;
11194 if (aViewportMetaData
.mInteractiveWidgetMode
.EqualsIgnoreCase(
11195 "resizes-content")) {
11196 return InteractiveWidget::ResizesContent
;
11198 if (aViewportMetaData
.mInteractiveWidgetMode
.EqualsIgnoreCase(
11199 "overlays-content")) {
11200 return InteractiveWidget::OverlaysContent
;
11202 return InteractiveWidgetUtils::DefaultInteractiveWidgetMode();
11205 void Document::SetMetaViewportData(UniquePtr
<ViewportMetaData
> aData
) {
11206 mLastModifiedViewportMetaData
= std::move(aData
);
11207 // Trigger recomputation of the nsViewportInfo the next time it's queried.
11208 mViewportType
= Unknown
;
11210 // Parse interactive-widget here anyway. Normally we parse any data in the
11211 // meta viewport tag in GetViewportInfo(), but GetViewportInfo() depends on
11212 // the document state (e.g. display size, fullscreen, desktop-mode etc.)
11213 // whereas interactive-widget is independent from the document state, it's
11214 // necessary whatever the document state is.
11215 dom::InteractiveWidget interactiveWidget
=
11216 ParseInteractiveWidget(*mLastModifiedViewportMetaData
);
11217 if (mInteractiveWidgetMode
!= interactiveWidget
) {
11218 mInteractiveWidgetMode
= interactiveWidget
;
11221 AsyncEventDispatcher::RunDOMEventWhenSafe(
11222 *this, u
"DOMMetaViewportFitChanged"_ns
, CanBubble::eYes
,
11223 ChromeOnlyDispatch::eYes
);
11226 EventListenerManager
* Document::GetOrCreateListenerManager() {
11227 if (!mListenerManager
) {
11229 new EventListenerManager(static_cast<EventTarget
*>(this));
11230 SetFlags(NODE_HAS_LISTENERMANAGER
);
11233 return mListenerManager
;
11236 EventListenerManager
* Document::GetExistingListenerManager() const {
11237 return mListenerManager
;
11240 void Document::GetEventTargetParent(EventChainPreVisitor
& aVisitor
) {
11241 aVisitor
.mCanHandle
= true;
11242 // FIXME! This is a hack to make middle mouse paste working also in Editor.
11244 aVisitor
.mForceContentDispatch
= true;
11246 // Load events must not propagate to |window| object, see bug 335251.
11247 if (aVisitor
.mEvent
->mMessage
!= eLoad
) {
11248 nsGlobalWindowOuter
* window
= nsGlobalWindowOuter::Cast(GetWindow());
11249 aVisitor
.SetParentTarget(
11250 window
? window
->GetTargetForEventTargetChain() : nullptr, false);
11254 already_AddRefed
<Event
> Document::CreateEvent(const nsAString
& aEventType
,
11255 CallerType aCallerType
,
11256 ErrorResult
& rv
) const {
11257 nsPresContext
* presContext
= GetPresContext();
11259 // Create event even without presContext.
11261 EventDispatcher::CreateEvent(const_cast<Document
*>(this), presContext
,
11262 nullptr, aEventType
, aCallerType
);
11264 rv
.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR
);
11267 WidgetEvent
* e
= ev
->WidgetEventPtr();
11268 e
->mFlags
.mBubbles
= false;
11269 e
->mFlags
.mCancelable
= false;
11270 return ev
.forget();
11273 void Document::FlushPendingNotifications(FlushType aType
) {
11274 mozilla::ChangesToFlush
flush(aType
, aType
>= FlushType::Style
);
11275 FlushPendingNotifications(flush
);
11278 void Document::FlushPendingNotifications(mozilla::ChangesToFlush aFlush
) {
11279 FlushType flushType
= aFlush
.mFlushType
;
11281 RefPtr
<Document
> documentOnStack
= this;
11283 // We need to flush the sink for non-HTML documents (because the XML
11284 // parser still does insertion with deferred notifications). We
11285 // also need to flush the sink if this is a layout-related flush, to
11286 // make sure that layout is started as needed. But we can skip that
11287 // part if we have no presshell or if it's already done an initial
11289 if ((!IsHTMLDocument() || (flushType
> FlushType::ContentAndNotify
&&
11290 mPresShell
&& !mPresShell
->DidInitialize())) &&
11291 (mParser
|| mWeakSink
)) {
11292 nsCOMPtr
<nsIContentSink
> sink
;
11294 sink
= mParser
->GetContentSink();
11296 sink
= do_QueryReferent(mWeakSink
);
11298 mWeakSink
= nullptr;
11301 // Determine if it is safe to flush the sink notifications
11302 // by determining if it safe to flush all the presshells.
11303 if (sink
&& (flushType
== FlushType::Content
|| IsSafeToFlush())) {
11304 sink
->FlushPendingNotifications(flushType
);
11308 // Should we be flushing pending binding constructors in here?
11310 if (flushType
<= FlushType::ContentAndNotify
) {
11311 // Nothing to do here
11315 // If we have a parent we must flush the parent too to ensure that our
11316 // container is reflowed if its size was changed.
11318 // We do it only if the subdocument and the parent can observe each other
11319 // synchronously (that is, if we're not cross-origin), to avoid work that is
11320 // not observable, and if the parent document has finished loading all its
11321 // render-blocking stylesheets and may start laying out the document, to avoid
11322 // unnecessary flashes of unstyled content on the parent document. Note that
11323 // this last bit means that size-dependent media queries in this document may
11324 // produce incorrect results temporarily.
11326 // But if it's not safe to flush ourselves, then don't flush the parent, since
11327 // that can cause things like resizes of our frame's widget, which we can't
11328 // handle while flushing is unsafe.
11329 if (StyleOrLayoutObservablyDependsOnParentDocumentLayout() &&
11330 mParentDocument
->MayStartLayout() && IsSafeToFlush()) {
11331 ChangesToFlush parentFlush
= aFlush
;
11332 if (flushType
>= FlushType::Style
) {
11333 // Since media queries mean that a size change of our container can affect
11334 // style, we need to promote a style flush on ourself to a layout flush on
11335 // our parent, since we need our container to be the correct size to
11336 // determine the correct style.
11337 parentFlush
.mFlushType
= std::max(FlushType::Layout
, flushType
);
11339 mParentDocument
->FlushPendingNotifications(parentFlush
);
11342 if (RefPtr
<PresShell
> presShell
= GetPresShell()) {
11343 presShell
->FlushPendingNotifications(aFlush
);
11347 void Document::FlushExternalResources(FlushType aType
) {
11349 aType
>= FlushType::Style
,
11350 "should only need to flush for style or higher in external resources");
11351 if (GetDisplayDocument()) {
11355 EnumerateExternalResources([aType
](Document
& aDoc
) {
11356 aDoc
.FlushPendingNotifications(aType
);
11357 return CallState::Continue
;
11361 void Document::SetXMLDeclaration(const char16_t
* aVersion
,
11362 const char16_t
* aEncoding
,
11363 const int32_t aStandalone
) {
11364 if (!aVersion
|| *aVersion
== '\0') {
11365 mXMLDeclarationBits
= 0;
11369 mXMLDeclarationBits
= XML_DECLARATION_BITS_DECLARATION_EXISTS
;
11371 if (aEncoding
&& *aEncoding
!= '\0') {
11372 mXMLDeclarationBits
|= XML_DECLARATION_BITS_ENCODING_EXISTS
;
11375 if (aStandalone
== 1) {
11376 mXMLDeclarationBits
|= XML_DECLARATION_BITS_STANDALONE_EXISTS
|
11377 XML_DECLARATION_BITS_STANDALONE_YES
;
11378 } else if (aStandalone
== 0) {
11379 mXMLDeclarationBits
|= XML_DECLARATION_BITS_STANDALONE_EXISTS
;
11383 void Document::GetXMLDeclaration(nsAString
& aVersion
, nsAString
& aEncoding
,
11384 nsAString
& aStandalone
) {
11385 aVersion
.Truncate();
11386 aEncoding
.Truncate();
11387 aStandalone
.Truncate();
11389 if (!(mXMLDeclarationBits
& XML_DECLARATION_BITS_DECLARATION_EXISTS
)) {
11393 // always until we start supporting 1.1 etc.
11394 aVersion
.AssignLiteral("1.0");
11396 if (mXMLDeclarationBits
& XML_DECLARATION_BITS_ENCODING_EXISTS
) {
11397 // This is what we have stored, not necessarily what was written
11399 GetCharacterSet(aEncoding
);
11402 if (mXMLDeclarationBits
& XML_DECLARATION_BITS_STANDALONE_EXISTS
) {
11403 if (mXMLDeclarationBits
& XML_DECLARATION_BITS_STANDALONE_YES
) {
11404 aStandalone
.AssignLiteral("yes");
11406 aStandalone
.AssignLiteral("no");
11411 void Document::AddColorSchemeMeta(HTMLMetaElement
& aMeta
) {
11412 mColorSchemeMetaTags
.Insert(aMeta
);
11413 RecomputeColorScheme();
11416 void Document::RemoveColorSchemeMeta(HTMLMetaElement
& aMeta
) {
11417 mColorSchemeMetaTags
.RemoveElement(aMeta
);
11418 RecomputeColorScheme();
11421 void Document::RecomputeColorScheme() {
11422 auto oldColorScheme
= mColorSchemeBits
;
11423 mColorSchemeBits
= 0;
11424 const nsTArray
<HTMLMetaElement
*>& elements
= mColorSchemeMetaTags
;
11425 for (const HTMLMetaElement
* el
: elements
) {
11426 nsAutoString content
;
11427 if (!el
->GetAttr(nsGkAtoms::content
, content
)) {
11431 NS_ConvertUTF16toUTF8
contentU8(content
);
11432 if (Servo_ColorScheme_Parse(&contentU8
, &mColorSchemeBits
)) {
11437 if (mColorSchemeBits
== oldColorScheme
) {
11441 if (nsPresContext
* pc
= GetPresContext()) {
11442 // This affects system colors, which are inherited, so we need to recascade.
11443 pc
->RebuildAllStyleData(nsChangeHint(0), RestyleHint::RecascadeSubtree());
11447 bool Document::IsScriptEnabled() const {
11448 // If this document is sandboxed without 'allow-scripts'
11449 // script is not enabled
11450 if (HasScriptsBlockedBySandbox()) {
11454 nsCOMPtr
<nsIScriptGlobalObject
> globalObject
=
11455 do_QueryInterface(GetInnerWindow());
11456 if (!globalObject
|| !globalObject
->HasJSGlobal()) {
11460 return xpc::Scriptability::Get(globalObject
->GetGlobalJSObjectPreserveColor())
11464 void Document::RetrieveRelevantHeaders(nsIChannel
* aChannel
) {
11465 PRTime modDate
= 0;
11468 nsCOMPtr
<nsIHttpChannel
> httpChannel
;
11469 rv
= GetHttpChannelHelper(aChannel
, getter_AddRefs(httpChannel
));
11470 if (NS_WARN_IF(NS_FAILED(rv
))) {
11476 rv
= httpChannel
->GetResponseHeader("last-modified"_ns
, tmp
);
11478 if (NS_SUCCEEDED(rv
)) {
11480 PRStatus st
= PR_ParseTimeString(tmp
.get(), true, &time
);
11481 if (st
== PR_SUCCESS
) {
11486 static const char* const headers
[] = {
11487 "default-style", "content-style-type", "content-language",
11488 "content-disposition", "refresh", "x-dns-prefetch-control",
11489 "x-frame-options", "origin-trial",
11490 // add more http headers if you need
11491 // XXXbz don't add content-location support without reading bug
11492 // 238654 and its dependencies/dups first.
11495 nsAutoCString headerVal
;
11496 const char* const* name
= headers
;
11498 rv
= httpChannel
->GetResponseHeader(nsDependentCString(*name
), headerVal
);
11499 if (NS_SUCCEEDED(rv
) && !headerVal
.IsEmpty()) {
11500 RefPtr
<nsAtom
> key
= NS_Atomize(*name
);
11501 SetHeaderData(key
, NS_ConvertASCIItoUTF16(headerVal
));
11506 nsCOMPtr
<nsIFileChannel
> fileChannel
= do_QueryInterface(aChannel
);
11508 nsCOMPtr
<nsIFile
> file
;
11509 fileChannel
->GetFile(getter_AddRefs(file
));
11512 rv
= file
->GetLastModifiedTime(&msecs
);
11514 if (NS_SUCCEEDED(rv
)) {
11515 modDate
= msecs
* int64_t(PR_USEC_PER_MSEC
);
11519 nsAutoCString contentDisp
;
11520 rv
= aChannel
->GetContentDispositionHeader(contentDisp
);
11521 if (NS_SUCCEEDED(rv
)) {
11522 SetHeaderData(nsGkAtoms::headerContentDisposition
,
11523 NS_ConvertASCIItoUTF16(contentDisp
));
11528 mLastModified
.Truncate();
11529 if (modDate
!= 0) {
11530 GetFormattedTimeString(modDate
,
11531 ShouldResistFingerprinting(RFPTarget::JSDateTimeUTC
),
11536 void Document::ProcessMETATag(HTMLMetaElement
* aMetaElement
) {
11537 // set any HTTP-EQUIV data into document's header data as well as url
11538 nsAutoString header
;
11539 aMetaElement
->GetAttr(nsGkAtoms::httpEquiv
, header
);
11540 if (!header
.IsEmpty()) {
11541 // Ignore META REFRESH when document is sandboxed from automatic features.
11542 nsContentUtils::ASCIIToLower(header
);
11543 if (nsGkAtoms::refresh
->Equals(header
) &&
11544 (GetSandboxFlags() & SANDBOXED_AUTOMATIC_FEATURES
)) {
11548 nsAutoString result
;
11549 aMetaElement
->GetAttr(nsGkAtoms::content
, result
);
11550 if (!result
.IsEmpty()) {
11551 RefPtr
<nsAtom
> fieldAtom(NS_Atomize(header
));
11552 SetHeaderData(fieldAtom
, result
);
11556 if (aMetaElement
->AttrValueIs(kNameSpaceID_None
, nsGkAtoms::name
,
11557 nsGkAtoms::handheldFriendly
, eIgnoreCase
)) {
11558 nsAutoString result
;
11559 aMetaElement
->GetAttr(nsGkAtoms::content
, result
);
11560 if (!result
.IsEmpty()) {
11561 nsContentUtils::ASCIIToLower(result
);
11562 SetHeaderData(nsGkAtoms::handheldFriendly
, result
);
11567 already_AddRefed
<Element
> Document::CreateElem(const nsAString
& aName
,
11569 int32_t aNamespaceID
,
11570 const nsAString
* aIs
) {
11572 nsAutoString qName
;
11574 aPrefix
->ToString(qName
);
11577 qName
.Append(aName
);
11579 // Note: "a:b:c" is a valid name in non-namespaces XML, and
11580 // Document::CreateElement can call us with such a name and no prefix,
11581 // which would cause an error if we just used true here.
11582 bool nsAware
= aPrefix
!= nullptr || aNamespaceID
!= GetDefaultNamespaceID();
11583 NS_ASSERTION(NS_SUCCEEDED(nsContentUtils::CheckQName(qName
, nsAware
)),
11584 "Don't pass invalid prefixes to Document::CreateElem, "
11588 RefPtr
<mozilla::dom::NodeInfo
> nodeInfo
;
11589 mNodeInfoManager
->GetNodeInfo(aName
, aPrefix
, aNamespaceID
, ELEMENT_NODE
,
11590 getter_AddRefs(nodeInfo
));
11591 NS_ENSURE_TRUE(nodeInfo
, nullptr);
11593 nsCOMPtr
<Element
> element
;
11594 nsresult rv
= NS_NewElement(getter_AddRefs(element
), nodeInfo
.forget(),
11595 NOT_FROM_PARSER
, aIs
);
11596 return NS_SUCCEEDED(rv
) ? element
.forget() : nullptr;
11599 bool Document::IsSafeToFlush() const {
11600 PresShell
* presShell
= GetPresShell();
11604 return presShell
->IsSafeToFlush();
11607 void Document::Sanitize() {
11608 // Sanitize the document by resetting all (current and former) password fields
11609 // and any form fields with autocomplete=off to their default values. We do
11610 // this now, instead of when the presentation is restored, to offer some
11611 // protection in case there is ever an exploit that allows a cached document
11612 // to be accessed from a different document.
11614 // First locate all input elements, regardless of whether they are
11615 // in a form, and reset the password and autocomplete=off elements.
11617 RefPtr
<nsContentList
> nodes
= GetElementsByTagName(u
"input"_ns
);
11619 nsAutoString value
;
11621 uint32_t length
= nodes
->Length(true);
11622 for (uint32_t i
= 0; i
< length
; ++i
) {
11623 NS_ASSERTION(nodes
->Item(i
), "null item in node list!");
11625 RefPtr
<HTMLInputElement
> input
=
11626 HTMLInputElement::FromNodeOrNull(nodes
->Item(i
));
11627 if (!input
) continue;
11629 input
->GetAttr(nsGkAtoms::autocomplete
, value
);
11630 if (value
.LowerCaseEqualsLiteral("off") || input
->HasBeenTypePassword()) {
11635 // Now locate all _form_ elements that have autocomplete=off and reset them
11636 nodes
= GetElementsByTagName(u
"form"_ns
);
11638 length
= nodes
->Length(true);
11639 for (uint32_t i
= 0; i
< length
; ++i
) {
11640 // Reset() may change the list dynamically.
11641 RefPtr
<HTMLFormElement
> form
=
11642 HTMLFormElement::FromNodeOrNull(nodes
->Item(i
));
11643 if (!form
) continue;
11645 form
->GetAttr(nsGkAtoms::autocomplete
, value
);
11646 if (value
.LowerCaseEqualsLiteral("off")) form
->Reset();
11650 void Document::EnumerateSubDocuments(SubDocEnumFunc aCallback
) {
11651 if (!mSubDocuments
) {
11655 // PLDHashTable::Iterator can't handle modifications while iterating so we
11656 // copy all entries to an array first before calling any callbacks.
11657 AutoTArray
<RefPtr
<Document
>, 8> subdocs
;
11658 for (auto iter
= mSubDocuments
->Iter(); !iter
.Done(); iter
.Next()) {
11659 auto entry
= static_cast<SubDocMapEntry
*>(iter
.Get());
11660 if (Document
* subdoc
= entry
->mSubDocument
) {
11661 subdocs
.AppendElement(subdoc
);
11664 for (auto& subdoc
: subdocs
) {
11665 if (aCallback(*subdoc
) == CallState::Stop
) {
11671 void Document::CollectDescendantDocuments(
11672 nsTArray
<RefPtr
<Document
>>& aDescendants
, nsDocTestFunc aCallback
) const {
11673 if (!mSubDocuments
) {
11677 for (auto iter
= mSubDocuments
->Iter(); !iter
.Done(); iter
.Next()) {
11678 auto entry
= static_cast<SubDocMapEntry
*>(iter
.Get());
11679 const Document
* subdoc
= entry
->mSubDocument
;
11681 if (aCallback(subdoc
)) {
11682 aDescendants
.AppendElement(entry
->mSubDocument
);
11684 subdoc
->CollectDescendantDocuments(aDescendants
, aCallback
);
11689 bool Document::CanSavePresentation(nsIRequest
* aNewRequest
,
11690 uint32_t& aBFCacheCombo
,
11691 bool aIncludeSubdocuments
,
11692 bool aAllowUnloadListeners
) {
11695 if (!IsBFCachingAllowed()) {
11696 aBFCacheCombo
|= BFCacheStatus::NOT_ALLOWED
;
11701 if (MOZ_UNLIKELY(MOZ_LOG_TEST(gPageCacheLog
, LogLevel::Verbose
))) {
11702 if (mDocumentURI
) {
11703 mDocumentURI
->GetSpec(uri
);
11707 if (EventHandlingSuppressed()) {
11708 MOZ_LOG(gPageCacheLog
, mozilla::LogLevel::Verbose
,
11709 ("Save of %s blocked on event handling suppression", uri
.get()));
11710 aBFCacheCombo
|= BFCacheStatus::EVENT_HANDLING_SUPPRESSED
;
11714 // Do not allow suspended windows to be placed in the
11715 // bfcache. This method is also used to verify a document
11716 // coming out of the bfcache is ok to restore, though. So
11717 // we only want to block suspend windows that aren't also
11719 auto* win
= nsGlobalWindowInner::Cast(GetInnerWindow());
11720 if (win
&& win
->IsSuspended() && !win
->IsFrozen()) {
11721 MOZ_LOG(gPageCacheLog
, mozilla::LogLevel::Verbose
,
11722 ("Save of %s blocked on suspended Window", uri
.get()));
11723 aBFCacheCombo
|= BFCacheStatus::SUSPENDED
;
11727 nsCOMPtr
<nsIChannel
> channel
= do_QueryInterface(aNewRequest
);
11728 bool thirdParty
= false;
11729 // Currently some other mobile browsers seem to bfcache only cross-domain
11730 // pages, but bfcache those also when there are unload event listeners, so
11731 // this is trying to match that behavior as much as possible.
11732 bool allowUnloadListeners
=
11733 aAllowUnloadListeners
&&
11734 StaticPrefs::docshell_shistory_bfcache_allow_unload_listeners() &&
11735 (!channel
|| (NS_SUCCEEDED(NodePrincipal()->IsThirdPartyChannel(
11736 channel
, &thirdParty
)) &&
11739 // Check our event listener manager for unload/beforeunload listeners.
11740 nsCOMPtr
<EventTarget
> piTarget
= do_QueryInterface(mScriptGlobalObject
);
11741 if (!allowUnloadListeners
&& piTarget
) {
11742 EventListenerManager
* manager
= piTarget
->GetExistingListenerManager();
11744 if (manager
->HasUnloadListeners()) {
11745 MOZ_LOG(gPageCacheLog
, mozilla::LogLevel::Verbose
,
11746 ("Save of %s blocked due to unload handlers", uri
.get()));
11747 aBFCacheCombo
|= BFCacheStatus::UNLOAD_LISTENER
;
11750 if (manager
->HasBeforeUnloadListeners()) {
11751 if (!mozilla::SessionHistoryInParent() ||
11753 docshell_shistory_bfcache_ship_allow_beforeunload_listeners()) {
11755 gPageCacheLog
, mozilla::LogLevel::Verbose
,
11756 ("Save of %s blocked due to beforeUnload handlers", uri
.get()));
11757 aBFCacheCombo
|= BFCacheStatus::BEFOREUNLOAD_LISTENER
;
11764 // Check if we have pending network requests
11765 nsCOMPtr
<nsILoadGroup
> loadGroup
= GetDocumentLoadGroup();
11767 nsCOMPtr
<nsISimpleEnumerator
> requests
;
11768 loadGroup
->GetRequests(getter_AddRefs(requests
));
11770 bool hasMore
= false;
11772 // We want to bail out if we have any requests other than aNewRequest (or
11773 // in the case when aNewRequest is a part of a multipart response the base
11774 // channel the multipart response is coming in on).
11775 nsCOMPtr
<nsIChannel
> baseChannel
;
11776 nsCOMPtr
<nsIMultiPartChannel
> part(do_QueryInterface(aNewRequest
));
11778 part
->GetBaseChannel(getter_AddRefs(baseChannel
));
11781 while (NS_SUCCEEDED(requests
->HasMoreElements(&hasMore
)) && hasMore
) {
11782 nsCOMPtr
<nsISupports
> elem
;
11783 requests
->GetNext(getter_AddRefs(elem
));
11785 nsCOMPtr
<nsIRequest
> request
= do_QueryInterface(elem
);
11786 if (request
&& request
!= aNewRequest
&& request
!= baseChannel
) {
11787 // Favicon loads don't need to block caching.
11788 nsCOMPtr
<nsIChannel
> channel
= do_QueryInterface(request
);
11790 nsCOMPtr
<nsILoadInfo
> li
= channel
->LoadInfo();
11791 if (li
->InternalContentPolicyType() ==
11792 nsIContentPolicy::TYPE_INTERNAL_IMAGE_FAVICON
) {
11797 if (MOZ_UNLIKELY(MOZ_LOG_TEST(gPageCacheLog
, LogLevel::Verbose
))) {
11798 nsAutoCString requestName
;
11799 request
->GetName(requestName
);
11800 MOZ_LOG(gPageCacheLog
, LogLevel::Verbose
,
11801 ("Save of %s blocked because document has request %s",
11802 uri
.get(), requestName
.get()));
11804 aBFCacheCombo
|= BFCacheStatus::REQUEST
;
11810 // Check if we have active GetUserMedia use
11811 if (MediaManager::Exists() && win
&&
11812 MediaManager::Get()->IsWindowStillActive(win
->WindowID())) {
11813 MOZ_LOG(gPageCacheLog
, mozilla::LogLevel::Verbose
,
11814 ("Save of %s blocked due to GetUserMedia", uri
.get()));
11815 aBFCacheCombo
|= BFCacheStatus::ACTIVE_GET_USER_MEDIA
;
11820 // Check if we have active PeerConnections
11821 if (win
&& win
->HasActivePeerConnections()) {
11822 MOZ_LOG(gPageCacheLog
, mozilla::LogLevel::Verbose
,
11823 ("Save of %s blocked due to PeerConnection", uri
.get()));
11824 aBFCacheCombo
|= BFCacheStatus::ACTIVE_PEER_CONNECTION
;
11827 #endif // MOZ_WEBRTC
11829 // Don't save presentations for documents containing EME content, so that
11830 // CDMs reliably shutdown upon user navigation.
11831 if (ContainsEMEContent()) {
11832 aBFCacheCombo
|= BFCacheStatus::CONTAINS_EME_CONTENT
;
11836 // Don't save presentations for documents containing MSE content, to
11837 // reduce memory usage.
11838 if (ContainsMSEContent()) {
11839 MOZ_LOG(gPageCacheLog
, mozilla::LogLevel::Verbose
,
11840 ("Save of %s blocked due to MSE use", uri
.get()));
11841 aBFCacheCombo
|= BFCacheStatus::CONTAINS_MSE_CONTENT
;
11845 if (aIncludeSubdocuments
&& mSubDocuments
) {
11846 for (auto iter
= mSubDocuments
->Iter(); !iter
.Done(); iter
.Next()) {
11847 auto entry
= static_cast<SubDocMapEntry
*>(iter
.Get());
11848 Document
* subdoc
= entry
->mSubDocument
;
11850 uint32_t subDocBFCacheCombo
= 0;
11851 // The aIgnoreRequest we were passed is only for us, so don't pass it on.
11853 subdoc
? subdoc
->CanSavePresentation(nullptr, subDocBFCacheCombo
,
11854 true, allowUnloadListeners
)
11857 MOZ_LOG(gPageCacheLog
, mozilla::LogLevel::Verbose
,
11858 ("Save of %s blocked due to subdocument blocked", uri
.get()));
11859 aBFCacheCombo
|= subDocBFCacheCombo
;
11865 if (!mozilla::BFCacheInParent()) {
11866 // BFCache is currently not compatible with remote subframes (bug 1609324)
11867 if (RefPtr
<BrowsingContext
> browsingContext
= GetBrowsingContext()) {
11868 for (auto& child
: browsingContext
->Children()) {
11869 if (!child
->IsInProcess()) {
11870 aBFCacheCombo
|= BFCacheStatus::CONTAINS_REMOTE_SUBFRAMES
;
11879 auto* globalWindow
= nsGlobalWindowInner::Cast(win
);
11880 #ifdef MOZ_WEBSPEECH
11881 if (globalWindow
->HasActiveSpeechSynthesis()) {
11882 MOZ_LOG(gPageCacheLog
, mozilla::LogLevel::Verbose
,
11883 ("Save of %s blocked due to Speech use", uri
.get()));
11884 aBFCacheCombo
|= BFCacheStatus::HAS_ACTIVE_SPEECH_SYNTHESIS
;
11888 if (globalWindow
->HasUsedVR()) {
11889 MOZ_LOG(gPageCacheLog
, mozilla::LogLevel::Verbose
,
11890 ("Save of %s blocked due to having used VR", uri
.get()));
11891 aBFCacheCombo
|= BFCacheStatus::HAS_USED_VR
;
11895 if (win
->HasActiveLocks()) {
11897 gPageCacheLog
, mozilla::LogLevel::Verbose
,
11898 ("Save of %s blocked due to having active lock requests", uri
.get()));
11899 aBFCacheCombo
|= BFCacheStatus::ACTIVE_LOCK
;
11903 if (win
->HasActiveWebTransports()) {
11904 MOZ_LOG(gPageCacheLog
, mozilla::LogLevel::Verbose
,
11905 ("Save of %s blocked due to WebTransport", uri
.get()));
11906 aBFCacheCombo
|= BFCacheStatus::ACTIVE_WEBTRANSPORT
;
11914 void Document::Destroy() {
11915 // The DocumentViewer wants to release the document now. So, tell our content
11916 // to drop any references to the document so that it can be destroyed.
11917 if (mIsGoingAway
) {
11921 if (RefPtr transition
= mActiveViewTransition
) {
11922 transition
->SkipTransition(SkipTransitionReason::DocumentHidden
);
11925 ReportDocumentUseCounters();
11927 SetDevToolsWatchingDOMMutations(false);
11929 mIsGoingAway
= true;
11931 ScriptLoader()->Destroy();
11932 SetScriptGlobalObject(nullptr);
11933 RemovedFromDocShell();
11935 bool oldVal
= mInUnlinkOrDeletion
;
11936 mInUnlinkOrDeletion
= true;
11939 uint32_t oldChildCount
= GetChildCount();
11942 for (nsIContent
* child
= GetFirstChild(); child
;
11943 child
= child
->GetNextSibling()) {
11944 child
->DestroyContent();
11945 MOZ_ASSERT(child
->GetParentNode() == this);
11947 MOZ_ASSERT(oldChildCount
== GetChildCount());
11948 MOZ_ASSERT(!mSubDocuments
|| mSubDocuments
->EntryCount() == 0);
11950 mInUnlinkOrDeletion
= oldVal
;
11952 mLayoutHistoryState
= nullptr;
11954 if (mOriginalDocument
) {
11955 mOriginalDocument
->mLatestStaticClone
= nullptr;
11958 if (IsStaticDocument()) {
11959 RemoveProperty(nsGkAtoms::printisfocuseddoc
);
11960 RemoveProperty(nsGkAtoms::printselectionranges
);
11963 // Shut down our external resource map. We might not need this for
11964 // leak-fixing if we fix nsDocumentViewer to do cycle-collection, but
11965 // tearing down all those frame trees right now is the right thing to do.
11966 mExternalResourceMap
.Shutdown();
11968 // Manually break cycles via promise's global object pointer.
11969 mReadyForIdle
= nullptr;
11970 mOrientationPendingPromise
= nullptr;
11972 // To break cycles.
11973 mPreloadService
.ClearAllPreloads();
11975 if (mDocumentL10n
) {
11976 mDocumentL10n
->Destroy();
11984 void Document::RemovedFromDocShell() {
11985 mEditingState
= EditingState::eOff
;
11987 if (mRemovedFromDocShell
) return;
11989 mRemovedFromDocShell
= true;
11990 NotifyActivityChanged();
11992 for (nsIContent
* child
= GetFirstChild(); child
;
11993 child
= child
->GetNextSibling()) {
11994 child
->SaveSubtreeState();
11997 nsIDocShell
* docShell
= GetDocShell();
11999 docShell
->SynchronizeLayoutHistoryState();
12003 already_AddRefed
<nsILayoutHistoryState
> Document::GetLayoutHistoryState()
12005 nsCOMPtr
<nsILayoutHistoryState
> state
;
12006 if (!mScriptGlobalObject
) {
12007 state
= mLayoutHistoryState
;
12009 nsCOMPtr
<nsIDocShell
> docShell(mDocumentContainer
);
12011 docShell
->GetLayoutHistoryState(getter_AddRefs(state
));
12015 return state
.forget();
12018 void Document::EnsureOnloadBlocker() {
12019 // If mScriptGlobalObject is null, we shouldn't be messing with the loadgroup
12020 // -- it's not ours.
12021 if (mOnloadBlockCount
!= 0 && mScriptGlobalObject
) {
12022 nsCOMPtr
<nsILoadGroup
> loadGroup
= GetDocumentLoadGroup();
12024 // Check first to see if mOnloadBlocker is in the loadgroup.
12025 nsCOMPtr
<nsISimpleEnumerator
> requests
;
12026 loadGroup
->GetRequests(getter_AddRefs(requests
));
12028 bool hasMore
= false;
12029 while (NS_SUCCEEDED(requests
->HasMoreElements(&hasMore
)) && hasMore
) {
12030 nsCOMPtr
<nsISupports
> elem
;
12031 requests
->GetNext(getter_AddRefs(elem
));
12032 nsCOMPtr
<nsIRequest
> request
= do_QueryInterface(elem
);
12033 if (request
&& request
== mOnloadBlocker
) {
12038 // Not in the loadgroup, so add it.
12039 loadGroup
->AddRequest(mOnloadBlocker
, nullptr);
12044 void Document::BlockOnload() {
12045 if (mDisplayDocument
) {
12046 mDisplayDocument
->BlockOnload();
12050 // If mScriptGlobalObject is null, we shouldn't be messing with the loadgroup
12051 // -- it's not ours.
12052 if (mOnloadBlockCount
== 0 && mScriptGlobalObject
) {
12053 if (nsCOMPtr
<nsILoadGroup
> loadGroup
= GetDocumentLoadGroup()) {
12054 loadGroup
->AddRequest(mOnloadBlocker
, nullptr);
12057 ++mOnloadBlockCount
;
12060 void Document::UnblockOnload(bool aFireSync
) {
12061 if (mDisplayDocument
) {
12062 mDisplayDocument
->UnblockOnload(aFireSync
);
12066 --mOnloadBlockCount
;
12068 if (mOnloadBlockCount
== 0) {
12069 if (mScriptGlobalObject
) {
12070 // Only manipulate the loadgroup in this case, because if
12071 // mScriptGlobalObject is null, it's not ours.
12073 // Increment mOnloadBlockCount, since DoUnblockOnload will decrement it
12074 ++mOnloadBlockCount
;
12077 PostUnblockOnloadEvent();
12079 } else if (mIsBeingUsedAsImage
) {
12080 // To correctly unblock onload for a document that contains an SVG
12081 // image, we need to know when all of the SVG document's resources are
12082 // done loading, in a way comparable to |window.onload|. We fire this
12083 // event to indicate that the SVG should be considered fully loaded.
12084 // Because scripting is disabled on SVG-as-image documents, this event
12085 // is not accessible to content authors. (See bug 837315.)
12086 RefPtr
<AsyncEventDispatcher
> asyncDispatcher
=
12087 new AsyncEventDispatcher(this, u
"MozSVGAsImageDocumentLoad"_ns
,
12088 CanBubble::eNo
, ChromeOnlyDispatch::eNo
);
12089 asyncDispatcher
->PostDOMEvent();
12094 class nsUnblockOnloadEvent
: public Runnable
{
12096 explicit nsUnblockOnloadEvent(Document
* aDoc
)
12097 : mozilla::Runnable("nsUnblockOnloadEvent"), mDoc(aDoc
) {}
12098 NS_IMETHOD
Run() override
{
12099 mDoc
->DoUnblockOnload();
12104 RefPtr
<Document
> mDoc
;
12107 void Document::PostUnblockOnloadEvent() {
12108 MOZ_RELEASE_ASSERT(NS_IsMainThread());
12109 nsCOMPtr
<nsIRunnable
> evt
= new nsUnblockOnloadEvent(this);
12110 nsresult rv
= Dispatch(evt
.forget());
12111 if (NS_SUCCEEDED(rv
)) {
12112 // Stabilize block count so we don't post more events while this one is up
12113 ++mOnloadBlockCount
;
12115 NS_WARNING("failed to dispatch nsUnblockOnloadEvent");
12119 void Document::DoUnblockOnload() {
12120 MOZ_ASSERT(!mDisplayDocument
, "Shouldn't get here for resource document");
12121 MOZ_ASSERT(mOnloadBlockCount
!= 0,
12122 "Shouldn't have a count of zero here, since we stabilized in "
12123 "PostUnblockOnloadEvent");
12125 --mOnloadBlockCount
;
12127 if (mOnloadBlockCount
!= 0) {
12128 // We blocked again after the last unblock. Nothing to do here. We'll
12129 // post a new event when we unblock again.
12133 // If mScriptGlobalObject is null, we shouldn't be messing with the loadgroup
12134 // -- it's not ours.
12135 if (mScriptGlobalObject
) {
12136 if (nsCOMPtr
<nsILoadGroup
> loadGroup
= GetDocumentLoadGroup()) {
12137 loadGroup
->RemoveRequest(mOnloadBlocker
, nullptr, NS_OK
);
12142 nsIContent
* Document::GetContentInThisDocument(nsIFrame
* aFrame
) const {
12143 for (nsIFrame
* f
= aFrame
; f
;
12144 f
= nsLayoutUtils::GetParentOrPlaceholderForCrossDoc(f
)) {
12145 nsIContent
* content
= f
->GetContent();
12150 if (content
->OwnerDoc() == this) {
12153 // We must be in a subdocument so jump directly to the root frame.
12154 // GetParentOrPlaceholderForCrossDoc gets called immediately to jump up to
12155 // the containing document.
12156 f
= f
->PresContext()->GetPresShell()->GetRootFrame();
12162 void Document::DispatchPageTransition(EventTarget
* aDispatchTarget
,
12163 const nsAString
& aType
, bool aInFrameSwap
,
12164 bool aPersisted
, bool aOnlySystemGroup
) {
12165 if (!aDispatchTarget
) {
12169 PageTransitionEventInit init
;
12170 init
.mBubbles
= true;
12171 init
.mCancelable
= true;
12172 init
.mPersisted
= aPersisted
;
12173 init
.mInFrameSwap
= aInFrameSwap
;
12175 RefPtr
<PageTransitionEvent
> event
=
12176 PageTransitionEvent::Constructor(this, aType
, init
);
12178 event
->SetTrusted(true);
12179 event
->SetTarget(this);
12180 if (aOnlySystemGroup
) {
12181 event
->WidgetEventPtr()->mFlags
.mOnlySystemGroupDispatchInContent
= true;
12183 EventDispatcher::DispatchDOMEvent(aDispatchTarget
, nullptr, event
, nullptr,
12187 void Document::OnPageShow(bool aPersisted
, EventTarget
* aDispatchStartTarget
,
12188 bool aOnlySystemGroup
) {
12189 if (MOZ_LOG_TEST(gSHIPBFCacheLog
, LogLevel::Debug
)) {
12191 if (GetDocumentURI()) {
12192 uri
= GetDocumentURI()->GetSpecOrDefault();
12194 MOZ_LOG(gSHIPBFCacheLog
, LogLevel::Debug
,
12195 ("Document::OnPageShow [%s] persisted=%i", uri
.get(), aPersisted
));
12198 const bool inFrameLoaderSwap
= !!aDispatchStartTarget
;
12199 MOZ_DIAGNOSTIC_ASSERT(
12200 inFrameLoaderSwap
==
12201 (mDocumentContainer
&& mDocumentContainer
->InFrameSwap()));
12203 Element
* root
= GetRootElement();
12204 if (aPersisted
&& root
) {
12205 // Send out notifications that our <link> elements are attached.
12206 RefPtr
<nsContentList
> links
=
12207 NS_GetContentList(root
, kNameSpaceID_XHTML
, u
"link"_ns
);
12209 uint32_t linkCount
= links
->Length(true);
12210 for (uint32_t i
= 0; i
< linkCount
; ++i
) {
12211 static_cast<HTMLLinkElement
*>(links
->Item(i
, false))->LinkAdded();
12216 if (!inFrameLoaderSwap
) {
12218 ImageTracker()->SetAnimatingState(true);
12221 // Set mIsShowing before firing events, in case those event handlers
12226 UpdateVisibilityState();
12229 NotifyActivityChanged();
12231 EnumerateExternalResources([aPersisted
](Document
& aExternalResource
) {
12232 aExternalResource
.OnPageShow(aPersisted
, nullptr);
12233 return CallState::Continue
;
12236 if (mAnimationController
) {
12237 mAnimationController
->OnPageShow();
12240 if (!mIsBeingUsedAsImage
) {
12241 // Dispatch observer notification to notify observers page is shown.
12242 nsCOMPtr
<nsIObserverService
> os
= mozilla::services::GetObserverService();
12244 nsIPrincipal
* principal
= NodePrincipal();
12245 os
->NotifyObservers(ToSupports(this),
12246 principal
->IsSystemPrincipal() ? "chrome-page-shown"
12247 : "content-page-shown",
12251 nsCOMPtr
<EventTarget
> target
= aDispatchStartTarget
;
12253 target
= do_QueryInterface(GetWindow());
12255 DispatchPageTransition(target
, u
"pageshow"_ns
, inFrameLoaderSwap
,
12256 aPersisted
, aOnlySystemGroup
);
12260 static void DispatchFullscreenChange(Document
& aDocument
, nsINode
* aTarget
) {
12261 if (nsPresContext
* presContext
= aDocument
.GetPresContext()) {
12262 auto pendingEvent
= MakeUnique
<PendingFullscreenEvent
>(
12263 FullscreenEventType::Change
, &aDocument
, aTarget
);
12264 presContext
->RefreshDriver()->ScheduleFullscreenEvent(
12265 std::move(pendingEvent
));
12269 void Document::OnPageHide(bool aPersisted
, EventTarget
* aDispatchStartTarget
,
12270 bool aOnlySystemGroup
) {
12271 if (MOZ_LOG_TEST(gSHIPBFCacheLog
, LogLevel::Debug
)) {
12273 if (GetDocumentURI()) {
12274 uri
= GetDocumentURI()->GetSpecOrDefault();
12276 MOZ_LOG(gSHIPBFCacheLog
, LogLevel::Debug
,
12277 ("Document::OnPageHide %s persisted=%i", uri
.get(), aPersisted
));
12280 const bool inFrameLoaderSwap
= !!aDispatchStartTarget
;
12281 MOZ_DIAGNOSTIC_ASSERT(
12282 inFrameLoaderSwap
==
12283 (mDocumentContainer
&& mDocumentContainer
->InFrameSwap()));
12285 if (mAnimationController
) {
12286 mAnimationController
->OnPageHide();
12289 if (!inFrameLoaderSwap
) {
12291 // We do not stop the animations (bug 1024343) when the page is refreshing
12292 // while being dragged out.
12293 ImageTracker()->SetAnimatingState(false);
12296 // Set mIsShowing before firing events, in case those event handlers
12298 mIsShowing
= false;
12302 PointerLockManager::Unlock("Document::OnPageHide", this);
12304 if (!mIsBeingUsedAsImage
) {
12305 // Dispatch observer notification to notify observers page is hidden.
12306 nsCOMPtr
<nsIObserverService
> os
= mozilla::services::GetObserverService();
12308 nsIPrincipal
* principal
= NodePrincipal();
12309 os
->NotifyObservers(ToSupports(this),
12310 principal
->IsSystemPrincipal()
12311 ? "chrome-page-hidden"
12312 : "content-page-hidden",
12316 // Now send out a PageHide event.
12317 nsCOMPtr
<EventTarget
> target
= aDispatchStartTarget
;
12319 target
= do_QueryInterface(GetWindow());
12322 PageUnloadingEventTimeStamp
timeStamp(this);
12323 DispatchPageTransition(target
, u
"pagehide"_ns
, inFrameLoaderSwap
,
12324 aPersisted
, aOnlySystemGroup
);
12328 if (!inFrameLoaderSwap
) {
12329 UpdateVisibilityState();
12332 EnumerateExternalResources([aPersisted
](Document
& aExternalResource
) {
12333 aExternalResource
.OnPageHide(aPersisted
, nullptr);
12334 return CallState::Continue
;
12336 NotifyActivityChanged();
12338 ClearPendingFullscreenRequests(this);
12339 if (Fullscreen()) {
12340 // If this document was fullscreen, we should exit fullscreen in this
12341 // doctree branch. This ensures that if the user navigates while in
12342 // fullscreen mode we don't leave its still visible ancestor documents
12343 // in fullscreen mode. So exit fullscreen in the document's fullscreen
12344 // root document, as this will exit fullscreen in all the root's
12345 // descendant documents. Note that documents are removed from the
12346 // doctree by the time OnPageHide() is called, so we must store a
12347 // reference to the root (in Document::mFullscreenRoot) since we can't
12348 // just traverse the doctree to get the root.
12349 Document::ExitFullscreenInDocTree(this);
12351 // Since the document is removed from the doctree before OnPageHide() is
12352 // called, ExitFullscreen() can't traverse from the root down to *this*
12353 // document, so we must manually call CleanupFullscreenState() below too.
12354 // Note that CleanupFullscreenState() clears Document::mFullscreenRoot,
12355 // so we *must* call it after ExitFullscreen(), not before.
12356 // OnPageHide() is called in every hidden (i.e. descendant) document,
12357 // so calling CleanupFullscreenState() here will ensure all hidden
12358 // documents have their fullscreen state reset.
12359 CleanupFullscreenState();
12361 // The fullscreenchange event is to be queued in the refresh driver,
12362 // however a hidden page wouldn't trigger that again, so it makes no
12363 // sense to dispatch such event here.
12367 void Document::WillDispatchMutationEvent(nsINode
* aTarget
) {
12369 mSubtreeModifiedDepth
!= 0 || mSubtreeModifiedTargets
.Count() == 0,
12370 "mSubtreeModifiedTargets not cleared after dispatching?");
12371 ++mSubtreeModifiedDepth
;
12373 // MayDispatchMutationEvent is often called just before this method,
12374 // so it has already appended the node to mSubtreeModifiedTargets.
12375 int32_t count
= mSubtreeModifiedTargets
.Count();
12376 if (!count
|| mSubtreeModifiedTargets
[count
- 1] != aTarget
) {
12377 mSubtreeModifiedTargets
.AppendObject(aTarget
);
12382 void Document::MutationEventDispatched(nsINode
* aTarget
) {
12383 if (--mSubtreeModifiedDepth
) {
12387 int32_t count
= mSubtreeModifiedTargets
.Count();
12392 nsPIDOMWindowInner
* window
= GetInnerWindow();
12394 !window
->HasMutationListeners(NS_EVENT_BITS_MUTATION_SUBTREEMODIFIED
)) {
12395 mSubtreeModifiedTargets
.Clear();
12399 nsCOMArray
<nsINode
> realTargets
;
12400 for (nsINode
* possibleTarget
: mSubtreeModifiedTargets
) {
12401 if (possibleTarget
->ChromeOnlyAccess()) {
12405 nsINode
* commonAncestor
= nullptr;
12406 int32_t realTargetCount
= realTargets
.Count();
12407 for (int32_t j
= 0; j
< realTargetCount
; ++j
) {
12408 commonAncestor
= nsContentUtils::GetClosestCommonInclusiveAncestor(
12409 possibleTarget
, realTargets
[j
]);
12410 if (commonAncestor
) {
12411 realTargets
.ReplaceObjectAt(commonAncestor
, j
);
12415 if (!commonAncestor
) {
12416 realTargets
.AppendObject(possibleTarget
);
12420 mSubtreeModifiedTargets
.Clear();
12422 for (const nsCOMPtr
<nsINode
>& target
: realTargets
) {
12423 InternalMutationEvent
mutation(true, eLegacySubtreeModified
);
12424 // MOZ_KnownLive due to bug 1620312
12425 AsyncEventDispatcher::RunDOMEventWhenSafe(MOZ_KnownLive(*target
), mutation
);
12429 void Document::DestroyElementMaps() {
12431 mStyledLinksCleared
= true;
12433 mStyledLinks
.Clear();
12434 // Notify ID change listeners before clearing the identifier map.
12435 for (auto iter
= mIdentifierMap
.Iter(); !iter
.Done(); iter
.Next()) {
12436 iter
.Get()->ClearAndNotify();
12438 mIdentifierMap
.Clear();
12439 mComposedShadowRoots
.Clear();
12440 mResponsiveContent
.Clear();
12441 IncrementExpandoGeneration(*this);
12444 void Document::RefreshLinkHrefs() {
12445 // Get a list of all links we know about. We will reset them, which will
12446 // remove them from the document, so we need a copy of what is in the
12448 const nsTArray
<Link
*> linksToNotify
= ToArray(mStyledLinks
);
12450 // Reset all of our styled links.
12451 nsAutoScriptBlocker scriptBlocker
;
12452 for (Link
* link
: linksToNotify
) {
12453 link
->ResetLinkState(true);
12457 nsresult
Document::CloneDocHelper(Document
* clone
) const {
12458 clone
->mIsStaticDocument
= mCreatingStaticClone
;
12461 nsresult rv
= clone
->Init(NodePrincipal(), mPartitionedPrincipal
);
12462 NS_ENSURE_SUCCESS(rv
, rv
);
12464 if (mCreatingStaticClone
) {
12465 if (mOriginalDocument
) {
12466 clone
->mOriginalDocument
= mOriginalDocument
;
12468 clone
->mOriginalDocument
= const_cast<Document
*>(this);
12470 clone
->mOriginalDocument
->mLatestStaticClone
= clone
;
12471 clone
->mOriginalDocument
->mStaticCloneCount
++;
12473 nsCOMPtr
<nsILoadGroup
> loadGroup
;
12475 // |mDocumentContainer| is the container of the document that is being
12476 // created and not the original container. See CreateStaticClone function().
12477 nsCOMPtr
<nsIDocumentLoader
> docLoader(mDocumentContainer
);
12479 docLoader
->GetLoadGroup(getter_AddRefs(loadGroup
));
12481 nsCOMPtr
<nsIChannel
> channel
= GetChannel();
12482 nsCOMPtr
<nsIURI
> uri
;
12484 NS_GetFinalChannelURI(channel
, getter_AddRefs(uri
));
12486 uri
= Document::GetDocumentURI();
12488 clone
->mChannel
= channel
;
12489 clone
->mShouldResistFingerprinting
= mShouldResistFingerprinting
;
12491 clone
->ResetToURI(uri
, loadGroup
, NodePrincipal(), mPartitionedPrincipal
);
12494 clone
->mIsSrcdocDocument
= mIsSrcdocDocument
;
12495 clone
->SetContainer(mDocumentContainer
);
12497 // Setup the navigation time. This will be needed by any animations in the
12498 // document, even if they are only paused.
12499 MOZ_ASSERT(!clone
->GetNavigationTiming(),
12500 "Navigation time was already set?");
12502 RefPtr
<nsDOMNavigationTiming
> timing
=
12503 mTiming
->CloneNavigationTime(nsDocShell::Cast(clone
->GetDocShell()));
12504 clone
->SetNavigationTiming(timing
);
12506 clone
->SetCsp(mCSP
);
12509 // Now ensure that our clone has the same URI, base URI, and principal as us.
12510 // We do this after the mCreatingStaticClone block above, because that block
12511 // can set the base URI to an incorrect value in cases when base URI
12512 // information came from the channel. So we override explicitly, and do it
12513 // for all these properties, in case ResetToURI messes with any of the rest of
12515 clone
->SetDocumentURI(Document::GetDocumentURI());
12516 clone
->SetChromeXHRDocURI(mChromeXHRDocURI
);
12517 clone
->mActiveStoragePrincipal
= mActiveStoragePrincipal
;
12518 clone
->mActiveCookiePrincipal
= mActiveCookiePrincipal
;
12519 // NOTE(emilio): Intentionally setting this to the GetDocBaseURI rather than
12520 // just mDocumentBaseURI, so that srcdoc iframes get the right base URI even
12521 // when printed standalone via window.print() (where there won't be a parent
12522 // document to grab the URI from).
12523 clone
->mDocumentBaseURI
= GetDocBaseURI();
12524 clone
->SetChromeXHRDocBaseURI(mChromeXHRDocBaseURI
);
12525 clone
->mReferrerInfo
=
12526 static_cast<dom::ReferrerInfo
*>(mReferrerInfo
.get())->Clone();
12527 clone
->mPreloadReferrerInfo
= clone
->mReferrerInfo
;
12529 bool hasHadScriptObject
= true;
12530 nsIScriptGlobalObject
* scriptObject
=
12531 GetScriptHandlingObject(hasHadScriptObject
);
12532 NS_ENSURE_STATE(scriptObject
|| !hasHadScriptObject
);
12533 if (mCreatingStaticClone
) {
12534 // If we're doing a static clone (print, print preview), then we're going to
12535 // be setting a scope object after the clone. It's better to set it only
12536 // once, so we don't do that here. However, we do want to act as if there is
12537 // a script handling object. So we set mHasHadScriptHandlingObject.
12538 clone
->mHasHadScriptHandlingObject
= true;
12539 } else if (scriptObject
) {
12540 clone
->SetScriptHandlingObject(scriptObject
);
12542 clone
->SetScopeObject(GetScopeObject());
12544 // Make the clone a data document
12545 clone
->SetLoadedAsData(
12547 /* aConsiderForMemoryReporting */ !mCreatingStaticClone
);
12551 // State from Document
12552 clone
->mCharacterSet
= mCharacterSet
;
12553 clone
->mCharacterSetSource
= mCharacterSetSource
;
12554 clone
->SetCompatibilityMode(mCompatMode
);
12555 clone
->mBidiOptions
= mBidiOptions
;
12556 clone
->mContentLanguage
= mContentLanguage
;
12557 clone
->SetContentType(GetContentTypeInternal());
12558 clone
->mSecurityInfo
= mSecurityInfo
;
12560 // State from Document
12561 clone
->mType
= mType
;
12562 clone
->mXMLDeclarationBits
= mXMLDeclarationBits
;
12563 clone
->mBaseTarget
= mBaseTarget
;
12568 void Document::NotifyLoading(bool aNewParentIsLoading
,
12569 const ReadyState
& aCurrentState
,
12570 ReadyState aNewState
) {
12571 // Mirror the top-level loading state down to all subdocuments
12572 bool was_loading
= mAncestorIsLoading
||
12573 aCurrentState
== READYSTATE_LOADING
||
12574 aCurrentState
== READYSTATE_INTERACTIVE
;
12575 bool is_loading
= aNewParentIsLoading
|| aNewState
== READYSTATE_LOADING
||
12576 aNewState
== READYSTATE_INTERACTIVE
; // new value for state
12577 bool set_load_state
= was_loading
!= is_loading
;
12580 gTimeoutDeferralLog
, mozilla::LogLevel::Debug
,
12581 ("NotifyLoading for doc %p: currentAncestor: %d, newParent: %d, "
12582 "currentState %d newState: %d, was_loading: %d, is_loading: %d, "
12583 "set_load_state: %d",
12584 (void*)this, mAncestorIsLoading
, aNewParentIsLoading
, (int)aCurrentState
,
12585 (int)aNewState
, was_loading
, is_loading
, set_load_state
));
12587 mAncestorIsLoading
= aNewParentIsLoading
;
12588 if (set_load_state
&& StaticPrefs::dom_timeout_defer_during_load()) {
12589 // Tell our innerwindow (and thus TimeoutManager)
12590 nsPIDOMWindowInner
* inner
= GetInnerWindow();
12592 inner
->SetActiveLoadingState(is_loading
);
12594 BrowsingContext
* context
= GetBrowsingContext();
12596 // Don't use PreOrderWalk to mirror this down; go down one level as a
12597 // time so we can set mAncestorIsLoading and take into account the
12598 // readystates of the subdocument. In the child process it will call
12599 // NotifyLoading() to notify the innerwindow/TimeoutManager, and then
12600 // iterate it's children
12601 for (auto& child
: context
->Children()) {
12602 MOZ_LOG(gTimeoutDeferralLog
, mozilla::LogLevel::Debug
,
12603 ("bc: %p SetAncestorLoading(%d)", (void*)child
, is_loading
));
12604 // Setting ancestor loading on a discarded browsing context has no
12606 Unused
<< child
->SetAncestorLoading(is_loading
);
12612 void Document::SetReadyStateInternal(ReadyState aReadyState
,
12613 bool aUpdateTimingInformation
) {
12614 if (aReadyState
== READYSTATE_UNINITIALIZED
) {
12615 // Transition back to uninitialized happens only to keep assertions happy
12616 // right before readyState transitions to something else. Make this
12617 // transition undetectable by Web content.
12618 mReadyState
= aReadyState
;
12622 if (IsTopLevelContentDocument()) {
12623 if (aReadyState
== READYSTATE_LOADING
) {
12624 AddToplevelLoadingDocument(this);
12625 } else if (aReadyState
== READYSTATE_COMPLETE
) {
12626 RemoveToplevelLoadingDocument(this);
12630 if (aUpdateTimingInformation
&& READYSTATE_LOADING
== aReadyState
) {
12631 SetLoadingOrRestoredFromBFCacheTimeStampToNow();
12633 NotifyLoading(mAncestorIsLoading
, mReadyState
, aReadyState
);
12634 mReadyState
= aReadyState
;
12635 if (aUpdateTimingInformation
&& mTiming
) {
12636 switch (aReadyState
) {
12637 case READYSTATE_LOADING
:
12638 mTiming
->NotifyDOMLoading(GetDocumentURI());
12640 case READYSTATE_INTERACTIVE
:
12641 mTiming
->NotifyDOMInteractive(GetDocumentURI());
12643 case READYSTATE_COMPLETE
:
12644 mTiming
->NotifyDOMComplete(GetDocumentURI());
12647 MOZ_ASSERT_UNREACHABLE("Unexpected ReadyState value");
12651 // At the time of loading start, we don't have timing object, record time.
12653 if (READYSTATE_INTERACTIVE
== aReadyState
&&
12654 NodePrincipal()->IsSystemPrincipal()) {
12655 if (!mXULPersist
&& XRE_IsParentProcess()) {
12656 mXULPersist
= new XULPersist(this);
12657 mXULPersist
->Init();
12659 if (!mChromeObserver
) {
12660 mChromeObserver
= new ChromeObserver(this);
12661 mChromeObserver
->Init();
12665 if (aUpdateTimingInformation
) {
12666 RecordNavigationTiming(aReadyState
);
12669 AsyncEventDispatcher::RunDOMEventWhenSafe(
12670 *this, u
"readystatechange"_ns
, CanBubble::eNo
, ChromeOnlyDispatch::eNo
);
12673 void Document::GetReadyState(nsAString
& aReadyState
) const {
12674 switch (mReadyState
) {
12675 case READYSTATE_LOADING
:
12676 aReadyState
.AssignLiteral(u
"loading");
12678 case READYSTATE_INTERACTIVE
:
12679 aReadyState
.AssignLiteral(u
"interactive");
12681 case READYSTATE_COMPLETE
:
12682 aReadyState
.AssignLiteral(u
"complete");
12685 aReadyState
.AssignLiteral(u
"uninitialized");
12689 void Document::SuppressEventHandling(uint32_t aIncrease
) {
12690 mEventsSuppressed
+= aIncrease
;
12691 if (mEventsSuppressed
== aIncrease
) {
12692 if (WindowGlobalChild
* wgc
= GetWindowGlobalChild()) {
12693 wgc
->BlockBFCacheFor(BFCacheStatus::EVENT_HANDLING_SUPPRESSED
);
12696 for (uint32_t i
= 0; i
< aIncrease
; ++i
) {
12697 ScriptLoader()->AddExecuteBlocker();
12700 EnumerateSubDocuments([aIncrease
](Document
& aSubDoc
) {
12701 aSubDoc
.SuppressEventHandling(aIncrease
);
12702 return CallState::Continue
;
12706 void Document::NotifyAbortedLoad() {
12707 // If we still have outstanding work blocking DOMContentLoaded,
12708 // then don't try to change the readystate now, but wait until
12709 // they finish and then do so.
12710 if (mBlockDOMContentLoaded
> 0 && !mDidFireDOMContentLoaded
) {
12711 mSetCompleteAfterDOMContentLoaded
= true;
12715 // Otherwise we're fully done at this point, so set the
12716 // readystate to complete.
12717 if (GetReadyStateEnum() == Document::READYSTATE_INTERACTIVE
) {
12718 SetReadyStateInternal(Document::READYSTATE_COMPLETE
);
12722 MOZ_CAN_RUN_SCRIPT
static void FireOrClearDelayedEvents(
12723 nsTArray
<nsCOMPtr
<Document
>>&& aDocuments
, bool aFireEvents
) {
12724 RefPtr
<nsFocusManager
> fm
= nsFocusManager::GetFocusManager();
12725 if (MOZ_UNLIKELY(!fm
)) {
12729 nsTArray
<nsCOMPtr
<Document
>> documents
= std::move(aDocuments
);
12730 for (uint32_t i
= 0; i
< documents
.Length(); ++i
) {
12731 nsCOMPtr
<Document
> document
= std::move(documents
[i
]);
12732 // NB: Don't bother trying to fire delayed events on documents that were
12733 // closed before this event ran.
12734 if (!document
->EventHandlingSuppressed()) {
12735 fm
->FireDelayedEvents(document
);
12736 RefPtr
<PresShell
> presShell
= document
->GetPresShell();
12738 // Only fire events for active documents.
12739 bool fire
= aFireEvents
&& document
->GetInnerWindow() &&
12740 document
->GetInnerWindow()->IsCurrentInnerWindow();
12741 presShell
->FireOrClearDelayedEvents(fire
);
12743 document
->FireOrClearPostMessageEvents(aFireEvents
);
12748 void Document::PreloadPictureClosed() {
12749 MOZ_ASSERT(mPreloadPictureDepth
> 0);
12750 mPreloadPictureDepth
--;
12751 if (mPreloadPictureDepth
== 0) {
12752 mPreloadPictureFoundSource
.SetIsVoid(true);
12756 void Document::PreloadPictureImageSource(const nsAString
& aSrcsetAttr
,
12757 const nsAString
& aSizesAttr
,
12758 const nsAString
& aTypeAttr
,
12759 const nsAString
& aMediaAttr
) {
12760 // Nested pictures are not valid syntax, so while we'll eventually load them,
12761 // it's not worth tracking sources mixed between nesting levels to preload
12762 // them effectively.
12763 if (mPreloadPictureDepth
== 1 && mPreloadPictureFoundSource
.IsVoid()) {
12764 // <picture> selects the first matching source, so if this returns a URI we
12765 // needn't consider new sources until a new <picture> is encountered.
12766 bool found
= HTMLImageElement::SelectSourceForTagWithAttrs(
12767 this, true, VoidString(), aSrcsetAttr
, aSizesAttr
, aTypeAttr
,
12768 aMediaAttr
, mPreloadPictureFoundSource
);
12769 if (found
&& mPreloadPictureFoundSource
.IsVoid()) {
12770 // Found an empty source, which counts
12771 mPreloadPictureFoundSource
.SetIsVoid(false);
12776 already_AddRefed
<nsIURI
> Document::ResolvePreloadImage(
12777 nsIURI
* aBaseURI
, const nsAString
& aSrcAttr
, const nsAString
& aSrcsetAttr
,
12778 const nsAString
& aSizesAttr
, bool* aIsImgSet
) {
12779 nsString sourceURL
;
12781 if (mPreloadPictureDepth
== 1 && !mPreloadPictureFoundSource
.IsVoid()) {
12782 // We're in a <picture> element and found a URI from a source previous to
12783 // this image, use it.
12784 sourceURL
= mPreloadPictureFoundSource
;
12787 // Otherwise try to use this <img> as a source
12788 HTMLImageElement::SelectSourceForTagWithAttrs(
12789 this, false, aSrcAttr
, aSrcsetAttr
, aSizesAttr
, VoidString(),
12790 VoidString(), sourceURL
);
12791 isImgSet
= !aSrcsetAttr
.IsEmpty();
12794 // Empty sources are not loaded by <img> (i.e. not resolved to the baseURI)
12795 if (sourceURL
.IsEmpty()) {
12799 // Construct into URI using passed baseURI (the parser may know of base URI
12800 // changes that have not reached us)
12802 nsCOMPtr
<nsIURI
> uri
;
12803 rv
= nsContentUtils::NewURIWithDocumentCharset(getter_AddRefs(uri
), sourceURL
,
12805 if (NS_FAILED(rv
)) {
12809 *aIsImgSet
= isImgSet
;
12811 // We don't clear mPreloadPictureFoundSource because subsequent <img> tags in
12812 // this this <picture> share the same <sources> (though this is not valid per
12814 return uri
.forget();
12817 void Document::PreLoadImage(nsIURI
* aUri
, const nsAString
& aCrossOriginAttr
,
12818 ReferrerPolicyEnum aReferrerPolicy
, bool aIsImgSet
,
12819 bool aLinkPreload
, uint64_t aEarlyHintPreloaderId
,
12820 const nsAString
& aFetchPriority
) {
12821 nsLoadFlags loadFlags
= nsIRequest::LOAD_NORMAL
|
12822 nsContentUtils::CORSModeToLoadImageFlags(
12823 Element::StringToCORSMode(aCrossOriginAttr
));
12825 nsContentPolicyType policyType
=
12826 aIsImgSet
? nsIContentPolicy::TYPE_IMAGESET
12827 : nsIContentPolicy::TYPE_INTERNAL_IMAGE_PRELOAD
;
12829 nsCOMPtr
<nsIReferrerInfo
> referrerInfo
=
12830 ReferrerInfo::CreateFromDocumentAndPolicyOverride(this, aReferrerPolicy
);
12832 RefPtr
<imgRequestProxy
> request
;
12834 nsLiteralString initiator
= aEarlyHintPreloaderId
12835 ? u
"early-hints"_ns
12836 : (aLinkPreload
? u
"link"_ns
: u
"img"_ns
);
12838 nsresult rv
= nsContentUtils::LoadImage(
12839 aUri
, static_cast<nsINode
*>(this), this, NodePrincipal(), 0, referrerInfo
,
12840 nullptr /* no observer */, loadFlags
, initiator
, getter_AddRefs(request
),
12841 policyType
, false /* urgent */, aLinkPreload
, aEarlyHintPreloaderId
,
12842 nsGenericHTMLElement::ToFetchPriority(aFetchPriority
));
12844 // Pin image-reference to avoid evicting it from the img-cache before
12845 // the "real" load occurs. Unpinned in DispatchContentLoadedEvents and
12847 if (!aLinkPreload
&& NS_SUCCEEDED(rv
)) {
12848 mPreloadingImages
.InsertOrUpdate(aUri
, std::move(request
));
12852 void Document::MaybePreLoadImage(nsIURI
* aUri
,
12853 const nsAString
& aCrossOriginAttr
,
12854 ReferrerPolicyEnum aReferrerPolicy
,
12855 bool aIsImgSet
, bool aLinkPreload
,
12856 const nsAString
& aFetchPriority
) {
12857 const CORSMode corsMode
= dom::Element::StringToCORSMode(aCrossOriginAttr
);
12858 if (aLinkPreload
) {
12859 // Check if the image was already preloaded in this document to avoid
12860 // duplicate preloading.
12861 PreloadHashKey key
=
12862 PreloadHashKey::CreateAsImage(aUri
, NodePrincipal(), corsMode
);
12863 if (!mPreloadService
.PreloadExists(key
)) {
12864 PreLoadImage(aUri
, aCrossOriginAttr
, aReferrerPolicy
, aIsImgSet
,
12865 aLinkPreload
, 0, aFetchPriority
);
12870 // Early exit if the img is already present in the img-cache
12871 // which indicates that the "real" load has already started and
12872 // that we shouldn't preload it.
12873 if (nsContentUtils::IsImageAvailable(aUri
, NodePrincipal(), corsMode
, this)) {
12877 // Image not in cache - trigger preload
12878 PreLoadImage(aUri
, aCrossOriginAttr
, aReferrerPolicy
, aIsImgSet
, aLinkPreload
,
12879 0, aFetchPriority
);
12882 void Document::MaybePreconnect(nsIURI
* aOrigURI
, mozilla::CORSMode aCORSMode
) {
12883 if (!StaticPrefs::network_preconnect()) {
12887 NS_MutateURI
mutator(aOrigURI
);
12888 if (NS_FAILED(mutator
.GetStatus())) {
12892 // The URI created here is used in 2 contexts. One is nsISpeculativeConnect
12893 // which ignores the path and uses only the origin. The other is for the
12894 // document mPreloadedPreconnects de-duplication hash. Anonymous vs
12895 // non-Anonymous preconnects create different connections on the wire and
12896 // therefore should not be considred duplicates of each other and we
12897 // normalize the path before putting it in the hash to accomplish that.
12899 if (aCORSMode
== CORS_ANONYMOUS
) {
12900 mutator
.SetPathQueryRef("/anonymous"_ns
);
12902 mutator
.SetPathQueryRef("/"_ns
);
12905 nsCOMPtr
<nsIURI
> uri
;
12906 nsresult rv
= mutator
.Finalize(uri
);
12907 if (NS_FAILED(rv
)) {
12911 const bool existingEntryFound
=
12912 mPreloadedPreconnects
.WithEntryHandle(uri
, [](auto&& entry
) {
12916 entry
.Insert(true);
12919 if (existingEntryFound
) {
12923 nsCOMPtr
<nsISpeculativeConnect
> speculator
=
12924 mozilla::components::IO::Service();
12929 OriginAttributes oa
;
12930 StoragePrincipalHelper::GetOriginAttributesForNetworkState(this, oa
);
12931 speculator
->SpeculativeConnectWithOriginAttributesNative(
12932 uri
, std::move(oa
), nullptr, aCORSMode
== CORS_ANONYMOUS
);
12935 void Document::ForgetImagePreload(nsIURI
* aURI
) {
12936 // Checking count is faster than hashing the URI in the common
12937 // case of empty table.
12938 if (mPreloadingImages
.Count() != 0) {
12939 nsCOMPtr
<imgIRequest
> req
;
12940 mPreloadingImages
.Remove(aURI
, getter_AddRefs(req
));
12942 // Make sure to cancel the request so imagelib knows it's gone.
12943 req
->CancelAndForgetObserver(NS_BINDING_ABORTED
);
12948 void Document::UpdateDocumentStates(DocumentState aMaybeChangedStates
,
12950 const DocumentState oldStates
= mState
;
12951 if (aMaybeChangedStates
.HasAtLeastOneOfStates(
12952 DocumentState::ALL_LOCALEDIR_BITS
)) {
12953 mState
&= ~DocumentState::ALL_LOCALEDIR_BITS
;
12954 if (IsDocumentRightToLeft()) {
12955 mState
|= DocumentState::RTL_LOCALE
;
12957 mState
|= DocumentState::LTR_LOCALE
;
12961 if (aMaybeChangedStates
.HasState(DocumentState::WINDOW_INACTIVE
)) {
12962 BrowsingContext
* bc
= GetBrowsingContext();
12963 if (!bc
|| !bc
->GetIsActiveBrowserWindow()) {
12964 mState
|= DocumentState::WINDOW_INACTIVE
;
12966 mState
&= ~DocumentState::WINDOW_INACTIVE
;
12970 const DocumentState changedStates
= oldStates
^ mState
;
12971 if (aNotify
&& !changedStates
.IsEmpty()) {
12972 if (PresShell
* ps
= GetObservingPresShell()) {
12973 ps
->DocumentStatesChanged(changedStates
);
12981 * Stub for LoadSheet(), since all we want is to get the sheet into
12982 * the CSSLoader's style cache
12984 class StubCSSLoaderObserver final
: public nsICSSLoaderObserver
{
12985 ~StubCSSLoaderObserver() = default;
12989 StyleSheetLoaded(StyleSheet
*, bool, nsresult
) override
{ return NS_OK
; }
12992 NS_IMPL_ISUPPORTS(StubCSSLoaderObserver
, nsICSSLoaderObserver
)
12996 SheetPreloadStatus
Document::PreloadStyle(
12997 nsIURI
* uri
, const Encoding
* aEncoding
, const nsAString
& aCrossOriginAttr
,
12998 const enum ReferrerPolicy aReferrerPolicy
, const nsAString
& aNonce
,
12999 const nsAString
& aIntegrity
, css::StylePreloadKind aKind
,
13000 uint64_t aEarlyHintPreloaderId
, const nsAString
& aFetchPriority
) {
13001 MOZ_ASSERT(aKind
!= css::StylePreloadKind::None
);
13003 // The CSSLoader will retain this object after we return.
13004 nsCOMPtr
<nsICSSLoaderObserver
> obs
= new StubCSSLoaderObserver();
13006 nsCOMPtr
<nsIReferrerInfo
> referrerInfo
=
13007 ReferrerInfo::CreateFromDocumentAndPolicyOverride(this, aReferrerPolicy
);
13009 // Charset names are always ASCII.
13010 auto result
= CSSLoader()->LoadSheet(
13011 uri
, aKind
, aEncoding
, referrerInfo
, obs
, aEarlyHintPreloaderId
,
13012 Element::StringToCORSMode(aCrossOriginAttr
), aNonce
, aIntegrity
,
13013 nsGenericHTMLElement::ToFetchPriority(aFetchPriority
));
13014 if (result
.isErr()) {
13015 return SheetPreloadStatus::Errored
;
13017 RefPtr
<StyleSheet
> sheet
= result
.unwrap();
13018 if (sheet
->IsComplete()) {
13019 return SheetPreloadStatus::AlreadyComplete
;
13021 return SheetPreloadStatus::InProgress
;
13024 RefPtr
<StyleSheet
> Document::LoadChromeSheetSync(nsIURI
* uri
) {
13026 ->LoadSheetSync(uri
, css::eAuthorSheetFeatures
)
13027 .unwrapOr(nullptr);
13030 void Document::ResetDocumentDirection() {
13031 if (!nsContentUtils::IsChromeDoc(this)) {
13034 UpdateDocumentStates(DocumentState::ALL_LOCALEDIR_BITS
, true);
13037 bool Document::IsDocumentRightToLeft() {
13038 if (!nsContentUtils::IsChromeDoc(this)) {
13041 // setting the localedir attribute on the root element forces a
13042 // specific direction for the document.
13043 Element
* element
= GetRootElement();
13045 static Element::AttrValuesArray strings
[] = {nsGkAtoms::ltr
, nsGkAtoms::rtl
,
13047 switch (element
->FindAttrValueIn(kNameSpaceID_None
, nsGkAtoms::localedir
,
13048 strings
, eCaseMatters
)) {
13054 break; // otherwise, not a valid value, so fall through
13058 if (!mDocumentURI
->SchemeIs("chrome") && !mDocumentURI
->SchemeIs("about") &&
13059 !mDocumentURI
->SchemeIs("resource")) {
13063 return intl::LocaleService::GetInstance()->IsAppLocaleRTL();
13066 class nsDelayedEventDispatcher
: public Runnable
{
13068 explicit nsDelayedEventDispatcher(nsTArray
<nsCOMPtr
<Document
>>&& aDocuments
)
13069 : mozilla::Runnable("nsDelayedEventDispatcher"),
13070 mDocuments(std::move(aDocuments
)) {}
13071 virtual ~nsDelayedEventDispatcher() = default;
13073 // MOZ_CAN_RUN_SCRIPT_BOUNDARY until Runnable::Run is MOZ_CAN_RUN_SCRIPT. See
13075 MOZ_CAN_RUN_SCRIPT_BOUNDARY NS_IMETHOD
Run() override
{
13076 FireOrClearDelayedEvents(std::move(mDocuments
), true);
13081 nsTArray
<nsCOMPtr
<Document
>> mDocuments
;
13084 static void GetAndUnsuppressSubDocuments(
13085 Document
& aDocument
, nsTArray
<nsCOMPtr
<Document
>>& aDocuments
) {
13086 if (aDocument
.EventHandlingSuppressed() > 0) {
13087 aDocument
.DecreaseEventSuppression();
13088 aDocument
.ScriptLoader()->RemoveExecuteBlocker();
13090 aDocuments
.AppendElement(&aDocument
);
13091 aDocument
.EnumerateSubDocuments([&aDocuments
](Document
& aSubDoc
) {
13092 GetAndUnsuppressSubDocuments(aSubDoc
, aDocuments
);
13093 return CallState::Continue
;
13097 void Document::UnsuppressEventHandlingAndFireEvents(bool aFireEvents
) {
13098 nsTArray
<nsCOMPtr
<Document
>> documents
;
13099 GetAndUnsuppressSubDocuments(*this, documents
);
13101 for (nsCOMPtr
<Document
>& doc
: documents
) {
13102 if (!doc
->EventHandlingSuppressed()) {
13103 if (WindowGlobalChild
* wgc
= doc
->GetWindowGlobalChild()) {
13104 wgc
->UnblockBFCacheFor(BFCacheStatus::EVENT_HANDLING_SUPPRESSED
);
13107 MOZ_ASSERT(NS_IsMainThread());
13108 nsTArray
<RefPtr
<net::ChannelEventQueue
>> queues
=
13109 std::move(doc
->mSuspendedQueues
);
13110 for (net::ChannelEventQueue
* queue
: queues
) {
13114 // If there have been any events driven by the refresh driver which were
13115 // delayed due to events being suppressed in this document, make sure
13116 // there is a refresh scheduled soon so the events will run.
13117 if (doc
->mHasDelayedRefreshEvent
) {
13118 doc
->mHasDelayedRefreshEvent
= false;
13120 if (doc
->mPresShell
) {
13121 nsRefreshDriver
* rd
=
13122 doc
->mPresShell
->GetPresContext()->RefreshDriver();
13123 rd
->RunDelayedEventsSoon();
13130 MOZ_RELEASE_ASSERT(NS_IsMainThread());
13131 nsCOMPtr
<nsIRunnable
> ded
=
13132 new nsDelayedEventDispatcher(std::move(documents
));
13133 Dispatch(ded
.forget());
13135 FireOrClearDelayedEvents(std::move(documents
), false);
13139 bool Document::AreClipboardCommandsUnconditionallyEnabled() const {
13140 return IsHTMLOrXHTML() && !nsContentUtils::IsChromeDoc(this);
13143 void Document::AddSuspendedChannelEventQueue(net::ChannelEventQueue
* aQueue
) {
13144 MOZ_ASSERT(NS_IsMainThread());
13145 MOZ_ASSERT(EventHandlingSuppressed());
13146 mSuspendedQueues
.AppendElement(aQueue
);
13149 bool Document::SuspendPostMessageEvent(PostMessageEvent
* aEvent
) {
13150 MOZ_ASSERT(NS_IsMainThread());
13152 if (EventHandlingSuppressed() || !mSuspendedPostMessageEvents
.IsEmpty()) {
13153 mSuspendedPostMessageEvents
.AppendElement(aEvent
);
13159 void Document::FireOrClearPostMessageEvents(bool aFireEvents
) {
13160 nsTArray
<RefPtr
<PostMessageEvent
>> events
=
13161 std::move(mSuspendedPostMessageEvents
);
13164 for (PostMessageEvent
* event
: events
) {
13170 void Document::SetSuppressedEventListener(EventListener
* aListener
) {
13171 mSuppressedEventListener
= aListener
;
13172 EnumerateSubDocuments([&](Document
& aDocument
) {
13173 aDocument
.SetSuppressedEventListener(aListener
);
13174 return CallState::Continue
;
13178 bool Document::IsActive() const {
13179 return mDocumentContainer
&& !mRemovedFromDocShell
&& GetBrowsingContext() &&
13180 !GetBrowsingContext()->IsInBFCache();
13183 nsISupports
* Document::GetCurrentContentSink() {
13184 return mParser
? mParser
->GetContentSink() : nullptr;
13187 Document
* Document::GetTemplateContentsOwner() {
13188 if (!mTemplateContentsOwner
) {
13189 bool hasHadScriptObject
= true;
13190 nsIScriptGlobalObject
* scriptObject
=
13191 GetScriptHandlingObject(hasHadScriptObject
);
13193 nsCOMPtr
<Document
> document
;
13194 nsresult rv
= NS_NewDOMDocument(
13195 getter_AddRefs(document
),
13196 u
""_ns
, // aNamespaceURI
13197 u
""_ns
, // aQualifiedName
13198 nullptr, // aDoctype
13199 Document::GetDocumentURI(), Document::GetDocBaseURI(), NodePrincipal(),
13200 true, // aLoadedAsData
13201 scriptObject
, // aEventObject
13202 IsHTMLDocument() ? DocumentFlavorHTML
: DocumentFlavorXML
);
13203 NS_ENSURE_SUCCESS(rv
, nullptr);
13205 mTemplateContentsOwner
= document
;
13206 NS_ENSURE_TRUE(mTemplateContentsOwner
, nullptr);
13208 if (!scriptObject
) {
13209 mTemplateContentsOwner
->SetScopeObject(GetScopeObject());
13212 mTemplateContentsOwner
->mHasHadScriptHandlingObject
= hasHadScriptObject
;
13214 // Set |mTemplateContentsOwner| as the template contents owner of itself so
13215 // that it is the template contents owner of nested template elements.
13216 mTemplateContentsOwner
->mTemplateContentsOwner
= mTemplateContentsOwner
;
13219 MOZ_ASSERT(mTemplateContentsOwner
->IsTemplateContentsOwner());
13220 return mTemplateContentsOwner
;
13223 // https://html.spec.whatwg.org/#the-autofocus-attribute
13224 void Document::ElementWithAutoFocusInserted(Element
* aAutoFocusCandidate
) {
13225 BrowsingContext
* bc
= GetBrowsingContext();
13230 // If target is not fully active, then return.
13231 if (!IsCurrentActiveDocument()) {
13235 // If target's active sandboxing flag set has the sandboxed automatic features
13236 // browsing context flag, then return.
13237 if (GetSandboxFlags() & SANDBOXED_AUTOMATIC_FEATURES
) {
13241 // For each ancestorBC of target's browsing context's ancestor browsing
13242 // contexts: if ancestorBC's active document's origin is not same origin with
13243 // target's origin, then return.
13245 BrowsingContext
* parent
= bc
->GetParent();
13249 // AncestorBC is not the same site
13250 if (!parent
->IsInProcess()) {
13254 Document
* currentDocument
= bc
->GetDocument();
13255 if (!currentDocument
) {
13259 Document
* parentDocument
= parent
->GetDocument();
13260 if (!parentDocument
) {
13265 if (!currentDocument
->NodePrincipal()->Equals(
13266 parentDocument
->NodePrincipal())) {
13272 MOZ_ASSERT(bc
->IsTop());
13274 Document
* topDocument
= bc
->GetDocument();
13275 MOZ_ASSERT(topDocument
);
13276 topDocument
->AppendAutoFocusCandidateToTopDocument(aAutoFocusCandidate
);
13279 void Document::ScheduleFlushAutoFocusCandidates() {
13280 MOZ_ASSERT(mPresShell
&& mPresShell
->DidInitialize());
13281 MOZ_ASSERT(GetBrowsingContext()->IsTop());
13282 if (nsRefreshDriver
* rd
= mPresShell
->GetRefreshDriver()) {
13283 rd
->ScheduleAutoFocusFlush(this);
13287 void Document::AppendAutoFocusCandidateToTopDocument(
13288 Element
* aAutoFocusCandidate
) {
13289 MOZ_ASSERT(GetBrowsingContext()->IsTop());
13290 if (mAutoFocusFired
) {
13294 if (!HasAutoFocusCandidates()) {
13295 // PresShell may be initialized later
13296 if (mPresShell
&& mPresShell
->DidInitialize()) {
13297 ScheduleFlushAutoFocusCandidates();
13301 nsWeakPtr element
= do_GetWeakReference(aAutoFocusCandidate
);
13302 mAutoFocusCandidates
.RemoveElement(element
);
13303 mAutoFocusCandidates
.AppendElement(element
);
13306 void Document::SetAutoFocusFired() {
13307 mAutoFocusCandidates
.Clear();
13308 mAutoFocusFired
= true;
13311 // https://html.spec.whatwg.org/#flush-autofocus-candidates
13312 void Document::FlushAutoFocusCandidates() {
13313 MOZ_ASSERT(GetBrowsingContext()->IsTop());
13314 if (mAutoFocusFired
) {
13322 MOZ_ASSERT(HasAutoFocusCandidates());
13323 MOZ_ASSERT(mPresShell
->DidInitialize());
13325 nsCOMPtr
<nsPIDOMWindowOuter
> topWindow
= GetWindow();
13326 // We should be the top document
13333 // Trying to find the top window (equivalent to window.top).
13334 nsCOMPtr
<nsPIDOMWindowOuter
> top
= topWindow
->GetInProcessTop();
13335 MOZ_ASSERT(topWindow
== top
);
13339 // Don't steal the focus from the user
13340 if (topWindow
->GetFocusedElement()) {
13341 SetAutoFocusFired();
13345 MOZ_ASSERT(mDocumentURI
);
13347 // GetRef never fails
13348 nsresult rv
= mDocumentURI
->GetRef(ref
);
13349 if (NS_SUCCEEDED(rv
) &&
13350 nsContentUtils::GetTargetElement(this, NS_ConvertUTF8toUTF16(ref
))) {
13351 SetAutoFocusFired();
13355 nsTObserverArray
<nsWeakPtr
>::ForwardIterator
iter(mAutoFocusCandidates
);
13356 while (iter
.HasMore()) {
13357 nsCOMPtr
<Element
> autoFocusElement
= do_QueryReferent(iter
.GetNext());
13358 if (!autoFocusElement
) {
13361 RefPtr
<Document
> autoFocusElementDoc
= autoFocusElement
->OwnerDoc();
13362 // Get the latest info about the frame and allow scripts
13363 // to run which might affect the focusability of this element.
13364 autoFocusElementDoc
->FlushPendingNotifications(FlushType::Frames
);
13366 // Above layout flush may cause the PresShell to disappear.
13371 // Re-get the element because the ownerDoc() might have changed
13372 autoFocusElementDoc
= autoFocusElement
->OwnerDoc();
13373 BrowsingContext
* bc
= autoFocusElementDoc
->GetBrowsingContext();
13378 // If doc is not fully active, then remove element from candidates, and
13380 if (!autoFocusElementDoc
->IsCurrentActiveDocument()) {
13385 nsCOMPtr
<nsIContentSink
> sink
=
13386 do_QueryInterface(autoFocusElementDoc
->GetCurrentContentSink());
13388 nsHtml5TreeOpExecutor
* executor
=
13389 static_cast<nsHtml5TreeOpExecutor
*>(sink
->AsExecutor());
13391 // This is a HTML5 document
13392 MOZ_ASSERT(autoFocusElementDoc
->IsHTMLDocument());
13393 // If doc's script-blocking style sheet counter is greater than 0, th
13395 if (executor
->WaitForPendingSheets()) {
13396 // In this case, element is the currently-best candidate, but doc is
13397 // not ready for autofocusing. We'll try again next time flush
13398 // autofocus candidates is called.
13399 ScheduleFlushAutoFocusCandidates();
13405 // The autofocus element could be moved to a different
13407 if (bc
->Top()->GetDocument() != this) {
13413 // Let inclusiveAncestorDocuments be a list consisting of doc, plus the
13414 // active documents of each of doc's browsing context's ancestor browsing
13416 // If any Document in inclusiveAncestorDocuments has non-null target
13417 // element, then continue.
13418 bool shouldFocus
= true;
13420 Document
* doc
= bc
->GetDocument();
13422 shouldFocus
= false;
13426 nsIURI
* uri
= doc
->GetDocumentURI();
13428 shouldFocus
= false;
13433 nsresult rv
= uri
->GetRef(ref
);
13434 // If there is an element in the document tree that has an ID equal to
13436 if (NS_SUCCEEDED(rv
) &&
13437 nsContentUtils::GetTargetElement(doc
, NS_ConvertUTF8toUTF16(ref
))) {
13438 shouldFocus
= false;
13441 bc
= bc
->GetParent();
13444 if (!shouldFocus
) {
13448 MOZ_ASSERT(topWindow
);
13449 if (TryAutoFocusCandidate(*autoFocusElement
)) {
13450 // We've successfully autofocused an element, don't
13451 // need to try to focus the rest.
13452 SetAutoFocusFired();
13457 if (HasAutoFocusCandidates()) {
13458 ScheduleFlushAutoFocusCandidates();
13462 bool Document::TryAutoFocusCandidate(Element
& aElement
) {
13463 const FocusOptions options
;
13464 if (RefPtr
<Element
> target
= nsFocusManager::GetTheFocusableArea(
13465 &aElement
, nsFocusManager::ProgrammaticFocusFlags(options
))) {
13466 target
->Focus(options
, CallerType::NonSystem
, IgnoreErrors());
13473 void Document::SetScrollToRef(nsIURI
* aDocumentURI
) {
13474 if (!aDocumentURI
) {
13480 // Since all URI's that pass through here aren't URL's we can't
13481 // rely on the nsIURI implementation for providing a way for
13482 // finding the 'ref' part of the URI, we'll haveto revert to
13483 // string routines for finding the data past '#'
13485 nsresult rv
= aDocumentURI
->GetSpec(ref
);
13486 if (NS_FAILED(rv
)) {
13487 Unused
<< aDocumentURI
->GetRef(mScrollToRef
);
13491 nsReadingIterator
<char> start
, end
;
13493 ref
.BeginReading(start
);
13494 ref
.EndReading(end
);
13496 if (FindCharInReadable('#', start
, end
)) {
13497 ++start
; // Skip over the '#'
13499 mScrollToRef
= Substring(start
, end
);
13503 // https://html.spec.whatwg.org/#scrolling-to-a-fragment
13504 void Document::ScrollToRef() {
13505 RefPtr
<PresShell
> presShell
= GetPresShell();
13510 // https://wicg.github.io/scroll-to-text-fragment/#invoking-text-directives
13511 // Monkeypatching HTML § 7.4.6.3 Scrolling to a fragment:
13512 // 1. Let text directives be the document's pending text directives.
13513 const RefPtr fragmentDirective
= FragmentDirective();
13514 const nsTArray
<RefPtr
<nsRange
>> textDirectives
=
13515 fragmentDirective
->FindTextFragmentsInDocument();
13516 // 2. If ranges is non-empty, then:
13517 // 2.1 Let firstRange be the first item of ranges
13518 const RefPtr
<nsRange
> textDirectiveToScroll
=
13519 !textDirectives
.IsEmpty() ? textDirectives
[0] : nullptr;
13520 // 2.2 Visually indicate each range in ranges in an implementation-defined
13521 // way. The indication must not be observable from author script. See § 3.7
13522 // Indicating The Text Match.
13523 fragmentDirective
->HighlightTextDirectives(textDirectives
);
13525 // In a subsequent call to `ScrollToRef()` during page load, `textDirectives`
13526 // would only contain text directives that were not found in the previous
13527 // runs. If an earlier call during the same page load already found a text
13528 // directive to scroll to, only highlighting of the text directives needs to
13530 // This is indicated by `mScrolledToRefAlready`.
13531 if (mScrolledToRefAlready
) {
13532 presShell
->ScrollToAnchor();
13535 // 2. If fragment is the empty string and no text directives have been
13536 // scrolled to, then return the special value top of the document.
13537 if (!textDirectiveToScroll
&& mScrollToRef
.IsEmpty()) {
13541 // TODO(mccr8): This will incorrectly block scrolling from a same-document
13542 // navigation triggered before the document is full loaded. See bug 1898630.
13543 if (ForceLoadAtTop()) {
13547 // 3. Let potentialIndicatedElement be the result of finding a potential
13548 // indicated element given document and fragment.
13549 NS_ConvertUTF8toUTF16
ref(mScrollToRef
);
13550 // This also covers 2.3 of the Monkeypatch for text fragments mentioned above:
13551 // 2.3 Set firstRange as document's indicated part, return.
13553 const bool scrollToTextDirective
=
13554 textDirectiveToScroll
13555 ? fragmentDirective
->IsTextDirectiveAllowedToBeScrolledTo()
13556 : mChangeScrollPosWhenScrollingToRef
;
13559 presShell
->GoToAnchor(ref
, textDirectiveToScroll
, scrollToTextDirective
);
13561 // 4. If potentialIndicatedElement is not null, then return
13562 // potentialIndicatedElement.
13563 if (NS_SUCCEEDED(rv
)) {
13564 mScrolledToRefAlready
= true;
13568 // 5. Let fragmentBytes be the result of percent-decoding fragment.
13569 nsAutoCString fragmentBytes
;
13570 const bool unescaped
=
13571 NS_UnescapeURL(mScrollToRef
.Data(), mScrollToRef
.Length(),
13572 /* aFlags = */ 0, fragmentBytes
);
13574 if (!unescaped
|| fragmentBytes
.IsEmpty()) {
13575 // Another attempt is only necessary if characters were unescaped.
13579 // 6. Let decodedFragment be the result of running UTF-8 decode without BOM on
13581 nsAutoString decodedFragment
;
13582 rv
= UTF_8_ENCODING
->DecodeWithoutBOMHandling(fragmentBytes
, decodedFragment
);
13583 NS_ENSURE_SUCCESS_VOID(rv
);
13585 // 7. Set potentialIndicatedElement to the result of finding a potential
13586 // indicated element given document and decodedFragment.
13587 rv
= presShell
->GoToAnchor(decodedFragment
, nullptr,
13588 mChangeScrollPosWhenScrollingToRef
);
13589 if (NS_SUCCEEDED(rv
)) {
13590 mScrolledToRefAlready
= true;
13594 void Document::RegisterActivityObserver(nsISupports
* aSupports
) {
13595 if (!mActivityObservers
) {
13596 mActivityObservers
= MakeUnique
<nsTHashSet
<nsISupports
*>>();
13598 mActivityObservers
->Insert(aSupports
);
13601 bool Document::UnregisterActivityObserver(nsISupports
* aSupports
) {
13602 if (!mActivityObservers
) {
13605 return mActivityObservers
->EnsureRemoved(aSupports
);
13608 void Document::EnumerateActivityObservers(
13609 ActivityObserverEnumerator aEnumerator
) {
13610 if (!mActivityObservers
) {
13614 const auto keyArray
=
13615 ToTArray
<nsTArray
<nsCOMPtr
<nsISupports
>>>(*mActivityObservers
);
13616 for (auto& observer
: keyArray
) {
13617 aEnumerator(observer
.get());
13621 void Document::RegisterPendingLinkUpdate(Link
* aLink
) {
13622 if (aLink
->HasPendingLinkUpdate()) {
13626 aLink
->SetHasPendingLinkUpdate();
13628 if (!mHasLinksToUpdateRunnable
&& !mFlushingPendingLinkUpdates
) {
13629 nsCOMPtr
<nsIRunnable
> event
=
13630 NewRunnableMethod("Document::FlushPendingLinkUpdates", this,
13631 &Document::FlushPendingLinkUpdates
);
13632 // Do this work in a second in the worst case.
13633 nsresult rv
= NS_DispatchToCurrentThreadQueue(event
.forget(), 1000,
13634 EventQueuePriority::Idle
);
13635 if (NS_FAILED(rv
)) {
13636 // If during shutdown posting a runnable doesn't succeed, we probably
13637 // don't need to update link states.
13640 mHasLinksToUpdateRunnable
= true;
13643 mLinksToUpdate
.InfallibleAppend(aLink
);
13646 void Document::FlushPendingLinkUpdates() {
13647 MOZ_DIAGNOSTIC_ASSERT(!mFlushingPendingLinkUpdates
);
13648 MOZ_ASSERT(mHasLinksToUpdateRunnable
);
13649 mHasLinksToUpdateRunnable
= false;
13651 auto restore
= MakeScopeExit([&] { mFlushingPendingLinkUpdates
= false; });
13652 mFlushingPendingLinkUpdates
= true;
13654 while (!mLinksToUpdate
.IsEmpty()) {
13655 LinksToUpdateList
links(std::move(mLinksToUpdate
));
13656 for (auto iter
= links
.Iter(); !iter
.Done(); iter
.Next()) {
13657 Link
* link
= iter
.Get();
13658 Element
* element
= link
->GetElement();
13659 if (element
->OwnerDoc() == this) {
13660 link
->ClearHasPendingLinkUpdate();
13661 if (element
->IsInComposedDoc()) {
13662 link
->TriggerLinkUpdate(/* aNotify = */ true);
13670 * Retrieves the node in a static-clone document that corresponds to aOrigNode,
13671 * which is a node in the original document from which aStaticClone was cloned.
13673 static nsINode
* GetCorrespondingNodeInDocument(const nsINode
* aOrigNode
,
13674 Document
& aStaticClone
) {
13675 MOZ_ASSERT(aOrigNode
);
13677 // Selections in anonymous subtrees aren't supported.
13678 if (NS_WARN_IF(aOrigNode
->IsInNativeAnonymousSubtree())) {
13682 // If the node is disconnected, this is a bug in the selection code, but it
13683 // can happen with shadow DOM so handle it.
13684 if (NS_WARN_IF(!aOrigNode
->IsInComposedDoc())) {
13688 AutoTArray
<Maybe
<uint32_t>, 32> indexArray
;
13689 const nsINode
* current
= aOrigNode
;
13690 while (const nsINode
* parent
= current
->GetParentNode()) {
13691 Maybe
<uint32_t> index
= parent
->ComputeIndexOf(current
);
13692 NS_ENSURE_TRUE(index
.isSome(), nullptr);
13693 indexArray
.AppendElement(std::move(index
));
13696 MOZ_ASSERT(current
->IsDocument() || current
->IsShadowRoot());
13697 nsINode
* correspondingNode
= [&]() -> nsINode
* {
13698 if (current
->IsDocument()) {
13699 return &aStaticClone
;
13701 const auto* shadow
= ShadowRoot::FromNode(*current
);
13705 nsINode
* correspondingHost
=
13706 GetCorrespondingNodeInDocument(shadow
->Host(), aStaticClone
);
13707 if (NS_WARN_IF(!correspondingHost
|| !correspondingHost
->IsElement())) {
13710 return correspondingHost
->AsElement()->GetShadowRoot();
13713 if (NS_WARN_IF(!correspondingNode
)) {
13716 for (const Maybe
<uint32_t>& index
: Reversed(indexArray
)) {
13717 correspondingNode
= correspondingNode
->GetChildAt_Deprecated(*index
);
13718 NS_ENSURE_TRUE(correspondingNode
, nullptr);
13720 return correspondingNode
;
13724 * Caches the selection ranges from the source document onto the static clone in
13725 * case the "Print Selection Only" functionality is invoked.
13727 * Note that we cannot use the selection obtained from GetOriginalDocument()
13728 * since that selection may have mutated after the print was invoked.
13730 * Note also that because nsRange objects point into a specific document's
13731 * nodes, we cannot reuse an array of nsRange objects across multiple static
13732 * clone documents. For that reason we cache a new array of ranges on each
13733 * static clone that we create.
13735 * TODO(emilio): This can be simplified once we don't re-clone from static
13738 * @param aSourceDoc the document from which we are caching selection ranges
13739 * @param aStaticClone the document that will hold the cache
13740 * @return true if a selection range was cached
13742 static void CachePrintSelectionRanges(const Document
& aSourceDoc
,
13743 Document
& aStaticClone
) {
13744 MOZ_ASSERT(aStaticClone
.IsStaticDocument());
13745 MOZ_ASSERT(!aStaticClone
.GetProperty(nsGkAtoms::printisfocuseddoc
));
13746 MOZ_ASSERT(!aStaticClone
.GetProperty(nsGkAtoms::printselectionranges
));
13748 bool sourceDocIsStatic
= aSourceDoc
.IsStaticDocument();
13750 // When the user opts to "Print Selection Only", the print code prefers any
13751 // selection in the static clone corresponding to the focused frame. If this
13752 // is that static clone, flag it for the printing code:
13753 const bool isFocusedDoc
= [&] {
13754 if (sourceDocIsStatic
) {
13755 return bool(aSourceDoc
.GetProperty(nsGkAtoms::printisfocuseddoc
));
13757 nsPIDOMWindowOuter
* window
= aSourceDoc
.GetWindow();
13761 nsCOMPtr
<nsPIDOMWindowOuter
> rootWindow
= window
->GetPrivateRoot();
13765 nsCOMPtr
<nsPIDOMWindowOuter
> focusedWindow
;
13766 nsFocusManager::GetFocusedDescendant(rootWindow
,
13767 nsFocusManager::eIncludeAllDescendants
,
13768 getter_AddRefs(focusedWindow
));
13769 return focusedWindow
&& focusedWindow
->GetExtantDoc() == &aSourceDoc
;
13771 if (isFocusedDoc
) {
13772 aStaticClone
.SetProperty(nsGkAtoms::printisfocuseddoc
,
13773 reinterpret_cast<void*>(true));
13776 const Selection
* origSelection
= nullptr;
13777 const nsTArray
<RefPtr
<nsRange
>>* origRanges
= nullptr;
13779 if (sourceDocIsStatic
) {
13780 origRanges
= static_cast<nsTArray
<RefPtr
<nsRange
>>*>(
13781 aSourceDoc
.GetProperty(nsGkAtoms::printselectionranges
));
13782 } else if (PresShell
* shell
= aSourceDoc
.GetPresShell()) {
13783 origSelection
= shell
->GetCurrentSelection(SelectionType::eNormal
);
13786 if (!origSelection
&& !origRanges
) {
13790 const uint32_t rangeCount
=
13791 sourceDocIsStatic
? origRanges
->Length() : origSelection
->RangeCount();
13792 auto printRanges
= MakeUnique
<nsTArray
<RefPtr
<nsRange
>>>(rangeCount
);
13794 for (const uint32_t i
: IntegerRange(rangeCount
)) {
13795 MOZ_ASSERT_IF(!sourceDocIsStatic
,
13796 origSelection
->RangeCount() == rangeCount
);
13797 const nsRange
* range
= sourceDocIsStatic
? origRanges
->ElementAt(i
).get()
13798 : origSelection
->GetRangeAt(i
);
13800 nsINode
* startContainer
= range
->GetMayCrossShadowBoundaryStartContainer();
13801 nsINode
* endContainer
= range
->GetMayCrossShadowBoundaryEndContainer();
13803 if (!startContainer
|| !endContainer
) {
13807 nsINode
* startNode
=
13808 GetCorrespondingNodeInDocument(startContainer
, aStaticClone
);
13810 GetCorrespondingNodeInDocument(endContainer
, aStaticClone
);
13812 if (NS_WARN_IF(!startNode
|| !endNode
)) {
13816 RefPtr
<nsRange
> clonedRange
= nsRange::Create(
13817 startNode
, range
->MayCrossShadowBoundaryStartOffset(), endNode
,
13818 range
->MayCrossShadowBoundaryEndOffset(), IgnoreErrors());
13820 !clonedRange
->AreNormalRangeAndCrossShadowBoundaryRangeCollapsed()) {
13821 printRanges
->AppendElement(std::move(clonedRange
));
13825 if (printRanges
->IsEmpty()) {
13829 aStaticClone
.SetProperty(nsGkAtoms::printselectionranges
,
13830 printRanges
.release(),
13831 nsINode::DeleteProperty
<nsTArray
<RefPtr
<nsRange
>>>);
13834 already_AddRefed
<Document
> Document::CreateStaticClone(
13835 nsIDocShell
* aCloneContainer
, nsIDocumentViewer
* aViewer
,
13836 nsIPrintSettings
* aPrintSettings
, bool* aOutHasInProcessPrintCallbacks
) {
13837 MOZ_ASSERT(!mCreatingStaticClone
);
13838 MOZ_ASSERT(!GetProperty(nsGkAtoms::adoptedsheetclones
));
13839 MOZ_DIAGNOSTIC_ASSERT(aViewer
);
13841 mCreatingStaticClone
= true;
13842 SetProperty(nsGkAtoms::adoptedsheetclones
, new AdoptedStyleSheetCloneCache(),
13843 nsINode::DeleteProperty
<AdoptedStyleSheetCloneCache
>);
13845 auto raii
= MakeScopeExit([&] {
13846 RemoveProperty(nsGkAtoms::adoptedsheetclones
);
13847 mCreatingStaticClone
= false;
13850 // Make document use different container during cloning.
13852 // FIXME(emilio): Why is this needed?
13853 RefPtr
<nsDocShell
> originalShell
= mDocumentContainer
.get();
13854 SetContainer(nsDocShell::Cast(aCloneContainer
));
13855 IgnoredErrorResult rv
;
13856 nsCOMPtr
<nsINode
> clonedNode
= CloneNode(true, rv
);
13857 SetContainer(originalShell
);
13862 nsCOMPtr
<Document
> clonedDoc
= do_QueryInterface(clonedNode
);
13867 size_t sheetsCount
= SheetCount();
13868 for (size_t i
= 0; i
< sheetsCount
; ++i
) {
13869 RefPtr
<StyleSheet
> sheet
= SheetAt(i
);
13871 if (sheet
->IsApplicable()) {
13872 RefPtr
<StyleSheet
> clonedSheet
= sheet
->Clone(nullptr, clonedDoc
);
13873 NS_WARNING_ASSERTION(clonedSheet
, "Cloning a stylesheet didn't work!");
13875 clonedDoc
->AddStyleSheet(clonedSheet
);
13880 clonedDoc
->CloneAdoptedSheetsFrom(*this);
13882 for (int t
= 0; t
< AdditionalSheetTypeCount
; ++t
) {
13883 auto& sheets
= mAdditionalSheets
[additionalSheetType(t
)];
13884 for (StyleSheet
* sheet
: sheets
) {
13885 if (sheet
->IsApplicable()) {
13886 RefPtr
<StyleSheet
> clonedSheet
= sheet
->Clone(nullptr, clonedDoc
);
13887 NS_WARNING_ASSERTION(clonedSheet
, "Cloning a stylesheet didn't work!");
13889 clonedDoc
->AddAdditionalStyleSheet(additionalSheetType(t
),
13896 // Font faces created with the JS API will not be reflected in the
13897 // stylesheets and need to be copied over to the cloned document.
13898 if (const FontFaceSet
* set
= GetFonts()) {
13899 set
->CopyNonRuleFacesTo(clonedDoc
->Fonts());
13902 clonedDoc
->mReferrerInfo
=
13903 static_cast<dom::ReferrerInfo
*>(mReferrerInfo
.get())->Clone();
13904 clonedDoc
->mPreloadReferrerInfo
= clonedDoc
->mReferrerInfo
;
13905 CachePrintSelectionRanges(*this, *clonedDoc
);
13907 // We're done with the clone, embed ourselves into the document viewer and
13908 // clone our children. The order here is pretty important, because our
13909 // document our document needs to have an owner global before we can create
13910 // the frame loaders for subdocuments.
13911 aViewer
->SetDocument(clonedDoc
);
13913 *aOutHasInProcessPrintCallbacks
|= clonedDoc
->HasPrintCallbacks();
13915 auto pendingClones
= std::move(clonedDoc
->mPendingFrameStaticClones
);
13916 for (const auto& clone
: pendingClones
) {
13917 RefPtr
<Element
> element
= do_QueryObject(clone
.mElement
);
13918 RefPtr
<nsFrameLoader
> frameLoader
=
13919 nsFrameLoader::Create(element
, /* aNetworkCreated */ false);
13921 if (NS_WARN_IF(!frameLoader
)) {
13925 clone
.mElement
->SetFrameLoader(frameLoader
);
13927 nsresult rv
= frameLoader
->FinishStaticClone(
13928 clone
.mStaticCloneOf
, aPrintSettings
, aOutHasInProcessPrintCallbacks
);
13929 Unused
<< NS_WARN_IF(NS_FAILED(rv
));
13932 return clonedDoc
.forget();
13935 void Document::UnlinkOriginalDocumentIfStatic() {
13936 if (IsStaticDocument() && mOriginalDocument
) {
13937 MOZ_ASSERT(mOriginalDocument
->mStaticCloneCount
> 0);
13938 mOriginalDocument
->mStaticCloneCount
--;
13939 mOriginalDocument
= nullptr;
13941 MOZ_ASSERT(!mOriginalDocument
);
13944 nsresult
Document::ScheduleFrameRequestCallback(FrameRequestCallback
& aCallback
,
13945 uint32_t* aHandle
) {
13946 const bool wasEmpty
= mFrameRequestManager
.IsEmpty();
13947 MOZ_TRY(mFrameRequestManager
.Schedule(aCallback
, aHandle
));
13949 MaybeScheduleFrameRequestCallbacks();
13954 void Document::CancelFrameRequestCallback(uint32_t aHandle
) {
13955 mFrameRequestManager
.Cancel(aHandle
);
13958 bool Document::IsCanceledFrameRequestCallback(uint32_t aHandle
) const {
13959 return mFrameRequestManager
.IsCanceled(aHandle
);
13962 void Document::ScheduleVideoFrameCallbacks(HTMLVideoElement
* aElement
) {
13963 const bool wasEmpty
= mFrameRequestManager
.IsEmpty();
13964 mFrameRequestManager
.Schedule(aElement
);
13966 MaybeScheduleFrameRequestCallbacks();
13970 void Document::CancelVideoFrameCallbacks(HTMLVideoElement
* aElement
) {
13971 mFrameRequestManager
.Cancel(aElement
);
13974 nsresult
Document::GetStateObject(JS::MutableHandle
<JS::Value
> aState
) {
13975 // Get the document's current state object. This is the object backing both
13976 // history.state and popStateEvent.state.
13978 // mStateObjectContainer may be null; this just means that there's no
13979 // current state object.
13981 if (!mCachedStateObjectValid
) {
13982 if (mStateObjectContainer
) {
13984 // Init with null is "OK" in the sense that it will just fail.
13985 if (!jsapi
.Init(GetScopeObject())) {
13986 return NS_ERROR_UNEXPECTED
;
13988 JS::Rooted
<JS::Value
> value(jsapi
.cx());
13990 mStateObjectContainer
->DeserializeToJsval(jsapi
.cx(), &value
);
13991 NS_ENSURE_SUCCESS(rv
, rv
);
13993 mCachedStateObject
= value
;
13994 if (!value
.isNullOrUndefined()) {
13995 mozilla::HoldJSObjects(this);
13998 mCachedStateObject
= JS::NullValue();
14000 mCachedStateObjectValid
= true;
14003 aState
.set(mCachedStateObject
);
14007 void Document::SetNavigationTiming(nsDOMNavigationTiming
* aTiming
) {
14009 if (!mLoadingOrRestoredFromBFCacheTimeStamp
.IsNull() && mTiming
) {
14010 mTiming
->SetDOMLoadingTimeStamp(GetDocumentURI(),
14011 mLoadingOrRestoredFromBFCacheTimeStamp
);
14014 // If there's already the DocumentTimeline instance, tell it since the
14015 // DocumentTimeline is based on both the navigation start time stamp and the
14016 // refresh driver timestamp.
14017 if (mDocumentTimeline
) {
14018 mDocumentTimeline
->UpdateLastRefreshDriverTime();
14022 nsContentList
* Document::ImageMapList() {
14024 mImageMaps
= new nsContentList(this, kNameSpaceID_XHTML
, nsGkAtoms::map
,
14031 #define DEPRECATED_OPERATION(_op) #_op "Warning",
14032 static const char* kDeprecationWarnings
[] = {
14033 #include "nsDeprecatedOperationList.h"
14035 #undef DEPRECATED_OPERATION
14037 #define DOCUMENT_WARNING(_op) #_op "Warning",
14038 static const char* kDocumentWarnings
[] = {
14039 #include "nsDocumentWarningList.h"
14041 #undef DOCUMENT_WARNING
14043 static UseCounter
OperationToUseCounter(DeprecatedOperations aOperation
) {
14044 switch (aOperation
) {
14045 #define DEPRECATED_OPERATION(_op) \
14046 case DeprecatedOperations::e##_op: \
14047 return eUseCounter_##_op;
14048 #include "nsDeprecatedOperationList.h"
14049 #undef DEPRECATED_OPERATION
14055 bool Document::HasWarnedAbout(DeprecatedOperations aOperation
) const {
14056 return mDeprecationWarnedAbout
[static_cast<size_t>(aOperation
)];
14059 void Document::WarnOnceAbout(
14060 DeprecatedOperations aOperation
, bool asError
/* = false */,
14061 const nsTArray
<nsString
>& aParams
/* = empty array */) const {
14062 MOZ_ASSERT(NS_IsMainThread());
14063 if (HasWarnedAbout(aOperation
)) {
14066 mDeprecationWarnedAbout
[static_cast<size_t>(aOperation
)] = true;
14067 // Don't count deprecated operations for about pages since those pages
14068 // are almost in our control, and we always need to remove uses there
14069 // before we remove the operation itself anyway.
14070 if (!IsAboutPage()) {
14071 const_cast<Document
*>(this)->SetUseCounter(
14072 OperationToUseCounter(aOperation
));
14075 asError
? nsIScriptError::errorFlag
: nsIScriptError::warningFlag
;
14076 nsContentUtils::ReportToConsole(
14077 flags
, "DOM Core"_ns
, this, nsContentUtils::eDOM_PROPERTIES
,
14078 kDeprecationWarnings
[static_cast<size_t>(aOperation
)], aParams
);
14081 bool Document::HasWarnedAbout(DocumentWarnings aWarning
) const {
14082 return mDocWarningWarnedAbout
[aWarning
];
14085 void Document::WarnOnceAbout(
14086 DocumentWarnings aWarning
, bool asError
/* = false */,
14087 const nsTArray
<nsString
>& aParams
/* = empty array */) const {
14088 MOZ_ASSERT(NS_IsMainThread());
14089 if (HasWarnedAbout(aWarning
)) {
14092 mDocWarningWarnedAbout
[aWarning
] = true;
14094 asError
? nsIScriptError::errorFlag
: nsIScriptError::warningFlag
;
14095 nsContentUtils::ReportToConsole(flags
, "DOM Core"_ns
, this,
14096 nsContentUtils::eDOM_PROPERTIES
,
14097 kDocumentWarnings
[aWarning
], aParams
);
14100 mozilla::dom::ImageTracker
* Document::ImageTracker() {
14101 if (!mImageTracker
) {
14102 mImageTracker
= new mozilla::dom::ImageTracker
;
14104 return mImageTracker
;
14107 void Document::ScheduleSVGUseElementShadowTreeUpdate(
14108 SVGUseElement
& aUseElement
) {
14109 MOZ_ASSERT(aUseElement
.IsInComposedDoc());
14111 if (MOZ_UNLIKELY(mIsStaticDocument
)) {
14112 // Printing doesn't deal well with dynamic DOM mutations.
14116 mSVGUseElementsNeedingShadowTreeUpdate
.Insert(&aUseElement
);
14118 if (PresShell
* presShell
= GetPresShell()) {
14119 presShell
->EnsureStyleFlush();
14123 void Document::DoUpdateSVGUseElementShadowTrees() {
14124 MOZ_ASSERT(!mSVGUseElementsNeedingShadowTreeUpdate
.IsEmpty());
14126 MOZ_ASSERT(!mCloningForSVGUse
);
14127 nsAutoScriptBlockerSuppressNodeRemoved blocker
;
14128 mCloningForSVGUse
= true;
14131 const auto useElementsToUpdate
= ToTArray
<nsTArray
<RefPtr
<SVGUseElement
>>>(
14132 mSVGUseElementsNeedingShadowTreeUpdate
);
14133 mSVGUseElementsNeedingShadowTreeUpdate
.Clear();
14135 for (const auto& useElement
: useElementsToUpdate
) {
14136 if (MOZ_UNLIKELY(!useElement
->IsInComposedDoc())) {
14137 // The element was in another <use> shadow tree which we processed
14138 // already and also needed an update, and is removed from the document
14139 // now, so nothing to do here.
14140 MOZ_ASSERT(useElementsToUpdate
.Length() > 1);
14143 useElement
->UpdateShadowTree();
14145 } while (!mSVGUseElementsNeedingShadowTreeUpdate
.IsEmpty());
14147 mCloningForSVGUse
= false;
14150 void Document::NotifyMediaFeatureValuesChanged() {
14151 for (RefPtr
<HTMLImageElement
> imageElement
: mResponsiveContent
) {
14152 imageElement
->MediaFeatureValuesChanged();
14156 already_AddRefed
<Touch
> Document::CreateTouch(
14157 nsGlobalWindowInner
* aView
, EventTarget
* aTarget
, int32_t aIdentifier
,
14158 int32_t aPageX
, int32_t aPageY
, int32_t aScreenX
, int32_t aScreenY
,
14159 int32_t aClientX
, int32_t aClientY
, int32_t aRadiusX
, int32_t aRadiusY
,
14160 float aRotationAngle
, float aForce
) {
14161 RefPtr
<Touch
> touch
=
14162 new Touch(aTarget
, aIdentifier
, aPageX
, aPageY
, aScreenX
, aScreenY
,
14163 aClientX
, aClientY
, aRadiusX
, aRadiusY
, aRotationAngle
, aForce
);
14164 return touch
.forget();
14167 already_AddRefed
<TouchList
> Document::CreateTouchList() {
14168 RefPtr
<TouchList
> retval
= new TouchList(ToSupports(this));
14169 return retval
.forget();
14172 already_AddRefed
<TouchList
> Document::CreateTouchList(
14173 Touch
& aTouch
, const Sequence
<OwningNonNull
<Touch
>>& aTouches
) {
14174 RefPtr
<TouchList
> retval
= new TouchList(ToSupports(this));
14175 retval
->Append(&aTouch
);
14176 for (uint32_t i
= 0; i
< aTouches
.Length(); ++i
) {
14177 retval
->Append(aTouches
[i
].get());
14179 return retval
.forget();
14182 already_AddRefed
<TouchList
> Document::CreateTouchList(
14183 const Sequence
<OwningNonNull
<Touch
>>& aTouches
) {
14184 RefPtr
<TouchList
> retval
= new TouchList(ToSupports(this));
14185 for (uint32_t i
= 0; i
< aTouches
.Length(); ++i
) {
14186 retval
->Append(aTouches
[i
].get());
14188 return retval
.forget();
14191 // https://drafts.csswg.org/cssom-view/Overview#dom-document-caretpositionfrompoint
14192 already_AddRefed
<nsDOMCaretPosition
> Document::CaretPositionFromPoint(
14193 float aX
, float aY
, const CaretPositionFromPointOptions
& aOptions
) {
14194 using FrameForPointOption
= nsLayoutUtils::FrameForPointOption
;
14196 nscoord x
= nsPresContext::CSSPixelsToAppUnits(aX
);
14197 nscoord y
= nsPresContext::CSSPixelsToAppUnits(aY
);
14200 FlushPendingNotifications(FlushType::Layout
);
14202 PresShell
* presShell
= GetPresShell();
14207 nsIFrame
* rootFrame
= presShell
->GetRootFrame();
14209 // XUL docs, unlike HTML, have no frame tree until everything's done loading
14214 nsIFrame
* ptFrame
= nsLayoutUtils::GetFrameForPoint(
14215 RelativeTo
{rootFrame
}, pt
,
14216 {{FrameForPointOption::IgnorePaintSuppression
,
14217 FrameForPointOption::IgnoreCrossDoc
}});
14222 // We require frame-relative coordinates for GetContentOffsetsFromPoint.
14223 nsPoint adjustedPoint
= pt
;
14224 if (nsLayoutUtils::TransformPoint(RelativeTo
{rootFrame
}, RelativeTo
{ptFrame
},
14226 nsLayoutUtils::TRANSFORM_SUCCEEDED
) {
14230 nsIFrame::ContentOffsets offsets
=
14231 ptFrame
->GetContentOffsetsFromPoint(adjustedPoint
);
14233 nsCOMPtr
<nsINode
> node
= offsets
.content
;
14234 uint32_t offset
= offsets
.offset
;
14235 nsCOMPtr
<nsINode
> anonNode
= node
;
14236 bool nodeIsAnonymous
= node
&& node
->IsInNativeAnonymousSubtree();
14237 if (nodeIsAnonymous
) {
14238 node
= ptFrame
->GetContent();
14239 nsINode
* nonChrome
=
14240 node
->AsContent()->FindFirstNonChromeOnlyAccessContent();
14241 HTMLTextAreaElement
* textArea
= HTMLTextAreaElement::FromNode(nonChrome
);
14242 nsTextControlFrame
* textFrame
=
14243 do_QueryFrame(nonChrome
->AsContent()->GetPrimaryFrame());
14248 // If the anonymous content node has a child, then we need to make sure
14249 // that we get the appropriate child, as otherwise the offset may not be
14250 // correct when we construct a range for it.
14251 nsCOMPtr
<nsINode
> firstChild
= anonNode
->GetFirstChild();
14253 anonNode
= firstChild
;
14257 offset
= nsContentUtils::GetAdjustedOffsetInTextControl(ptFrame
, offset
);
14263 bool offsetAndNodeNeedsAdjustment
= false;
14266 dom_shadowdom_new_caretPositionFromPoint_behavior_enabled()) {
14267 while (node
->IsInShadowTree() &&
14268 !aOptions
.mShadowRoots
.Contains(node
->GetContainingShadow())) {
14269 node
= node
->GetContainingShadowHost();
14270 offsetAndNodeNeedsAdjustment
= true;
14274 if (offsetAndNodeNeedsAdjustment
) {
14275 const Maybe
<uint32_t> maybeIndex
= node
->ComputeIndexInParentContent();
14276 if (MOZ_UNLIKELY(maybeIndex
.isNothing())) {
14277 // Unlikely to happen, but still return nullptr to avoid leaking
14278 // information about the shadow tree.
14281 // 5.3.1: Set startOffset to index of startNode’s root's host.
14282 offset
= maybeIndex
.value();
14283 // 5.3.2: Set startNode to startNode’s root's host's parent.
14284 node
= node
->GetParentNode();
14287 RefPtr
<nsDOMCaretPosition
> aCaretPos
= new nsDOMCaretPosition(node
, offset
);
14288 if (nodeIsAnonymous
) {
14289 aCaretPos
->SetAnonymousContentNode(anonNode
);
14291 return aCaretPos
.forget();
14294 bool Document::IsPotentiallyScrollable(HTMLBodyElement
* aBody
) {
14295 // We rely on correct frame information here, so need to flush frames.
14296 FlushPendingNotifications(FlushType::Frames
);
14298 // An element that is the HTML body element is potentially scrollable if all
14299 // of the following conditions are true:
14301 // The element has an associated CSS layout box.
14302 nsIFrame
* bodyFrame
= nsLayoutUtils::GetStyleFrame(aBody
);
14307 // The element's parent element's computed value of the overflow-x and
14308 // overflow-y properties are visible.
14309 MOZ_ASSERT(aBody
->GetParent() == aBody
->OwnerDoc()->GetRootElement());
14310 nsIFrame
* parentFrame
= nsLayoutUtils::GetStyleFrame(aBody
->GetParent());
14312 parentFrame
->StyleDisplay()->OverflowIsVisibleInBothAxis()) {
14316 // The element's computed value of the overflow-x or overflow-y properties is
14318 return !bodyFrame
->StyleDisplay()->OverflowIsVisibleInBothAxis();
14321 Element
* Document::GetScrollingElement() {
14322 // Keep this in sync with IsScrollingElement.
14323 if (GetCompatibilityMode() == eCompatibility_NavQuirks
) {
14324 RefPtr
<HTMLBodyElement
> body
= GetBodyElement();
14325 if (body
&& !IsPotentiallyScrollable(body
)) {
14332 return GetRootElement();
14335 bool Document::IsScrollingElement(Element
* aElement
) {
14336 // Keep this in sync with GetScrollingElement.
14337 MOZ_ASSERT(aElement
);
14339 if (GetCompatibilityMode() != eCompatibility_NavQuirks
) {
14340 return aElement
== GetRootElement();
14343 // In the common case when aElement != body, avoid refcounting.
14344 HTMLBodyElement
* body
= GetBodyElement();
14345 if (aElement
!= body
) {
14349 // Now we know body is non-null, since aElement is not null. It's the
14350 // scrolling element for the document if it itself is not potentially
14352 RefPtr
<HTMLBodyElement
> strongBody(body
);
14353 return !IsPotentiallyScrollable(strongBody
);
14356 class UnblockParsingPromiseHandler final
: public PromiseNativeHandler
{
14358 NS_DECL_CYCLE_COLLECTING_ISUPPORTS
14359 NS_DECL_CYCLE_COLLECTION_CLASS(UnblockParsingPromiseHandler
)
14361 explicit UnblockParsingPromiseHandler(Document
* aDocument
, Promise
* aPromise
,
14362 const BlockParsingOptions
& aOptions
)
14363 : mPromise(aPromise
) {
14364 nsCOMPtr
<nsIParser
> parser
= aDocument
->CreatorParserOrNull();
14366 (aOptions
.mBlockScriptCreated
|| !parser
->IsScriptCreated())) {
14367 parser
->BlockParser();
14368 mParser
= do_GetWeakReference(parser
);
14369 mDocument
= aDocument
;
14370 mDocument
->BlockOnload();
14371 mDocument
->BlockDOMContentLoaded();
14375 void ResolvedCallback(JSContext
* aCx
, JS::Handle
<JS::Value
> aValue
,
14376 ErrorResult
& aRv
) override
{
14377 MaybeUnblockParser();
14379 mPromise
->MaybeResolve(aValue
);
14382 void RejectedCallback(JSContext
* aCx
, JS::Handle
<JS::Value
> aValue
,
14383 ErrorResult
& aRv
) override
{
14384 MaybeUnblockParser();
14386 mPromise
->MaybeReject(aValue
);
14390 virtual ~UnblockParsingPromiseHandler() {
14391 // If we're being cleaned up by the cycle collector, our mDocument reference
14392 // may have been unlinked while our mParser weak reference is still alive.
14394 MaybeUnblockParser();
14399 void MaybeUnblockParser() {
14400 nsCOMPtr
<nsIParser
> parser
= do_QueryReferent(mParser
);
14402 MOZ_DIAGNOSTIC_ASSERT(mDocument
);
14403 nsCOMPtr
<nsIParser
> docParser
= mDocument
->CreatorParserOrNull();
14404 if (parser
== docParser
) {
14405 parser
->UnblockParser();
14406 parser
->ContinueInterruptedParsingAsync();
14410 // We blocked DOMContentLoaded and load events on this document. Unblock
14411 // them. Note that we want to do that no matter what's going on with the
14412 // parser state for this document. Maybe someone caused it to stop being
14413 // parsed, so CreatorParserOrNull() is returning null, but we still want
14414 // to unblock these.
14415 mDocument
->UnblockDOMContentLoaded();
14416 mDocument
->UnblockOnload(false);
14419 mDocument
= nullptr;
14423 RefPtr
<Promise
> mPromise
;
14424 RefPtr
<Document
> mDocument
;
14427 NS_IMPL_CYCLE_COLLECTION(UnblockParsingPromiseHandler
, mDocument
, mPromise
)
14429 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(UnblockParsingPromiseHandler
)
14430 NS_INTERFACE_MAP_ENTRY(nsISupports
)
14431 NS_INTERFACE_MAP_END
14433 NS_IMPL_CYCLE_COLLECTING_ADDREF(UnblockParsingPromiseHandler
)
14434 NS_IMPL_CYCLE_COLLECTING_RELEASE(UnblockParsingPromiseHandler
)
14436 already_AddRefed
<Promise
> Document::BlockParsing(
14437 Promise
& aPromise
, const BlockParsingOptions
& aOptions
, ErrorResult
& aRv
) {
14438 RefPtr
<Promise
> resultPromise
=
14439 Promise::Create(aPromise
.GetParentObject(), aRv
);
14440 if (aRv
.Failed()) {
14444 RefPtr
<PromiseNativeHandler
> promiseHandler
=
14445 new UnblockParsingPromiseHandler(this, resultPromise
, aOptions
);
14446 aPromise
.AppendNativeHandler(promiseHandler
);
14448 return resultPromise
.forget();
14451 already_AddRefed
<nsIURI
> Document::GetMozDocumentURIIfNotForErrorPages() {
14452 if (mFailedChannel
) {
14453 nsCOMPtr
<nsIURI
> failedURI
;
14454 if (NS_SUCCEEDED(mFailedChannel
->GetURI(getter_AddRefs(failedURI
)))) {
14455 return failedURI
.forget();
14459 nsCOMPtr
<nsIURI
> uri
= GetDocumentURIObject();
14464 return uri
.forget();
14467 Promise
* Document::GetDocumentReadyForIdle(ErrorResult
& aRv
) {
14468 if (mIsGoingAway
) {
14469 aRv
.Throw(NS_ERROR_NOT_AVAILABLE
);
14473 if (!mReadyForIdle
) {
14474 nsIGlobalObject
* global
= GetScopeObject();
14476 aRv
.Throw(NS_ERROR_NOT_AVAILABLE
);
14480 mReadyForIdle
= Promise::Create(global
, aRv
);
14481 if (aRv
.Failed()) {
14486 return mReadyForIdle
;
14489 void Document::MaybeResolveReadyForIdle() {
14490 IgnoredErrorResult rv
;
14491 Promise
* readyPromise
= GetDocumentReadyForIdle(rv
);
14492 if (readyPromise
) {
14493 readyPromise
->MaybeResolveWithUndefined();
14497 mozilla::dom::FeaturePolicy
* Document::FeaturePolicy() const {
14498 // The policy is created when the document is initialized. We _must_ have a
14499 // policy here even if the featurePolicy pref is off. If this assertion fails,
14500 // it means that ::FeaturePolicy() is called before ::StartDocumentLoad().
14501 MOZ_ASSERT(mFeaturePolicy
);
14502 return mFeaturePolicy
;
14505 nsIDOMXULCommandDispatcher
* Document::GetCommandDispatcher() {
14506 // Only chrome documents are allowed to use command dispatcher.
14507 if (!nsContentUtils::IsChromeDoc(this)) {
14510 if (!mCommandDispatcher
) {
14511 // Create our command dispatcher and hook it up.
14512 mCommandDispatcher
= new nsXULCommandDispatcher(this);
14514 return mCommandDispatcher
;
14517 void Document::InitializeXULBroadcastManager() {
14518 if (mXULBroadcastManager
) {
14521 mXULBroadcastManager
= new XULBroadcastManager(this);
14526 class DevToolsMutationObserver final
: public nsStubMutationObserver
{
14528 NS_DECL_NSIMUTATIONOBSERVER_ATTRIBUTECHANGED
14529 NS_DECL_NSIMUTATIONOBSERVER_CONTENTAPPENDED
14530 NS_DECL_NSIMUTATIONOBSERVER_CONTENTINSERTED
14532 // We handle this in nsContentUtils::MaybeFireNodeRemoved, since devtools
14533 // relies on the event firing _before_ the removal happens.
14534 // NS_DECL_NSIMUTATIONOBSERVER_CONTENTREMOVED
14536 // NOTE(emilio, bug 1694627): DevTools doesn't seem to deal with character
14537 // data changes right now (maybe intentionally?).
14538 // NS_DECL_NSIMUTATIONOBSERVER_CHARACTERDATACHANGED
14540 DevToolsMutationObserver() = default;
14543 void FireEvent(nsINode
* aTarget
, const nsAString
& aType
);
14545 ~DevToolsMutationObserver() = default;
14548 NS_IMPL_ISUPPORTS(DevToolsMutationObserver
, nsIMutationObserver
)
14550 void DevToolsMutationObserver::FireEvent(nsINode
* aTarget
,
14551 const nsAString
& aType
) {
14552 AsyncEventDispatcher::RunDOMEventWhenSafe(*aTarget
, aType
, CanBubble::eNo
,
14553 ChromeOnlyDispatch::eYes
,
14557 void DevToolsMutationObserver::AttributeChanged(Element
* aElement
,
14558 int32_t aNamespaceID
,
14559 nsAtom
* aAttribute
,
14561 const nsAttrValue
* aOldValue
) {
14562 FireEvent(aElement
, u
"devtoolsattrmodified"_ns
);
14565 void DevToolsMutationObserver::ContentAppended(nsIContent
* aFirstNewContent
) {
14566 for (nsIContent
* c
= aFirstNewContent
; c
; c
= c
->GetNextSibling()) {
14567 ContentInserted(c
);
14571 void DevToolsMutationObserver::ContentInserted(nsIContent
* aChild
) {
14572 FireEvent(aChild
, u
"devtoolschildinserted"_ns
);
14575 static StaticRefPtr
<DevToolsMutationObserver
> sDevToolsMutationObserver
;
14579 void Document::SetDevToolsWatchingDOMMutations(bool aValue
) {
14580 if (mDevToolsWatchingDOMMutations
== aValue
|| mIsGoingAway
) {
14583 mDevToolsWatchingDOMMutations
= aValue
;
14585 if (MOZ_UNLIKELY(!sDevToolsMutationObserver
)) {
14586 sDevToolsMutationObserver
= new DevToolsMutationObserver();
14587 ClearOnShutdown(&sDevToolsMutationObserver
);
14589 AddMutationObserver(sDevToolsMutationObserver
);
14590 } else if (sDevToolsMutationObserver
) {
14591 RemoveMutationObserver(sDevToolsMutationObserver
);
14595 void EvaluateMediaQueryLists(nsTArray
<RefPtr
<MediaQueryList
>>& aListsToNotify
,
14596 Document
& aDocument
, bool aRecurse
) {
14597 if (nsPresContext
* pc
= aDocument
.GetPresContext()) {
14598 pc
->FlushPendingMediaFeatureValuesChanged();
14601 for (MediaQueryList
* mql
: aDocument
.MediaQueryLists()) {
14602 if (mql
->EvaluateOnRenderingUpdate()) {
14603 aListsToNotify
.AppendElement(mql
);
14609 aDocument
.EnumerateSubDocuments([&](Document
& aSubDoc
) {
14610 EvaluateMediaQueryLists(aListsToNotify
, aSubDoc
, true);
14611 return CallState::Continue
;
14615 void Document::EvaluateMediaQueriesAndReportChanges(bool aRecurse
) {
14616 AutoTArray
<RefPtr
<MediaQueryList
>, 32> mqls
;
14617 EvaluateMediaQueryLists(mqls
, *this, aRecurse
);
14618 for (auto& mql
: mqls
) {
14619 mql
->FireChangeEvent();
14623 nsIHTMLCollection
* Document::Children() {
14624 if (!mChildrenCollection
) {
14625 mChildrenCollection
=
14626 new nsContentList(this, kNameSpaceID_Wildcard
, nsGkAtoms::_asterisk
,
14627 nsGkAtoms::_asterisk
, false);
14630 return mChildrenCollection
;
14633 uint32_t Document::ChildElementCount() { return Children()->Length(); }
14635 // Singleton class to manage the list of fullscreen documents which are the
14636 // root of a branch which contains fullscreen documents. We maintain this list
14637 // so that we can easily exit all windows from fullscreen when the user
14638 // presses the escape key.
14639 class FullscreenRoots
{
14641 // Adds the root of given document to the manager. Calling this method
14642 // with a document whose root is already contained has no effect.
14643 static void Add(Document
* aDoc
);
14645 // Iterates over every root in the root list, and calls aFunction, passing
14646 // each root once to aFunction. It is safe to call Add() and Remove() while
14647 // iterating over the list (i.e. in aFunction). Documents that are removed
14648 // from the manager during traversal are not traversed, and documents that
14649 // are added to the manager during traversal are also not traversed.
14650 static void ForEach(void (*aFunction
)(Document
* aDoc
));
14652 // Removes the root of a specific document from the manager.
14653 static void Remove(Document
* aDoc
);
14655 // Returns true if all roots added to the list have been removed.
14656 static bool IsEmpty();
14659 MOZ_COUNTED_DEFAULT_CTOR(FullscreenRoots
)
14660 MOZ_COUNTED_DTOR(FullscreenRoots
)
14662 using RootsArray
= nsTArray
<WeakPtr
<Document
>>;
14664 // Returns true if aRoot is in the list of fullscreen roots.
14665 static bool Contains(Document
* aRoot
);
14667 // Singleton instance of the FullscreenRoots. This is instantiated when a
14668 // root is added, and it is deleted when the last root is removed.
14669 static FullscreenRoots
* sInstance
;
14671 // List of weak pointers to roots.
14675 FullscreenRoots
* FullscreenRoots::sInstance
= nullptr;
14678 void FullscreenRoots::ForEach(void (*aFunction
)(Document
* aDoc
)) {
14682 // Create a copy of the roots array, and iterate over the copy. This is so
14683 // that if an element is removed from mRoots we don't mess up our iteration.
14684 RootsArray
roots(sInstance
->mRoots
.Clone());
14685 // Call aFunction on all entries.
14686 for (uint32_t i
= 0; i
< roots
.Length(); i
++) {
14687 nsCOMPtr
<Document
> root(roots
[i
]);
14688 // Check that the root isn't in the manager. This is so that new additions
14689 // while we were running don't get traversed.
14690 if (root
&& FullscreenRoots::Contains(root
)) {
14697 bool FullscreenRoots::Contains(Document
* aRoot
) {
14698 return sInstance
&& sInstance
->mRoots
.Contains(aRoot
);
14702 void FullscreenRoots::Add(Document
* aDoc
) {
14703 nsCOMPtr
<Document
> root
=
14704 nsContentUtils::GetInProcessSubtreeRootDocument(aDoc
);
14705 if (!FullscreenRoots::Contains(root
)) {
14707 sInstance
= new FullscreenRoots();
14709 sInstance
->mRoots
.AppendElement(root
);
14714 void FullscreenRoots::Remove(Document
* aDoc
) {
14715 nsCOMPtr
<Document
> root
=
14716 nsContentUtils::GetInProcessSubtreeRootDocument(aDoc
);
14717 if (!sInstance
|| !sInstance
->mRoots
.RemoveElement(root
)) {
14718 NS_ERROR("Should only try to remove roots which are still added!");
14721 if (sInstance
->mRoots
.IsEmpty()) {
14723 sInstance
= nullptr;
14728 bool FullscreenRoots::IsEmpty() { return !sInstance
; }
14730 // Any fullscreen change waiting for the widget to finish transition
14731 // is queued here. This is declared static instead of a member of
14732 // Document because in the majority of time, there would be at most
14733 // one document requesting or exiting fullscreen. We shouldn't waste
14734 // the space to hold for it in every document.
14735 class PendingFullscreenChangeList
{
14737 PendingFullscreenChangeList() = delete;
14739 template <typename T
>
14740 static void Add(UniquePtr
<T
> aChange
) {
14741 sList
.insertBack(aChange
.release());
14744 static const FullscreenChange
* GetLast() { return sList
.getLast(); }
14746 enum IteratorOption
{
14747 // When we are committing fullscreen changes or preparing for
14748 // that, we generally want to iterate all requests in the same
14749 // window with eDocumentsWithSameRoot option.
14750 eDocumentsWithSameRoot
,
14751 // If we are removing a document from the tree, we would only
14752 // want to remove the requests from the given document and its
14753 // descendants. For that case, use eInclusiveDescendants.
14754 eInclusiveDescendants
14757 template <typename T
>
14760 explicit Iterator(Document
* aDoc
, IteratorOption aOption
)
14761 : mCurrent(PendingFullscreenChangeList::sList
.getFirst()) {
14763 if (aDoc
->GetBrowsingContext()) {
14764 mRootBCForIteration
= aDoc
->GetBrowsingContext();
14765 if (aOption
== eDocumentsWithSameRoot
) {
14766 BrowsingContext
* bc
=
14767 GetParentIgnoreChromeBoundary(mRootBCForIteration
);
14769 mRootBCForIteration
= bc
;
14770 bc
= GetParentIgnoreChromeBoundary(mRootBCForIteration
);
14778 UniquePtr
<T
> TakeAndNext() {
14779 auto thisChange
= TakeAndNextInternal();
14783 bool AtEnd() const { return mCurrent
== nullptr; }
14786 static BrowsingContext
* GetParentIgnoreChromeBoundary(
14787 BrowsingContext
* aBC
) {
14788 // Chrome BrowsingContexts are only available in the parent process, so if
14789 // we're in a content process, we only worry about the context tree.
14790 if (XRE_IsParentProcess()) {
14791 return aBC
->Canonical()->GetParentCrossChromeBoundary();
14793 return aBC
->GetParent();
14796 UniquePtr
<T
> TakeAndNextInternal() {
14797 FullscreenChange
* thisChange
= mCurrent
;
14798 MOZ_ASSERT(thisChange
->Type() == T::kType
);
14799 mCurrent
= mCurrent
->removeAndGetNext();
14800 return WrapUnique(static_cast<T
*>(thisChange
));
14802 void SkipToNextMatch() {
14804 if (mCurrent
->Type() == T::kType
) {
14805 BrowsingContext
* bc
= mCurrent
->Document()->GetBrowsingContext();
14807 // Always automatically drop fullscreen changes which are
14808 // from a document detached from the doc shell.
14809 UniquePtr
<T
> change
= TakeAndNextInternal();
14810 change
->MayRejectPromise("Document is not active");
14813 while (bc
&& bc
!= mRootBCForIteration
) {
14814 bc
= GetParentIgnoreChromeBoundary(bc
);
14820 // The current one either don't have matched type, or isn't
14821 // inside the given subtree, so skip this item.
14822 mCurrent
= mCurrent
->getNext();
14826 FullscreenChange
* mCurrent
;
14827 RefPtr
<BrowsingContext
> mRootBCForIteration
;
14831 static LinkedList
<FullscreenChange
> sList
;
14835 MOZ_RUNINIT LinkedList
<FullscreenChange
> PendingFullscreenChangeList::sList
;
14837 size_t Document::CountFullscreenElements() const {
14839 for (const nsWeakPtr
& ptr
: mTopLayer
) {
14840 if (nsCOMPtr
<Element
> elem
= do_QueryReferent(ptr
)) {
14841 if (elem
->State().HasState(ElementState::FULLSCREEN
)) {
14849 // https://github.com/whatwg/html/issues/9143
14850 // We need to consider the precedence between active modal dialog, topmost auto
14851 // popover and fullscreen element once it's specified.
14852 void Document::HandleEscKey() {
14853 for (const nsWeakPtr
& weakPtr
: Reversed(mTopLayer
)) {
14854 nsCOMPtr
<Element
> element(do_QueryReferent(weakPtr
));
14855 if (RefPtr popoverHTMLEl
= nsGenericHTMLElement::FromNodeOrNull(element
)) {
14856 if (element
->IsAutoPopover() && element
->IsPopoverOpen()) {
14857 popoverHTMLEl
->HidePopover(IgnoreErrors());
14861 if (auto* dialog
= HTMLDialogElement::FromNodeOrNull(element
)) {
14862 dialog
->QueueCancelDialog();
14868 MOZ_CAN_RUN_SCRIPT
void Document::ProcessCloseRequest() {
14869 if (RefPtr win
= GetInnerWindow()) {
14870 if (win
->IsFullyActive()) {
14871 RefPtr manager
= win
->EnsureCloseWatcherManager();
14872 manager
->ProcessCloseRequest();
14877 already_AddRefed
<Promise
> Document::ExitFullscreen(ErrorResult
& aRv
) {
14878 UniquePtr
<FullscreenExit
> exit
= FullscreenExit::Create(this, aRv
);
14879 RefPtr
<Promise
> promise
= exit
->GetPromise();
14880 RestorePreviousFullscreenState(std::move(exit
));
14881 return promise
.forget();
14884 static void AskWindowToExitFullscreen(Document
* aDoc
) {
14885 if (XRE_GetProcessType() == GeckoProcessType_Content
) {
14886 nsContentUtils::DispatchEventOnlyToChrome(
14887 aDoc
, aDoc
, u
"MozDOMFullscreen:Exit"_ns
, CanBubble::eYes
,
14888 Cancelable::eNo
, /* DefaultAction */ nullptr);
14890 if (nsPIDOMWindowOuter
* win
= aDoc
->GetWindow()) {
14891 win
->SetFullscreenInternal(FullscreenReason::ForFullscreenAPI
, false);
14896 class nsCallExitFullscreen
: public Runnable
{
14898 explicit nsCallExitFullscreen(Document
* aDoc
)
14899 : mozilla::Runnable("nsCallExitFullscreen"), mDoc(aDoc
) {}
14901 NS_IMETHOD
Run() final
{
14903 FullscreenRoots::ForEach(&AskWindowToExitFullscreen
);
14905 AskWindowToExitFullscreen(mDoc
);
14911 nsCOMPtr
<Document
> mDoc
;
14915 void Document::AsyncExitFullscreen(Document
* aDoc
) {
14916 MOZ_RELEASE_ASSERT(NS_IsMainThread());
14917 nsCOMPtr
<nsIRunnable
> exit
= new nsCallExitFullscreen(aDoc
);
14918 NS_DispatchToCurrentThread(exit
.forget());
14921 static uint32_t CountFullscreenSubDocuments(Document
& aDoc
) {
14922 uint32_t count
= 0;
14923 // FIXME(emilio): Should this be recursive and dig into our nested subdocs?
14924 aDoc
.EnumerateSubDocuments([&count
](Document
& aSubDoc
) {
14925 if (aSubDoc
.Fullscreen()) {
14928 return CallState::Continue
;
14933 bool Document::IsFullscreenLeaf() {
14934 // A fullscreen leaf document is fullscreen, and has no fullscreen
14937 // FIXME(emilio): This doesn't seem to account for fission iframes, is that
14939 return Fullscreen() && CountFullscreenSubDocuments(*this) == 0;
14942 static Document
* GetFullscreenLeaf(Document
& aDoc
) {
14943 if (aDoc
.IsFullscreenLeaf()) {
14946 if (!aDoc
.Fullscreen()) {
14949 Document
* leaf
= nullptr;
14950 aDoc
.EnumerateSubDocuments([&leaf
](Document
& aSubDoc
) {
14951 leaf
= GetFullscreenLeaf(aSubDoc
);
14952 return leaf
? CallState::Stop
: CallState::Continue
;
14957 static Document
* GetFullscreenLeaf(Document
* aDoc
) {
14958 if (Document
* leaf
= GetFullscreenLeaf(*aDoc
)) {
14961 // Otherwise we could be either in a non-fullscreen doc tree, or we're
14962 // below the fullscreen doc. Start the search from the root.
14963 Document
* root
= nsContentUtils::GetInProcessSubtreeRootDocument(aDoc
);
14964 return GetFullscreenLeaf(*root
);
14967 static CallState
ResetFullscreen(Document
& aDocument
) {
14968 if (Element
* fsElement
= aDocument
.GetUnretargetedFullscreenElement()) {
14969 NS_ASSERTION(CountFullscreenSubDocuments(aDocument
) <= 1,
14970 "Should have at most 1 fullscreen subdocument.");
14971 aDocument
.CleanupFullscreenState();
14972 NS_ASSERTION(!aDocument
.Fullscreen(), "Should reset fullscreen");
14973 DispatchFullscreenChange(aDocument
, fsElement
);
14974 aDocument
.EnumerateSubDocuments(ResetFullscreen
);
14976 return CallState::Continue
;
14979 // Since Document::ExitFullscreenInDocTree() could be called from
14980 // Element::UnbindFromTree() where it is not safe to synchronously run
14981 // script. This runnable is the script part of that function.
14982 class ExitFullscreenScriptRunnable
: public Runnable
{
14984 explicit ExitFullscreenScriptRunnable(Document
* aRoot
, Document
* aLeaf
)
14985 : mozilla::Runnable("ExitFullscreenScriptRunnable"),
14989 NS_IMETHOD
Run() override
{
14990 // Dispatch MozDOMFullscreen:Exited to the original fullscreen leaf
14991 // document since we want this event to follow the same path that
14992 // MozDOMFullscreen:Entered was dispatched.
14993 nsContentUtils::DispatchEventOnlyToChrome(
14994 mLeaf
, mLeaf
, u
"MozDOMFullscreen:Exited"_ns
, CanBubble::eYes
,
14995 Cancelable::eNo
, /* DefaultAction */ nullptr);
14996 // Ensure the window exits fullscreen, as long as we don't have
14997 // pending fullscreen requests.
14998 if (nsPIDOMWindowOuter
* win
= mRoot
->GetWindow()) {
14999 if (!mRoot
->HasPendingFullscreenRequests()) {
15000 win
->SetFullscreenInternal(FullscreenReason::ForForceExitFullscreen
,
15008 nsCOMPtr
<Document
> mRoot
;
15009 nsCOMPtr
<Document
> mLeaf
;
15013 void Document::ExitFullscreenInDocTree(Document
* aMaybeNotARootDoc
) {
15014 MOZ_ASSERT(aMaybeNotARootDoc
);
15016 // Unlock the pointer
15017 PointerLockManager::Unlock("Document::ExitFullscreenInDocTree");
15019 // Resolve all promises which waiting for exit fullscreen.
15020 PendingFullscreenChangeList::Iterator
<FullscreenExit
> iter(
15021 aMaybeNotARootDoc
, PendingFullscreenChangeList::eDocumentsWithSameRoot
);
15022 while (!iter
.AtEnd()) {
15023 UniquePtr
<FullscreenExit
> exit
= iter
.TakeAndNext();
15024 exit
->MayResolvePromise();
15027 nsCOMPtr
<Document
> root
= aMaybeNotARootDoc
->GetFullscreenRoot();
15028 if (!root
|| !root
->Fullscreen()) {
15029 // If a document was detached before exiting from fullscreen, it is
15030 // possible that the root had left fullscreen state. In this case,
15031 // we would not get anything from the ResetFullscreen() call. Root's
15032 // not being a fullscreen doc also means the widget should have
15033 // exited fullscreen state. It means even if we do not return here,
15034 // we would actually do nothing below except crashing ourselves via
15035 // dispatching the "MozDOMFullscreen:Exited" event to an nonexistent
15040 // Record the fullscreen leaf document for MozDOMFullscreen:Exited.
15041 // See ExitFullscreenScriptRunnable::Run for details. We have to
15042 // record it here because we don't have such information after we
15043 // reset the fullscreen state below.
15044 Document
* fullscreenLeaf
= GetFullscreenLeaf(root
);
15046 // Walk the tree of fullscreen documents, and reset their fullscreen state.
15047 ResetFullscreen(*root
);
15049 NS_ASSERTION(!root
->Fullscreen(),
15050 "Fullscreen root should no longer be a fullscreen doc...");
15052 // Move the top-level window out of fullscreen mode.
15053 FullscreenRoots::Remove(root
);
15055 nsContentUtils::AddScriptRunner(
15056 new ExitFullscreenScriptRunnable(root
, fullscreenLeaf
));
15059 static void DispatchFullscreenNewOriginEvent(Document
* aDoc
) {
15060 RefPtr
<AsyncEventDispatcher
> asyncDispatcher
=
15061 new AsyncEventDispatcher(aDoc
, u
"MozDOMFullscreen:NewOrigin"_ns
,
15062 CanBubble::eYes
, ChromeOnlyDispatch::eYes
);
15063 asyncDispatcher
->PostDOMEvent();
15066 void Document::RestorePreviousFullscreenState(UniquePtr
<FullscreenExit
> aExit
) {
15067 NS_ASSERTION(!Fullscreen() || !FullscreenRoots::IsEmpty(),
15068 "Should have at least 1 fullscreen root when fullscreen!");
15070 if (!GetWindow()) {
15071 aExit
->MayRejectPromise("No active window");
15074 if (!Fullscreen() || FullscreenRoots::IsEmpty()) {
15075 aExit
->MayRejectPromise("Not in fullscreen mode");
15079 nsCOMPtr
<Document
> fullScreenDoc
= GetFullscreenLeaf(this);
15080 AutoTArray
<Element
*, 8> exitElements
;
15082 Document
* doc
= fullScreenDoc
;
15083 // Collect all subdocuments.
15084 for (; doc
!= this; doc
= doc
->GetInProcessParentDocument()) {
15085 Element
* fsElement
= doc
->GetUnretargetedFullscreenElement();
15086 MOZ_ASSERT(fsElement
,
15087 "Parent document of "
15088 "a fullscreen document without fullscreen element?");
15089 exitElements
.AppendElement(fsElement
);
15091 MOZ_ASSERT(doc
== this, "Must have reached this doc");
15092 // Collect all ancestor documents which we are going to change.
15093 for (; doc
; doc
= doc
->GetInProcessParentDocument()) {
15094 Element
* fsElement
= doc
->GetUnretargetedFullscreenElement();
15095 MOZ_ASSERT(fsElement
,
15096 "Ancestor of fullscreen document must also be in fullscreen");
15098 if (auto* iframe
= HTMLIFrameElement::FromNode(fsElement
)) {
15099 if (iframe
->FullscreenFlag()) {
15100 // If this is an iframe, and it explicitly requested
15101 // fullscreen, don't rollback it automatically.
15106 exitElements
.AppendElement(fsElement
);
15107 if (doc
->CountFullscreenElements() > 1) {
15112 Document
* lastDoc
= exitElements
.LastElement()->OwnerDoc();
15113 size_t fullscreenCount
= lastDoc
->CountFullscreenElements();
15114 if (!lastDoc
->GetInProcessParentDocument() && fullscreenCount
== 1) {
15115 // If we are fully exiting fullscreen, don't touch anything here,
15116 // just wait for the window to get out from fullscreen first.
15117 PendingFullscreenChangeList::Add(std::move(aExit
));
15118 AskWindowToExitFullscreen(this);
15122 // If fullscreen mode is updated the pointer should be unlocked
15123 PointerLockManager::Unlock("Document::RestorePreviousFullscreenState");
15124 // All documents listed in the array except the last one are going to
15125 // completely exit from the fullscreen state.
15126 for (auto i
: IntegerRange(exitElements
.Length() - 1)) {
15127 exitElements
[i
]->OwnerDoc()->CleanupFullscreenState();
15129 // The last document will either rollback one fullscreen element, or
15130 // completely exit from the fullscreen state as well.
15131 Document
* newFullscreenDoc
;
15132 if (fullscreenCount
> 1) {
15133 DebugOnly
<bool> removedFullscreenElement
= lastDoc
->PopFullscreenElement();
15134 MOZ_ASSERT(removedFullscreenElement
);
15135 newFullscreenDoc
= lastDoc
;
15137 lastDoc
->CleanupFullscreenState();
15138 newFullscreenDoc
= lastDoc
->GetInProcessParentDocument();
15140 // Dispatch the fullscreenchange event to all document listed. Note
15141 // that the loop order is reversed so that events are dispatched in
15142 // the tree order as indicated in the spec.
15143 for (Element
* e
: Reversed(exitElements
)) {
15144 DispatchFullscreenChange(*e
->OwnerDoc(), e
);
15146 aExit
->MayResolvePromise();
15148 MOZ_ASSERT(newFullscreenDoc
,
15149 "If we were going to exit from fullscreen on "
15150 "all documents in this doctree, we should've asked the window to "
15151 "exit first instead of reaching here.");
15152 if (fullScreenDoc
!= newFullscreenDoc
&&
15153 !nsContentUtils::HaveEqualPrincipals(fullScreenDoc
, newFullscreenDoc
)) {
15154 // We've popped so enough off the stack that we've rolled back to
15155 // a fullscreen element in a parent document. If this document is
15156 // cross origin, dispatch an event to chrome so it knows to show
15158 DispatchFullscreenNewOriginEvent(newFullscreenDoc
);
15162 static void UpdateViewportScrollbarOverrideForFullscreen(Document
* aDoc
) {
15163 if (nsPresContext
* presContext
= aDoc
->GetPresContext()) {
15164 presContext
->UpdateViewportScrollStylesOverride();
15168 static void NotifyFullScreenChangedForMediaElement(Element
& aElement
) {
15169 // When a media element enters the fullscreen, we would like to notify that
15170 // to the media controller in order to update its status.
15171 if (auto* mediaElem
= HTMLMediaElement::FromNode(aElement
)) {
15172 mediaElem
->NotifyFullScreenChanged();
15176 void Document::CleanupFullscreenState() {
15177 while (PopFullscreenElement(UpdateViewport::No
)) {
15178 // Remove the next one if appropriate
15181 UpdateViewportScrollbarOverrideForFullscreen(this);
15182 mFullscreenRoot
= nullptr;
15184 // Restore the zoom level that was in place prior to entering fullscreen.
15185 if (PresShell
* presShell
= GetPresShell()) {
15186 if (presShell
->GetMobileViewportManager()) {
15187 presShell
->SetResolutionAndScaleTo(
15188 mSavedResolution
, ResolutionChangeOrigin::MainThreadRestore
);
15193 bool Document::PopFullscreenElement(UpdateViewport aUpdateViewport
) {
15194 Element
* removedElement
= TopLayerPop([](Element
* element
) -> bool {
15195 return element
->State().HasState(ElementState::FULLSCREEN
);
15198 if (!removedElement
) {
15202 MOZ_ASSERT(removedElement
->State().HasState(ElementState::FULLSCREEN
));
15203 removedElement
->RemoveStates(ElementState::FULLSCREEN
| ElementState::MODAL
);
15204 NotifyFullScreenChangedForMediaElement(*removedElement
);
15205 // Reset iframe fullscreen flag.
15206 if (auto* iframe
= HTMLIFrameElement::FromNode(removedElement
)) {
15207 iframe
->SetFullscreenFlag(false);
15209 if (aUpdateViewport
== UpdateViewport::Yes
) {
15210 UpdateViewportScrollbarOverrideForFullscreen(this);
15215 void Document::SetFullscreenElement(Element
& aElement
) {
15216 ElementState statesToAdd
= ElementState::FULLSCREEN
;
15217 if (!IsInChromeDocShell()) {
15218 // Don't make the document modal in chrome documents, since we don't want
15219 // the browser UI like the context menu / etc to be inert.
15220 statesToAdd
|= ElementState::MODAL
;
15222 aElement
.AddStates(statesToAdd
);
15223 TopLayerPush(aElement
);
15224 NotifyFullScreenChangedForMediaElement(aElement
);
15225 UpdateViewportScrollbarOverrideForFullscreen(this);
15228 void Document::TopLayerPush(Element
& aElement
) {
15229 const bool modal
= aElement
.State().HasState(ElementState::MODAL
);
15231 TopLayerPop(aElement
);
15232 if (nsIFrame
* f
= aElement
.GetPrimaryFrame()) {
15233 f
->MarkNeedsDisplayItemRebuild();
15236 mTopLayer
.AppendElement(do_GetWeakReference(&aElement
));
15237 NS_ASSERTION(GetTopLayerTop() == &aElement
, "Should match");
15240 aElement
.AddStates(ElementState::TOPMOST_MODAL
);
15242 bool foundExistingModalElement
= false;
15243 for (const nsWeakPtr
& weakPtr
: Reversed(mTopLayer
)) {
15244 nsCOMPtr
<Element
> element(do_QueryReferent(weakPtr
));
15245 if (element
&& element
!= &aElement
&&
15246 element
->State().HasState(ElementState::TOPMOST_MODAL
)) {
15247 element
->RemoveStates(ElementState::TOPMOST_MODAL
);
15248 foundExistingModalElement
= true;
15253 if (!foundExistingModalElement
) {
15254 Element
* root
= GetRootElement();
15255 MOZ_RELEASE_ASSERT(root
, "top layer element outside of document?");
15256 if (&aElement
!= root
) {
15257 // Add inert to the root element so that the inertness is applied to the
15258 // entire document.
15259 root
->AddStates(ElementState::INERT
);
15265 void Document::AddModalDialog(HTMLDialogElement
& aDialogElement
) {
15266 aDialogElement
.AddStates(ElementState::MODAL
);
15267 TopLayerPush(aDialogElement
);
15270 void Document::RemoveModalDialog(HTMLDialogElement
& aDialogElement
) {
15271 DebugOnly
<Element
*> removedElement
= TopLayerPop(aDialogElement
);
15272 MOZ_ASSERT(removedElement
== &aDialogElement
);
15273 aDialogElement
.RemoveStates(ElementState::MODAL
);
15276 Element
* Document::TopLayerPop(FunctionRef
<bool(Element
*)> aPredicate
) {
15277 if (mTopLayer
.IsEmpty()) {
15281 // Remove the topmost element that qualifies aPredicate; This
15282 // is required is because the top layer contains not only
15283 // fullscreen elements, but also dialog elements.
15284 Element
* removedElement
= nullptr;
15285 for (auto i
: Reversed(IntegerRange(mTopLayer
.Length()))) {
15286 nsCOMPtr
<Element
> element(do_QueryReferent(mTopLayer
[i
]));
15287 if (element
&& aPredicate(element
)) {
15288 removedElement
= element
;
15289 if (nsIFrame
* f
= element
->GetPrimaryFrame()) {
15290 f
->MarkNeedsDisplayItemRebuild();
15292 mTopLayer
.RemoveElementAt(i
);
15297 // Pop from the stack null elements (references to elements which have
15298 // been GC'd since they were added to the stack) and elements which are
15299 // no longer in this document.
15301 // FIXME(emilio): If this loop does something, it'd violate the assertions
15302 // from GetTopLayerTop()... What gives?
15303 while (!mTopLayer
.IsEmpty()) {
15304 Element
* element
= GetTopLayerTop();
15305 if (!element
|| element
->GetComposedDoc() != this) {
15307 if (nsIFrame
* f
= element
->GetPrimaryFrame()) {
15308 f
->MarkNeedsDisplayItemRebuild();
15312 mTopLayer
.RemoveLastElement();
15314 // The top element of the stack is now an in-doc element. Return here.
15319 if (!removedElement
) {
15323 const bool modal
= removedElement
->State().HasState(ElementState::MODAL
);
15326 removedElement
->RemoveStates(ElementState::TOPMOST_MODAL
);
15327 bool foundExistingModalElement
= false;
15328 for (const nsWeakPtr
& weakPtr
: Reversed(mTopLayer
)) {
15329 nsCOMPtr
<Element
> element(do_QueryReferent(weakPtr
));
15330 if (element
&& element
->State().HasState(ElementState::MODAL
)) {
15331 element
->AddStates(ElementState::TOPMOST_MODAL
);
15332 foundExistingModalElement
= true;
15336 // No more modal elements, make the document not inert anymore.
15337 if (!foundExistingModalElement
) {
15338 Element
* root
= GetRootElement();
15339 if (root
&& !root
->GetBoolAttr(nsGkAtoms::inert
)) {
15340 root
->RemoveStates(ElementState::INERT
);
15345 return removedElement
;
15348 Element
* Document::TopLayerPop(Element
& aElement
) {
15349 auto predictFunc
= [&aElement
](Element
* element
) {
15350 return element
== &aElement
;
15352 return TopLayerPop(predictFunc
);
15355 void Document::GetWireframe(bool aIncludeNodes
,
15356 Nullable
<Wireframe
>& aWireframe
) {
15357 FlushPendingNotifications(FlushType::Layout
);
15358 GetWireframeWithoutFlushing(aIncludeNodes
, aWireframe
);
15361 void Document::GetWireframeWithoutFlushing(bool aIncludeNodes
,
15362 Nullable
<Wireframe
>& aWireframe
) {
15363 using FrameForPointOptions
= nsLayoutUtils::FrameForPointOptions
;
15364 using FrameForPointOption
= nsLayoutUtils::FrameForPointOption
;
15366 PresShell
* shell
= GetPresShell();
15371 nsPresContext
* pc
= shell
->GetPresContext();
15376 nsIFrame
* rootFrame
= shell
->GetRootFrame();
15381 auto& wireframe
= aWireframe
.SetValue();
15382 wireframe
.mCanvasBackground
= shell
->ComputeCanvasBackground().mViewportColor
;
15384 FrameForPointOptions options
;
15385 options
.mBits
+= FrameForPointOption::IgnoreCrossDoc
;
15386 options
.mBits
+= FrameForPointOption::IgnorePaintSuppression
;
15387 options
.mBits
+= FrameForPointOption::OnlyVisible
;
15389 AutoTArray
<nsIFrame
*, 32> frames
;
15390 const RelativeTo relativeTo
{rootFrame
, mozilla::ViewportType::Layout
};
15391 nsLayoutUtils::GetFramesForArea(relativeTo
, pc
->GetVisibleArea(), frames
,
15394 // TODO(emilio): We could rewrite hit testing to return nsDisplayItem*s or
15395 // something perhaps, but seems hard / like it'd involve at least some extra
15396 // copying around, since they don't outlive GetFramesForArea.
15397 auto& rects
= wireframe
.mRects
.Construct();
15398 if (!rects
.SetCapacity(frames
.Length(), fallible
)) {
15401 for (nsIFrame
* frame
: Reversed(frames
)) {
15403 rectType
] = [&]() -> std::tuple
<nscolor
, WireframeRectType
> {
15404 if (frame
->IsTextFrame()) {
15405 return {frame
->StyleText()->mWebkitTextFillColor
.CalcColor(frame
),
15406 WireframeRectType::Text
};
15408 if (frame
->IsImageFrame() || frame
->IsSVGOuterSVGFrame()) {
15409 return {0, WireframeRectType::Image
};
15411 if (frame
->IsThemed()) {
15412 return {0, WireframeRectType::Background
};
15414 bool drawImage
= false;
15415 bool drawColor
= false;
15416 if (const auto* bgStyle
= nsCSSRendering::FindBackground(frame
)) {
15417 const nscolor color
= nsCSSRendering::DetermineBackgroundColor(
15418 pc
, bgStyle
, frame
, drawImage
, drawColor
);
15420 !bgStyle
->StyleBackground()->mImage
.BottomLayer().mImage
.IsNone()) {
15421 return {color
, WireframeRectType::Image
};
15423 if (drawColor
&& !frame
->IsCanvasFrame()) {
15424 // Canvas frame background already accounted for in mCanvasBackground.
15425 return {color
, WireframeRectType::Background
};
15428 return {0, WireframeRectType::Unknown
};
15431 if (rectType
== WireframeRectType::Unknown
) {
15436 CSSRect::FromAppUnits(nsLayoutUtils::TransformFrameRectToAncestor(
15437 frame
, frame
->GetRectRelativeToSelf(), relativeTo
));
15438 if ((uint32_t)r
.Area() <
15439 StaticPrefs::browser_history_wireframeAreaThreshold()) {
15443 // Can't really fail because SetCapacity succeeded.
15444 auto& taggedRect
= *rects
.AppendElement(fallible
);
15446 if (aIncludeNodes
) {
15447 if (nsIContent
* c
= frame
->GetContent()) {
15448 taggedRect
.mNode
.Construct(c
);
15451 taggedRect
.mX
= r
.x
;
15452 taggedRect
.mY
= r
.y
;
15453 taggedRect
.mWidth
= r
.width
;
15454 taggedRect
.mHeight
= r
.height
;
15455 taggedRect
.mColor
= rectColor
;
15456 taggedRect
.mType
.Construct(rectType
);
15460 Element
* Document::GetTopLayerTop() {
15461 if (mTopLayer
.IsEmpty()) {
15464 uint32_t last
= mTopLayer
.Length() - 1;
15465 nsCOMPtr
<Element
> element(do_QueryReferent(mTopLayer
[last
]));
15466 NS_ASSERTION(element
, "Should have a top layer element!");
15467 NS_ASSERTION(element
->IsInComposedDoc(),
15468 "Top layer element should be in doc");
15469 NS_ASSERTION(element
->OwnerDoc() == this,
15470 "Top layer element should be in this doc");
15474 Element
* Document::GetUnretargetedFullscreenElement() const {
15475 for (const nsWeakPtr
& weakPtr
: Reversed(mTopLayer
)) {
15476 nsCOMPtr
<Element
> element(do_QueryReferent(weakPtr
));
15477 // Per spec, the fullscreen element is the topmost element in the document’s
15478 // top layer whose fullscreen flag is set, if any, and null otherwise.
15479 if (element
&& element
->State().HasState(ElementState::FULLSCREEN
)) {
15486 nsTArray
<Element
*> Document::GetTopLayer() const {
15487 nsTArray
<Element
*> elements
;
15488 for (const nsWeakPtr
& ptr
: mTopLayer
) {
15489 if (nsCOMPtr
<Element
> elem
= do_QueryReferent(ptr
)) {
15490 elements
.AppendElement(elem
);
15496 bool Document::TopLayerContains(Element
& aElement
) const {
15497 if (mTopLayer
.IsEmpty()) {
15500 nsWeakPtr weakElement
= do_GetWeakReference(&aElement
);
15501 return mTopLayer
.Contains(weakElement
);
15504 void Document::HideAllPopoversUntil(nsINode
& aEndpoint
,
15505 bool aFocusPreviousElement
,
15506 bool aFireEvents
) {
15507 auto closeAllOpenPopovers
= [&aFocusPreviousElement
, &aFireEvents
,
15508 this]() MOZ_CAN_RUN_SCRIPT_FOR_DEFINITION
{
15509 while (RefPtr
<Element
> topmost
= GetTopmostAutoPopover()) {
15510 HidePopover(*topmost
, aFocusPreviousElement
, aFireEvents
, IgnoreErrors());
15514 if (aEndpoint
.IsElement() && !aEndpoint
.AsElement()->IsPopoverOpen()) {
15518 if (&aEndpoint
== this) {
15519 closeAllOpenPopovers();
15523 // https://github.com/whatwg/html/pull/9198
15524 auto needRepeatingHide
= [&]() {
15525 auto autoList
= AutoPopoverList();
15526 return autoList
.Contains(&aEndpoint
) &&
15527 &aEndpoint
!= autoList
.LastElement();
15530 MOZ_ASSERT((&aEndpoint
)->IsElement() &&
15531 (&aEndpoint
)->AsElement()->IsAutoPopover());
15532 bool repeatingHide
= false;
15533 bool fireEvents
= aFireEvents
;
15535 RefPtr
<const Element
> lastToHide
= nullptr;
15536 bool foundEndpoint
= false;
15537 for (const Element
* popover
: AutoPopoverList()) {
15538 if (popover
== &aEndpoint
) {
15539 foundEndpoint
= true;
15540 } else if (foundEndpoint
) {
15541 lastToHide
= popover
;
15546 if (!foundEndpoint
) {
15547 closeAllOpenPopovers();
15551 while (lastToHide
&& lastToHide
->IsPopoverOpen()) {
15552 RefPtr
<Element
> topmost
= GetTopmostAutoPopover();
15556 HidePopover(*topmost
, aFocusPreviousElement
, fireEvents
, IgnoreErrors());
15559 repeatingHide
= needRepeatingHide();
15560 if (repeatingHide
) {
15561 fireEvents
= false;
15563 } while (repeatingHide
);
15566 void Document::HidePopover(Element
& aPopover
, bool aFocusPreviousElement
,
15567 bool aFireEvents
, ErrorResult
& aRv
) {
15568 RefPtr
<nsGenericHTMLElement
> popoverHTMLEl
=
15569 nsGenericHTMLElement::FromNode(aPopover
);
15570 NS_ASSERTION(popoverHTMLEl
, "Not a HTML element");
15572 if (!popoverHTMLEl
->CheckPopoverValidity(PopoverVisibilityState::Showing
,
15577 bool wasShowingOrHiding
=
15578 popoverHTMLEl
->GetPopoverData()->IsShowingOrHiding();
15579 popoverHTMLEl
->GetPopoverData()->SetIsShowingOrHiding(true);
15580 const bool fireEvents
= aFireEvents
&& !wasShowingOrHiding
;
15581 auto cleanupHidingFlag
= MakeScopeExit([&]() {
15582 if (auto* popoverData
= popoverHTMLEl
->GetPopoverData()) {
15583 popoverData
->SetIsShowingOrHiding(wasShowingOrHiding
);
15584 if (auto* closeWatcher
= popoverData
->GetCloseWatcher()) {
15585 closeWatcher
->Destroy();
15590 if (popoverHTMLEl
->IsAutoPopover()) {
15591 HideAllPopoversUntil(*popoverHTMLEl
, aFocusPreviousElement
, fireEvents
);
15592 if (!popoverHTMLEl
->CheckPopoverValidity(PopoverVisibilityState::Showing
,
15596 // TODO: we can't always guarantee:
15597 // The last item in document's auto popover list is popoverHTMLEl.
15598 // See, https://github.com/whatwg/html/issues/9197
15599 // If popoverHTMLEl is not on top, hide popovers again without firing
15601 if (NS_WARN_IF(GetTopmostAutoPopover() != popoverHTMLEl
)) {
15602 HideAllPopoversUntil(*popoverHTMLEl
, aFocusPreviousElement
, false);
15603 if (!popoverHTMLEl
->CheckPopoverValidity(PopoverVisibilityState::Showing
,
15607 MOZ_ASSERT(GetTopmostAutoPopover() == popoverHTMLEl
,
15608 "popoverHTMLEl should be on top of auto popover list");
15612 auto* data
= popoverHTMLEl
->GetPopoverData();
15613 MOZ_ASSERT(data
, "Should have popover data");
15614 data
->SetInvoker(nullptr);
15616 // Fire beforetoggle event and re-check popover validity.
15618 // Intentionally ignore the return value here as only on open event for
15619 // beforetoggle the cancelable attribute is initialized to true.
15620 popoverHTMLEl
->FireToggleEvent(u
"open"_ns
, u
"closed"_ns
,
15621 u
"beforetoggle"_ns
);
15623 // https://html.spec.whatwg.org/multipage/popover.html#hide-popover-algorithm
15625 // Hide all popovers when beforetoggle shows a popover.
15626 if (popoverHTMLEl
->IsAutoPopover() &&
15627 GetTopmostAutoPopover() != popoverHTMLEl
&&
15628 popoverHTMLEl
->PopoverOpen()) {
15629 HideAllPopoversUntil(*popoverHTMLEl
, aFocusPreviousElement
, false);
15632 if (!popoverHTMLEl
->CheckPopoverValidity(PopoverVisibilityState::Showing
,
15638 RemovePopoverFromTopLayer(aPopover
);
15640 popoverHTMLEl
->PopoverPseudoStateUpdate(false, true);
15641 popoverHTMLEl
->GetPopoverData()->SetPopoverVisibilityState(
15642 PopoverVisibilityState::Hidden
);
15644 // Queue popover toggle event task.
15646 popoverHTMLEl
->QueuePopoverEventTask(PopoverVisibilityState::Showing
);
15649 if (aFocusPreviousElement
) {
15650 popoverHTMLEl
->FocusPreviousElementAfterHidingPopover();
15652 popoverHTMLEl
->ForgetPreviouslyFocusedElementAfterHidingPopover();
15656 nsTArray
<Element
*> Document::AutoPopoverList() const {
15657 nsTArray
<Element
*> elements
;
15658 for (const nsWeakPtr
& ptr
: mTopLayer
) {
15659 if (nsCOMPtr
<Element
> element
= do_QueryReferent(ptr
)) {
15660 if (element
&& element
->IsAutoPopover() && element
->IsPopoverOpen()) {
15661 elements
.AppendElement(element
);
15668 Element
* Document::GetTopmostAutoPopover() const {
15669 for (const nsWeakPtr
& weakPtr
: Reversed(mTopLayer
)) {
15670 nsCOMPtr
<Element
> element(do_QueryReferent(weakPtr
));
15671 if (element
&& element
->IsAutoPopover() && element
->IsPopoverOpen()) {
15678 void Document::AddToAutoPopoverList(Element
& aElement
) {
15679 MOZ_ASSERT(aElement
.IsAutoPopover());
15680 TopLayerPush(aElement
);
15683 void Document::RemoveFromAutoPopoverList(Element
& aElement
) {
15684 MOZ_ASSERT(aElement
.IsAutoPopover());
15685 TopLayerPop(aElement
);
15688 void Document::AddPopoverToTopLayer(Element
& aElement
) {
15689 MOZ_ASSERT(aElement
.GetPopoverData());
15690 TopLayerPush(aElement
);
15693 void Document::RemovePopoverFromTopLayer(Element
& aElement
) {
15694 MOZ_ASSERT(aElement
.GetPopoverData());
15695 TopLayerPop(aElement
);
15698 // Returns true if aDoc browsing context is focused.
15699 bool IsInFocusedTab(Document
* aDoc
) {
15700 BrowsingContext
* bc
= aDoc
->GetBrowsingContext();
15705 nsFocusManager
* fm
= nsFocusManager::GetFocusManager();
15710 if (XRE_IsParentProcess()) {
15711 // Keep dom/tests/mochitest/chrome/test_MozDomFullscreen_event.xhtml happy
15712 // by retaining the old code path for the parent process.
15713 nsIDocShell
* docshell
= aDoc
->GetDocShell();
15717 nsCOMPtr
<nsIDocShellTreeItem
> rootItem
;
15718 docshell
->GetInProcessRootTreeItem(getter_AddRefs(rootItem
));
15722 nsCOMPtr
<nsPIDOMWindowOuter
> rootWin
= rootItem
->GetWindow();
15727 nsCOMPtr
<nsPIDOMWindowOuter
> activeWindow
;
15728 activeWindow
= fm
->GetActiveWindow();
15729 if (!activeWindow
) {
15733 return activeWindow
== rootWin
;
15736 return fm
->GetActiveBrowsingContext() == bc
->Top();
15739 // Returns true if aDoc browsing context is focused and is also active.
15740 bool IsInActiveTab(Document
* aDoc
) {
15741 if (!IsInFocusedTab(aDoc
)) {
15745 BrowsingContext
* bc
= aDoc
->GetBrowsingContext();
15746 MOZ_ASSERT(bc
, "With no BrowsingContext, we should have failed earlier.");
15747 return bc
->IsActive();
15750 void Document::RemoteFrameFullscreenChanged(Element
* aFrameElement
) {
15751 // Ensure the frame element is the fullscreen element in this document.
15752 // If the frame element is already the fullscreen element in this document,
15753 // this has no effect.
15754 auto request
= FullscreenRequest::CreateForRemote(aFrameElement
);
15755 RequestFullscreen(std::move(request
), XRE_IsContentProcess());
15758 void Document::RemoteFrameFullscreenReverted() {
15759 UniquePtr
<FullscreenExit
> exit
= FullscreenExit::CreateForRemote(this);
15760 RestorePreviousFullscreenState(std::move(exit
));
15763 static bool HasFullscreenSubDocument(Document
& aDoc
) {
15764 uint32_t count
= CountFullscreenSubDocuments(aDoc
);
15765 NS_ASSERTION(count
<= 1,
15766 "Fullscreen docs should have at most 1 fullscreen child!");
15770 // Returns nullptr if a request for Fullscreen API is currently enabled
15771 // in the given document. Returns a static string indicates the reason
15772 // why it is not enabled otherwise.
15773 const char* Document::GetFullscreenError(CallerType aCallerType
) {
15774 if (!StaticPrefs::full_screen_api_enabled()) {
15775 return "FullscreenDeniedDisabled";
15778 if (aCallerType
== CallerType::System
) {
15779 // Chrome code can always use the fullscreen API, provided it's not
15780 // explicitly disabled.
15784 if (!IsVisible()) {
15785 return "FullscreenDeniedHidden";
15788 if (!FeaturePolicyUtils::IsFeatureAllowed(this, u
"fullscreen"_ns
)) {
15789 return "FullscreenDeniedFeaturePolicy";
15792 // Ensure that all containing elements are <iframe> and have allowfullscreen
15794 BrowsingContext
* bc
= GetBrowsingContext();
15795 if (!bc
|| !bc
->FullscreenAllowed()) {
15796 return "FullscreenDeniedContainerNotAllowed";
15802 bool Document::FullscreenElementReadyCheck(FullscreenRequest
& aRequest
) {
15803 Element
* elem
= aRequest
.Element();
15804 // Strictly speaking, this isn't part of the fullscreen element ready
15805 // check in the spec, but per steps in the spec, when an element which
15806 // is already the fullscreen element requests fullscreen, nothing
15807 // should change and no event should be dispatched, but we still need
15808 // to resolve the returned promise.
15809 Element
* fullscreenElement
= GetUnretargetedFullscreenElement();
15810 if (elem
== fullscreenElement
) {
15811 aRequest
.MayResolvePromise();
15814 if (!elem
->IsInComposedDoc()) {
15815 aRequest
.Reject("FullscreenDeniedNotInDocument");
15818 if (elem
->IsPopoverOpen()) {
15819 aRequest
.Reject("FullscreenDeniedPopoverOpen");
15822 if (elem
->OwnerDoc() != this) {
15823 aRequest
.Reject("FullscreenDeniedMovedDocument");
15826 if (!GetWindow()) {
15827 aRequest
.Reject("FullscreenDeniedLostWindow");
15830 if (const char* msg
= GetFullscreenError(aRequest
.mCallerType
)) {
15831 aRequest
.Reject(msg
);
15834 if (HasFullscreenSubDocument(*this)) {
15835 aRequest
.Reject("FullscreenDeniedSubDocFullScreen");
15838 if (elem
->IsHTMLElement(nsGkAtoms::dialog
)) {
15839 aRequest
.Reject("FullscreenDeniedHTMLDialog");
15842 if (!nsContentUtils::IsChromeDoc(this) && !IsInFocusedTab(this)) {
15843 aRequest
.Reject("FullscreenDeniedNotFocusedTab");
15849 static nsCOMPtr
<nsPIDOMWindowOuter
> GetRootWindow(Document
* aDoc
) {
15850 MOZ_ASSERT(XRE_IsParentProcess());
15851 nsIDocShell
* docShell
= aDoc
->GetDocShell();
15855 nsCOMPtr
<nsIDocShellTreeItem
> rootItem
;
15856 docShell
->GetInProcessRootTreeItem(getter_AddRefs(rootItem
));
15857 return rootItem
? rootItem
->GetWindow() : nullptr;
15860 static bool ShouldApplyFullscreenDirectly(Document
* aDoc
,
15861 nsPIDOMWindowOuter
* aRootWin
) {
15862 MOZ_ASSERT(XRE_IsParentProcess());
15863 // If we are in the chrome process, and the window has not been in
15864 // fullscreen, we certainly need to make that fullscreen first.
15865 if (!aRootWin
->GetFullScreen()) {
15868 // The iterator not being at end indicates there is still some
15869 // pending fullscreen request relates to this document. We have to
15870 // push the request to the pending queue so requests are handled
15871 // in the correct order.
15872 PendingFullscreenChangeList::Iterator
<FullscreenRequest
> iter(
15873 aDoc
, PendingFullscreenChangeList::eDocumentsWithSameRoot
);
15874 if (!iter
.AtEnd()) {
15878 // Same thing for exits. If we have any pending, we have to push
15879 // to the pending queue.
15880 PendingFullscreenChangeList::Iterator
<FullscreenExit
> iterExit(
15881 aDoc
, PendingFullscreenChangeList::eDocumentsWithSameRoot
);
15882 if (!iterExit
.AtEnd()) {
15886 // We have to apply the fullscreen state directly in this case,
15887 // because nsGlobalWindow::SetFullscreenInternal() will do nothing
15888 // if it is already in fullscreen. If we do not apply the state but
15889 // instead add it to the queue and wait for the window as normal,
15890 // we would get stuck.
15894 static bool CheckFullscreenAllowedElementType(const Element
* elem
) {
15895 // Per spec only HTML, <svg>, and <math> should be allowed, but
15896 // we also need to allow XUL elements right now.
15897 return elem
->IsHTMLElement() || elem
->IsXULElement() ||
15898 elem
->IsSVGElement(nsGkAtoms::svg
) ||
15899 elem
->IsMathMLElement(nsGkAtoms::math
);
15902 void Document::RequestFullscreen(UniquePtr
<FullscreenRequest
> aRequest
,
15903 bool aApplyFullscreenDirectly
) {
15904 if (XRE_IsContentProcess()) {
15905 RequestFullscreenInContentProcess(std::move(aRequest
),
15906 aApplyFullscreenDirectly
);
15908 RequestFullscreenInParentProcess(std::move(aRequest
),
15909 aApplyFullscreenDirectly
);
15913 void Document::RequestFullscreenInContentProcess(
15914 UniquePtr
<FullscreenRequest
> aRequest
, bool aApplyFullscreenDirectly
) {
15915 MOZ_ASSERT(XRE_IsContentProcess());
15917 // If we are in the content process, we can apply the fullscreen
15918 // state directly only if we have been in DOM fullscreen, because
15919 // otherwise we always need to notify the chrome.
15920 if (aApplyFullscreenDirectly
||
15921 nsContentUtils::GetInProcessSubtreeRootDocument(this)->Fullscreen()) {
15922 ApplyFullscreen(std::move(aRequest
));
15926 if (!CheckFullscreenAllowedElementType(aRequest
->Element())) {
15927 aRequest
->Reject("FullscreenDeniedNotHTMLSVGOrMathML");
15931 // We don't need to check element ready before this point, because
15932 // if we called ApplyFullscreen, it would check that for us.
15933 if (!FullscreenElementReadyCheck(*aRequest
)) {
15937 PendingFullscreenChangeList::Add(std::move(aRequest
));
15938 // If we are not the top level process, dispatch an event to make
15939 // our parent process go fullscreen first.
15940 Dispatch(NS_NewRunnableFunction(
15941 "Document::RequestFullscreenInContentProcess", [self
= RefPtr
{this}] {
15942 if (!self
->HasPendingFullscreenRequests()) {
15945 nsContentUtils::DispatchEventOnlyToChrome(
15946 self
, self
, u
"MozDOMFullscreen:Request"_ns
, CanBubble::eYes
,
15947 Cancelable::eNo
, /* DefaultAction */ nullptr);
15951 void Document::RequestFullscreenInParentProcess(
15952 UniquePtr
<FullscreenRequest
> aRequest
, bool aApplyFullscreenDirectly
) {
15953 MOZ_ASSERT(XRE_IsParentProcess());
15954 nsCOMPtr
<nsPIDOMWindowOuter
> rootWin
= GetRootWindow(this);
15956 aRequest
->MayRejectPromise("No active window");
15960 if (aApplyFullscreenDirectly
||
15961 ShouldApplyFullscreenDirectly(this, rootWin
)) {
15962 ApplyFullscreen(std::move(aRequest
));
15966 if (!CheckFullscreenAllowedElementType(aRequest
->Element())) {
15967 aRequest
->Reject("FullscreenDeniedNotHTMLSVGOrMathML");
15971 // See if we're waiting on an exit. If so, just make this one pending.
15972 PendingFullscreenChangeList::Iterator
<FullscreenExit
> iter(
15973 this, PendingFullscreenChangeList::eDocumentsWithSameRoot
);
15974 if (!iter
.AtEnd()) {
15975 PendingFullscreenChangeList::Add(std::move(aRequest
));
15976 rootWin
->SetFullscreenInternal(FullscreenReason::ForFullscreenAPI
, true);
15980 // We don't need to check element ready before this point, because
15981 // if we called ApplyFullscreen, it would check that for us.
15982 if (!FullscreenElementReadyCheck(*aRequest
)) {
15986 PendingFullscreenChangeList::Add(std::move(aRequest
));
15987 // Make the window fullscreen.
15988 rootWin
->SetFullscreenInternal(FullscreenReason::ForFullscreenAPI
, true);
15992 bool Document::HandlePendingFullscreenRequests(Document
* aDoc
) {
15993 bool handled
= false;
15994 PendingFullscreenChangeList::Iterator
<FullscreenRequest
> iter(
15995 aDoc
, PendingFullscreenChangeList::eDocumentsWithSameRoot
);
15996 while (!iter
.AtEnd()) {
15997 UniquePtr
<FullscreenRequest
> request
= iter
.TakeAndNext();
15998 Document
* doc
= request
->Document();
15999 if (doc
->ApplyFullscreen(std::move(request
))) {
16007 void Document::ClearPendingFullscreenRequests(Document
* aDoc
) {
16008 PendingFullscreenChangeList::Iterator
<FullscreenRequest
> iter(
16009 aDoc
, PendingFullscreenChangeList::eInclusiveDescendants
);
16010 while (!iter
.AtEnd()) {
16011 UniquePtr
<FullscreenRequest
> request
= iter
.TakeAndNext();
16012 request
->MayRejectPromise("Fullscreen request aborted");
16016 bool Document::HasPendingFullscreenRequests() {
16017 PendingFullscreenChangeList::Iterator
<FullscreenRequest
> iter(
16018 this, PendingFullscreenChangeList::eDocumentsWithSameRoot
);
16019 return !iter
.AtEnd();
16022 MOZ_CAN_RUN_SCRIPT_BOUNDARY
16023 bool Document::ApplyFullscreen(UniquePtr
<FullscreenRequest
> aRequest
) {
16024 if (!FullscreenElementReadyCheck(*aRequest
)) {
16028 Element
* elem
= aRequest
->Element();
16030 RefPtr
<nsINode
> hideUntil
= elem
->GetTopmostPopoverAncestor(nullptr, false);
16032 hideUntil
= OwnerDoc();
16035 RefPtr
<Document
> doc
= aRequest
->Document();
16036 doc
->HideAllPopoversUntil(*hideUntil
, false, true);
16038 // Stash a reference to any existing fullscreen doc, we'll use this later
16039 // to detect if the origin which is fullscreen has changed.
16040 nsCOMPtr
<Document
> previousFullscreenDoc
= GetFullscreenLeaf(this);
16042 // Stores a list of documents which we must dispatch "fullscreenchange"
16043 // too. We're required by the spec to dispatch the events in root-to-leaf
16044 // order, but we traverse the doctree in a leaf-to-root order, so we save
16045 // references to the documents we must dispatch to so that we get the order
16047 AutoTArray
<Document
*, 8> changed
;
16049 // Remember the root document, so that if a fullscreen document is hidden
16050 // we can reset fullscreen state in the remaining visible fullscreen
16052 Document
* fullScreenRootDoc
=
16053 nsContentUtils::GetInProcessSubtreeRootDocument(this);
16055 // If a document is already in fullscreen, then unlock the mouse pointer
16056 // before setting a new document to fullscreen
16057 PointerLockManager::Unlock("Document::ApplyFullscreen");
16059 // Set the fullscreen element. This sets the fullscreen style on the
16060 // element, and the fullscreen-ancestor styles on ancestors of the element
16061 // in this document.
16062 SetFullscreenElement(*elem
);
16063 // Set the iframe fullscreen flag.
16064 if (auto* iframe
= HTMLIFrameElement::FromNode(elem
)) {
16065 iframe
->SetFullscreenFlag(true);
16067 changed
.AppendElement(this);
16069 // Propagate up the document hierarchy, setting the fullscreen element as
16070 // the element's container in ancestor documents. This also sets the
16071 // appropriate css styles as well. Note we don't propagate down the
16072 // document hierarchy, the fullscreen element (or its container) is not
16073 // visible there. Stop when we reach the root document.
16074 Document
* child
= this;
16076 child
->SetFullscreenRoot(fullScreenRootDoc
);
16078 // When entering fullscreen, reset the RCD's resolution to the intrinsic
16079 // resolution, otherwise the fullscreen content could be sized larger than
16080 // the screen (since fullscreen is implemented using position:fixed and
16081 // fixed elements are sized to the layout viewport).
16082 // This also ensures that things like video controls aren't zoomed in
16083 // when in fullscreen mode.
16084 if (PresShell
* presShell
= child
->GetPresShell()) {
16085 if (RefPtr
<MobileViewportManager
> manager
=
16086 presShell
->GetMobileViewportManager()) {
16087 // Save the previous resolution so it can be restored.
16088 child
->mSavedResolution
= presShell
->GetResolution();
16089 presShell
->SetResolutionAndScaleTo(
16090 manager
->ComputeIntrinsicResolution(),
16091 ResolutionChangeOrigin::MainThreadRestore
);
16095 NS_ASSERTION(child
->GetFullscreenRoot() == fullScreenRootDoc
,
16096 "Fullscreen root should be set!");
16097 if (child
== fullScreenRootDoc
) {
16101 Element
* element
= child
->GetEmbedderElement();
16103 // We've reached the root.No more changes need to be made
16104 // to the top layer stacks of documents further up the tree.
16108 Document
* parent
= child
->GetInProcessParentDocument();
16109 parent
->SetFullscreenElement(*element
);
16110 changed
.AppendElement(parent
);
16114 FullscreenRoots::Add(this);
16116 // If it is the first entry of the fullscreen, trigger an event so
16117 // that the UI can response to this change, e.g. hide chrome, or
16118 // notifying parent process to enter fullscreen. Note that chrome
16119 // code may also want to listen to MozDOMFullscreen:NewOrigin event
16120 // to pop up warning UI.
16121 if (!previousFullscreenDoc
) {
16122 nsContentUtils::DispatchEventOnlyToChrome(
16123 this, elem
, u
"MozDOMFullscreen:Entered"_ns
, CanBubble::eYes
,
16124 Cancelable::eNo
, /* DefaultAction */ nullptr);
16127 // The origin which is fullscreen gets changed. Trigger an event so
16128 // that the chrome knows to pop up a warning UI. Note that
16129 // previousFullscreenDoc == nullptr upon first entry, we show the warning UI
16130 // directly as soon as chrome document goes into fullscreen state. Also note
16131 // that, in a multi-process browser, the code in content process is
16132 // responsible for sending message with the origin to its parent, and the
16133 // parent shouldn't rely on this event itself.
16134 if (aRequest
->mShouldNotifyNewOrigin
&& previousFullscreenDoc
&&
16135 !nsContentUtils::HaveEqualPrincipals(previousFullscreenDoc
, this)) {
16136 DispatchFullscreenNewOriginEvent(this);
16139 // Dispatch "fullscreenchange" events. Note that the loop order is
16140 // reversed so that events are dispatched in the tree order as
16141 // indicated in the spec.
16142 for (Document
* d
: Reversed(changed
)) {
16143 DispatchFullscreenChange(*d
, d
->GetUnretargetedFullscreenElement());
16145 aRequest
->MayResolvePromise();
16149 void Document::ClearOrientationPendingPromise() {
16150 mOrientationPendingPromise
= nullptr;
16153 bool Document::SetOrientationPendingPromise(Promise
* aPromise
) {
16154 if (mIsGoingAway
) {
16158 mOrientationPendingPromise
= aPromise
;
16162 void Document::UpdateVisibilityState(DispatchVisibilityChange aDispatchEvent
) {
16163 dom::VisibilityState oldState
= mVisibilityState
;
16164 mVisibilityState
= ComputeVisibilityState();
16165 if (oldState
!= mVisibilityState
) {
16166 if (aDispatchEvent
== DispatchVisibilityChange::Yes
) {
16167 nsContentUtils::DispatchTrustedEvent(this, this, u
"visibilitychange"_ns
,
16168 CanBubble::eYes
, Cancelable::eNo
);
16170 NotifyActivityChanged();
16171 if (mVisibilityState
== dom::VisibilityState::Visible
) {
16172 MaybeActiveMediaComponents();
16175 bool visible
= !Hidden();
16176 for (auto* listener
: mWorkerListeners
) {
16177 listener
->OnVisible(visible
);
16180 // https://w3c.github.io/screen-wake-lock/#handling-document-loss-of-visibility
16182 UnlockAllWakeLocks(WakeLockType::Screen
);
16187 void Document::AddWorkerDocumentListener(WorkerDocumentListener
* aListener
) {
16188 mWorkerListeners
.Insert(aListener
);
16189 aListener
->OnVisible(!Hidden());
16192 void Document::RemoveWorkerDocumentListener(WorkerDocumentListener
* aListener
) {
16193 mWorkerListeners
.Remove(aListener
);
16196 VisibilityState
Document::ComputeVisibilityState() const {
16197 // We have to check a few pieces of information here:
16198 // 1) Are we in bfcache (!IsVisible())? If so, nothing else matters.
16199 // 2) Do we have an outer window? If not, we're hidden. Note that we don't
16200 // want to use GetWindow here because it does weird groveling for windows
16202 // 3) Is our outer window background? If so, we're hidden.
16203 // Otherwise, we're visible.
16204 if (!IsVisible() || !mWindow
|| !mWindow
->GetOuterWindow() ||
16205 mWindow
->GetOuterWindow()->IsBackground()) {
16206 return dom::VisibilityState::Hidden
;
16209 return dom::VisibilityState::Visible
;
16212 void Document::PostVisibilityUpdateEvent() {
16213 nsCOMPtr
<nsIRunnable
> event
= NewRunnableMethod
<DispatchVisibilityChange
>(
16214 "Document::UpdateVisibilityState", this, &Document::UpdateVisibilityState
,
16215 DispatchVisibilityChange::Yes
);
16216 Dispatch(event
.forget());
16219 void Document::MaybeActiveMediaComponents() {
16220 auto* window
= GetWindow();
16221 if (!window
|| !window
->ShouldDelayMediaFromStart()) {
16224 window
->ActivateMediaComponents();
16227 void Document::DocAddSizeOfExcludingThis(nsWindowSizes
& aWindowSizes
) const {
16228 nsINode::AddSizeOfExcludingThis(aWindowSizes
,
16229 &aWindowSizes
.mDOMSizes
.mDOMOtherSize
);
16231 for (nsIContent
* kid
= GetFirstChild(); kid
; kid
= kid
->GetNextSibling()) {
16232 AddSizeOfNodeTree(*kid
, aWindowSizes
);
16235 // IMPORTANT: for our ComputedValues measurements, we want to measure
16236 // ComputedValues accessible from DOM elements before ComputedValues not
16237 // accessible from DOM elements (i.e. accessible only from the frame tree).
16239 // Therefore, the measurement of the Document superclass must happen after
16240 // the measurement of DOM nodes (above), because Document contains the
16241 // PresShell, which contains the frame tree.
16243 mPresShell
->AddSizeOfIncludingThis(aWindowSizes
);
16247 mStyleSet
->AddSizeOfIncludingThis(aWindowSizes
);
16250 aWindowSizes
.mPropertyTablesSize
+=
16251 mPropertyTable
.SizeOfExcludingThis(aWindowSizes
.mState
.mMallocSizeOf
);
16253 if (EventListenerManager
* elm
= GetExistingListenerManager()) {
16254 aWindowSizes
.mDOMEventListenersCount
+= elm
->ListenerCount();
16257 if (mNodeInfoManager
) {
16258 mNodeInfoManager
->AddSizeOfIncludingThis(aWindowSizes
);
16261 aWindowSizes
.mDOMSizes
.mDOMMediaQueryLists
+=
16262 mDOMMediaQueryLists
.sizeOfExcludingThis(
16263 aWindowSizes
.mState
.mMallocSizeOf
);
16265 for (const MediaQueryList
* mql
: mDOMMediaQueryLists
) {
16266 aWindowSizes
.mDOMSizes
.mDOMMediaQueryLists
+=
16267 mql
->SizeOfExcludingThis(aWindowSizes
.mState
.mMallocSizeOf
);
16270 DocumentOrShadowRoot::AddSizeOfExcludingThis(aWindowSizes
);
16272 for (auto& sheetArray
: mAdditionalSheets
) {
16273 AddSizeOfOwnedSheetArrayExcludingThis(aWindowSizes
, sheetArray
);
16275 // Lumping in the loader with the style-sheets size is not ideal,
16276 // but most of the things in there are in fact stylesheets, so it
16277 // doesn't seem worthwhile to separate it out.
16278 // This can be null if we've already been unlinked.
16280 aWindowSizes
.mLayoutStyleSheetsSize
+=
16281 mCSSLoader
->SizeOfIncludingThis(aWindowSizes
.mState
.mMallocSizeOf
);
16284 aWindowSizes
.mDOMSizes
.mDOMResizeObserverControllerSize
+=
16285 mResizeObservers
.ShallowSizeOfExcludingThis(
16286 aWindowSizes
.mState
.mMallocSizeOf
);
16288 if (mAttributeStyles
) {
16289 aWindowSizes
.mDOMSizes
.mDOMOtherSize
+=
16290 mAttributeStyles
->DOMSizeOfIncludingThis(
16291 aWindowSizes
.mState
.mMallocSizeOf
);
16294 if (mRadioGroupContainer
) {
16295 aWindowSizes
.mDOMSizes
.mDOMOtherSize
+=
16296 mRadioGroupContainer
->SizeOfIncludingThis(
16297 aWindowSizes
.mState
.mMallocSizeOf
);
16300 aWindowSizes
.mDOMSizes
.mDOMOtherSize
+=
16301 mStyledLinks
.ShallowSizeOfExcludingThis(
16302 aWindowSizes
.mState
.mMallocSizeOf
);
16304 // Measurement of the following members may be added later if DMD finds it
16306 // - mMidasCommandManager
16310 void Document::DocAddSizeOfIncludingThis(nsWindowSizes
& aWindowSizes
) const {
16311 aWindowSizes
.mDOMSizes
.mDOMOtherSize
+=
16312 aWindowSizes
.mState
.mMallocSizeOf(this);
16313 DocAddSizeOfExcludingThis(aWindowSizes
);
16316 void Document::AddSizeOfExcludingThis(nsWindowSizes
& aSizes
,
16317 size_t* aNodeSize
) const {
16318 // This AddSizeOfExcludingThis() overrides the one from nsINode. But
16319 // nsDocuments can only appear at the top of the DOM tree, and we use the
16320 // specialized DocAddSizeOfExcludingThis() in that case. So this should never
16326 void Document::AddSizeOfNodeTree(nsINode
& aNode
, nsWindowSizes
& aWindowSizes
) {
16327 size_t nodeSize
= 0;
16328 aNode
.AddSizeOfIncludingThis(aWindowSizes
, &nodeSize
);
16330 // This is where we transfer the nodeSize obtained from
16331 // nsINode::AddSizeOfIncludingThis() to a value in nsWindowSizes.
16332 switch (aNode
.NodeType()) {
16333 case nsINode::ELEMENT_NODE
:
16334 aWindowSizes
.mDOMSizes
.mDOMElementNodesSize
+= nodeSize
;
16336 case nsINode::TEXT_NODE
:
16337 aWindowSizes
.mDOMSizes
.mDOMTextNodesSize
+= nodeSize
;
16339 case nsINode::CDATA_SECTION_NODE
:
16340 aWindowSizes
.mDOMSizes
.mDOMCDATANodesSize
+= nodeSize
;
16342 case nsINode::COMMENT_NODE
:
16343 aWindowSizes
.mDOMSizes
.mDOMCommentNodesSize
+= nodeSize
;
16346 aWindowSizes
.mDOMSizes
.mDOMOtherSize
+= nodeSize
;
16350 if (EventListenerManager
* elm
= aNode
.GetExistingListenerManager()) {
16351 aWindowSizes
.mDOMEventListenersCount
+= elm
->ListenerCount();
16354 if (aNode
.IsContent()) {
16355 nsTArray
<nsIContent
*> anonKids
;
16356 nsContentUtils::AppendNativeAnonymousChildren(aNode
.AsContent(), anonKids
,
16357 nsIContent::eAllChildren
);
16358 for (nsIContent
* anonKid
: anonKids
) {
16359 AddSizeOfNodeTree(*anonKid
, aWindowSizes
);
16362 if (auto* element
= Element::FromNode(aNode
)) {
16363 if (ShadowRoot
* shadow
= element
->GetShadowRoot()) {
16364 AddSizeOfNodeTree(*shadow
, aWindowSizes
);
16369 // NOTE(emilio): If you feel smart and want to change this function to use
16370 // GetNextNode(), think twice, since you'd need to handle <xbl:content> in a
16371 // sane way, and kids of <content> won't point to the parent, so we'd never
16372 // find the root node where we should stop at.
16373 for (nsIContent
* kid
= aNode
.GetFirstChild(); kid
;
16374 kid
= kid
->GetNextSibling()) {
16375 AddSizeOfNodeTree(*kid
, aWindowSizes
);
16379 already_AddRefed
<Document
> Document::Constructor(const GlobalObject
& aGlobal
,
16381 nsCOMPtr
<nsIScriptGlobalObject
> global
=
16382 do_QueryInterface(aGlobal
.GetAsSupports());
16384 rv
.Throw(NS_ERROR_UNEXPECTED
);
16388 nsCOMPtr
<nsIScriptObjectPrincipal
> prin
=
16389 do_QueryInterface(aGlobal
.GetAsSupports());
16391 rv
.Throw(NS_ERROR_UNEXPECTED
);
16395 nsCOMPtr
<nsIURI
> uri
;
16396 NS_NewURI(getter_AddRefs(uri
), "about:blank");
16398 rv
.Throw(NS_ERROR_OUT_OF_MEMORY
);
16402 nsCOMPtr
<Document
> doc
;
16403 nsresult res
= NS_NewDOMDocument(getter_AddRefs(doc
), VoidString(), u
""_ns
,
16404 nullptr, uri
, uri
, prin
->GetPrincipal(),
16405 true, global
, DocumentFlavorPlain
);
16406 if (NS_FAILED(res
)) {
16411 doc
->SetReadyStateInternal(Document::READYSTATE_COMPLETE
);
16413 return doc
.forget();
16416 UniquePtr
<XPathExpression
> Document::CreateExpression(
16417 const nsAString
& aExpression
, XPathNSResolver
* aResolver
, ErrorResult
& rv
) {
16418 return XPathEvaluator()->CreateExpression(aExpression
, aResolver
, rv
);
16421 nsINode
* Document::CreateNSResolver(nsINode
& aNodeResolver
) {
16422 return XPathEvaluator()->CreateNSResolver(aNodeResolver
);
16425 already_AddRefed
<XPathResult
> Document::Evaluate(
16426 JSContext
* aCx
, const nsAString
& aExpression
, nsINode
& aContextNode
,
16427 XPathNSResolver
* aResolver
, uint16_t aType
, JS::Handle
<JSObject
*> aResult
,
16429 return XPathEvaluator()->Evaluate(aCx
, aExpression
, aContextNode
, aResolver
,
16430 aType
, aResult
, rv
);
16433 already_AddRefed
<nsIAppWindow
> Document::GetAppWindowIfToplevelChrome() const {
16434 nsCOMPtr
<nsIDocShellTreeItem
> item
= GetDocShell();
16438 nsCOMPtr
<nsIDocShellTreeOwner
> owner
;
16439 item
->GetTreeOwner(getter_AddRefs(owner
));
16440 nsCOMPtr
<nsIAppWindow
> appWin
= do_GetInterface(owner
);
16444 nsCOMPtr
<nsIDocShell
> appWinShell
;
16445 appWin
->GetDocShell(getter_AddRefs(appWinShell
));
16446 if (!SameCOMIdentity(appWinShell
, item
)) {
16449 return appWin
.forget();
16452 WindowContext
* Document::GetTopLevelWindowContext() const {
16453 WindowContext
* windowContext
= GetWindowContext();
16454 return windowContext
? windowContext
->TopWindowContext() : nullptr;
16457 Document
* Document::GetTopLevelContentDocumentIfSameProcess() {
16460 if (!mLoadedAsData
) {
16463 nsCOMPtr
<nsPIDOMWindowInner
> window
= do_QueryInterface(GetScopeObject());
16468 parent
= window
->GetExtantDoc();
16475 if (parent
->IsTopLevelContentDocument()) {
16479 // If we ever have a non-content parent before we hit a toplevel content
16480 // parent, then we're never going to find one. Just bail.
16481 if (!parent
->IsContentDocument()) {
16485 parent
= parent
->GetInProcessParentDocument();
16491 const Document
* Document::GetTopLevelContentDocumentIfSameProcess() const {
16492 return const_cast<Document
*>(this)->GetTopLevelContentDocumentIfSameProcess();
16495 void Document::PropagateImageUseCounters(Document
* aReferencingDocument
) {
16496 MOZ_ASSERT(IsBeingUsedAsImage());
16497 MOZ_ASSERT(aReferencingDocument
);
16499 if (!aReferencingDocument
->mShouldReportUseCounters
) {
16500 // No need to propagate use counters to a document that itself won't report
16505 MOZ_LOG(gUseCountersLog
, LogLevel::Debug
,
16506 ("PropagateImageUseCounters from %s to %s",
16507 nsContentUtils::TruncatedURLForDisplay(mDocumentURI
).get(),
16508 nsContentUtils::TruncatedURLForDisplay(
16509 aReferencingDocument
->mDocumentURI
)
16512 if (aReferencingDocument
->IsBeingUsedAsImage()) {
16514 "Page use counters from nested image documents may not "
16515 "propagate to the top-level document (bug 1657805)");
16518 SetCssUseCounterBits();
16519 aReferencingDocument
->mChildDocumentUseCounters
|= mUseCounters
;
16520 aReferencingDocument
->mChildDocumentUseCounters
|= mChildDocumentUseCounters
;
16523 bool Document::HasScriptsBlockedBySandbox() const {
16524 return mSandboxFlags
& SANDBOXED_SCRIPTS
;
16527 void Document::SetCssUseCounterBits() {
16528 if (StaticPrefs::layout_css_use_counters_enabled()) {
16529 for (size_t i
= 0; i
< eCSSProperty_COUNT_with_aliases
; ++i
) {
16530 auto id
= nsCSSPropertyID(i
);
16531 if (Servo_IsPropertyIdRecordedInUseCounter(mStyleUseCounters
.get(), id
)) {
16532 SetUseCounter(nsCSSProps::UseCounterFor(id
));
16537 if (StaticPrefs::layout_css_use_counters_unimplemented_enabled()) {
16538 for (size_t i
= 0; i
< size_t(CountedUnknownProperty::Count
); ++i
) {
16539 auto id
= CountedUnknownProperty(i
);
16540 if (Servo_IsUnknownPropertyRecordedInUseCounter(mStyleUseCounters
.get(),
16542 SetUseCounter(UseCounter(eUseCounter_FirstCountedUnknownProperty
+ i
));
16548 void Document::InitUseCounters() {
16549 // We can be called more than once, e.g. when session history navigation shows
16550 // us a second time.
16551 if (mUseCountersInitialized
) {
16554 mUseCountersInitialized
= true;
16556 if (!ShouldIncludeInTelemetry()) {
16560 // Now we know for sure that we should report use counters from this document.
16561 mShouldReportUseCounters
= true;
16563 WindowContext
* top
= GetWindowContextForPageUseCounters();
16565 // This is the case for SVG image documents. They are not displayed in a
16566 // window, but we still do want to record document use counters for them.
16568 // Page use counter propagation is handled in PropagateImageUseCounters,
16569 // so there is no need to use the cross-process machinery to send them.
16570 MOZ_LOG(gUseCountersLog
, LogLevel::Debug
,
16571 ("InitUseCounters for a non-displayed document [%s]",
16572 nsContentUtils::TruncatedURLForDisplay(mDocumentURI
).get()));
16576 RefPtr
<WindowGlobalChild
> wgc
= GetWindowGlobalChild();
16581 MOZ_LOG(gUseCountersLog
, LogLevel::Debug
,
16582 ("InitUseCounters for a displayed document: %" PRIu64
" -> %" PRIu64
16584 wgc
->InnerWindowId(), top
->Id(),
16585 nsContentUtils::TruncatedURLForDisplay(mDocumentURI
).get()));
16587 // Inform the parent process that we will send it page use counters later on.
16588 wgc
->SendExpectPageUseCounters(top
);
16589 mShouldSendPageUseCounters
= true;
16592 // We keep separate counts for individual documents and top-level
16593 // pages to more accurately track how many web pages might break if
16594 // certain features were removed. Consider the case of a single
16595 // HTML document with several SVG images and/or iframes with
16596 // sub-documents of their own. If we maintained a single set of use
16597 // counters and all the sub-documents use a particular feature, then
16598 // telemetry would indicate that we would be breaking N documents if
16599 // that feature were removed. Whereas with a document/top-level
16600 // page split, we can see that N documents would be affected, but
16601 // only a single web page would be affected.
16603 // The difference between the values of these two counts and the
16604 // related use counters below tell us how many pages did *not* use
16605 // the feature in question. For instance, if we see that a given
16606 // session has destroyed 30 content documents, but a particular use
16607 // counter shows only a count of 5, we can infer that the use
16608 // counter was *not* used in 25 of those 30 documents.
16610 // We do things this way, rather than accumulating a boolean flag
16611 // for each use counter, to avoid sending data for features
16612 // that don't get widely used. Doing things in this fashion means
16613 // smaller telemetry payloads and faster processing on the server
16615 void Document::ReportDocumentUseCounters() {
16616 if (!mShouldReportUseCounters
|| mReportedDocumentUseCounters
) {
16620 mReportedDocumentUseCounters
= true;
16622 // Note that a document is being destroyed. See the comment above for how
16623 // use counter data are interpreted relative to this measurement.
16624 glean::use_counter::content_documents_destroyed
.Add();
16626 // Ask all of our resource documents to report their own document use
16628 EnumerateExternalResources([](Document
& aDoc
) {
16629 aDoc
.ReportDocumentUseCounters();
16630 return CallState::Continue
;
16633 // Copy StyleUseCounters into our document use counters.
16634 SetCssUseCounterBits();
16636 Maybe
<nsCString
> urlForLogging
;
16637 const bool dumpCounters
= StaticPrefs::dom_use_counters_dump_document();
16638 if (dumpCounters
) {
16639 urlForLogging
.emplace(
16640 nsContentUtils::TruncatedURLForDisplay(GetDocumentURI()));
16643 // Report our per-document use counters.
16644 for (int32_t c
= 0; c
< eUseCounter_Count
; ++c
) {
16645 auto uc
= static_cast<UseCounter
>(c
);
16646 if (!mUseCounters
[uc
]) {
16650 const char* metricName
= IncrementUseCounter(uc
, /* aIsPage = */ false);
16651 if (dumpCounters
) {
16652 printf_stderr("USE_COUNTER_DOCUMENT: %s - %s\n", metricName
,
16653 urlForLogging
->get());
16658 void Document::ReportLCP() {
16659 const nsDOMNavigationTiming
* timing
= GetNavigationTiming();
16661 if (!ShouldIncludeInTelemetry() || !IsTopLevelContentDocument() || !timing
||
16662 !timing
->DocShellHasBeenActiveSinceNavigationStart()) {
16666 TimeStamp lcpTime
= timing
->GetLargestContentfulRenderTimeStamp();
16672 mozilla::glean::perf::largest_contentful_paint
.AccumulateRawDuration(
16673 lcpTime
- timing
->GetNavigationStartTimeStamp());
16675 if (!GetChannel()) {
16679 nsCOMPtr
<nsITimedChannel
> timedChannel(do_QueryInterface(GetChannel()));
16680 if (!timedChannel
) {
16684 TimeStamp responseStart
;
16685 timedChannel
->GetResponseStart(&responseStart
);
16687 if (!responseStart
) {
16691 mozilla::glean::perf::largest_contentful_paint_from_response_start
16692 .AccumulateRawDuration(lcpTime
- responseStart
);
16694 if (profiler_thread_is_being_profiled_for_markers()) {
16695 MarkerInnerWindowId innerWindowID
=
16696 MarkerInnerWindowIdFromDocShell(GetDocShell());
16697 GetNavigationTiming()->MaybeAddLCPProfilerMarker(innerWindowID
);
16701 void Document::SendPageUseCounters() {
16702 if (!mShouldReportUseCounters
|| !mShouldSendPageUseCounters
) {
16706 // Ask all of our resource documents to send their own document use
16707 // counters to the parent process to be counted as page use counters.
16708 EnumerateExternalResources([](Document
& aDoc
) {
16709 aDoc
.SendPageUseCounters();
16710 return CallState::Continue
;
16713 // Send our use counters to the parent process to accumulate them towards the
16714 // page use counters for the top-level document.
16716 // We take our own document use counters (those in mUseCounters) and any child
16717 // document use counters (those in mChildDocumentUseCounters) that have been
16718 // explicitly propagated up to us, which includes resource documents, static
16719 // clones, and SVG images.
16720 RefPtr
<WindowGlobalChild
> wgc
= GetWindowGlobalChild();
16722 MOZ_ASSERT_UNREACHABLE(
16723 "SendPageUseCounters should be called while we still have access "
16724 "to our WindowContext");
16725 MOZ_LOG(gUseCountersLog
, LogLevel::Debug
,
16726 (" > too late to send page use counters"));
16730 MOZ_LOG(gUseCountersLog
, LogLevel::Debug
,
16731 ("Sending page use counters: from WindowContext %" PRIu64
" [%s]",
16732 wgc
->WindowContext()->Id(),
16733 nsContentUtils::TruncatedURLForDisplay(GetDocumentURI()).get()));
16735 // Copy StyleUseCounters into our document use counters.
16736 SetCssUseCounterBits();
16738 UseCounters counters
= mUseCounters
| mChildDocumentUseCounters
;
16739 wgc
->SendAccumulatePageUseCounters(counters
);
16742 void Document::MaybeRecomputePartitionKey() {
16743 // We only need to recompute the partition key for the top-level content
16745 if (!IsTopLevelContentDocument()) {
16749 // Bail out early if there is no cookieJarSettings for this document. For
16750 // example, a chrome document.
16751 if (!mCookieJarSettings
) {
16755 // Check whether the partition key matches the document's node principal. They
16756 // can be different if the document is sandboxed. In this case, the node
16757 // principal is a null principal. But the partitionKey of the
16758 // cookieJarSettings was derived from the channel URI of the top-level
16759 // loading, which isn't a null principal. Therefore, we need to recompute
16760 // the partition Key from the document's node principal to reflect the actual
16762 nsAutoCString originNoSuffix
;
16763 nsresult rv
= NodePrincipal()->GetOriginNoSuffix(originNoSuffix
);
16764 NS_ENSURE_SUCCESS_VOID(rv
);
16766 nsCOMPtr
<nsIURI
> originURI
;
16767 rv
= NS_NewURI(getter_AddRefs(originURI
), originNoSuffix
);
16768 NS_ENSURE_SUCCESS_VOID(rv
);
16770 // Bail out early if we don't have a principal URI.
16775 OriginAttributes attrs
;
16776 attrs
.SetPartitionKey(originURI
, false);
16778 // We don't need to set the partition key if the cooieJarSettings'
16779 // partitionKey matches the document's node principal.
16780 if (attrs
.mPartitionKey
.Equals(
16781 net::CookieJarSettings::Cast(mCookieJarSettings
)
16782 ->GetPartitionKey())) {
16786 // Set the partition key to the document's node principal. So we will use the
16787 // right partition key afterward.
16788 mozilla::net::CookieJarSettings::Cast(mCookieJarSettings
)
16789 ->SetPartitionKey(originURI
, false);
16792 bool Document::RecomputeResistFingerprinting() {
16793 mOverriddenFingerprintingSettings
.reset();
16794 const bool previous
= mShouldResistFingerprinting
;
16796 RefPtr
<BrowsingContext
> opener
=
16797 GetBrowsingContext() ? GetBrowsingContext()->GetOpener() : nullptr;
16798 // If we have a parent or opener document, defer to it only when we have a
16799 // null principal (e.g. a sandboxed iframe or a data: uri) or when the
16800 // document's principal matches. This means we will defer about:blank,
16801 // about:srcdoc, blob and same-origin iframes/popups to the parent/opener,
16802 // but not cross-origin ones. Cross-origin iframes/popups may inherit a
16803 // CookieJarSettings.mShouldRFP = false bit however, which will be respected.
16804 auto shouldInheritFrom
= [this](Document
* aDoc
) {
16805 return aDoc
&& (this->NodePrincipal()->Equals(aDoc
->NodePrincipal()) ||
16806 this->NodePrincipal()->GetIsNullPrincipal());
16809 if (shouldInheritFrom(mParentDocument
)) {
16811 nsContentUtils::ResistFingerprintingLog(), LogLevel::Debug
,
16812 ("Inside RecomputeResistFingerprinting with URI %s and deferring "
16813 "to parent document %s",
16814 GetDocumentURI() ? GetDocumentURI()->GetSpecOrDefault().get() : "null",
16815 mParentDocument
->GetDocumentURI()->GetSpecOrDefault().get()));
16816 mShouldResistFingerprinting
= mParentDocument
->ShouldResistFingerprinting(
16817 RFPTarget::IsAlwaysEnabledForPrecompute
);
16818 mOverriddenFingerprintingSettings
=
16819 mParentDocument
->mOverriddenFingerprintingSettings
;
16820 } else if (opener
&& shouldInheritFrom(opener
->GetDocument())) {
16822 nsContentUtils::ResistFingerprintingLog(), LogLevel::Debug
,
16823 ("Inside RecomputeResistFingerprinting with URI %s and deferring to "
16824 "opener document %s",
16825 GetDocumentURI() ? GetDocumentURI()->GetSpecOrDefault().get() : "null",
16826 opener
->GetDocument()->GetDocumentURI()->GetSpecOrDefault().get()));
16827 mShouldResistFingerprinting
=
16828 opener
->GetDocument()->ShouldResistFingerprinting(
16829 RFPTarget::IsAlwaysEnabledForPrecompute
);
16830 mOverriddenFingerprintingSettings
=
16831 opener
->GetDocument()->mOverriddenFingerprintingSettings
;
16832 } else if (nsContentUtils::IsChromeDoc(this)) {
16833 MOZ_LOG(nsContentUtils::ResistFingerprintingLog(), LogLevel::Debug
,
16834 ("Inside RecomputeResistFingerprinting with a ChromeDoc"));
16836 mShouldResistFingerprinting
= false;
16837 mOverriddenFingerprintingSettings
.reset();
16838 } else if (mChannel
) {
16839 MOZ_LOG(nsContentUtils::ResistFingerprintingLog(), LogLevel::Debug
,
16840 ("Inside RecomputeResistFingerprinting with URI %s",
16841 GetDocumentURI() ? GetDocumentURI()->GetSpecOrDefault().get()
16843 mShouldResistFingerprinting
= nsContentUtils::ShouldResistFingerprinting(
16844 mChannel
, RFPTarget::IsAlwaysEnabledForPrecompute
);
16846 nsCOMPtr
<nsILoadInfo
> loadInfo
= mChannel
->LoadInfo();
16847 mOverriddenFingerprintingSettings
=
16848 loadInfo
->GetOverriddenFingerprintingSettings();
16850 MOZ_LOG(nsContentUtils::ResistFingerprintingLog(), LogLevel::Debug
,
16851 ("Inside RecomputeResistFingerprinting fallback case."));
16852 // We still preserve the behavior in the fallback case. But, it means there
16853 // might be some cases we haven't considered yet and we need to investigate
16855 mShouldResistFingerprinting
= nsContentUtils::ShouldResistFingerprinting(
16856 mChannel
, RFPTarget::IsAlwaysEnabledForPrecompute
);
16857 mOverriddenFingerprintingSettings
.reset();
16860 MOZ_LOG(nsContentUtils::ResistFingerprintingLog(), LogLevel::Debug
,
16861 ("Finished RecomputeResistFingerprinting with result %x",
16862 mShouldResistFingerprinting
));
16864 bool changed
= previous
!= mShouldResistFingerprinting
;
16866 if (auto win
= nsGlobalWindowInner::Cast(GetInnerWindow())) {
16867 win
->RefreshReduceTimerPrecisionCallerType();
16873 bool Document::ShouldResistFingerprinting(RFPTarget aTarget
) const {
16874 return mShouldResistFingerprinting
&&
16875 nsRFPService::IsRFPEnabledFor(this->IsInPrivateBrowsing(), aTarget
,
16876 mOverriddenFingerprintingSettings
);
16879 void Document::RecordCanvasUsage(CanvasUsage
& aUsage
) {
16880 // Limit the number of recent canvas extraction uses that are tracked.
16881 const size_t kTrackedCanvasLimit
= 8;
16882 // Timeout between different canvas extractions.
16883 const uint64_t kTimeoutUsec
= 3000 * 1000;
16885 uint64_t now
= PR_Now();
16886 if ((mCanvasUsage
.Length() > kTrackedCanvasLimit
) ||
16887 ((now
- mLastCanvasUsage
) > kTimeoutUsec
)) {
16888 mCanvasUsage
.ClearAndRetainStorage();
16891 mCanvasUsage
.AppendElement(aUsage
);
16892 mLastCanvasUsage
= now
;
16894 nsCString originNoSuffix
;
16895 if (NS_FAILED(NodePrincipal()->GetOriginNoSuffix(originNoSuffix
))) {
16899 nsRFPService::MaybeReportCanvasFingerprinter(mCanvasUsage
, GetChannel(),
16903 void Document::RecordFontFingerprinting() {
16904 nsCString originNoSuffix
;
16905 if (NS_FAILED(NodePrincipal()->GetOriginNoSuffix(originNoSuffix
))) {
16909 nsRFPService::MaybeReportFontFingerprinter(GetChannel(), originNoSuffix
);
16912 bool Document::IsInPrivateBrowsing() const { return mIsInPrivateBrowsing
; }
16914 WindowContext
* Document::GetWindowContextForPageUseCounters() const {
16915 if (mDisplayDocument
) {
16916 // If we are a resource document, then go through it to find the
16917 // top-level document.
16918 return mDisplayDocument
->GetWindowContextForPageUseCounters();
16921 if (mOriginalDocument
) {
16922 // For static clones (print preview documents), contribute page use counters
16923 // towards the original document.
16924 return mOriginalDocument
->GetWindowContextForPageUseCounters();
16927 WindowContext
* wc
= GetTopLevelWindowContext();
16928 if (!wc
|| !wc
->GetBrowsingContext()->IsContent()) {
16935 void Document::UpdateIntersections(TimeStamp aNowTime
) {
16936 if (!mIntersectionObservers
.IsEmpty()) {
16937 DOMHighResTimeStamp time
= 0;
16938 if (nsPIDOMWindowInner
* win
= GetInnerWindow()) {
16939 if (Performance
* perf
= win
->GetPerformance()) {
16940 time
= perf
->TimeStampToDOMHighResForRendering(aNowTime
);
16943 for (DOMIntersectionObserver
* observer
: mIntersectionObservers
) {
16944 observer
->Update(*this, time
);
16946 Dispatch(NewRunnableMethod("Document::NotifyIntersectionObservers", this,
16947 &Document::NotifyIntersectionObservers
));
16949 EnumerateSubDocuments([aNowTime
](Document
& aDoc
) {
16950 aDoc
.UpdateIntersections(aNowTime
);
16951 return CallState::Continue
;
16955 static void UpdateEffectsOnBrowsingContext(BrowsingContext
* aBc
,
16956 const IntersectionInput
& aInput
,
16957 bool aIncludeInactive
) {
16958 Element
* el
= aBc
->GetEmbedderElement();
16962 auto* rb
= RemoteBrowser::GetFrom(el
);
16966 const bool isInactiveTop
= aBc
->IsTop() && !aBc
->IsActive();
16967 nsSubDocumentFrame
* subDocFrame
= do_QueryFrame(el
->GetPrimaryFrame());
16968 rb
->UpdateEffects([&] {
16969 if (isInactiveTop
) {
16970 // We don't use the visible rect of top browsers, so if they're inactive
16971 // don't waste time computing it. Note that for OOP iframes, it might seem
16972 // a bit wasteful to bother computing this in background tabs, but:
16973 // * We need it so that child frames know if they're visible or not, in
16974 // case they're preserving layers for example.
16975 // * If we're hidden, our refresh driver is throttled and we don't run
16976 // this code very often anyways.
16977 return EffectsInfo::FullyHidden();
16979 const IntersectionOutput output
= DOMIntersectionObserver::Intersect(
16980 aInput
, *el
, DOMIntersectionObserver::BoxToUse::Content
);
16981 if (!output
.Intersects()) {
16982 // XXX do we want to pass the scale and such down even if out of the
16984 return EffectsInfo::FullyHidden();
16986 MOZ_ASSERT(el
->GetPrimaryFrame(), "How do we intersect without a frame?");
16987 if (MOZ_UNLIKELY(NS_WARN_IF(!subDocFrame
))) {
16988 // <frame> not inside a <frameset> might not create a subdoc frame,
16990 return EffectsInfo::FullyHidden();
16992 Maybe
<nsRect
> visibleRect
= subDocFrame
->GetVisibleRect();
16993 // If we're paginated, we the display list rect might not be reasonable,
16994 // because it is the one from the last display item painted. We assume the
16995 // frame is fully visible, lacking something better.
16996 if (subDocFrame
->PresContext()->IsPaginated()) {
16997 visibleRect
= Some(subDocFrame
->GetDestRect());
16999 if (!visibleRect
) {
17000 // If we have no visible rect (e.g., because we are zero-sized) we
17001 // still want to provide the intersection rect in order to get the
17002 // right throttling behavior.
17003 visibleRect
.emplace(*output
.mIntersectionRect
-
17004 output
.mTargetRect
.TopLeft());
17006 gfx::MatrixScales rasterScale
= subDocFrame
->GetRasterScale();
17007 ParentLayerToScreenScale2D transformToAncestorScale
=
17008 ParentLayerToParentLayerScale(
17009 subDocFrame
->PresShell()->GetCumulativeResolution()) *
17010 nsLayoutUtils::GetTransformToAncestorScaleCrossProcessForFrameMetrics(
17012 return EffectsInfo::VisibleWithinRect(visibleRect
, rasterScale
,
17013 transformToAncestorScale
);
17015 if (subDocFrame
&& (!isInactiveTop
|| aIncludeInactive
)) {
17016 if (nsFrameLoader
* fl
= subDocFrame
->FrameLoader()) {
17017 fl
->UpdatePositionAndSize(subDocFrame
);
17022 void Document::UpdateRemoteFrameEffects(bool aIncludeInactive
) {
17023 auto margin
= DOMIntersectionObserver::LazyLoadingRootMargin();
17024 const IntersectionInput input
= DOMIntersectionObserver::ComputeInput(
17025 *this, /* aRoot = */ nullptr, &margin
);
17026 if (auto* wc
= GetWindowContext()) {
17027 for (const RefPtr
<BrowsingContext
>& child
: wc
->Children()) {
17028 UpdateEffectsOnBrowsingContext(child
, input
, aIncludeInactive
);
17031 if (XRE_IsParentProcess()) {
17032 if (auto* bc
= GetBrowsingContext(); bc
&& bc
->IsTop()) {
17033 bc
->Canonical()->CallOnAllTopDescendants(
17034 [&](CanonicalBrowsingContext
* aDescendant
) {
17035 UpdateEffectsOnBrowsingContext(aDescendant
, input
,
17037 return CallState::Continue
;
17039 /* aIncludeNestedBrowsers = */ false);
17042 EnumerateSubDocuments([aIncludeInactive
](Document
& aDoc
) {
17043 aDoc
.UpdateRemoteFrameEffects(aIncludeInactive
);
17044 return CallState::Continue
;
17048 void Document::SynchronouslyUpdateRemoteBrowserDimensions(
17049 bool aIncludeInactive
) {
17050 FlushPendingNotifications(FlushType::Layout
);
17051 UpdateRemoteFrameEffects(aIncludeInactive
);
17054 void Document::NotifyIntersectionObservers() {
17055 const auto observers
= ToTArray
<nsTArray
<RefPtr
<DOMIntersectionObserver
>>>(
17056 mIntersectionObservers
);
17057 for (const auto& observer
: observers
) {
17058 // MOZ_KnownLive because the 'observers' array guarantees to keep it
17060 MOZ_KnownLive(observer
)->Notify();
17064 DOMIntersectionObserver
& Document::EnsureLazyLoadObserver() {
17065 if (!mLazyLoadObserver
) {
17066 mLazyLoadObserver
= DOMIntersectionObserver::CreateLazyLoadObserver(*this);
17068 return *mLazyLoadObserver
;
17071 void Document::ObserveForLastRememberedSize(Element
& aElement
) {
17072 if (NS_WARN_IF(!IsActive())) {
17075 mElementsObservedForLastRememberedSize
.Insert(&aElement
);
17078 void Document::UnobserveForLastRememberedSize(Element
& aElement
) {
17079 mElementsObservedForLastRememberedSize
.Remove(&aElement
);
17082 void Document::UpdateLastRememberedSizes() {
17083 auto shouldRemoveElement
= [&](auto* element
) {
17084 if (element
->GetComposedDoc() != this) {
17085 element
->RemoveLastRememberedBSize();
17086 element
->RemoveLastRememberedISize();
17089 return !element
->GetPrimaryFrame();
17092 for (auto it
= mElementsObservedForLastRememberedSize
.begin(),
17093 end
= mElementsObservedForLastRememberedSize
.end();
17095 if (shouldRemoveElement(*it
)) {
17096 mElementsObservedForLastRememberedSize
.Remove(it
);
17099 const auto element
= *it
;
17100 MOZ_ASSERT(element
->GetComposedDoc() == this);
17101 nsIFrame
* frame
= element
->GetPrimaryFrame();
17104 // As for ResizeObserver, skip nodes hidden `content-visibility`.
17105 if (frame
->IsHiddenByContentVisibilityOnAnyAncestor()) {
17109 MOZ_ASSERT(!frame
->IsLineParticipant() || frame
->IsReplaced(),
17110 "Should have unobserved non-replaced inline.");
17111 MOZ_ASSERT(!frame
->HidesContent(),
17112 "Should have unobserved element skipping its contents.");
17113 const nsStylePosition
* stylePos
= frame
->StylePosition();
17114 const WritingMode wm
= frame
->GetWritingMode();
17115 bool canUpdateBSize
= stylePos
->ContainIntrinsicBSize(wm
).HasAuto();
17116 bool canUpdateISize
= stylePos
->ContainIntrinsicISize(wm
).HasAuto();
17117 MOZ_ASSERT(canUpdateBSize
|| !element
->HasLastRememberedBSize(),
17118 "Should have removed the last remembered block size.");
17119 MOZ_ASSERT(canUpdateISize
|| !element
->HasLastRememberedISize(),
17120 "Should have removed the last remembered inline size.");
17121 MOZ_ASSERT(canUpdateBSize
|| canUpdateISize
,
17122 "Should have unobserved if we can't update any size.");
17124 AutoTArray
<LogicalPixelSize
, 1> contentSizeList
=
17125 ResizeObserver::CalculateBoxSize(element
,
17126 ResizeObserverBoxOptions::Content_box
,
17127 /* aForceFragmentHandling */ true);
17128 MOZ_ASSERT(!contentSizeList
.IsEmpty());
17130 if (canUpdateBSize
) {
17132 for (const auto& current
: contentSizeList
) {
17133 bSize
+= current
.BSize();
17135 element
->SetLastRememberedBSize(bSize
);
17137 if (canUpdateISize
) {
17139 for (const auto& current
: contentSizeList
) {
17140 iSize
= std::max(iSize
, current
.ISize());
17142 element
->SetLastRememberedISize(iSize
);
17147 void Document::NotifyLayerManagerRecreated() {
17148 NotifyActivityChanged();
17149 EnumerateSubDocuments([](Document
& aSubDoc
) {
17150 aSubDoc
.NotifyLayerManagerRecreated();
17151 return CallState::Continue
;
17155 XPathEvaluator
* Document::XPathEvaluator() {
17156 if (!mXPathEvaluator
) {
17157 mXPathEvaluator
.reset(new dom::XPathEvaluator(this));
17159 return mXPathEvaluator
.get();
17162 already_AddRefed
<nsIDocumentEncoder
> Document::GetCachedEncoder() {
17163 return mCachedEncoder
.forget();
17166 void Document::SetCachedEncoder(already_AddRefed
<nsIDocumentEncoder
> aEncoder
) {
17167 mCachedEncoder
= aEncoder
;
17170 nsILoadContext
* Document::GetLoadContext() const { return mDocumentContainer
; }
17172 nsIDocShell
* Document::GetDocShell() const { return mDocumentContainer
; }
17174 void Document::SetStateObject(nsIStructuredCloneContainer
* scContainer
) {
17175 mStateObjectContainer
= scContainer
;
17176 mCachedStateObject
= JS::UndefinedValue();
17177 mCachedStateObjectValid
= false;
17180 already_AddRefed
<Element
> Document::CreateHTMLElement(nsAtom
* aTag
) {
17181 RefPtr
<mozilla::dom::NodeInfo
> nodeInfo
;
17182 nodeInfo
= mNodeInfoManager
->GetNodeInfo(aTag
, nullptr, kNameSpaceID_XHTML
,
17184 MOZ_ASSERT(nodeInfo
, "GetNodeInfo should never fail");
17186 nsCOMPtr
<Element
> element
;
17187 DebugOnly
<nsresult
> rv
=
17188 NS_NewHTMLElement(getter_AddRefs(element
), nodeInfo
.forget(),
17189 mozilla::dom::NOT_FROM_PARSER
);
17191 MOZ_ASSERT(NS_SUCCEEDED(rv
), "NS_NewHTMLElement should never fail");
17192 return element
.forget();
17195 void AutoWalkBrowsingContextGroup::SuppressBrowsingContext(
17196 BrowsingContext
* aContext
) {
17197 aContext
->PreOrderWalk([&](BrowsingContext
* aBC
) {
17198 if (nsCOMPtr
<nsPIDOMWindowOuter
> win
= aBC
->GetDOMWindow()) {
17199 if (RefPtr
<Document
> doc
= win
->GetExtantDoc()) {
17200 SuppressDocument(doc
);
17201 mDocuments
.AppendElement(doc
);
17207 void AutoWalkBrowsingContextGroup::SuppressBrowsingContextGroup(
17208 BrowsingContextGroup
* aGroup
) {
17209 for (const auto& bc
: aGroup
->Toplevels()) {
17210 SuppressBrowsingContext(bc
);
17214 nsAutoSyncOperation::nsAutoSyncOperation(Document
* aDoc
,
17215 SyncOperationBehavior aSyncBehavior
)
17216 : mSyncBehavior(aSyncBehavior
) {
17217 mMicroTaskLevel
= 0;
17218 if (CycleCollectedJSContext
* ccjs
= CycleCollectedJSContext::Get()) {
17219 mMicroTaskLevel
= ccjs
->MicroTaskLevel();
17220 ccjs
->SetMicroTaskLevel(0);
17223 mBrowsingContext
= aDoc
->GetBrowsingContext();
17224 if (InputTaskManager::CanSuspendInputEvent()) {
17225 if (auto* bcg
= aDoc
->GetDocGroup()->GetBrowsingContextGroup()) {
17226 SuppressBrowsingContextGroup(bcg
);
17228 } else if (mBrowsingContext
) {
17229 SuppressBrowsingContext(mBrowsingContext
->Top());
17231 if (mBrowsingContext
&&
17232 mSyncBehavior
== SyncOperationBehavior::eSuspendInput
&&
17233 InputTaskManager::CanSuspendInputEvent()) {
17234 mBrowsingContext
->Group()->IncInputEventSuspensionLevel();
17239 void nsAutoSyncOperation::SuppressDocument(Document
* aDoc
) {
17240 if (RefPtr
<nsGlobalWindowInner
> win
=
17241 nsGlobalWindowInner::Cast(aDoc
->GetInnerWindow())) {
17242 win
->GetTimeoutManager()->BeginSyncOperation();
17244 aDoc
->SetIsInSyncOperation(true);
17247 void nsAutoSyncOperation::UnsuppressDocument(Document
* aDoc
) {
17248 if (RefPtr
<nsGlobalWindowInner
> win
=
17249 nsGlobalWindowInner::Cast(aDoc
->GetInnerWindow())) {
17250 win
->GetTimeoutManager()->EndSyncOperation();
17252 aDoc
->SetIsInSyncOperation(false);
17255 nsAutoSyncOperation::~nsAutoSyncOperation() {
17256 UnsuppressDocuments();
17257 CycleCollectedJSContext
* ccjs
= CycleCollectedJSContext::Get();
17259 ccjs
->SetMicroTaskLevel(mMicroTaskLevel
);
17261 if (mBrowsingContext
&&
17262 mSyncBehavior
== SyncOperationBehavior::eSuspendInput
&&
17263 InputTaskManager::CanSuspendInputEvent()) {
17264 mBrowsingContext
->Group()->DecInputEventSuspensionLevel();
17268 void Document::SetIsInSyncOperation(bool aSync
) {
17269 if (CycleCollectedJSContext
* ccjs
= CycleCollectedJSContext::Get()) {
17270 ccjs
->UpdateMicroTaskSuppressionGeneration();
17274 ++mInSyncOperationCount
;
17276 --mInSyncOperationCount
;
17280 gfxUserFontSet
* Document::GetUserFontSet() {
17281 if (!mFontFaceSet
) {
17285 return mFontFaceSet
->GetImpl();
17288 void Document::FlushUserFontSet() {
17289 if (!mFontFaceSetDirty
) {
17293 mFontFaceSetDirty
= false;
17295 if (gfxPlatform::GetPlatform()->DownloadableFontsEnabled()) {
17296 nsTArray
<nsFontFaceRuleContainer
> rules
;
17297 RefPtr
<PresShell
> presShell
= GetPresShell();
17299 MOZ_ASSERT(mStyleSetFilled
);
17300 EnsureStyleSet().AppendFontFaceRules(rules
);
17303 if (!mFontFaceSet
&& !rules
.IsEmpty()) {
17304 mFontFaceSet
= FontFaceSet::CreateForDocument(this);
17307 bool changed
= false;
17308 if (mFontFaceSet
) {
17309 changed
= mFontFaceSet
->UpdateRules(rules
);
17312 // We need to enqueue a style change reflow (for later) to
17313 // reflect that we're modifying @font-face rules. (However,
17314 // without a reflow, nothing will happen to start any downloads
17315 // that are needed.)
17316 if (changed
&& presShell
) {
17317 if (nsPresContext
* presContext
= presShell
->GetPresContext()) {
17318 presContext
->UserFontSetUpdated();
17324 void Document::MarkUserFontSetDirty() {
17325 if (mFontFaceSetDirty
) {
17328 mFontFaceSetDirty
= true;
17329 if (PresShell
* presShell
= GetPresShell()) {
17330 presShell
->EnsureStyleFlush();
17334 FontFaceSet
* Document::Fonts() {
17335 if (!mFontFaceSet
) {
17336 mFontFaceSet
= FontFaceSet::CreateForDocument(this);
17337 FlushUserFontSet();
17339 return mFontFaceSet
;
17342 void Document::ReportHasScrollLinkedEffect(const TimeStamp
& aTimeStamp
) {
17343 MOZ_ASSERT(!aTimeStamp
.IsNull());
17345 if (!mLastScrollLinkedEffectDetectionTime
.IsNull() &&
17346 mLastScrollLinkedEffectDetectionTime
>= aTimeStamp
) {
17350 if (mLastScrollLinkedEffectDetectionTime
.IsNull()) {
17351 // Report to console just once.
17352 nsContentUtils::ReportToConsole(
17353 nsIScriptError::warningFlag
, "Async Pan/Zoom"_ns
, this,
17354 nsContentUtils::eLAYOUT_PROPERTIES
, "ScrollLinkedEffectFound3");
17357 mLastScrollLinkedEffectDetectionTime
= aTimeStamp
;
17360 bool Document::HasScrollLinkedEffect() const {
17361 if (nsPresContext
* pc
= GetPresContext()) {
17362 return mLastScrollLinkedEffectDetectionTime
==
17363 pc
->RefreshDriver()->MostRecentRefresh();
17369 void Document::SetSHEntryHasUserInteraction(bool aHasInteraction
) {
17370 if (RefPtr
<WindowContext
> topWc
= GetTopLevelWindowContext()) {
17371 // Setting has user interction on a discarded browsing context has
17373 Unused
<< topWc
->SetSHEntryHasUserInteraction(aHasInteraction
);
17377 bool Document::GetSHEntryHasUserInteraction() {
17378 if (RefPtr
<WindowContext
> topWc
= GetTopLevelWindowContext()) {
17379 return topWc
->GetSHEntryHasUserInteraction();
17384 void Document::SetUserHasInteracted() {
17385 MOZ_LOG(gUserInteractionPRLog
, LogLevel::Debug
,
17386 ("Document %p has been interacted by user.", this));
17388 // We maybe need to update the user-interaction permission.
17389 MaybeStoreUserInteractionAsPermission();
17391 // For purposes of reducing irrelevant session history entries on
17392 // the back button, we annotate entries with whether they had user
17393 // interaction. This is gated on its own flag on the WindowContext
17394 // (instead of mUserHasInteracted) to account for the fact that multiple
17395 // top-level SH entries can be associated with the same document.
17396 // Thus, whenever we create a new SH entry for this document,
17397 // this flag is reset.
17398 if (!GetSHEntryHasUserInteraction()) {
17399 nsIDocShell
* docShell
= GetDocShell();
17401 nsCOMPtr
<nsISHEntry
> currentEntry
;
17404 docShell
->GetCurrentSHEntry(getter_AddRefs(currentEntry
), &oshe
);
17405 if (!NS_WARN_IF(NS_FAILED(rv
)) && currentEntry
) {
17406 currentEntry
->SetHasUserInteraction(true);
17409 SetSHEntryHasUserInteraction(true);
17412 if (mUserHasInteracted
) {
17416 mUserHasInteracted
= true;
17419 nsCOMPtr
<nsILoadInfo
> loadInfo
= mChannel
->LoadInfo();
17420 loadInfo
->SetDocumentHasUserInteracted(true);
17422 // Tell the parent process about user interaction
17423 if (auto* wgc
= GetWindowGlobalChild()) {
17424 wgc
->SendUpdateDocumentHasUserInteracted(true);
17427 MaybeAllowStorageForOpenerAfterUserInteraction();
17430 BrowsingContext
* Document::GetBrowsingContext() const {
17431 return mDocumentContainer
? mDocumentContainer
->GetBrowsingContext()
17435 void Document::NotifyUserGestureActivation(
17436 UserActivation::Modifiers
17437 aModifiers
/* = UserActivation::Modifiers::None() */) {
17438 // https://html.spec.whatwg.org/multipage/interaction.html#activation-notification
17439 // 1. "Assert: document is fully active."
17440 RefPtr
<BrowsingContext
> currentBC
= GetBrowsingContext();
17445 RefPtr
<WindowContext
> currentWC
= GetWindowContext();
17450 // 2. "Let windows be « document's relevant global object"
17451 // Instead of assembling a list, we just call notify for wanted windows as we
17453 currentWC
->NotifyUserGestureActivation(aModifiers
);
17455 // 3. "...windows with the active window of each of document's ancestor
17457 for (WindowContext
* wc
= currentWC
; wc
; wc
= wc
->GetParentWindowContext()) {
17458 wc
->NotifyUserGestureActivation(aModifiers
);
17461 // 4. "windows with the active window of each of document's descendant
17462 // navigables, filtered to include only those navigables whose active
17463 // document's origin is same origin with document's origin"
17464 currentBC
->PreOrderWalk([&](BrowsingContext
* bc
) {
17465 WindowContext
* wc
= bc
->GetCurrentWindowContext();
17470 // Check same-origin as current document
17471 WindowGlobalChild
* wgc
= wc
->GetWindowGlobalChild();
17472 if (!wgc
|| !wgc
->IsSameOriginWith(currentWC
)) {
17476 wc
->NotifyUserGestureActivation(aModifiers
);
17479 // If there has been a user activation, mark the current session history
17480 // entry as having been interacted with.
17481 SetSHEntryHasUserInteraction(true);
17484 bool Document::HasBeenUserGestureActivated() {
17485 RefPtr
<WindowContext
> wc
= GetWindowContext();
17486 return wc
&& wc
->HasBeenUserGestureActivated();
17489 bool Document::ConsumeTextDirectiveUserActivation() {
17493 nsCOMPtr
<nsILoadInfo
> loadInfo
= mChannel
->LoadInfo();
17497 const bool textDirectiveUserActivation
=
17498 loadInfo
->GetTextDirectiveUserActivation();
17499 loadInfo
->SetTextDirectiveUserActivation(false);
17500 return textDirectiveUserActivation
;
17503 DOMHighResTimeStamp
Document::LastUserGestureTimeStamp() {
17504 if (RefPtr
<WindowContext
> wc
= GetWindowContext()) {
17505 if (nsGlobalWindowInner
* innerWindow
= wc
->GetInnerWindow()) {
17506 if (Performance
* perf
= innerWindow
->GetPerformance()) {
17507 return perf
->GetDOMTiming()->TimeStampToDOMHighRes(
17508 wc
->GetUserGestureStart());
17514 "Unable to calculate DOMHighResTimeStamp for LastUserGestureTimeStamp");
17518 void Document::ClearUserGestureActivation() {
17519 if (RefPtr
<BrowsingContext
> bc
= GetBrowsingContext()) {
17521 bc
->PreOrderWalk([&](BrowsingContext
* aBC
) {
17522 if (WindowContext
* windowContext
= aBC
->GetCurrentWindowContext()) {
17523 windowContext
->NotifyResetUserGestureActivation();
17529 bool Document::HasValidTransientUserGestureActivation() const {
17530 RefPtr
<WindowContext
> wc
= GetWindowContext();
17531 return wc
&& wc
->HasValidTransientUserGestureActivation();
17534 bool Document::ConsumeTransientUserGestureActivation() {
17535 RefPtr
<WindowContext
> wc
= GetWindowContext();
17536 return wc
&& wc
->ConsumeTransientUserGestureActivation();
17539 bool Document::GetTransientUserGestureActivationModifiers(
17540 UserActivation::Modifiers
* aModifiers
) {
17541 RefPtr
<WindowContext
> wc
= GetWindowContext();
17542 return wc
&& wc
->GetTransientUserGestureActivationModifiers(aModifiers
);
17545 void Document::SetDocTreeHadMedia() {
17546 RefPtr
<WindowContext
> topWc
= GetTopLevelWindowContext();
17547 if (topWc
&& !topWc
->IsDiscarded() && !topWc
->GetDocTreeHadMedia()) {
17548 MOZ_ALWAYS_SUCCEEDS(topWc
->SetDocTreeHadMedia(true));
17552 void Document::MaybeAllowStorageForOpenerAfterUserInteraction() {
17553 if (!CookieJarSettings()->GetRejectThirdPartyContexts()) {
17557 // This will probably change for project fission, but currently this document
17558 // and the opener are on the same process. In the future, we should make this
17560 nsPIDOMWindowInner
* inner
= GetInnerWindow();
17561 if (NS_WARN_IF(!inner
)) {
17565 // We care about first-party tracking resources only.
17566 if (!nsContentUtils::IsFirstPartyTrackingResourceWindow(inner
)) {
17570 auto* outer
= nsGlobalWindowOuter::Cast(inner
->GetOuterWindow());
17571 if (NS_WARN_IF(!outer
)) {
17575 RefPtr
<BrowsingContext
> openerBC
= outer
->GetOpenerBrowsingContext();
17581 // We want to ensure the following check works for both fission mode and
17582 // non-fission mode:
17583 // "If the opener is not a 3rd party and if this window is not a 3rd party
17584 // with respect to the opener, we should not continue."
17586 // In non-fission mode, the opener and the opened window are in the same
17587 // process, we can use AntiTrackingUtils::IsThirdPartyWindow to do the check.
17588 // In fission mode, if this window is not a 3rd party with respect to the
17589 // opener, they must be in the same process, so we can still use
17590 // IsThirdPartyWindow(openerInner) to continue to check if the opener is a 3rd
17592 if (openerBC
->IsInProcess()) {
17593 nsCOMPtr
<nsPIDOMWindowOuter
> outerOpener
= openerBC
->GetDOMWindow();
17594 if (NS_WARN_IF(!outerOpener
)) {
17598 nsCOMPtr
<nsPIDOMWindowInner
> openerInner
=
17599 outerOpener
->GetCurrentInnerWindow();
17600 if (NS_WARN_IF(!openerInner
)) {
17604 RefPtr
<Document
> openerDocument
= openerInner
->GetExtantDoc();
17605 if (NS_WARN_IF(!openerDocument
)) {
17609 nsCOMPtr
<nsIURI
> openerURI
= openerDocument
->GetDocumentURI();
17610 if (NS_WARN_IF(!openerURI
)) {
17614 // If the opener is not a 3rd party and if this window is not
17615 // a 3rd party with respect to the opener, we should not continue.
17616 if (!AntiTrackingUtils::IsThirdPartyWindow(inner
, openerURI
) &&
17617 !AntiTrackingUtils::IsThirdPartyWindow(openerInner
, nullptr)) {
17622 // We don't care when the asynchronous work finishes here.
17623 Unused
<< StorageAccessAPIHelper::AllowAccessForOnChildProcess(
17624 NodePrincipal(), openerBC
,
17625 ContentBlockingNotifier::eOpenerAfterUserInteraction
);
17630 // Documents can stay alive for days. We don't want to update the permission
17631 // value at any user-interaction, and, using a timer triggered any X seconds
17632 // should be good enough. 'X' is taken from
17633 // privacy.userInteraction.document.interval pref.
17634 // We also want to store the user-interaction before shutting down, and, for
17635 // this reason, this class implements nsIAsyncShutdownBlocker interface.
17636 class UserInteractionTimer final
: public Runnable
,
17637 public nsITimerCallback
,
17638 public nsIAsyncShutdownBlocker
{
17640 NS_DECL_ISUPPORTS_INHERITED
17642 explicit UserInteractionTimer(Document
* aDocument
)
17643 : Runnable("UserInteractionTimer"),
17644 mPrincipal(aDocument
->NodePrincipal()),
17645 mDocument(aDocument
) {
17646 static int32_t userInteractionTimerId
= 0;
17647 // Blocker names must be unique. Let's create it now because when needed,
17648 // the document could be already gone.
17649 mBlockerName
.AppendPrintf("UserInteractionTimer %d for document %p",
17650 ++userInteractionTimerId
, aDocument
);
17652 // For ContentBlockingUserInteraction we care about user-interaction stored
17653 // only for top-level documents and documents with access to the Storage
17655 if (aDocument
->IsTopLevelContentDocument()) {
17656 mShouldRecordContentBlockingUserInteraction
= true;
17659 nsresult rv
= aDocument
->HasStorageAccessSync(hasSA
);
17660 mShouldRecordContentBlockingUserInteraction
= NS_SUCCEEDED(rv
) && hasSA
;
17664 // Runnable interface
17668 uint32_t interval
=
17669 StaticPrefs::privacy_userInteraction_document_interval();
17674 RefPtr
<UserInteractionTimer
> self
= this;
17676 MakeScopeExit([self
] { self
->CancelTimerAndStoreUserInteraction(); });
17678 nsresult rv
= NS_NewTimerWithCallback(
17679 getter_AddRefs(mTimer
), this, interval
* 1000, nsITimer::TYPE_ONE_SHOT
);
17680 NS_ENSURE_SUCCESS(rv
, NS_OK
);
17682 nsCOMPtr
<nsIAsyncShutdownClient
> phase
= GetShutdownPhase();
17683 NS_ENSURE_TRUE(!!phase
, NS_OK
);
17685 rv
= phase
->AddBlocker(this, NS_LITERAL_STRING_FROM_CSTRING(__FILE__
),
17686 __LINE__
, u
"UserInteractionTimer shutdown"_ns
);
17687 NS_ENSURE_SUCCESS(rv
, NS_OK
);
17693 // nsITimerCallback interface
17696 Notify(nsITimer
* aTimer
) override
{
17697 StoreUserInteraction();
17701 #ifdef MOZ_COLLECTING_RUNNABLE_TELEMETRY
17702 using nsINamed::GetName
;
17705 // nsIAsyncShutdownBlocker interface
17708 GetName(nsAString
& aName
) override
{
17709 aName
= mBlockerName
;
17714 BlockShutdown(nsIAsyncShutdownClient
* aClient
) override
{
17715 CancelTimerAndStoreUserInteraction();
17720 GetState(nsIPropertyBag
**) override
{ return NS_OK
; }
17723 ~UserInteractionTimer() = default;
17725 void StoreUserInteraction() {
17726 // Remove the shutting down blocker
17727 nsCOMPtr
<nsIAsyncShutdownClient
> phase
= GetShutdownPhase();
17729 phase
->RemoveBlocker(this);
17732 // If the document is not gone, let's reset its timer flag.
17733 nsCOMPtr
<Document
> document(mDocument
);
17735 if (mShouldRecordContentBlockingUserInteraction
) {
17736 ContentBlockingUserInteraction::Observe(mPrincipal
);
17738 Unused
<< BounceTrackingProtection::RecordUserActivation(
17739 mDocument
->GetWindowContext());
17740 document
->ResetUserInteractionTimer();
17744 void CancelTimerAndStoreUserInteraction() {
17750 StoreUserInteraction();
17753 static already_AddRefed
<nsIAsyncShutdownClient
> GetShutdownPhase() {
17754 nsCOMPtr
<nsIAsyncShutdownService
> svc
= services::GetAsyncShutdownService();
17755 NS_ENSURE_TRUE(!!svc
, nullptr);
17757 nsCOMPtr
<nsIAsyncShutdownClient
> phase
;
17758 nsresult rv
= svc
->GetXpcomWillShutdown(getter_AddRefs(phase
));
17759 NS_ENSURE_SUCCESS(rv
, nullptr);
17761 return phase
.forget();
17764 nsCOMPtr
<nsIPrincipal
> mPrincipal
;
17765 WeakPtr
<Document
> mDocument
;
17766 bool mShouldRecordContentBlockingUserInteraction
= false;
17768 nsCOMPtr
<nsITimer
> mTimer
;
17770 nsString mBlockerName
;
17773 NS_IMPL_ISUPPORTS_INHERITED(UserInteractionTimer
, Runnable
, nsITimerCallback
,
17774 nsIAsyncShutdownBlocker
)
17778 void Document::MaybeStoreUserInteractionAsPermission() {
17779 if (!mUserHasInteracted
) {
17780 // First interaction, let's store this info now.
17781 Unused
<< BounceTrackingProtection::RecordUserActivation(
17782 GetWindowContext());
17784 // For ContentBlockingUserInteraction we care about user-interaction stored
17785 // only for top-level documents and documents with access to the Storage
17787 if (!IsTopLevelContentDocument()) {
17789 nsresult rv
= HasStorageAccessSync(hasSA
);
17790 if (NS_FAILED(rv
) || !hasSA
) {
17794 ContentBlockingUserInteraction::Observe(NodePrincipal());
17798 if (mHasUserInteractionTimerScheduled
) {
17802 nsCOMPtr
<nsIRunnable
> task
= new UserInteractionTimer(this);
17803 nsresult rv
= NS_DispatchToCurrentThreadQueue(task
.forget(), 2500,
17804 EventQueuePriority::Idle
);
17805 if (NS_WARN_IF(NS_FAILED(rv
))) {
17809 // This value will be reset by the timer.
17810 mHasUserInteractionTimerScheduled
= true;
17813 void Document::ResetUserInteractionTimer() {
17814 mHasUserInteractionTimerScheduled
= false;
17817 bool Document::IsExtensionPage() const {
17818 return BasePrincipal::Cast(NodePrincipal())->AddonPolicy();
17821 PermissionDelegateHandler
* Document::GetPermissionDelegateHandler() {
17822 if (!mPermissionDelegateHandler
) {
17823 mPermissionDelegateHandler
= MakeAndAddRef
<PermissionDelegateHandler
>(this);
17826 if (!mPermissionDelegateHandler
->Initialize()) {
17827 mPermissionDelegateHandler
= nullptr;
17830 return mPermissionDelegateHandler
;
17833 void Document::ScheduleResizeObserversNotification() const {
17837 if (nsRefreshDriver
* rd
= mPresShell
->GetRefreshDriver()) {
17838 rd
->EnsureResizeObserverUpdateHappens();
17842 static void FlushLayoutForWholeBrowsingContextTree(Document
& aDoc
) {
17843 const ChangesToFlush
ctf(FlushType::Layout
, /* aFlushAnimations = */ false);
17844 BrowsingContext
* bc
= aDoc
.GetBrowsingContext();
17845 if (bc
&& bc
->GetExtantDocument() == &aDoc
) {
17846 RefPtr
<BrowsingContext
> top
= bc
->Top();
17847 top
->PreOrderWalk([ctf
](BrowsingContext
* aCur
) {
17848 if (Document
* doc
= aCur
->GetExtantDocument()) {
17849 doc
->FlushPendingNotifications(ctf
);
17853 // If there is no browsing context, or we're not the current document of the
17854 // browsing context, then we just flush this document itself.
17855 aDoc
.FlushPendingNotifications(ctf
);
17859 void Document::DetermineProximityToViewportAndNotifyResizeObservers() {
17860 uint32_t shallowestTargetDepth
= 0;
17861 bool initialResetOfScrolledIntoViewFlagsDone
= false;
17863 // Flush layout, so that any callback functions' style changes / resizes
17864 // get a chance to take effect. The callback functions may do changes in its
17865 // sub-documents or ancestors, so flushing layout for the whole browsing
17866 // context tree makes sure we don't miss anyone.
17867 FlushLayoutForWholeBrowsingContextTree(*this);
17869 // Last remembered sizes are recorded "at the time that ResizeObserver
17870 // events are determined and delivered".
17871 // https://drafts.csswg.org/css-sizing-4/#last-remembered
17873 // We do it right after layout to make sure sizes are up-to-date. If we do
17874 // it after determining the proximities to viewport of
17875 // 'content-visibility: auto' nodes, and if one of such node ever becomes
17876 // relevant to the user, then we would be incorrectly recording the size
17877 // of its rendering when it was skipping its content.
17878 UpdateLastRememberedSizes();
17880 if (PresShell
* presShell
= GetPresShell()) {
17881 auto result
= presShell
->DetermineProximityToViewport();
17882 if (result
.mHadInitialDetermination
) {
17885 if (result
.mAnyScrollIntoViewFlag
) {
17886 // Not defined in the spec: It's possible that some elements with
17887 // content-visibility: auto were forced to be visible in order to
17888 // perform scrollIntoView() so clear their flags now and restart the
17890 // See https://github.com/w3c/csswg-drafts/issues/9337
17891 presShell
->ClearTemporarilyVisibleForScrolledIntoViewDescendantFlags();
17892 presShell
->ScheduleContentRelevancyUpdate(
17893 ContentRelevancyReason::Visible
);
17894 if (!initialResetOfScrolledIntoViewFlagsDone
) {
17895 initialResetOfScrolledIntoViewFlagsDone
= true;
17901 // To avoid infinite resize loop, we only gather all active observations
17902 // that have the depth of observed target element more than current
17903 // shallowestTargetDepth.
17904 GatherAllActiveResizeObservations(shallowestTargetDepth
);
17906 if (!HasAnyActiveResizeObservations()) {
17910 DebugOnly
<uint32_t> oldShallowestTargetDepth
= shallowestTargetDepth
;
17911 shallowestTargetDepth
= BroadcastAllActiveResizeObservations();
17912 NS_ASSERTION(oldShallowestTargetDepth
< shallowestTargetDepth
,
17913 "shallowestTargetDepth should be getting strictly deeper");
17916 if (HasAnySkippedResizeObservations()) {
17917 if (nsCOMPtr
<nsPIDOMWindowInner
> window
= GetInnerWindow()) {
17918 // Per spec, we deliver an error if the document has any skipped
17919 // observations. Also, we re-register via ScheduleNotification().
17920 RootedDictionary
<ErrorEventInit
> init(RootingCx());
17921 init
.mMessage
.AssignLiteral(
17922 "ResizeObserver loop completed with undelivered notifications.");
17923 init
.mBubbles
= false;
17924 init
.mCancelable
= false;
17926 nsEventStatus status
= nsEventStatus_eIgnore
;
17927 nsCOMPtr
<nsIScriptGlobalObject
> sgo
= do_QueryInterface(window
);
17929 if (NS_WARN_IF(sgo
->HandleScriptError(init
, &status
))) {
17930 status
= nsEventStatus_eIgnore
;
17933 // We don't fire error events at any global for non-window JS on the main
17937 // We need to deliver pending notifications in next cycle.
17938 ScheduleResizeObserversNotification();
17942 void Document::GatherAllActiveResizeObservations(uint32_t aDepth
) {
17943 for (ResizeObserver
* observer
: mResizeObservers
) {
17944 observer
->GatherActiveObservations(aDepth
);
17948 uint32_t Document::BroadcastAllActiveResizeObservations() {
17949 uint32_t shallowestTargetDepth
= std::numeric_limits
<uint32_t>::max();
17951 // Copy the observers as this invokes the callbacks and could register and
17952 // unregister observers at will.
17953 const auto observers
=
17954 ToTArray
<nsTArray
<RefPtr
<ResizeObserver
>>>(mResizeObservers
);
17955 for (const auto& observer
: observers
) {
17956 // MOZ_KnownLive because 'observers' is guaranteed to keep it
17959 // This can go away once
17960 // https://bugzilla.mozilla.org/show_bug.cgi?id=1620312 is fixed.
17961 uint32_t targetDepth
=
17962 MOZ_KnownLive(observer
)->BroadcastActiveObservations();
17963 if (targetDepth
< shallowestTargetDepth
) {
17964 shallowestTargetDepth
= targetDepth
;
17968 return shallowestTargetDepth
;
17971 bool Document::HasAnySkippedResizeObservations() const {
17972 for (const auto& observer
: mResizeObservers
) {
17973 if (observer
->HasSkippedObservations()) {
17980 bool Document::HasAnyActiveResizeObservations() const {
17981 for (const auto& observer
: mResizeObservers
) {
17982 if (observer
->HasActiveObservations()) {
17989 void Document::ClearStaleServoData() {
17990 DocumentStyleRootIterator
iter(this);
17991 while (Element
* root
= iter
.GetNextStyleRoot()) {
17992 RestyleManager::ClearServoDataFromSubtree(root
);
17996 // https://drafts.csswg.org/css-view-transitions-1/#dom-document-startviewtransition
17997 already_AddRefed
<ViewTransition
> Document::StartViewTransition(
17998 const Optional
<OwningNonNull
<ViewTransitionUpdateCallback
>>& aCallback
) {
18000 RefPtr transition
= new ViewTransition(
18001 *this, aCallback
.WasPassed() ? &aCallback
.Value() : nullptr);
18005 // If document's visibility state is "hidden", then skip transition with an
18006 // "InvalidStateError" DOMException, and return transition.
18007 transition
->SkipTransition(SkipTransitionReason::DocumentHidden
);
18008 return transition
.forget();
18010 if (mActiveViewTransition
) {
18012 // If document's active view transition is not null, then skip that view
18013 // transition with an "AbortError" DOMException in this's relevant Realm.
18014 mActiveViewTransition
->SkipTransition(
18015 SkipTransitionReason::ClobberedActiveTransition
);
18017 // Step 6: Set document's active view transition to transition.
18018 mActiveViewTransition
= transition
;
18021 if (nsRefreshDriver
* rd
= mPresShell
->GetRefreshDriver()) {
18022 rd
->EnsureViewTransitionOperationsHappen();
18025 // Step 7: return transition
18026 return transition
.forget();
18029 void Document::ClearActiveViewTransition() { mActiveViewTransition
= nullptr; }
18031 void Document::PerformPendingViewTransitionOperations() {
18032 if (mActiveViewTransition
) {
18033 mActiveViewTransition
->PerformPendingOperations();
18035 EnumerateSubDocuments([](Document
& aDoc
) {
18036 aDoc
.PerformPendingViewTransitionOperations();
18037 return CallState::Continue
;
18041 Selection
* Document::GetSelection(ErrorResult
& aRv
) {
18042 nsCOMPtr
<nsPIDOMWindowInner
> window
= GetInnerWindow();
18047 if (!window
->IsCurrentInnerWindow()) {
18051 return nsGlobalWindowInner::Cast(window
)->GetSelection(aRv
);
18054 void Document::MakeBrowsingContextNonSynthetic() {
18055 if (BrowsingContext
* bc
= GetBrowsingContext()) {
18056 if (bc
->GetIsSyntheticDocumentContainer()) {
18057 Unused
<< bc
->SetIsSyntheticDocumentContainer(false);
18062 nsresult
Document::HasStorageAccessSync(bool& aHasStorageAccess
) {
18063 // Step 1: check if cookie permissions are available or denied to this
18064 // document's principal
18065 nsCOMPtr
<nsPIDOMWindowInner
> inner
= GetInnerWindow();
18067 aHasStorageAccess
= false;
18070 Maybe
<bool> resultBecauseCookiesApproved
=
18071 StorageAccessAPIHelper::CheckCookiesPermittedDecidesStorageAccessAPI(
18072 CookieJarSettings(), NodePrincipal());
18073 if (resultBecauseCookiesApproved
.isSome()) {
18074 if (resultBecauseCookiesApproved
.value()) {
18075 aHasStorageAccess
= true;
18078 aHasStorageAccess
= false;
18083 // Step 2: Check if the browser settings determine whether or not this
18084 // document has access to its unpartitioned cookies.
18085 bool isThirdPartyDocument
= AntiTrackingUtils::IsThirdPartyDocument(this);
18086 bool isOnThirdPartySkipList
= false;
18088 nsCOMPtr
<nsILoadInfo
> loadInfo
= mChannel
->LoadInfo();
18089 isOnThirdPartySkipList
= loadInfo
->GetStoragePermission() ==
18090 nsILoadInfo::StoragePermissionAllowListed
;
18092 bool isThirdPartyTracker
=
18093 nsContentUtils::IsThirdPartyTrackingResourceWindow(inner
);
18094 Maybe
<bool> resultBecauseBrowserSettings
=
18095 StorageAccessAPIHelper::CheckBrowserSettingsDecidesStorageAccessAPI(
18096 CookieJarSettings(), isThirdPartyDocument
, isOnThirdPartySkipList
,
18097 isThirdPartyTracker
);
18098 if (resultBecauseBrowserSettings
.isSome()) {
18099 if (resultBecauseBrowserSettings
.value()) {
18100 aHasStorageAccess
= true;
18103 aHasStorageAccess
= false;
18108 // Step 3: Check if the location of this call (embedded, top level, same-site)
18109 // determines if cookies are permitted or not.
18110 Maybe
<bool> resultBecauseCallContext
=
18111 StorageAccessAPIHelper::CheckCallingContextDecidesStorageAccessAPI(this,
18113 if (resultBecauseCallContext
.isSome()) {
18114 if (resultBecauseCallContext
.value()) {
18115 aHasStorageAccess
= true;
18118 aHasStorageAccess
= false;
18123 // Step 4: Check if the permissions for this document determine if if has
18124 // access or is denied cookies.
18125 Maybe
<bool> resultBecausePreviousPermission
=
18126 StorageAccessAPIHelper::CheckExistingPermissionDecidesStorageAccessAPI(
18128 if (resultBecausePreviousPermission
.isSome()) {
18129 if (resultBecausePreviousPermission
.value()) {
18130 aHasStorageAccess
= true;
18133 aHasStorageAccess
= false;
18137 // If you get here, we default to not giving you permission.
18138 aHasStorageAccess
= false;
18142 already_AddRefed
<mozilla::dom::Promise
> Document::HasStorageAccess(
18143 mozilla::ErrorResult
& aRv
) {
18144 nsIGlobalObject
* global
= GetScopeObject();
18146 aRv
.Throw(NS_ERROR_NOT_AVAILABLE
);
18150 RefPtr
<Promise
> promise
=
18151 Promise::Create(global
, aRv
, Promise::ePropagateUserInteraction
);
18152 if (aRv
.Failed()) {
18156 if (!IsCurrentActiveDocument()) {
18157 promise
->MaybeRejectWithInvalidStateError(
18158 "hasStorageAccess requires an active document");
18159 return promise
.forget();
18162 bool hasStorageAccess
;
18163 nsresult rv
= HasStorageAccessSync(hasStorageAccess
);
18164 if (NS_FAILED(rv
)) {
18165 promise
->MaybeRejectWithUndefined();
18167 promise
->MaybeResolve(hasStorageAccess
);
18170 return promise
.forget();
18173 RefPtr
<Document::GetContentBlockingEventsPromise
>
18174 Document::GetContentBlockingEvents() {
18175 RefPtr
<WindowGlobalChild
> wgc
= GetWindowGlobalChild();
18180 return wgc
->SendGetContentBlockingEvents()->Then(
18181 GetCurrentSerialEventTarget(), __func__
,
18182 [](const WindowGlobalChild::GetContentBlockingEventsPromise::
18183 ResolveOrRejectValue
& aValue
) {
18184 if (aValue
.IsResolve()) {
18185 return Document::GetContentBlockingEventsPromise::CreateAndResolve(
18186 aValue
.ResolveValue(), __func__
);
18189 return Document::GetContentBlockingEventsPromise::CreateAndReject(
18194 StorageAccessAPIHelper::PerformPermissionGrant
18195 Document::CreatePermissionGrantPromise(
18196 nsPIDOMWindowInner
* aInnerWindow
, nsIPrincipal
* aPrincipal
,
18197 bool aHasUserInteraction
, bool aRequireUserInteraction
,
18198 const Maybe
<nsCString
>& aTopLevelBaseDomain
, bool aFrameOnly
) {
18199 MOZ_ASSERT(aInnerWindow
);
18200 MOZ_ASSERT(aPrincipal
);
18201 RefPtr
<Document
> self(this);
18202 RefPtr
<nsPIDOMWindowInner
> inner(aInnerWindow
);
18203 RefPtr
<nsIPrincipal
> principal(aPrincipal
);
18205 return [inner
, self
, principal
, aHasUserInteraction
, aRequireUserInteraction
,
18206 aTopLevelBaseDomain
, aFrameOnly
]() {
18207 RefPtr
<StorageAccessAPIHelper::StorageAccessPermissionGrantPromise::Private
>
18208 p
= new StorageAccessAPIHelper::StorageAccessPermissionGrantPromise::
18211 // Before we prompt, see if we are same-site
18213 nsIChannel
* channel
= self
->GetChannel();
18215 nsCOMPtr
<nsILoadInfo
> loadInfo
= channel
->LoadInfo();
18216 if (!loadInfo
->GetIsThirdPartyContextToTopWindow()) {
18217 p
->Resolve(StorageAccessAPIHelper::eAllow
, __func__
);
18223 RefPtr
<PWindowGlobalChild::GetStorageAccessPermissionPromise
> promise
;
18224 // Test the permission
18225 MOZ_ASSERT(XRE_IsContentProcess());
18227 WindowGlobalChild
* wgc
= inner
->GetWindowGlobalChild();
18230 promise
= wgc
->SendGetStorageAccessPermission(true);
18231 MOZ_ASSERT(promise
);
18233 GetCurrentSerialEventTarget(), __func__
,
18234 [self
, p
, inner
, principal
, aHasUserInteraction
,
18235 aRequireUserInteraction
, aTopLevelBaseDomain
,
18236 aFrameOnly
](uint32_t aAction
) {
18237 if (aAction
== nsIPermissionManager::ALLOW_ACTION
) {
18238 p
->Resolve(StorageAccessAPIHelper::eAllow
, __func__
);
18241 if (aAction
== nsIPermissionManager::DENY_ACTION
) {
18242 p
->Reject(false, __func__
);
18246 // We require user activation before conducting a permission request
18248 // https://privacycg.github.io/storage-access/#dom-document-requeststorageaccess
18249 // where we "If has transient activation is false: ..." immediately
18250 // before we "Let permissionState be the result of requesting
18251 // permission to use "storage-access"" from in parallel.
18252 if (!aHasUserInteraction
&& aRequireUserInteraction
) {
18253 // Report an error to the console for this case
18254 nsContentUtils::ReportToConsole(
18255 nsIScriptError::errorFlag
,
18256 nsLiteralCString("requestStorageAccess"), self
,
18257 nsContentUtils::eDOM_PROPERTIES
,
18258 "RequestStorageAccessUserGesture");
18259 p
->Reject(false, __func__
);
18263 // Create the user prompt
18264 RefPtr
<StorageAccessPermissionRequest
> sapr
=
18265 StorageAccessPermissionRequest::Create(
18266 inner
, principal
, aTopLevelBaseDomain
, aFrameOnly
,
18269 Telemetry::AccumulateCategorical(
18270 Telemetry::LABELS_STORAGE_ACCESS_API_UI::Allow
);
18271 p
->Resolve(StorageAccessAPIHelper::eAllow
, __func__
);
18275 Telemetry::AccumulateCategorical(
18276 Telemetry::LABELS_STORAGE_ACCESS_API_UI::Deny
);
18277 p
->Reject(false, __func__
);
18280 using PromptResult
= ContentPermissionRequestBase::PromptResult
;
18281 PromptResult pr
= sapr
->CheckPromptPrefs();
18283 if (pr
== PromptResult::Pending
) {
18284 // We're about to show a prompt, record the request attempt
18285 Telemetry::AccumulateCategorical(
18286 Telemetry::LABELS_STORAGE_ACCESS_API_UI::Request
);
18289 // Try to auto-grant the storage access so the user doesn't see the
18291 self
->AutomaticStorageAccessPermissionCanBeGranted(
18292 aHasUserInteraction
)
18294 GetCurrentSerialEventTarget(), __func__
,
18295 // If the autogrant check didn't fail, call this function
18297 inner
](const Document::
18298 AutomaticStorageAccessPermissionGrantPromise::
18299 ResolveOrRejectValue
& aValue
) -> void {
18300 // Make a copy because we can't modified copy-captured
18301 // lambda variables.
18302 PromptResult pr2
= pr
;
18304 // If the user didn't already click "allow" and we can
18305 // autogrant, do that!
18306 bool storageAccessCanBeGrantedAutomatically
=
18307 aValue
.IsResolve() && aValue
.ResolveValue();
18308 bool autoGrant
= false;
18309 if (pr2
== PromptResult::Pending
&&
18310 storageAccessCanBeGrantedAutomatically
) {
18311 pr2
= PromptResult::Granted
;
18314 Telemetry::AccumulateCategorical(
18315 Telemetry::LABELS_STORAGE_ACCESS_API_UI::
18316 AllowAutomatically
);
18319 // If we can complete the permission request, do so.
18320 if (pr2
!= PromptResult::Pending
) {
18321 MOZ_ASSERT_IF(pr2
!= PromptResult::Granted
,
18322 pr2
== PromptResult::Denied
);
18323 if (pr2
== PromptResult::Granted
) {
18324 StorageAccessAPIHelper::StorageAccessPromptChoices
18325 choice
= StorageAccessAPIHelper::eAllow
;
18327 choice
= StorageAccessAPIHelper::eAllowAutoGrant
;
18330 p
->Resolve(choice
, __func__
);
18332 // We capture sapr here to prevent it from destructing
18333 // before the callbacks complete.
18334 sapr
->MaybeDelayAutomaticGrants()->Then(
18335 GetCurrentSerialEventTarget(), __func__
,
18336 [p
, sapr
, choice
] {
18337 p
->Resolve(choice
, __func__
);
18339 [p
, sapr
] { p
->Reject(false, __func__
); });
18343 p
->Reject(false, __func__
);
18347 // If we get here, the auto-decision failed and we need to
18348 // wait for the user prompt to complete.
18349 sapr
->RequestDelayedTask(
18350 GetMainThreadSerialEventTarget(),
18351 ContentPermissionRequestBase::DelayedTaskType::Request
);
18354 [p
](mozilla::ipc::ResponseRejectReason aError
) {
18355 p
->Reject(false, __func__
);
18363 already_AddRefed
<mozilla::dom::Promise
> Document::RequestStorageAccess(
18364 mozilla::ErrorResult
& aRv
) {
18365 nsIGlobalObject
* global
= GetScopeObject();
18367 aRv
.Throw(NS_ERROR_NOT_AVAILABLE
);
18371 RefPtr
<Promise
> promise
= Promise::Create(global
, aRv
);
18372 if (aRv
.Failed()) {
18376 if (!IsCurrentActiveDocument()) {
18377 promise
->MaybeRejectWithInvalidStateError(
18378 "requestStorageAccess requires an active document");
18379 return promise
.forget();
18382 // Get a pointer to the inner window- We need this for convenience sake
18383 RefPtr
<nsPIDOMWindowInner
> inner
= GetInnerWindow();
18385 ConsumeTransientUserGestureActivation();
18386 promise
->MaybeRejectWithNotAllowedError(
18387 "requestStorageAccess not allowed"_ns
);
18388 return promise
.forget();
18391 // Step 1: Check if the principal calling this has a permission that lets
18392 // them use cookies or forbids them from using cookies.
18393 // This is outside of the spec of the StorageAccess API, but makes the return
18394 // values to have proper semantics.
18395 Maybe
<bool> resultBecauseCookiesApproved
=
18396 StorageAccessAPIHelper::CheckCookiesPermittedDecidesStorageAccessAPI(
18397 CookieJarSettings(), NodePrincipal());
18398 if (resultBecauseCookiesApproved
.isSome()) {
18399 if (resultBecauseCookiesApproved
.value()) {
18400 promise
->MaybeResolveWithUndefined();
18401 return promise
.forget();
18403 ConsumeTransientUserGestureActivation();
18404 promise
->MaybeRejectWithNotAllowedError(
18405 "requestStorageAccess not allowed"_ns
);
18406 return promise
.forget();
18410 // Step 2: Check if the browser settings always allow or deny cookies.
18411 // We should always return a resolved promise if the cookieBehavior is ACCEPT.
18412 // This is outside of the spec of the StorageAccess API, but makes the return
18413 // values to have proper semantics.
18414 bool isThirdPartyDocument
= AntiTrackingUtils::IsThirdPartyDocument(this);
18415 bool isOnThirdPartySkipList
= false;
18417 nsCOMPtr
<nsILoadInfo
> loadInfo
= mChannel
->LoadInfo();
18418 isOnThirdPartySkipList
= loadInfo
->GetStoragePermission() ==
18419 nsILoadInfo::StoragePermissionAllowListed
;
18421 bool isThirdPartyTracker
=
18422 nsContentUtils::IsThirdPartyTrackingResourceWindow(inner
);
18423 Maybe
<bool> resultBecauseBrowserSettings
=
18424 StorageAccessAPIHelper::CheckBrowserSettingsDecidesStorageAccessAPI(
18425 CookieJarSettings(), isThirdPartyDocument
, isOnThirdPartySkipList
,
18426 isThirdPartyTracker
);
18427 if (resultBecauseBrowserSettings
.isSome()) {
18428 if (resultBecauseBrowserSettings
.value()) {
18429 promise
->MaybeResolveWithUndefined();
18430 return promise
.forget();
18432 ConsumeTransientUserGestureActivation();
18433 promise
->MaybeRejectWithNotAllowedError(
18434 "requestStorageAccess not allowed"_ns
);
18435 return promise
.forget();
18439 // Step 3: Check if the Document calling requestStorageAccess has anything to
18440 // gain from storage access. It should be embedded, non-null, etc.
18441 Maybe
<bool> resultBecauseCallContext
=
18442 StorageAccessAPIHelper::CheckCallingContextDecidesStorageAccessAPI(this,
18444 if (resultBecauseCallContext
.isSome()) {
18445 if (resultBecauseCallContext
.value()) {
18446 promise
->MaybeResolveWithUndefined();
18447 return promise
.forget();
18449 ConsumeTransientUserGestureActivation();
18450 promise
->MaybeRejectWithNotAllowedError(
18451 "requestStorageAccess not allowed"_ns
);
18452 return promise
.forget();
18456 // Step 4: Check if we already allowed or denied storage access for this
18457 // document's storage key.
18458 Maybe
<bool> resultBecausePreviousPermission
=
18459 StorageAccessAPIHelper::CheckExistingPermissionDecidesStorageAccessAPI(
18461 if (resultBecausePreviousPermission
.isSome()) {
18462 if (resultBecausePreviousPermission
.value()) {
18463 promise
->MaybeResolveWithUndefined();
18464 return promise
.forget();
18466 ConsumeTransientUserGestureActivation();
18467 promise
->MaybeRejectWithNotAllowedError(
18468 "requestStorageAccess not allowed"_ns
);
18469 return promise
.forget();
18473 // Get pointers to some objects that will be used in the async portion
18474 RefPtr
<BrowsingContext
> bc
= GetBrowsingContext();
18475 RefPtr
<nsGlobalWindowOuter
> outer
=
18476 nsGlobalWindowOuter::Cast(inner
->GetOuterWindow());
18478 ConsumeTransientUserGestureActivation();
18479 promise
->MaybeRejectWithNotAllowedError(
18480 "requestStorageAccess not allowed"_ns
);
18481 return promise
.forget();
18483 RefPtr
<Document
> self(this);
18485 // Step 5. Start an async call to request storage access. This will either
18486 // perform an automatic decision or notify the user, then perform some follow
18487 // on work changing state to reflect the result of the API. If it resolves,
18488 // the request was granted. If it rejects it was denied.
18489 StorageAccessAPIHelper::RequestStorageAccessAsyncHelper(
18490 this, inner
, bc
, NodePrincipal(),
18491 self
->HasValidTransientUserGestureActivation(), true, true,
18492 ContentBlockingNotifier::eStorageAccessAPI
, true)
18494 GetCurrentSerialEventTarget(), __func__
,
18495 [inner
] { return inner
->SaveStorageAccessPermissionGranted(); },
18497 return GenericPromise::CreateAndReject(NS_ERROR_FAILURE
, __func__
);
18500 GetCurrentSerialEventTarget(), __func__
,
18501 [promise
] { promise
->MaybeResolveWithUndefined(); },
18503 self
->ConsumeTransientUserGestureActivation();
18504 promise
->MaybeRejectWithNotAllowedError(
18505 "requestStorageAccess not allowed"_ns
);
18508 return promise
.forget();
18511 already_AddRefed
<mozilla::dom::Promise
> Document::RequestStorageAccessForOrigin(
18512 const nsAString
& aThirdPartyOrigin
, const bool aRequireUserActivation
,
18513 mozilla::ErrorResult
& aRv
) {
18514 nsIGlobalObject
* global
= GetScopeObject();
18516 aRv
.Throw(NS_ERROR_NOT_AVAILABLE
);
18519 RefPtr
<Promise
> promise
= Promise::Create(global
, aRv
);
18520 if (aRv
.Failed()) {
18524 // Step 0: Check that we have user activation before proceeding to prevent
18525 // rapid calls to the API to leak information.
18526 if (aRequireUserActivation
&& !HasValidTransientUserGestureActivation()) {
18527 // Report an error to the console for this case
18528 nsContentUtils::ReportToConsole(nsIScriptError::errorFlag
,
18529 nsLiteralCString("requestStorageAccess"),
18530 this, nsContentUtils::eDOM_PROPERTIES
,
18531 "RequestStorageAccessUserGesture");
18532 ConsumeTransientUserGestureActivation();
18533 promise
->MaybeRejectWithNotAllowedError(
18534 "requestStorageAccess not allowed"_ns
);
18535 return promise
.forget();
18538 // Step 1: Check if the provided URI is different-site to this Document
18539 nsCOMPtr
<nsIURI
> thirdPartyURI
;
18540 nsresult rv
= NS_NewURI(getter_AddRefs(thirdPartyURI
), aThirdPartyOrigin
);
18541 if (NS_WARN_IF(NS_FAILED(rv
))) {
18545 bool isThirdPartyDocument
;
18546 rv
= NodePrincipal()->IsThirdPartyURI(thirdPartyURI
, &isThirdPartyDocument
);
18547 if (NS_WARN_IF(NS_FAILED(rv
))) {
18551 Maybe
<bool> resultBecauseBrowserSettings
=
18552 StorageAccessAPIHelper::CheckBrowserSettingsDecidesStorageAccessAPI(
18553 CookieJarSettings(), isThirdPartyDocument
, false, true);
18554 if (resultBecauseBrowserSettings
.isSome()) {
18555 if (resultBecauseBrowserSettings
.value()) {
18556 promise
->MaybeResolveWithUndefined();
18557 return promise
.forget();
18559 ConsumeTransientUserGestureActivation();
18560 promise
->MaybeRejectWithNotAllowedError(
18561 "requestStorageAccess not allowed"_ns
);
18562 return promise
.forget();
18565 // Step 2: Check that this Document is same-site to the top, and check that
18566 // we have user activation if we require it.
18567 Maybe
<bool> resultBecauseCallContext
= StorageAccessAPIHelper::
18568 CheckSameSiteCallingContextDecidesStorageAccessAPI(
18569 this, aRequireUserActivation
);
18570 if (resultBecauseCallContext
.isSome()) {
18571 if (resultBecauseCallContext
.value()) {
18572 promise
->MaybeResolveWithUndefined();
18573 return promise
.forget();
18575 ConsumeTransientUserGestureActivation();
18576 promise
->MaybeRejectWithNotAllowedError(
18577 "requestStorageAccess not allowed"_ns
);
18578 return promise
.forget();
18581 // Step 3: Get some useful variables that can be captured by the lambda for
18582 // the asynchronous portion
18583 RefPtr
<BrowsingContext
> bc
= GetBrowsingContext();
18584 nsCOMPtr
<nsPIDOMWindowInner
> inner
= GetInnerWindow();
18586 ConsumeTransientUserGestureActivation();
18587 promise
->MaybeRejectWithNotAllowedError(
18588 "requestStorageAccess not allowed"_ns
);
18589 return promise
.forget();
18591 RefPtr
<nsGlobalWindowOuter
> outer
=
18592 nsGlobalWindowOuter::Cast(inner
->GetOuterWindow());
18594 ConsumeTransientUserGestureActivation();
18595 promise
->MaybeRejectWithNotAllowedError(
18596 "requestStorageAccess not allowed"_ns
);
18597 return promise
.forget();
18599 nsCOMPtr
<nsIPrincipal
> principal
= BasePrincipal::CreateContentPrincipal(
18600 thirdPartyURI
, NodePrincipal()->OriginAttributesRef());
18602 ConsumeTransientUserGestureActivation();
18603 promise
->MaybeRejectWithNotAllowedError(
18604 "requestStorageAccess not allowed"_ns
);
18605 return promise
.forget();
18608 RefPtr
<Document
> self(this);
18609 bool hasUserActivation
= HasValidTransientUserGestureActivation();
18611 // Consume user activation before entering the async part of this method.
18612 // This prevents usage of other transient activation-gated APIs.
18613 ConsumeTransientUserGestureActivation();
18615 ContentChild
* cc
= ContentChild::GetSingleton();
18617 // TODO(bug 1778561): Make this work in non-content processes.
18618 promise
->MaybeRejectWithUndefined();
18619 return promise
.forget();
18622 // Step 4a: Start the async part of this function. Check the cookie
18623 // permission, but this can't be done in this process. We needs the cookie
18624 // permission of the URL as if it were embedded on this page, so we need to
18625 // make this check in the ContentParent.
18626 StorageAccessAPIHelper::
18627 AsyncCheckCookiesPermittedDecidesStorageAccessAPIOnChildProcess(
18628 GetBrowsingContext(), principal
)
18630 GetCurrentSerialEventTarget(), __func__
,
18631 [inner
, thirdPartyURI
, bc
, principal
, hasUserActivation
,
18632 aRequireUserActivation
, self
,
18633 promise
](Maybe
<bool> cookieResult
) {
18634 // Handle the result of the cookie permission check that took
18635 // place in the ContentParent.
18636 if (cookieResult
.isSome()) {
18637 if (cookieResult
.value()) {
18638 return MozPromise
<int, bool, true>::CreateAndResolve(
18641 return MozPromise
<int, bool, true>::CreateAndReject(false,
18645 // Step 4b: Check for the existing storage access permission
18646 nsAutoCString type
;
18647 bool ok
= AntiTrackingUtils::CreateStoragePermissionKey(
18650 return MozPromise
<int, bool, true>::CreateAndReject(false,
18653 if (AntiTrackingUtils::CheckStoragePermission(
18654 self
->NodePrincipal(), type
,
18655 self
->IsInPrivateBrowsing(), nullptr, 0)) {
18656 return MozPromise
<int, bool, true>::CreateAndResolve(
18660 // Step 4c: Try to request storage access, either automatically
18661 // or with a user-prompt. This is the part that is async in the
18662 // typical requestStorageAccess function.
18663 return StorageAccessAPIHelper::RequestStorageAccessAsyncHelper(
18664 self
, inner
, bc
, principal
, hasUserActivation
,
18665 aRequireUserActivation
, false,
18666 ContentBlockingNotifier::
18667 ePrivilegeStorageAccessForOriginAPI
,
18670 // If the IPC rejects, we should reject our promise here which
18671 // will cause a rejection of the promise we already returned
18673 return MozPromise
<int, bool, true>::CreateAndReject(false,
18677 GetCurrentSerialEventTarget(), __func__
,
18678 // If the previous handlers resolved, we should reinstate user
18679 // activation and resolve the promise we returned in Step 5.
18680 [self
, inner
, promise
] {
18681 inner
->SaveStorageAccessPermissionGranted();
18682 self
->NotifyUserGestureActivation();
18683 promise
->MaybeResolveWithUndefined();
18685 // If the previous handler rejected, we should reject the promise
18686 // returned by this function.
18688 promise
->MaybeRejectWithNotAllowedError(
18689 "requestStorageAccess not allowed"_ns
);
18692 // Step 5: While the async stuff is happening, we should return the promise so
18693 // our caller can continue executing.
18694 return promise
.forget();
18697 already_AddRefed
<Promise
> Document::RequestStorageAccessUnderSite(
18698 const nsAString
& aSerializedSite
, ErrorResult
& aRv
) {
18699 nsIGlobalObject
* global
= GetScopeObject();
18701 aRv
.Throw(NS_ERROR_NOT_AVAILABLE
);
18704 RefPtr
<Promise
> promise
= Promise::Create(global
, aRv
);
18705 if (aRv
.Failed()) {
18709 // Check that we have user activation before proceeding to prevent
18710 // rapid calls to the API to leak information.
18711 if (!ConsumeTransientUserGestureActivation()) {
18712 // Report an error to the console for this case
18713 nsContentUtils::ReportToConsole(
18714 nsIScriptError::errorFlag
, "requestStorageAccess"_ns
, this,
18715 nsContentUtils::eDOM_PROPERTIES
, "RequestStorageAccessUserGesture");
18716 promise
->MaybeRejectWithUndefined();
18717 return promise
.forget();
18720 // Check if the provided URI is different-site to this Document
18721 nsCOMPtr
<nsIURI
> siteURI
;
18722 nsresult rv
= NS_NewURI(getter_AddRefs(siteURI
), aSerializedSite
);
18723 if (NS_WARN_IF(NS_FAILED(rv
))) {
18724 promise
->MaybeRejectWithUndefined();
18725 return promise
.forget();
18727 bool isCrossSiteArgument
;
18728 rv
= NodePrincipal()->IsThirdPartyURI(siteURI
, &isCrossSiteArgument
);
18729 if (NS_WARN_IF(NS_FAILED(rv
))) {
18733 if (!isCrossSiteArgument
) {
18734 promise
->MaybeRejectWithUndefined();
18735 return promise
.forget();
18738 // Check if this party has broad cookie permissions.
18739 Maybe
<bool> resultBecauseCookiesApproved
=
18740 StorageAccessAPIHelper::CheckCookiesPermittedDecidesStorageAccessAPI(
18741 CookieJarSettings(), NodePrincipal());
18742 if (resultBecauseCookiesApproved
.isSome()) {
18743 if (resultBecauseCookiesApproved
.value()) {
18744 promise
->MaybeResolveWithUndefined();
18745 return promise
.forget();
18747 promise
->MaybeRejectWithUndefined();
18748 return promise
.forget();
18751 // Check if browser settings preclude this document getting storage
18752 // access under the provided site
18753 Maybe
<bool> resultBecauseBrowserSettings
=
18754 StorageAccessAPIHelper::CheckBrowserSettingsDecidesStorageAccessAPI(
18755 CookieJarSettings(), true, false, true);
18756 if (resultBecauseBrowserSettings
.isSome()) {
18757 if (resultBecauseBrowserSettings
.value()) {
18758 promise
->MaybeResolveWithUndefined();
18759 return promise
.forget();
18761 promise
->MaybeRejectWithUndefined();
18762 return promise
.forget();
18765 // Check that this Document is same-site to the top
18766 Maybe
<bool> resultBecauseCallContext
= StorageAccessAPIHelper::
18767 CheckSameSiteCallingContextDecidesStorageAccessAPI(this, false);
18768 if (resultBecauseCallContext
.isSome()) {
18769 if (resultBecauseCallContext
.value()) {
18770 promise
->MaybeResolveWithUndefined();
18771 return promise
.forget();
18773 promise
->MaybeRejectWithUndefined();
18774 return promise
.forget();
18777 nsCOMPtr
<nsIPrincipal
> principal(NodePrincipal());
18779 // Test if the permission this is requesting is already set
18780 nsCOMPtr
<nsIPrincipal
> argumentPrincipal
=
18781 BasePrincipal::CreateContentPrincipal(
18782 siteURI
, NodePrincipal()->OriginAttributesRef());
18783 if (!argumentPrincipal
) {
18784 ConsumeTransientUserGestureActivation();
18785 promise
->MaybeRejectWithUndefined();
18786 return promise
.forget();
18788 nsCString originNoSuffix
;
18789 rv
= NodePrincipal()->GetOriginNoSuffix(originNoSuffix
);
18790 if (NS_WARN_IF(NS_FAILED(rv
))) {
18791 promise
->MaybeRejectWithUndefined();
18792 return promise
.forget();
18795 ContentChild
* cc
= ContentChild::GetSingleton();
18797 RefPtr
<Document
> self(this);
18798 cc
->SendTestStorageAccessPermission(argumentPrincipal
, originNoSuffix
)
18800 GetCurrentSerialEventTarget(), __func__
,
18802 self
](const ContentChild::TestStorageAccessPermissionPromise::
18803 ResolveValueType
& aResult
) {
18805 return StorageAccessAPIHelper::
18806 StorageAccessPermissionGrantPromise::CreateAndResolve(
18807 StorageAccessAPIHelper::eAllow
, __func__
);
18809 // Get a grant for the storage access permission that will be set
18810 // when this is completed in the embedding context
18811 nsCString serializedSite
;
18812 nsCOMPtr
<nsIEffectiveTLDService
> etld
=
18813 mozilla::components::EffectiveTLD::Service();
18815 return StorageAccessAPIHelper::
18816 StorageAccessPermissionGrantPromise::CreateAndReject(
18819 nsresult rv
= etld
->GetSite(siteURI
, serializedSite
);
18820 if (NS_FAILED(rv
)) {
18821 return StorageAccessAPIHelper::
18822 StorageAccessPermissionGrantPromise::CreateAndReject(
18825 return self
->CreatePermissionGrantPromise(
18826 self
->GetInnerWindow(), self
->NodePrincipal(), true, true,
18827 Some(serializedSite
), false)();
18829 [](const ContentChild::TestStorageAccessPermissionPromise::
18830 RejectValueType
& aResult
) {
18831 return StorageAccessAPIHelper::StorageAccessPermissionGrantPromise::
18832 CreateAndReject(false, __func__
);
18835 GetCurrentSerialEventTarget(), __func__
,
18836 [promise
, principal
, siteURI
](int result
) {
18837 ContentChild
* cc
= ContentChild::GetSingleton();
18839 // TODO(bug 1778561): Make this work in non-content processes.
18840 promise
->MaybeRejectWithUndefined();
18843 // Set a permission in the parent process that this document wants
18844 // storage access under the argument's site, resolving our returned
18845 // promise on success
18846 cc
->SendSetAllowStorageAccessRequestFlag(principal
, siteURI
)
18848 GetCurrentSerialEventTarget(), __func__
,
18849 [promise
](bool success
) {
18851 promise
->MaybeResolveWithUndefined();
18853 promise
->MaybeRejectWithUndefined();
18856 [promise
](mozilla::ipc::ResponseRejectReason reason
) {
18857 promise
->MaybeRejectWithUndefined();
18860 [promise
](bool result
) { promise
->MaybeRejectWithUndefined(); });
18862 // Return the promise that is resolved in the async handler above
18863 return promise
.forget();
18866 already_AddRefed
<Promise
> Document::CompleteStorageAccessRequestFromSite(
18867 const nsAString
& aSerializedOrigin
, ErrorResult
& aRv
) {
18868 nsIGlobalObject
* global
= GetScopeObject();
18870 aRv
.Throw(NS_ERROR_NOT_AVAILABLE
);
18873 RefPtr
<Promise
> promise
= Promise::Create(global
, aRv
);
18874 if (aRv
.Failed()) {
18878 // Check that the provided URI is different-site to this Document
18879 nsCOMPtr
<nsIURI
> argumentURI
;
18880 nsresult rv
= NS_NewURI(getter_AddRefs(argumentURI
), aSerializedOrigin
);
18881 if (NS_WARN_IF(NS_FAILED(rv
))) {
18882 promise
->MaybeRejectWithUndefined();
18883 return promise
.forget();
18885 bool isCrossSiteArgument
;
18886 rv
= NodePrincipal()->IsThirdPartyURI(argumentURI
, &isCrossSiteArgument
);
18887 if (NS_WARN_IF(NS_FAILED(rv
))) {
18891 if (!isCrossSiteArgument
) {
18892 promise
->MaybeRejectWithUndefined();
18893 return promise
.forget();
18896 // Check if browser settings preclude this document getting storage
18897 // access under the provided site
18898 Maybe
<bool> resultBecauseBrowserSettings
=
18899 StorageAccessAPIHelper::CheckBrowserSettingsDecidesStorageAccessAPI(
18900 CookieJarSettings(), true, false, true);
18901 if (resultBecauseBrowserSettings
.isSome()) {
18902 if (resultBecauseBrowserSettings
.value()) {
18903 promise
->MaybeResolveWithUndefined();
18904 return promise
.forget();
18906 promise
->MaybeRejectWithUndefined();
18907 return promise
.forget();
18910 // Check that this Document is same-site to the top
18911 Maybe
<bool> resultBecauseCallContext
= StorageAccessAPIHelper::
18912 CheckSameSiteCallingContextDecidesStorageAccessAPI(this, false);
18913 if (resultBecauseCallContext
.isSome()) {
18914 if (resultBecauseCallContext
.value()) {
18915 promise
->MaybeResolveWithUndefined();
18916 return promise
.forget();
18918 promise
->MaybeRejectWithUndefined();
18919 return promise
.forget();
18922 // Create principal of the embedded site requesting storage access
18923 nsCOMPtr
<nsIPrincipal
> principal
= BasePrincipal::CreateContentPrincipal(
18924 argumentURI
, NodePrincipal()->OriginAttributesRef());
18926 promise
->MaybeRejectWithUndefined();
18927 return promise
.forget();
18930 // Get versions of these objects that we can use in lambdas for callbacks
18931 RefPtr
<Document
> self(this);
18932 RefPtr
<BrowsingContext
> bc
= GetBrowsingContext();
18933 nsCOMPtr
<nsPIDOMWindowInner
> inner
= GetInnerWindow();
18935 // Test that the permission was set by a call to RequestStorageAccessUnderSite
18936 // from a top level document that is same-site with the argument
18937 ContentChild
* cc
= ContentChild::GetSingleton();
18939 // TODO(bug 1778561): Make this work in non-content processes.
18940 promise
->MaybeRejectWithUndefined();
18941 return promise
.forget();
18943 cc
->SendTestAllowStorageAccessRequestFlag(NodePrincipal(), argumentURI
)
18945 GetCurrentSerialEventTarget(), __func__
,
18946 [inner
, bc
, self
, principal
](bool success
) {
18948 // If that resolved with true, check that we don't already have a
18949 // permission that gives cookie access.
18950 return StorageAccessAPIHelper::
18951 AsyncCheckCookiesPermittedDecidesStorageAccessAPIOnChildProcess(
18954 return MozPromise
<Maybe
<bool>, nsresult
, true>::CreateAndReject(
18955 NS_ERROR_FAILURE
, __func__
);
18957 [](mozilla::ipc::ResponseRejectReason reason
) {
18958 return MozPromise
<Maybe
<bool>, nsresult
, true>::CreateAndReject(
18959 NS_ERROR_FAILURE
, __func__
);
18962 GetCurrentSerialEventTarget(), __func__
,
18963 [inner
, bc
, principal
, self
, promise
](Maybe
<bool> cookieResult
) {
18964 // Handle the result of the cookie permission check that took place
18965 // in the ContentParent.
18966 if (cookieResult
.isSome()) {
18967 if (cookieResult
.value()) {
18968 return StorageAccessAPIHelper::
18969 StorageAccessPermissionGrantPromise::CreateAndResolve(
18970 StorageAccessAPIHelper::eAllowAutoGrant
, __func__
);
18972 return StorageAccessAPIHelper::
18973 StorageAccessPermissionGrantPromise::CreateAndReject(
18977 // Check for the existing storage access permission
18978 nsAutoCString type
;
18980 AntiTrackingUtils::CreateStoragePermissionKey(principal
, type
);
18982 return StorageAccessAPIHelper::
18983 StorageAccessPermissionGrantPromise::CreateAndReject(
18986 if (AntiTrackingUtils::CheckStoragePermission(
18987 self
->NodePrincipal(), type
, self
->IsInPrivateBrowsing(),
18989 return StorageAccessAPIHelper::
18990 StorageAccessPermissionGrantPromise::CreateAndResolve(
18991 StorageAccessAPIHelper::eAllowAutoGrant
, __func__
);
18994 // Try to request storage access, ignoring the final checks.
18995 // We ignore the final checks because this is where the "grant"
18996 // either by prompt doorhanger or autogrant takes place. We already
18997 // gathered an equivalent grant in requestStorageAccessUnderSite.
18998 return StorageAccessAPIHelper::RequestStorageAccessAsyncHelper(
18999 self
, inner
, bc
, principal
, true, true, false,
19000 ContentBlockingNotifier::eStorageAccessAPI
, false);
19002 // If the IPC rejects, we should reject our promise here which will
19003 // cause a rejection of the promise we already returned
19005 return MozPromise
<int, bool, true>::CreateAndReject(false,
19009 GetCurrentSerialEventTarget(), __func__
,
19010 // If the previous handlers resolved, we should reinstate user
19011 // activation and resolve the promise we returned in Step 5.
19012 [self
, inner
, promise
] {
19013 inner
->SaveStorageAccessPermissionGranted();
19014 promise
->MaybeResolveWithUndefined();
19016 // If the previous handler rejected, we should reject the promise
19017 // returned by this function.
19018 [promise
] { promise
->MaybeRejectWithUndefined(); });
19020 return promise
.forget();
19023 nsTHashSet
<RefPtr
<WakeLockSentinel
>>& Document::ActiveWakeLocks(
19024 WakeLockType aType
) {
19025 return mActiveLocks
.LookupOrInsert(aType
);
19028 class UnlockAllWakeLockRunnable final
: public Runnable
{
19030 UnlockAllWakeLockRunnable(WakeLockType aType
, Document
* aDoc
)
19031 : Runnable("UnlockAllWakeLocks"), mType(aType
), mDoc(aDoc
) {}
19033 // MOZ_CAN_RUN_SCRIPT_BOUNDARY until Runnable::Run is MOZ_CAN_RUN_SCRIPT. See
19035 MOZ_CAN_RUN_SCRIPT_BOUNDARY
19036 NS_IMETHOD
Run() override
{
19037 // Move, as ReleaseWakeLock will try to remove from and possibly allow
19038 // scripts via onrelease to add to document.[[ActiveLocks]]["screen"]
19039 nsCOMPtr
<Document
> doc
= mDoc
;
19040 nsTHashSet
<RefPtr
<WakeLockSentinel
>> locks
=
19041 std::move(doc
->ActiveWakeLocks(mType
));
19042 for (const auto& lock
: locks
) {
19043 // ReleaseWakeLock runs script, which could release other locks
19044 if (!lock
->Released()) {
19045 ReleaseWakeLock(doc
, MOZ_KnownLive(lock
), mType
);
19052 ~UnlockAllWakeLockRunnable() = default;
19055 WakeLockType mType
;
19056 nsCOMPtr
<Document
> mDoc
;
19059 void Document::UnlockAllWakeLocks(WakeLockType aType
) {
19060 // Perform unlock in a runnable to prevent UnlockAll being MOZ_CAN_RUN_SCRIPT
19061 if (!ActiveWakeLocks(aType
).IsEmpty()) {
19062 RefPtr
<UnlockAllWakeLockRunnable
> runnable
=
19063 MakeRefPtr
<UnlockAllWakeLockRunnable
>(aType
, this);
19064 nsresult rv
= NS_DispatchToMainThread(runnable
);
19065 MOZ_DIAGNOSTIC_ASSERT(NS_SUCCEEDED(rv
));
19070 RefPtr
<Document::AutomaticStorageAccessPermissionGrantPromise
>
19071 Document::AutomaticStorageAccessPermissionCanBeGranted(bool hasUserActivation
) {
19072 // requestStorageAccessForOrigin may not require user activation. If we don't
19073 // have user activation at this point we should always show the prompt.
19074 if (!hasUserActivation
||
19075 !StaticPrefs::privacy_antitracking_enableWebcompat()) {
19076 return AutomaticStorageAccessPermissionGrantPromise::CreateAndResolve(
19079 if (XRE_IsContentProcess()) {
19080 // In the content process, we need to ask the parent process to compute
19081 // this. The reason is that nsIPermissionManager::GetAllWithTypePrefix()
19082 // isn't accessible in the content process.
19083 ContentChild
* cc
= ContentChild::GetSingleton();
19086 return cc
->SendAutomaticStorageAccessPermissionCanBeGranted(NodePrincipal())
19087 ->Then(GetCurrentSerialEventTarget(), __func__
,
19088 [](const ContentChild::
19089 AutomaticStorageAccessPermissionCanBeGrantedPromise::
19090 ResolveOrRejectValue
& aValue
) {
19091 if (aValue
.IsResolve()) {
19092 return AutomaticStorageAccessPermissionGrantPromise::
19093 CreateAndResolve(aValue
.ResolveValue(), __func__
);
19096 return AutomaticStorageAccessPermissionGrantPromise::
19097 CreateAndReject(false, __func__
);
19101 if (XRE_IsParentProcess()) {
19102 // In the parent process, we can directly compute this.
19103 return AutomaticStorageAccessPermissionGrantPromise::CreateAndResolve(
19104 AutomaticStorageAccessPermissionCanBeGranted(NodePrincipal()),
19108 return AutomaticStorageAccessPermissionGrantPromise::CreateAndReject(
19112 bool Document::AutomaticStorageAccessPermissionCanBeGranted(
19113 nsIPrincipal
* aPrincipal
) {
19114 if (!StaticPrefs::dom_storage_access_auto_grants()) {
19118 if (!ContentBlockingUserInteraction::Exists(aPrincipal
)) {
19122 nsCOMPtr
<nsIBrowserUsage
> bu
= do_ImportESModule(
19123 "resource:///modules/BrowserUsageTelemetry.sys.mjs", fallible
);
19124 if (NS_WARN_IF(!bu
)) {
19128 uint32_t uniqueDomainsVisitedInPast24Hours
= 0;
19129 nsresult rv
= bu
->GetUniqueDomainsVisitedInPast24Hours(
19130 &uniqueDomainsVisitedInPast24Hours
);
19131 if (NS_WARN_IF(NS_FAILED(rv
))) {
19135 Maybe
<size_t> maybeOriginsThirdPartyHasAccessTo
=
19136 AntiTrackingUtils::CountSitesAllowStorageAccess(aPrincipal
);
19137 if (maybeOriginsThirdPartyHasAccessTo
.isNothing()) {
19140 size_t originsThirdPartyHasAccessTo
=
19141 maybeOriginsThirdPartyHasAccessTo
.value();
19143 // one percent of the number of top-levels origins visited in the current
19144 // session (but not to exceed 24 hours), or the value of the
19145 // dom.storage_access.max_concurrent_auto_grants preference, whichever is
19147 size_t maxConcurrentAutomaticGrants
= std::max(
19148 std::max(int(std::floor(uniqueDomainsVisitedInPast24Hours
/ 100)),
19149 StaticPrefs::dom_storage_access_max_concurrent_auto_grants()),
19152 return originsThirdPartyHasAccessTo
< maxConcurrentAutomaticGrants
;
19155 void Document::RecordNavigationTiming(ReadyState aReadyState
) {
19156 if (!XRE_IsContentProcess()) {
19159 if (!IsTopLevelContentDocument()) {
19162 // If we dont have the timing yet (mostly because the doc is still loading),
19163 // get it from docshell.
19164 RefPtr
<nsDOMNavigationTiming
> timing
= mTiming
;
19166 if (!mDocumentContainer
) {
19169 timing
= mDocumentContainer
->GetNavigationTiming();
19174 TimeStamp startTime
= timing
->GetNavigationStartTimeStamp();
19175 switch (aReadyState
) {
19176 case READYSTATE_LOADING
:
19177 if (!mDOMLoadingSet
) {
19178 Telemetry::AccumulateTimeDelta(Telemetry::TIME_TO_DOM_LOADING_MS
,
19180 mDOMLoadingSet
= true;
19183 case READYSTATE_INTERACTIVE
:
19184 if (!mDOMInteractiveSet
) {
19185 glean::performance_time::dom_interactive
.AccumulateRawDuration(
19186 TimeStamp::Now() - startTime
);
19187 mDOMInteractiveSet
= true;
19190 case READYSTATE_COMPLETE
:
19191 if (!mDOMCompleteSet
) {
19192 glean::performance_time::dom_complete
.AccumulateRawDuration(
19193 TimeStamp::Now() - startTime
);
19194 mDOMCompleteSet
= true;
19198 NS_WARNING("Unexpected ReadyState value");
19203 void Document::ReportShadowDOMUsage() {
19204 nsPIDOMWindowInner
* inner
= GetInnerWindow();
19205 if (NS_WARN_IF(!inner
)) {
19209 WindowContext
* wc
= inner
->GetWindowContext();
19210 if (NS_WARN_IF(!wc
|| wc
->IsDiscarded())) {
19214 WindowContext
* topWc
= wc
->TopWindowContext();
19215 if (topWc
->GetHasReportedShadowDOMUsage()) {
19219 MOZ_ALWAYS_SUCCEEDS(topWc
->SetHasReportedShadowDOMUsage(true));
19223 bool Document::StorageAccessSandboxed(uint32_t aSandboxFlags
) {
19224 return StaticPrefs::dom_storage_access_enabled() &&
19225 (aSandboxFlags
& SANDBOXED_STORAGE_ACCESS
) != 0;
19228 bool Document::StorageAccessSandboxed() const {
19229 return Document::StorageAccessSandboxed(GetSandboxFlags());
19232 bool Document::GetCachedSizes(nsTabSizes
* aSizes
) {
19233 if (mCachedTabSizeGeneration
== 0 ||
19234 GetGeneration() != mCachedTabSizeGeneration
) {
19237 aSizes
->mDom
+= mCachedTabSizes
.mDom
;
19238 aSizes
->mStyle
+= mCachedTabSizes
.mStyle
;
19239 aSizes
->mOther
+= mCachedTabSizes
.mOther
;
19243 void Document::SetCachedSizes(nsTabSizes
* aSizes
) {
19244 mCachedTabSizes
.mDom
= aSizes
->mDom
;
19245 mCachedTabSizes
.mStyle
= aSizes
->mStyle
;
19246 mCachedTabSizes
.mOther
= aSizes
->mOther
;
19247 mCachedTabSizeGeneration
= GetGeneration();
19250 nsAtom
* Document::GetContentLanguageAsAtomForStyle() const {
19251 // Content-Language may be a comma-separated list of language codes,
19252 // in which case the HTML5 spec says to treat it as unknown
19253 if (mContentLanguage
&&
19254 !nsDependentAtomString(mContentLanguage
).Contains(char16_t(','))) {
19255 return GetContentLanguage();
19261 nsAtom
* Document::GetLanguageForStyle() const {
19262 if (nsAtom
* lang
= GetContentLanguageAsAtomForStyle()) {
19265 return mLanguageFromCharset
.get();
19268 void Document::GetContentLanguageForBindings(DOMString
& aString
) const {
19269 aString
.SetKnownLiveAtom(mContentLanguage
, DOMString::eTreatNullAsEmpty
);
19272 const LangGroupFontPrefs
* Document::GetFontPrefsForLang(
19273 nsAtom
* aLanguage
, bool* aNeedsToCache
) const {
19274 nsAtom
* lang
= aLanguage
? aLanguage
: mLanguageFromCharset
.get();
19275 return StaticPresData::Get()->GetFontPrefsForLang(lang
, aNeedsToCache
);
19278 void Document::DoCacheAllKnownLangPrefs() {
19279 MOZ_ASSERT(mMayNeedFontPrefsUpdate
);
19280 RefPtr
<nsAtom
> lang
= GetLanguageForStyle();
19281 StaticPresData
* data
= StaticPresData::Get();
19282 data
->GetFontPrefsForLang(lang
? lang
.get() : mLanguageFromCharset
.get());
19283 data
->GetFontPrefsForLang(nsGkAtoms::x_math
);
19284 // https://bugzilla.mozilla.org/show_bug.cgi?id=1362599#c12
19285 data
->GetFontPrefsForLang(nsGkAtoms::Unicode
);
19286 for (const auto& key
: mLanguagesUsed
) {
19287 data
->GetFontPrefsForLang(key
);
19289 mMayNeedFontPrefsUpdate
= false;
19292 void Document::RecomputeLanguageFromCharset() {
19293 RefPtr
<nsAtom
> language
;
19294 // Optimize the default character sets.
19295 if (mCharacterSet
== WINDOWS_1252_ENCODING
) {
19296 language
= nsGkAtoms::x_western
;
19298 nsLanguageAtomService
* service
= nsLanguageAtomService::GetService();
19299 if (mCharacterSet
== UTF_8_ENCODING
) {
19300 language
= nsGkAtoms::Unicode
;
19302 language
= service
->LookupCharSet(mCharacterSet
);
19305 if (language
== nsGkAtoms::Unicode
) {
19306 language
= service
->GetLocaleLanguage();
19310 if (language
== mLanguageFromCharset
) {
19314 mMayNeedFontPrefsUpdate
= true;
19315 mLanguageFromCharset
= std::move(language
);
19318 nsICookieJarSettings
* Document::CookieJarSettings() {
19319 // If we are here, this is probably a javascript: URL document. In any case,
19320 // we must have a nsCookieJarSettings. Let's create it.
19321 if (!mCookieJarSettings
) {
19322 Document
* inProcessParent
= GetInProcessParentDocument();
19324 auto shouldInheritFrom
= [this](Document
* aDoc
) {
19325 return aDoc
&& (this->NodePrincipal()->Equals(aDoc
->NodePrincipal()) ||
19326 this->NodePrincipal()->GetIsNullPrincipal());
19328 RefPtr
<BrowsingContext
> opener
=
19329 GetBrowsingContext() ? GetBrowsingContext()->GetOpener() : nullptr;
19331 if (inProcessParent
) {
19332 mCookieJarSettings
= net::CookieJarSettings::Create(
19333 inProcessParent
->CookieJarSettings()->GetCookieBehavior(),
19334 mozilla::net::CookieJarSettings::Cast(
19335 inProcessParent
->CookieJarSettings())
19336 ->GetPartitionKey(),
19337 inProcessParent
->CookieJarSettings()->GetIsFirstPartyIsolated(),
19338 inProcessParent
->CookieJarSettings()
19339 ->GetIsOnContentBlockingAllowList(),
19340 inProcessParent
->CookieJarSettings()
19341 ->GetShouldResistFingerprinting());
19343 // Inherit the fingerprinting random key from the parent.
19344 nsTArray
<uint8_t> randomKey
;
19345 nsresult rv
= inProcessParent
->CookieJarSettings()
19346 ->GetFingerprintingRandomizationKey(randomKey
);
19348 if (NS_SUCCEEDED(rv
)) {
19349 net::CookieJarSettings::Cast(mCookieJarSettings
)
19350 ->SetFingerprintingRandomizationKey(randomKey
);
19353 // Inerit the top level windowContext id from the parent.
19354 net::CookieJarSettings::Cast(mCookieJarSettings
)
19355 ->SetTopLevelWindowContextId(
19356 net::CookieJarSettings::Cast(inProcessParent
->CookieJarSettings())
19357 ->GetTopLevelWindowContextId());
19358 } else if (opener
&& shouldInheritFrom(opener
->GetDocument())) {
19359 mCookieJarSettings
= net::CookieJarSettings::Create(NodePrincipal());
19361 nsTArray
<uint8_t> randomKey
;
19362 nsresult rv
= opener
->GetDocument()
19363 ->CookieJarSettings()
19364 ->GetFingerprintingRandomizationKey(randomKey
);
19366 if (NS_SUCCEEDED(rv
)) {
19367 net::CookieJarSettings::Cast(mCookieJarSettings
)
19368 ->SetFingerprintingRandomizationKey(randomKey
);
19371 mCookieJarSettings
= net::CookieJarSettings::Create(NodePrincipal());
19373 if (IsTopLevelContentDocument()) {
19374 net::CookieJarSettings::Cast(mCookieJarSettings
)
19375 ->SetTopLevelWindowContextId(InnerWindowID());
19379 if (auto* wgc
= GetWindowGlobalChild()) {
19380 net::CookieJarSettingsArgs csArgs
;
19382 net::CookieJarSettings::Cast(mCookieJarSettings
)->Serialize(csArgs
);
19383 // Update cookie settings in the parent process
19384 if (!wgc
->SendUpdateCookieJarSettings(csArgs
)) {
19386 "Failed to update document's cookie jar settings on the "
19387 "WindowGlobalParent");
19392 return mCookieJarSettings
;
19395 bool Document::UsingStorageAccess() {
19396 if (WindowContext
* wc
= GetWindowContext()) {
19397 return wc
->GetUsingStorageAccess();
19400 // If we don't yet have a window context, we have to use the decision
19401 // from the Document's Channel's LoadInfo directly.
19406 nsCOMPtr
<nsILoadInfo
> loadInfo
= mChannel
->LoadInfo();
19407 return loadInfo
->GetStoragePermission() != nsILoadInfo::NoStoragePermission
;
19410 bool Document::IsOn3PCBExceptionList() const {
19414 nsCOMPtr
<nsILoadInfo
> loadInfo
= mChannel
->LoadInfo();
19416 return loadInfo
->GetIsOn3PCBExceptionList();
19419 bool Document::HasStorageAccessPermissionGrantedByAllowList() {
19420 // We only care about if the document gets the storage permission via the
19421 // allow list here. So we don't check the storage access cache in the inner
19428 nsCOMPtr
<nsILoadInfo
> loadInfo
= mChannel
->LoadInfo();
19429 return loadInfo
->GetStoragePermission() ==
19430 nsILoadInfo::StoragePermissionAllowListed
;
19433 nsIPrincipal
* Document::EffectiveStoragePrincipal() const {
19435 privacy_partition_always_partition_third_party_non_cookie_storage()) {
19436 return EffectiveCookiePrincipal();
19439 nsPIDOMWindowInner
* inner
= GetInnerWindow();
19441 return NodePrincipal();
19444 // Return our cached storage principal if one exists.
19445 if (mActiveStoragePrincipal
) {
19446 return mActiveStoragePrincipal
;
19449 // Calling StorageAllowedForDocument will notify the ContentBlockLog. This
19450 // loads TrackingDBService.sys.mjs, making us potentially
19451 // fail // browser/base/content/test/performance/browser_startup.js. To avoid
19452 // that, we short-circuit the check here by allowing storage access to system
19453 // and addon principles, avoiding the test-failure.
19454 nsIPrincipal
* principal
= NodePrincipal();
19455 if (principal
&& (principal
->IsSystemPrincipal() ||
19456 principal
->GetIsAddonOrExpandedAddonPrincipal())) {
19457 return mActiveStoragePrincipal
= NodePrincipal();
19460 auto cookieJarSettings
= const_cast<Document
*>(this)->CookieJarSettings();
19461 if (cookieJarSettings
->GetIsOnContentBlockingAllowList()) {
19462 return mActiveStoragePrincipal
= NodePrincipal();
19465 // We use the lower-level ContentBlocking API here to ensure this
19466 // check doesn't send notifications.
19467 uint32_t rejectedReason
= 0;
19468 if (ShouldAllowAccessFor(inner
, GetDocumentURI(), false, &rejectedReason
)) {
19469 return mActiveStoragePrincipal
= NodePrincipal();
19472 // Let's use the storage principal only if we need to partition the cookie
19473 // jar. When the permission is granted, access will be different and the
19474 // normal principal will be used.
19475 if (ShouldPartitionStorage(rejectedReason
) &&
19476 !StoragePartitioningEnabled(
19477 rejectedReason
, const_cast<Document
*>(this)->CookieJarSettings())) {
19478 return mActiveStoragePrincipal
= NodePrincipal();
19481 Unused
<< NS_WARN_IF(NS_FAILED(StoragePrincipalHelper::GetPrincipal(
19482 nsGlobalWindowInner::Cast(inner
),
19483 StoragePrincipalHelper::eForeignPartitionedPrincipal
,
19484 getter_AddRefs(mActiveStoragePrincipal
))));
19485 return mActiveStoragePrincipal
;
19488 nsIPrincipal
* Document::EffectiveCookiePrincipal() const {
19489 nsPIDOMWindowInner
* inner
= GetInnerWindow();
19491 return NodePrincipal();
19494 // Return our cached storage principal if one exists.
19496 // Handle special case where privacy_partition_always_partition_third_party
19497 // _non_cookie_storage is disabled and the loading document has
19498 // StorageAccess. The pref will lead to WindowGlobalChild::OnNewDocument
19499 // setting the documents StoragePrincipal on the parent to the documents
19500 // EffectiveCookiePrincipal. Since this happens before the WindowContext,
19501 // including possible StorageAccess, is set the PartitonedPrincipal will be
19502 // selected and cached. Since no change of permission occured it won't be
19503 // updated later. Avoid this by not using a cached PartitionedPrincipal if
19504 // the pref is disabled, this should rarely happen since the pref defaults to
19505 // true. See Bug 1899570.
19506 if (mActiveCookiePrincipal
&&
19508 privacy_partition_always_partition_third_party_non_cookie_storage() ||
19509 mActiveCookiePrincipal
!= mPartitionedPrincipal
)) {
19510 return mActiveCookiePrincipal
;
19513 // We use the lower-level ContentBlocking API here to ensure this
19514 // check doesn't send notifications.
19515 uint32_t rejectedReason
= 0;
19516 if (ShouldAllowAccessFor(inner
, GetDocumentURI(), true, &rejectedReason
)) {
19517 return mActiveCookiePrincipal
= NodePrincipal();
19520 // Let's use the storage principal only if we need to partition the cookie
19521 // jar. When the permission is granted, access will be different and the
19522 // normal principal will be used.
19523 if (ShouldPartitionStorage(rejectedReason
) &&
19524 !StoragePartitioningEnabled(
19525 rejectedReason
, const_cast<Document
*>(this)->CookieJarSettings())) {
19526 return mActiveCookiePrincipal
= NodePrincipal();
19529 return mActiveCookiePrincipal
= mPartitionedPrincipal
;
19532 nsIPrincipal
* Document::GetPrincipalForPrefBasedHacks() const {
19533 // If the document is sandboxed document or data: document, we should
19534 // get URI of the parent document.
19535 for (const Document
* document
= this;
19536 document
&& document
->IsContentDocument();
19537 document
= document
->GetInProcessParentDocument()) {
19538 // The document URI may be about:blank even if it comes from actual web
19539 // site. Therefore, we need to check the URI of its principal.
19540 nsIPrincipal
* principal
= document
->NodePrincipal();
19541 if (principal
->GetIsNullPrincipal()) {
19549 void Document::SetIsInitialDocument(bool aIsInitialDocument
) {
19550 mIsInitialDocumentInWindow
= aIsInitialDocument
;
19552 if (aIsInitialDocument
&& !mIsEverInitialDocumentInWindow
) {
19553 mIsEverInitialDocumentInWindow
= aIsInitialDocument
;
19556 // Asynchronously tell the parent process that we are, or are no longer, the
19557 // initial document. This happens async.
19558 if (auto* wgc
= GetWindowGlobalChild()) {
19559 wgc
->SendSetIsInitialDocument(aIsInitialDocument
);
19564 void Document::AddToplevelLoadingDocument(Document
* aDoc
) {
19565 MOZ_ASSERT(aDoc
&& aDoc
->IsTopLevelContentDocument());
19566 // Currently we're interested in foreground documents only, so bail out early.
19567 if (aDoc
->IsInBackgroundWindow() || !XRE_IsContentProcess()) {
19571 if (!sLoadingForegroundTopLevelContentDocument
) {
19572 sLoadingForegroundTopLevelContentDocument
= new AutoTArray
<Document
*, 8>();
19573 mozilla::ipc::IdleSchedulerChild
* idleScheduler
=
19574 mozilla::ipc::IdleSchedulerChild::GetMainThreadIdleScheduler();
19575 if (idleScheduler
) {
19576 idleScheduler
->SendRunningPrioritizedOperation();
19579 if (!sLoadingForegroundTopLevelContentDocument
->Contains(aDoc
)) {
19580 sLoadingForegroundTopLevelContentDocument
->AppendElement(aDoc
);
19585 void Document::RemoveToplevelLoadingDocument(Document
* aDoc
) {
19586 MOZ_ASSERT(aDoc
&& aDoc
->IsTopLevelContentDocument());
19587 if (sLoadingForegroundTopLevelContentDocument
) {
19588 sLoadingForegroundTopLevelContentDocument
->RemoveElement(aDoc
);
19589 if (sLoadingForegroundTopLevelContentDocument
->IsEmpty()) {
19590 delete sLoadingForegroundTopLevelContentDocument
;
19591 sLoadingForegroundTopLevelContentDocument
= nullptr;
19593 mozilla::ipc::IdleSchedulerChild
* idleScheduler
=
19594 mozilla::ipc::IdleSchedulerChild::GetMainThreadIdleScheduler();
19595 if (idleScheduler
) {
19596 idleScheduler
->SendPrioritizedOperationDone();
19602 ColorScheme
Document::DefaultColorScheme() const {
19603 return LookAndFeel::ColorSchemeForStyle(*this, {GetColorSchemeBits()});
19606 ColorScheme
Document::PreferredColorScheme(IgnoreRFP aIgnoreRFP
) const {
19607 if (ShouldResistFingerprinting(RFPTarget::CSSPrefersColorScheme
) &&
19608 aIgnoreRFP
== IgnoreRFP::No
) {
19609 return ColorScheme::Light
;
19612 if (nsPresContext
* pc
= GetPresContext()) {
19613 if (auto scheme
= pc
->GetOverriddenOrEmbedderColorScheme()) {
19618 return PreferenceSheet::PrefsFor(*this).mColorScheme
;
19621 bool Document::HasRecentlyStartedForegroundLoads() {
19622 if (!sLoadingForegroundTopLevelContentDocument
) {
19626 for (size_t i
= 0; i
< sLoadingForegroundTopLevelContentDocument
->Length();
19628 Document
* doc
= sLoadingForegroundTopLevelContentDocument
->ElementAt(i
);
19629 // A page loaded in foreground could be in background now.
19630 if (!doc
->IsInBackgroundWindow()) {
19631 nsPIDOMWindowInner
* win
= doc
->GetInnerWindow();
19633 Performance
* perf
= win
->GetPerformance();
19635 perf
->Now() < StaticPrefs::page_load_deprioritization_period()) {
19642 // Didn't find any loading foreground documents, just clear the array.
19643 delete sLoadingForegroundTopLevelContentDocument
;
19644 sLoadingForegroundTopLevelContentDocument
= nullptr;
19646 mozilla::ipc::IdleSchedulerChild
* idleScheduler
=
19647 mozilla::ipc::IdleSchedulerChild::GetMainThreadIdleScheduler();
19648 if (idleScheduler
) {
19649 idleScheduler
->SendPrioritizedOperationDone();
19654 void Document::AddPendingFrameStaticClone(nsFrameLoaderOwner
* aElement
,
19655 nsFrameLoader
* aStaticCloneOf
) {
19656 PendingFrameStaticClone
* clone
= mPendingFrameStaticClones
.AppendElement();
19657 clone
->mElement
= aElement
;
19658 clone
->mStaticCloneOf
= aStaticCloneOf
;
19661 bool Document::ShouldAvoidNativeTheme() const {
19662 return !IsInChromeDocShell() || XRE_IsContentProcess();
19665 bool Document::UseRegularPrincipal() const {
19666 return EffectiveStoragePrincipal() == NodePrincipal();
19669 bool Document::HasThirdPartyChannel() {
19670 nsCOMPtr
<nsIChannel
> channel
= GetChannel();
19672 // We assume that the channel is a third-party by default.
19673 bool thirdParty
= true;
19675 nsCOMPtr
<mozIThirdPartyUtil
> thirdPartyUtil
=
19676 components::ThirdPartyUtil::Service();
19677 if (!thirdPartyUtil
) {
19681 // Check that if the channel is a third-party to its parent.
19683 thirdPartyUtil
->IsThirdPartyChannel(channel
, nullptr, &thirdParty
);
19684 if (NS_FAILED(rv
)) {
19685 // Assume third-party in case of failure
19692 if (mParentDocument
) {
19693 return mParentDocument
->HasThirdPartyChannel();
19699 bool Document::IsLikelyContentInaccessibleTopLevelAboutBlank() const {
19700 if (!mDocumentURI
|| !NS_IsAboutBlank(mDocumentURI
)) {
19703 // FIXME(emilio): This is not quite edge-case free. See bug 1860098.
19705 // For stuff in frames, that makes our per-document telemetry probes not
19706 // really reliable but doesn't affect the correctness of our page probes, so
19707 // it's not too terrible.
19708 BrowsingContext
* bc
= GetBrowsingContext();
19709 return bc
&& bc
->IsTop() && !bc
->GetTopLevelCreatedByWebContent();
19712 bool Document::ShouldIncludeInTelemetry() const {
19713 if (!IsContentDocument() && !IsResourceDoc()) {
19717 if (IsLikelyContentInaccessibleTopLevelAboutBlank()) {
19721 nsIPrincipal
* prin
= NodePrincipal();
19722 // TODO(emilio): Should this use GetIsContentPrincipal() +
19723 // GetPrecursorPrincipal() instead (accounting for add-ons separately)?
19724 return !(prin
->GetIsAddonOrExpandedAddonPrincipal() ||
19725 prin
->IsSystemPrincipal() || prin
->SchemeIs("about") ||
19726 prin
->SchemeIs("chrome") || prin
->SchemeIs("resource"));
19729 void Document::GetConnectedShadowRoots(
19730 nsTArray
<RefPtr
<ShadowRoot
>>& aOut
) const {
19731 AppendToArray(aOut
, mComposedShadowRoots
);
19734 void Document::AddMediaElementWithMSE() {
19735 if (mMediaElementWithMSECount
++ == 0) {
19736 if (WindowGlobalChild
* wgc
= GetWindowGlobalChild()) {
19737 wgc
->BlockBFCacheFor(BFCacheStatus::CONTAINS_MSE_CONTENT
);
19742 void Document::RemoveMediaElementWithMSE() {
19743 MOZ_ASSERT(mMediaElementWithMSECount
> 0);
19744 if (--mMediaElementWithMSECount
== 0) {
19745 if (WindowGlobalChild
* wgc
= GetWindowGlobalChild()) {
19746 wgc
->UnblockBFCacheFor(BFCacheStatus::CONTAINS_MSE_CONTENT
);
19751 void Document::UnregisterFromMemoryReportingForDataDocument() {
19752 if (!mAddedToMemoryReportingAsDataDocument
) {
19755 mAddedToMemoryReportingAsDataDocument
= false;
19756 nsIGlobalObject
* global
= GetScopeObject();
19758 if (nsPIDOMWindowInner
* win
= global
->GetAsInnerWindow()) {
19759 nsGlobalWindowInner::Cast(win
)->UnregisterDataDocumentForMemoryReporting(
19764 void Document::OOPChildLoadStarted(BrowserBridgeChild
* aChild
) {
19765 MOZ_DIAGNOSTIC_ASSERT(!mOOPChildrenLoading
.Contains(aChild
));
19766 mOOPChildrenLoading
.AppendElement(aChild
);
19767 if (mOOPChildrenLoading
.Length() == 1) {
19768 // Let's block unload so that we're blocked from going into the BFCache
19769 // until the child has actually notified us that it has done loading.
19774 void Document::OOPChildLoadDone(BrowserBridgeChild
* aChild
) {
19775 // aChild will not be in the list if nsDocLoader::Stop() was called, since
19776 // that clears mOOPChildrenLoading. It also dispatches the 'load' event,
19777 // so we don't need to call DocLoaderIsEmpty in that case.
19778 if (mOOPChildrenLoading
.RemoveElement(aChild
)) {
19779 if (mOOPChildrenLoading
.IsEmpty()) {
19780 UnblockOnload(false);
19782 RefPtr
<nsDocLoader
> docLoader(mDocumentContainer
);
19784 docLoader
->OOPChildrenLoadingIsEmpty();
19789 void Document::ClearOOPChildrenLoading() {
19790 nsTArray
<const BrowserBridgeChild
*> oopChildrenLoading
;
19791 mOOPChildrenLoading
.SwapElements(oopChildrenLoading
);
19792 if (!oopChildrenLoading
.IsEmpty()) {
19793 UnblockOnload(false);
19797 bool Document::MayHaveDOMActivateListeners() const {
19798 if (nsPIDOMWindowInner
* inner
= GetInnerWindow()) {
19799 return inner
->HasDOMActivateEventListeners();
19802 // If we can't get information from the window object, default to true.
19806 HighlightRegistry
& Document::HighlightRegistry() {
19807 if (!mHighlightRegistry
) {
19808 mHighlightRegistry
= MakeRefPtr
<class HighlightRegistry
>(this);
19810 return *mHighlightRegistry
;
19813 FragmentDirective
* Document::FragmentDirective() {
19814 if (!mFragmentDirective
) {
19815 mFragmentDirective
= MakeRefPtr
<class FragmentDirective
>(this);
19817 return mFragmentDirective
;
19820 RadioGroupContainer
& Document::OwnedRadioGroupContainer() {
19821 if (!mRadioGroupContainer
) {
19822 mRadioGroupContainer
= MakeUnique
<RadioGroupContainer
>();
19824 return *mRadioGroupContainer
;
19827 void Document::UpdateHiddenByContentVisibilityForAnimations() {
19828 for (AnimationTimeline
* timeline
: Timelines()) {
19829 timeline
->UpdateHiddenByContentVisibility();
19833 void Document::SetAllowDeclarativeShadowRoots(
19834 bool aAllowDeclarativeShadowRoots
) {
19835 mAllowDeclarativeShadowRoots
= aAllowDeclarativeShadowRoots
;
19838 bool Document::AllowsDeclarativeShadowRoots() const {
19839 return mAllowDeclarativeShadowRoots
;
19843 already_AddRefed
<Document
> Document::ParseHTMLUnsafe(
19844 GlobalObject
& aGlobal
, const TrustedHTMLOrString
& aHTML
,
19845 ErrorResult
& aError
) {
19846 nsCOMPtr
<nsIURI
> uri
;
19847 NS_NewURI(getter_AddRefs(uri
), "about:blank");
19852 nsCOMPtr
<nsIGlobalObject
> global
= do_QueryInterface(aGlobal
.GetAsSupports());
19854 constexpr nsLiteralString sink
= u
"Document parseHTMLUnsafe"_ns
;
19855 Maybe
<nsAutoString
> compliantStringHolder
;
19856 const nsAString
* compliantString
=
19857 TrustedTypeUtils::GetTrustedTypesCompliantString(
19858 aHTML
, sink
, kTrustedTypesOnlySinkGroup
, *global
,
19859 compliantStringHolder
, aError
);
19860 if (aError
.Failed()) {
19864 nsCOMPtr
<Document
> doc
;
19866 NS_NewHTMLDocument(getter_AddRefs(doc
), aGlobal
.GetSubjectPrincipal(),
19867 aGlobal
.GetSubjectPrincipal());
19868 if (aError
.Failed()) {
19872 doc
->SetAllowDeclarativeShadowRoots(true);
19873 doc
->SetDocumentURI(uri
);
19875 nsCOMPtr
<nsIScriptGlobalObject
> scriptHandlingObject
=
19876 do_QueryInterface(aGlobal
.GetAsSupports());
19877 doc
->SetScriptHandlingObject(scriptHandlingObject
);
19878 doc
->SetDocumentCharacterSet(UTF_8_ENCODING
);
19879 aError
= nsContentUtils::ParseDocumentHTML(*compliantString
, doc
, false);
19880 if (aError
.Failed()) {
19884 return doc
.forget();
19887 bool Document::MutationEventsEnabled() {
19888 if (StaticPrefs::dom_mutation_events_enabled()) {
19891 if (mMutationEventsEnabled
.isNothing()) {
19892 mMutationEventsEnabled
.emplace(
19893 NodePrincipal()->IsURIInPrefList("dom.mutation_events.forceEnable"));
19895 return mMutationEventsEnabled
.value();
19898 } // namespace mozilla::dom