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/. */
6 
7 /*
8  * Base class for all our document implementations.
9  */
10 
11 #include "mozilla/dom/Document.h"
12 #include "mozilla/dom/DocumentInlines.h"
13 
14 #include <inttypes.h>
15 #include <stdlib.h>
16 #include <string.h>
17 #include <algorithm>
18 #include <cstddef>
19 #include <cstdint>
20 #include <initializer_list>
21 #include <iterator>
22 #include <limits>
23 #include <type_traits>
24 #include "Attr.h"
25 #include "AutoplayPolicy.h"
26 #include "ErrorList.h"
27 #include "ExpandedPrincipal.h"
28 #include "MainThreadUtils.h"
29 #include "MobileViewportManager.h"
30 #include "NodeUbiReporting.h"
31 #include "PLDHashTable.h"
32 #include "StorageAccessPermissionRequest.h"
33 #include "ThirdPartyUtil.h"
34 #include "domstubs.h"
35 #include "gfxPlatform.h"
36 #include "imgIContainer.h"
37 #include "imgLoader.h"
38 #include "imgRequestProxy.h"
39 #include "js/Value.h"
40 #include "jsapi.h"
41 #include "mozAutoDocUpdate.h"
42 #include "mozIDOMWindow.h"
43 #include "mozIThirdPartyUtil.h"
44 #include "mozilla/AbstractTimelineMarker.h"
45 #include "mozilla/AntiTrackingUtils.h"
46 #include "mozilla/ArrayIterator.h"
47 #include "mozilla/ArrayUtils.h"
48 #include "mozilla/AsyncEventDispatcher.h"
49 #include "mozilla/Base64.h"
50 #include "mozilla/BasePrincipal.h"
51 #include "mozilla/CSSEnabledState.h"
52 #include "mozilla/ContentBlocking.h"
53 #include "mozilla/ContentBlockingAllowList.h"
54 #include "mozilla/ContentBlockingNotifier.h"
55 #include "mozilla/ContentBlockingUserInteraction.h"
56 #include "mozilla/CycleCollectedJSContext.h"
57 #include "mozilla/DebugOnly.h"
58 #include "mozilla/DocLoadingTimelineMarker.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/HTMLEditor.h"
72 #include "mozilla/HoldDropJSObjects.h"
73 #include "mozilla/IdentifierMapEntry.h"
74 #include "mozilla/InputTaskManager.h"
75 #include "mozilla/IntegerRange.h"
76 #include "mozilla/InternalMutationEvent.h"
77 #include "mozilla/Likely.h"
78 #include "mozilla/Logging.h"
79 #include "mozilla/LookAndFeel.h"
80 #include "mozilla/MacroForEach.h"
81 #include "mozilla/Maybe.h"
82 #include "mozilla/MediaFeatureChange.h"
83 #include "mozilla/MediaManager.h"
84 #include "mozilla/MemoryReporting.h"
85 #include "mozilla/NullPrincipal.h"
86 #include "mozilla/OriginAttributes.h"
87 #include "mozilla/OwningNonNull.h"
88 #include "mozilla/PendingAnimationTracker.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/SMILAnimationController.h"
103 #include "mozilla/SMILTimeContainer.h"
104 #include "mozilla/ScopeExit.h"
105 #include "mozilla/Components.h"
106 #include "mozilla/ServoStyleConsts.h"
107 #include "mozilla/ServoStyleSet.h"
108 #include "mozilla/ServoTypes.h"
109 #include "mozilla/SizeOfState.h"
110 #include "mozilla/Span.h"
111 #include "mozilla/Sprintf.h"
112 #include "mozilla/StaticAnalysisFunctions.h"
113 #include "mozilla/StaticPrefs_apz.h"
114 #include "mozilla/StaticPrefs_browser.h"
115 #include "mozilla/StaticPrefs_docshell.h"
116 #include "mozilla/StaticPrefs_dom.h"
117 #include "mozilla/StaticPrefs_fission.h"
118 #include "mozilla/StaticPrefs_full_screen_api.h"
119 #include "mozilla/StaticPrefs_layout.h"
120 #include "mozilla/StaticPrefs_network.h"
121 #include "mozilla/StaticPrefs_page_load.h"
122 #include "mozilla/StaticPrefs_plugins.h"
123 #include "mozilla/StaticPrefs_privacy.h"
124 #include "mozilla/StaticPrefs_security.h"
125 #include "mozilla/StaticPrefs_widget.h"
126 #include "mozilla/StaticPresData.h"
127 #include "mozilla/StorageAccess.h"
128 #include "mozilla/StoragePrincipalHelper.h"
129 #include "mozilla/StyleSheet.h"
130 #include "mozilla/Telemetry.h"
131 #include "mozilla/TelemetryHistogramEnums.h"
132 #include "mozilla/TelemetryScalarEnums.h"
133 #include "mozilla/TextControlElement.h"
134 #include "mozilla/TextEditor.h"
135 #include "mozilla/TimelineConsumers.h"
136 #include "mozilla/TypedEnumBits.h"
137 #include "mozilla/URLDecorationStripper.h"
138 #include "mozilla/URLExtraData.h"
139 #include "mozilla/Unused.h"
140 #include "mozilla/css/ImageLoader.h"
141 #include "mozilla/css/Loader.h"
142 #include "mozilla/css/Rule.h"
143 #include "mozilla/css/SheetParsingMode.h"
144 #include "mozilla/dom/AnonymousContent.h"
145 #include "mozilla/dom/BrowserChild.h"
146 #include "mozilla/dom/BrowsingContext.h"
147 #include "mozilla/dom/BrowsingContextGroup.h"
148 #include "mozilla/dom/CDATASection.h"
149 #include "mozilla/dom/CSPDictionariesBinding.h"
150 #include "mozilla/dom/CanonicalBrowsingContext.h"
151 #include "mozilla/dom/ChromeObserver.h"
152 #include "mozilla/dom/ClientInfo.h"
153 #include "mozilla/dom/ClientState.h"
154 #include "mozilla/dom/Comment.h"
155 #include "mozilla/dom/ContentChild.h"
156 #include "mozilla/dom/DOMImplementation.h"
157 #include "mozilla/dom/DOMIntersectionObserver.h"
158 #include "mozilla/dom/DOMStringList.h"
159 #include "mozilla/dom/DocGroup.h"
160 #include "mozilla/dom/DocumentBinding.h"
161 #include "mozilla/dom/DocumentFragment.h"
162 #include "mozilla/dom/DocumentL10n.h"
163 #include "mozilla/dom/DocumentTimeline.h"
164 #include "mozilla/dom/DocumentType.h"
165 #include "mozilla/dom/ElementBinding.h"
166 #include "mozilla/dom/Event.h"
167 #include "mozilla/dom/EventListenerBinding.h"
168 #include "mozilla/dom/FailedCertSecurityInfoBinding.h"
169 #include "mozilla/dom/FeaturePolicy.h"
170 #include "mozilla/dom/FeaturePolicyUtils.h"
171 #include "mozilla/dom/FontFaceSet.h"
172 #include "mozilla/dom/FromParser.h"
173 #include "mozilla/dom/HTMLAllCollection.h"
174 #include "mozilla/dom/HTMLBodyElement.h"
175 #include "mozilla/dom/HTMLCollectionBinding.h"
176 #include "mozilla/dom/HTMLDialogElement.h"
177 #include "mozilla/dom/HTMLFormElement.h"
178 #include "mozilla/dom/HTMLIFrameElement.h"
179 #include "mozilla/dom/HTMLImageElement.h"
180 #include "mozilla/dom/HTMLInputElement.h"
181 #include "mozilla/dom/HTMLLinkElement.h"
182 #include "mozilla/dom/HTMLMediaElement.h"
183 #include "mozilla/dom/HTMLMetaElement.h"
184 #include "mozilla/dom/HTMLSharedElement.h"
185 #include "mozilla/dom/HTMLTextAreaElement.h"
186 #include "mozilla/dom/ImageTracker.h"
187 #include "mozilla/dom/Link.h"
188 #include "mozilla/dom/MediaQueryList.h"
189 #include "mozilla/dom/MediaSource.h"
190 #include "mozilla/dom/MutationObservers.h"
191 #include "mozilla/dom/NameSpaceConstants.h"
192 #include "mozilla/dom/Navigator.h"
193 #include "mozilla/dom/NetErrorInfoBinding.h"
194 #include "mozilla/dom/NodeInfo.h"
195 #include "mozilla/dom/NodeIterator.h"
196 #include "mozilla/dom/PContentChild.h"
197 #include "mozilla/dom/PWindowGlobalChild.h"
198 #include "mozilla/dom/PageTransitionEvent.h"
199 #include "mozilla/dom/PageTransitionEventBinding.h"
200 #include "mozilla/dom/Performance.h"
201 #include "mozilla/dom/PermissionMessageUtils.h"
202 #include "mozilla/dom/PostMessageEvent.h"
203 #include "mozilla/dom/ProcessingInstruction.h"
204 #include "mozilla/dom/Promise.h"
205 #include "mozilla/dom/PromiseNativeHandler.h"
206 #include "mozilla/dom/ResizeObserverController.h"
207 #include "mozilla/dom/SVGElement.h"
208 #include "mozilla/dom/SVGSVGElement.h"
209 #include "mozilla/dom/SVGUseElement.h"
210 #include "mozilla/dom/ScriptLoader.h"
211 #include "mozilla/dom/ScriptSettings.h"
212 #include "mozilla/dom/Selection.h"
213 #include "mozilla/dom/ServiceWorkerContainer.h"
214 #include "mozilla/dom/ServiceWorkerDescriptor.h"
215 #include "mozilla/dom/ServiceWorkerManager.h"
216 #include "mozilla/dom/ShadowIncludingTreeIterator.h"
217 #include "mozilla/dom/ShadowRoot.h"
218 #include "mozilla/dom/StyleSheetApplicableStateChangeEvent.h"
219 #include "mozilla/dom/StyleSheetApplicableStateChangeEventBinding.h"
220 #include "mozilla/dom/StyleSheetList.h"
221 #include "mozilla/dom/TimeoutManager.h"
222 #include "mozilla/dom/Touch.h"
223 #include "mozilla/dom/TouchEvent.h"
224 #include "mozilla/dom/TreeOrderedArrayInlines.h"
225 #include "mozilla/dom/TreeWalker.h"
226 #include "mozilla/dom/URL.h"
227 #include "mozilla/dom/UserActivation.h"
228 #include "mozilla/dom/WindowBinding.h"
229 #include "mozilla/dom/WindowContext.h"
230 #include "mozilla/dom/WindowGlobalChild.h"
231 #include "mozilla/dom/WindowProxyHolder.h"
232 #include "mozilla/dom/WorkerDocumentListener.h"
233 #include "mozilla/dom/XPathEvaluator.h"
234 #include "mozilla/dom/nsCSPContext.h"
235 #include "mozilla/dom/nsCSPUtils.h"
236 #include "mozilla/extensions/WebExtensionPolicy.h"
237 #include "mozilla/fallible.h"
238 #include "mozilla/gfx/BaseCoord.h"
239 #include "mozilla/gfx/BaseSize.h"
240 #include "mozilla/gfx/Coord.h"
241 #include "mozilla/gfx/Point.h"
242 #include "mozilla/gfx/ScaleFactor.h"
243 #include "mozilla/intl/LocaleService.h"
244 #include "mozilla/ipc/IdleSchedulerChild.h"
245 #include "mozilla/ipc/MessageChannel.h"
246 #include "mozilla/net/ChannelEventQueue.h"
247 #include "mozilla/net/CookieJarSettings.h"
248 #include "mozilla/net/NeckoChannelParams.h"
249 #include "mozilla/net/RequestContextService.h"
250 #include "nsAboutProtocolUtils.h"
251 #include "nsAlgorithm.h"
252 #include "nsAttrValue.h"
253 #include "nsAttrValueInlines.h"
254 #include "nsBaseHashtable.h"
255 #include "nsBidiUtils.h"
256 #include "nsCRT.h"
257 #include "nsCSSPropertyID.h"
258 #include "nsCSSProps.h"
259 #include "nsCSSPseudoElements.h"
260 #include "nsCSSRendering.h"
261 #include "nsCanvasFrame.h"
262 #include "nsCaseTreatment.h"
263 #include "nsCharsetSource.h"
264 #include "nsCommandManager.h"
265 #include "nsCommandParams.h"
266 #include "nsComponentManagerUtils.h"
267 #include "nsContentCreatorFunctions.h"
268 #include "nsContentList.h"
269 #include "nsContentPermissionHelper.h"
270 #include "nsContentSecurityUtils.h"
271 #include "nsContentUtils.h"
272 #include "nsCoord.h"
273 #include "nsCycleCollectionNoteChild.h"
274 #include "nsCycleCollectionTraversalCallback.h"
275 #include "nsDOMAttributeMap.h"
276 #include "nsDOMCaretPosition.h"
277 #include "nsDOMNavigationTiming.h"
278 #include "nsDOMString.h"
279 #include "nsDeviceContext.h"
280 #include "nsDocShell.h"
281 #include "nsError.h"
282 #include "nsEscape.h"
283 #include "nsFocusManager.h"
284 #include "nsFrameLoader.h"
285 #include "nsFrameLoaderOwner.h"
286 #include "nsGenericHTMLElement.h"
287 #include "nsGlobalWindowInner.h"
288 #include "nsGlobalWindowOuter.h"
289 #include "nsHTMLCSSStyleSheet.h"
290 #include "nsHTMLDocument.h"
291 #include "nsHTMLStyleSheet.h"
292 #include "nsHtml5Module.h"
293 #include "nsHtml5Parser.h"
294 #include "nsHtml5TreeOpExecutor.h"
295 #include "nsIAsyncShutdown.h"
296 #include "nsIAuthPrompt.h"
297 #include "nsIAuthPrompt2.h"
298 #include "nsIBFCacheEntry.h"
299 #include "nsIBaseWindow.h"
300 #include "nsIBrowserChild.h"
301 #include "nsIBrowserUsage.h"
302 #include "nsICSSLoaderObserver.h"
303 #include "nsICategoryManager.h"
304 #include "nsICertOverrideService.h"
305 #include "nsIContent.h"
306 #include "nsIContentInlines.h"
307 #include "nsIContentPolicy.h"
308 #include "nsIContentSecurityPolicy.h"
309 #include "nsIContentSink.h"
310 #include "nsICookieJarSettings.h"
311 #include "nsICookieService.h"
312 #include "nsIDOMXULCommandDispatcher.h"
313 #include "nsIDocShell.h"
314 #include "nsIDocShellTreeItem.h"
315 #include "nsIDocumentActivity.h"
316 #include "nsIDocumentEncoder.h"
317 #include "nsIDocumentLoader.h"
318 #include "nsIDocumentLoaderFactory.h"
319 #include "nsIDocumentObserver.h"
320 #include "nsIEditingSession.h"
321 #include "nsIEditor.h"
322 #include "nsIEffectiveTLDService.h"
323 #include "nsIFile.h"
324 #include "nsIFileChannel.h"
325 #include "nsIFrame.h"
326 #include "nsIGlobalObject.h"
327 #include "nsIHTMLCollection.h"
328 #include "nsIHttpChannel.h"
329 #include "nsIHttpChannelInternal.h"
330 #include "nsIIOService.h"
331 #include "nsIImageLoadingContent.h"
332 #include "nsIInlineSpellChecker.h"
333 #include "nsIInputStreamChannel.h"
334 #include "nsIInterfaceRequestorUtils.h"
335 #include "nsILayoutHistoryState.h"
336 #include "nsIMultiPartChannel.h"
337 #include "nsIMutationObserver.h"
338 #include "nsINSSErrorsService.h"
339 #include "nsINamed.h"
340 #include "nsINodeList.h"
341 #include "nsIObjectLoadingContent.h"
342 #include "nsIObserverService.h"
343 #include "nsIPermission.h"
344 #include "nsIPrompt.h"
345 #include "nsIPropertyBag2.h"
346 #include "nsIPublicKeyPinningService.h"
347 #include "nsIReferrerInfo.h"
348 #include "nsIRefreshURI.h"
349 #include "nsIRequest.h"
350 #include "nsIRequestContext.h"
351 #include "nsIRunnable.h"
352 #include "nsISHEntry.h"
353 #include "nsIScriptElement.h"
354 #include "nsIScriptError.h"
355 #include "nsIScriptGlobalObject.h"
356 #include "nsIScriptSecurityManager.h"
357 #include "nsISecurityConsoleMessage.h"
358 #include "nsISelectionController.h"
359 #include "nsISerialEventTarget.h"
360 #include "nsISerializable.h"
361 #include "nsISimpleEnumerator.h"
362 #include "nsISiteSecurityService.h"
363 #include "nsISocketProvider.h"
364 #include "nsISpeculativeConnect.h"
365 #include "nsIStructuredCloneContainer.h"
366 #include "nsIThread.h"
367 #include "nsITimedChannel.h"
368 #include "nsITimer.h"
369 #include "nsITransportSecurityInfo.h"
370 #include "nsIURIMutator.h"
371 #include "nsIVariant.h"
372 #include "nsIWeakReference.h"
373 #include "nsIWebNavigation.h"
374 #include "nsIWidget.h"
375 #include "nsIX509Cert.h"
376 #include "nsIX509CertValidity.h"
377 #include "nsIXMLContentSink.h"
378 #include "nsIXULRuntime.h"
379 #include "nsImageLoadingContent.h"
380 #include "nsImportModule.h"
381 #include "nsLanguageAtomService.h"
382 #include "nsLayoutUtils.h"
383 #include "nsNetCID.h"
384 #include "nsNetUtil.h"
385 #include "nsNodeInfoManager.h"
386 #include "nsObjectLoadingContent.h"
387 #include "nsPIDOMWindowInlines.h"
388 #include "nsPIWindowRoot.h"
389 #include "nsPoint.h"
390 #include "nsPointerHashKeys.h"
391 #include "nsPresContext.h"
392 #include "nsQueryFrame.h"
393 #include "nsQueryObject.h"
394 #include "nsRange.h"
395 #include "nsRect.h"
396 #include "nsRefreshDriver.h"
397 #include "nsSandboxFlags.h"
398 #include "nsSerializationHelper.h"
399 #include "nsServiceManagerUtils.h"
400 #include "nsStringFlags.h"
401 #include "nsStyleUtil.h"
402 #include "nsStringIterator.h"
403 #include "nsStyleSheetService.h"
404 #include "nsStyleStruct.h"
405 #include "nsTextNode.h"
406 #include "nsUnicharUtils.h"
407 #include "nsWrapperCache.h"
408 #include "nsWrapperCacheInlines.h"
409 #include "nsXPCOMCID.h"
410 #include "nsXULAppAPI.h"
411 #include "prthread.h"
412 #include "prtime.h"
413 #include "prtypes.h"
414 #include "xpcpublic.h"
415 
416 // XXX Must be included after mozilla/Encoding.h
417 #include "encoding_rs.h"
418 
419 #include "mozilla/dom/XULBroadcastManager.h"
420 #include "mozilla/dom/XULPersist.h"
421 #include "nsIAppWindow.h"
422 #include "nsXULPrototypeDocument.h"
423 #include "nsXULCommandDispatcher.h"
424 #include "nsXULPopupManager.h"
425 #include "nsIDocShellTreeOwner.h"
426 
427 #define XML_DECLARATION_BITS_DECLARATION_EXISTS (1 << 0)
428 #define XML_DECLARATION_BITS_ENCODING_EXISTS (1 << 1)
429 #define XML_DECLARATION_BITS_STANDALONE_EXISTS (1 << 2)
430 #define XML_DECLARATION_BITS_STANDALONE_YES (1 << 3)
431 
432 #define NS_MAX_DOCUMENT_WRITE_DEPTH 20
433 
434 mozilla::LazyLogModule gPageCacheLog("PageCache");
435 mozilla::LazyLogModule gSHIPBFCacheLog("SHIPBFCache");
436 mozilla::LazyLogModule gTimeoutDeferralLog("TimeoutDefer");
437 mozilla::LazyLogModule gUseCountersLog("UseCounters");
438 
439 namespace mozilla {
440 namespace dom {
441 
442 class Document::HeaderData {
443  public:
HeaderData(nsAtom * aField,const nsAString & aData)444   HeaderData(nsAtom* aField, const nsAString& aData)
445       : mField(aField), mData(aData) {}
446 
~HeaderData()447   ~HeaderData() {
448     // Delete iteratively to avoid blowing up the stack, though it shouldn't
449     // happen in practice.
450     UniquePtr<HeaderData> next = std::move(mNext);
451     while (next) {
452       next = std::move(next->mNext);
453     }
454   }
455 
456   RefPtr<nsAtom> mField;
457   nsString mData;
458   UniquePtr<HeaderData> mNext;
459 };
460 
461 using LinkArray = nsTArray<Link*>;
462 
463 AutoTArray<Document*, 8>* Document::sLoadingForegroundTopLevelContentDocument =
464     nullptr;
465 
466 static LazyLogModule gDocumentLeakPRLog("DocumentLeak");
467 static LazyLogModule gCspPRLog("CSP");
468 LazyLogModule gUserInteractionPRLog("UserInteraction");
469 
GetHttpChannelHelper(nsIChannel * aChannel,nsIHttpChannel ** aHttpChannel)470 static nsresult GetHttpChannelHelper(nsIChannel* aChannel,
471                                      nsIHttpChannel** aHttpChannel) {
472   nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(aChannel);
473   if (httpChannel) {
474     httpChannel.forget(aHttpChannel);
475     return NS_OK;
476   }
477 
478   nsCOMPtr<nsIMultiPartChannel> multipart = do_QueryInterface(aChannel);
479   if (!multipart) {
480     *aHttpChannel = nullptr;
481     return NS_OK;
482   }
483 
484   nsCOMPtr<nsIChannel> baseChannel;
485   nsresult rv = multipart->GetBaseChannel(getter_AddRefs(baseChannel));
486   if (NS_WARN_IF(NS_FAILED(rv))) {
487     return rv;
488   }
489 
490   httpChannel = do_QueryInterface(baseChannel);
491   httpChannel.forget(aHttpChannel);
492 
493   return NS_OK;
494 }
495 
496 }  // namespace dom
497 
498 #define NAME_NOT_VALID ((nsSimpleContentList*)1)
499 
IdentifierMapEntry(const IdentifierMapEntry::DependentAtomOrString * aKey)500 IdentifierMapEntry::IdentifierMapEntry(
501     const IdentifierMapEntry::DependentAtomOrString* aKey)
502     : mKey(aKey ? *aKey : nullptr) {}
503 
Traverse(nsCycleCollectionTraversalCallback * aCallback)504 void IdentifierMapEntry::Traverse(
505     nsCycleCollectionTraversalCallback* aCallback) {
506   NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*aCallback,
507                                      "mIdentifierMap mNameContentList");
508   aCallback->NoteXPCOMChild(static_cast<nsINodeList*>(mNameContentList));
509 
510   if (mImageElement) {
511     NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*aCallback,
512                                        "mIdentifierMap mImageElement element");
513     nsIContent* imageElement = mImageElement;
514     aCallback->NoteXPCOMChild(imageElement);
515   }
516 }
517 
IsEmpty()518 bool IdentifierMapEntry::IsEmpty() {
519   return mIdContentList->IsEmpty() && !mNameContentList && !mChangeCallbacks &&
520          !mImageElement;
521 }
522 
HasNameElement() const523 bool IdentifierMapEntry::HasNameElement() const {
524   return mNameContentList && mNameContentList->Length() != 0;
525 }
526 
AddContentChangeCallback(Document::IDTargetObserver aCallback,void * aData,bool aForImage)527 void IdentifierMapEntry::AddContentChangeCallback(
528     Document::IDTargetObserver aCallback, void* aData, bool aForImage) {
529   if (!mChangeCallbacks) {
530     mChangeCallbacks = MakeUnique<nsTHashtable<ChangeCallbackEntry>>();
531   }
532 
533   ChangeCallback cc = {aCallback, aData, aForImage};
534   mChangeCallbacks->PutEntry(cc);
535 }
536 
RemoveContentChangeCallback(Document::IDTargetObserver aCallback,void * aData,bool aForImage)537 void IdentifierMapEntry::RemoveContentChangeCallback(
538     Document::IDTargetObserver aCallback, void* aData, bool aForImage) {
539   if (!mChangeCallbacks) return;
540   ChangeCallback cc = {aCallback, aData, aForImage};
541   mChangeCallbacks->RemoveEntry(cc);
542   if (mChangeCallbacks->Count() == 0) {
543     mChangeCallbacks = nullptr;
544   }
545 }
546 
FireChangeCallbacks(Element * aOldElement,Element * aNewElement,bool aImageOnly)547 void IdentifierMapEntry::FireChangeCallbacks(Element* aOldElement,
548                                              Element* aNewElement,
549                                              bool aImageOnly) {
550   if (!mChangeCallbacks) return;
551 
552   for (auto iter = mChangeCallbacks->Iter(); !iter.Done(); iter.Next()) {
553     IdentifierMapEntry::ChangeCallbackEntry* entry = iter.Get();
554     // Don't fire image changes for non-image observers, and don't fire element
555     // changes for image observers when an image override is active.
556     if (entry->mKey.mForImage ? (mImageElement && !aImageOnly) : aImageOnly) {
557       continue;
558     }
559 
560     if (!entry->mKey.mCallback(aOldElement, aNewElement, entry->mKey.mData)) {
561       iter.Remove();
562     }
563   }
564 }
565 
AddIdElement(Element * aElement)566 void IdentifierMapEntry::AddIdElement(Element* aElement) {
567   MOZ_ASSERT(aElement, "Must have element");
568   MOZ_ASSERT(!mIdContentList->Contains(nullptr), "Why is null in our list?");
569 
570   size_t index = mIdContentList.Insert(*aElement);
571   if (index == 0) {
572     Element* oldElement = mIdContentList->SafeElementAt(1);
573     FireChangeCallbacks(oldElement, aElement);
574   }
575 }
576 
RemoveIdElement(Element * aElement)577 void IdentifierMapEntry::RemoveIdElement(Element* aElement) {
578   MOZ_ASSERT(aElement, "Missing element");
579 
580   // This should only be called while the document is in an update.
581   // Assertions near the call to this method guarantee this.
582 
583   // This could fire in OOM situations
584   // Only assert this in HTML documents for now as XUL does all sorts of weird
585   // crap.
586   NS_ASSERTION(!aElement->OwnerDoc()->IsHTMLDocument() ||
587                    mIdContentList->Contains(aElement),
588                "Removing id entry that doesn't exist");
589 
590   // XXXbz should this ever Compact() I guess when all the content is gone
591   // we'll just get cleaned up in the natural order of things...
592   Element* currentElement = mIdContentList->SafeElementAt(0);
593   mIdContentList.RemoveElement(*aElement);
594   if (currentElement == aElement) {
595     FireChangeCallbacks(currentElement, mIdContentList->SafeElementAt(0));
596   }
597 }
598 
SetImageElement(Element * aElement)599 void IdentifierMapEntry::SetImageElement(Element* aElement) {
600   Element* oldElement = GetImageIdElement();
601   mImageElement = aElement;
602   Element* newElement = GetImageIdElement();
603   if (oldElement != newElement) {
604     FireChangeCallbacks(oldElement, newElement, true);
605   }
606 }
607 
ClearAndNotify()608 void IdentifierMapEntry::ClearAndNotify() {
609   Element* currentElement = mIdContentList->SafeElementAt(0);
610   mIdContentList.Clear();
611   if (currentElement) {
612     FireChangeCallbacks(currentElement, nullptr);
613   }
614   mNameContentList = nullptr;
615   if (mImageElement) {
616     SetImageElement(nullptr);
617   }
618   mChangeCallbacks = nullptr;
619 }
620 
621 namespace dom {
622 
623 class SimpleHTMLCollection final : public nsSimpleContentList,
624                                    public nsIHTMLCollection {
625  public:
SimpleHTMLCollection(nsINode * aRoot)626   explicit SimpleHTMLCollection(nsINode* aRoot) : nsSimpleContentList(aRoot) {}
627 
628   NS_DECL_ISUPPORTS_INHERITED
629 
GetParentObject()630   virtual nsINode* GetParentObject() override {
631     return nsSimpleContentList::GetParentObject();
632   }
Length()633   virtual uint32_t Length() override { return nsSimpleContentList::Length(); }
GetElementAt(uint32_t aIndex)634   virtual Element* GetElementAt(uint32_t aIndex) override {
635     return mElements.SafeElementAt(aIndex)->AsElement();
636   }
637 
GetFirstNamedElement(const nsAString & aName,bool & aFound)638   virtual Element* GetFirstNamedElement(const nsAString& aName,
639                                         bool& aFound) override {
640     aFound = false;
641     RefPtr<nsAtom> name = NS_Atomize(aName);
642     for (uint32_t i = 0; i < mElements.Length(); i++) {
643       MOZ_DIAGNOSTIC_ASSERT(mElements[i]);
644       Element* element = mElements[i]->AsElement();
645       if (element->GetID() == name ||
646           (element->HasName() &&
647            element->GetParsedAttr(nsGkAtoms::name)->GetAtomValue() == name)) {
648         aFound = true;
649         return element;
650       }
651     }
652     return nullptr;
653   }
654 
GetSupportedNames(nsTArray<nsString> & aNames)655   virtual void GetSupportedNames(nsTArray<nsString>& aNames) override {
656     AutoTArray<nsAtom*, 8> atoms;
657     for (uint32_t i = 0; i < mElements.Length(); i++) {
658       MOZ_DIAGNOSTIC_ASSERT(mElements[i]);
659       Element* element = mElements[i]->AsElement();
660 
661       nsAtom* id = element->GetID();
662       MOZ_ASSERT(id != nsGkAtoms::_empty);
663       if (id && !atoms.Contains(id)) {
664         atoms.AppendElement(id);
665       }
666 
667       if (element->HasName()) {
668         nsAtom* name = element->GetParsedAttr(nsGkAtoms::name)->GetAtomValue();
669         MOZ_ASSERT(name && name != nsGkAtoms::_empty);
670         if (name && !atoms.Contains(name)) {
671           atoms.AppendElement(name);
672         }
673       }
674     }
675 
676     nsString* names = aNames.AppendElements(atoms.Length());
677     for (uint32_t i = 0; i < atoms.Length(); i++) {
678       atoms[i]->ToString(names[i]);
679     }
680   }
681 
GetWrapperPreserveColorInternal()682   virtual JSObject* GetWrapperPreserveColorInternal() override {
683     return nsWrapperCache::GetWrapperPreserveColor();
684   }
PreserveWrapperInternal(nsISupports * aScriptObjectHolder)685   virtual void PreserveWrapperInternal(
686       nsISupports* aScriptObjectHolder) override {
687     nsWrapperCache::PreserveWrapper(aScriptObjectHolder);
688   }
WrapObject(JSContext * aCx,JS::Handle<JSObject * > aGivenProto)689   virtual JSObject* WrapObject(JSContext* aCx,
690                                JS::Handle<JSObject*> aGivenProto) override {
691     return HTMLCollection_Binding::Wrap(aCx, this, aGivenProto);
692   }
693 
694   using nsBaseContentList::Item;
695 
696  private:
697   virtual ~SimpleHTMLCollection() = default;
698 };
699 
700 NS_IMPL_ISUPPORTS_INHERITED(SimpleHTMLCollection, nsSimpleContentList,
701                             nsIHTMLCollection)
702 
703 }  // namespace dom
704 
AddNameElement(nsINode * aNode,Element * aElement)705 void IdentifierMapEntry::AddNameElement(nsINode* aNode, Element* aElement) {
706   if (!mNameContentList) {
707     mNameContentList = new dom::SimpleHTMLCollection(aNode);
708   }
709 
710   mNameContentList->AppendElement(aElement);
711 }
712 
RemoveNameElement(Element * aElement)713 void IdentifierMapEntry::RemoveNameElement(Element* aElement) {
714   if (mNameContentList) {
715     mNameContentList->RemoveElement(aElement);
716   }
717 }
718 
HasIdElementExposedAsHTMLDocumentProperty() const719 bool IdentifierMapEntry::HasIdElementExposedAsHTMLDocumentProperty() const {
720   Element* idElement = GetIdElement();
721   return idElement &&
722          nsGenericHTMLElement::ShouldExposeIdAsHTMLDocumentProperty(idElement);
723 }
724 
SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const725 size_t IdentifierMapEntry::SizeOfExcludingThis(
726     MallocSizeOf aMallocSizeOf) const {
727   return mKey.mString.SizeOfExcludingThisIfUnshared(aMallocSizeOf);
728 }
729 
730 // Helper structs for the content->subdoc map
731 
732 class SubDocMapEntry : public PLDHashEntryHdr {
733  public:
734   // Both of these are strong references
735   dom::Element* mKey;  // must be first, to look like PLDHashEntryStub
736   dom::Document* mSubDocument;
737 };
738 
739 class OnloadBlocker final : public nsIRequest {
740  public:
741   OnloadBlocker() = default;
742 
743   NS_DECL_ISUPPORTS
744   NS_DECL_NSIREQUEST
745 
746  private:
747   ~OnloadBlocker() = default;
748 };
749 
NS_IMPL_ISUPPORTS(OnloadBlocker,nsIRequest)750 NS_IMPL_ISUPPORTS(OnloadBlocker, nsIRequest)
751 
752 NS_IMETHODIMP
753 OnloadBlocker::GetName(nsACString& aResult) {
754   aResult.AssignLiteral("about:document-onload-blocker");
755   return NS_OK;
756 }
757 
758 NS_IMETHODIMP
IsPending(bool * _retval)759 OnloadBlocker::IsPending(bool* _retval) {
760   *_retval = true;
761   return NS_OK;
762 }
763 
764 NS_IMETHODIMP
GetStatus(nsresult * status)765 OnloadBlocker::GetStatus(nsresult* status) {
766   *status = NS_OK;
767   return NS_OK;
768 }
769 
770 NS_IMETHODIMP
Cancel(nsresult status)771 OnloadBlocker::Cancel(nsresult status) { return NS_OK; }
772 NS_IMETHODIMP
Suspend(void)773 OnloadBlocker::Suspend(void) { return NS_OK; }
774 NS_IMETHODIMP
Resume(void)775 OnloadBlocker::Resume(void) { return NS_OK; }
776 
777 NS_IMETHODIMP
GetLoadGroup(nsILoadGroup ** aLoadGroup)778 OnloadBlocker::GetLoadGroup(nsILoadGroup** aLoadGroup) {
779   *aLoadGroup = nullptr;
780   return NS_OK;
781 }
782 
783 NS_IMETHODIMP
SetLoadGroup(nsILoadGroup * aLoadGroup)784 OnloadBlocker::SetLoadGroup(nsILoadGroup* aLoadGroup) { return NS_OK; }
785 
786 NS_IMETHODIMP
GetLoadFlags(nsLoadFlags * aLoadFlags)787 OnloadBlocker::GetLoadFlags(nsLoadFlags* aLoadFlags) {
788   *aLoadFlags = nsIRequest::LOAD_NORMAL;
789   return NS_OK;
790 }
791 
792 NS_IMETHODIMP
GetTRRMode(nsIRequest::TRRMode * aTRRMode)793 OnloadBlocker::GetTRRMode(nsIRequest::TRRMode* aTRRMode) {
794   return GetTRRModeImpl(aTRRMode);
795 }
796 
797 NS_IMETHODIMP
SetTRRMode(nsIRequest::TRRMode aTRRMode)798 OnloadBlocker::SetTRRMode(nsIRequest::TRRMode aTRRMode) {
799   return SetTRRModeImpl(aTRRMode);
800 }
801 
802 NS_IMETHODIMP
SetLoadFlags(nsLoadFlags aLoadFlags)803 OnloadBlocker::SetLoadFlags(nsLoadFlags aLoadFlags) { return NS_OK; }
804 
805 // ==================================================================
806 
807 namespace dom {
808 
ExternalResourceMap()809 ExternalResourceMap::ExternalResourceMap() : mHaveShutDown(false) {}
810 
RequestResource(nsIURI * aURI,nsIReferrerInfo * aReferrerInfo,nsINode * aRequestingNode,Document * aDisplayDocument,ExternalResourceLoad ** aPendingLoad)811 Document* ExternalResourceMap::RequestResource(
812     nsIURI* aURI, nsIReferrerInfo* aReferrerInfo, nsINode* aRequestingNode,
813     Document* aDisplayDocument, ExternalResourceLoad** aPendingLoad) {
814   // If we ever start allowing non-same-origin loads here, we might need to do
815   // something interesting with aRequestingPrincipal even for the hashtable
816   // gets.
817   MOZ_ASSERT(aURI, "Must have a URI");
818   MOZ_ASSERT(aRequestingNode, "Must have a node");
819   MOZ_ASSERT(aReferrerInfo, "Must have a referrerInfo");
820   *aPendingLoad = nullptr;
821   if (mHaveShutDown) {
822     return nullptr;
823   }
824 
825   // First, make sure we strip the ref from aURI.
826   nsCOMPtr<nsIURI> clone;
827   nsresult rv = NS_GetURIWithoutRef(aURI, getter_AddRefs(clone));
828   if (NS_FAILED(rv) || !clone) {
829     return nullptr;
830   }
831 
832   ExternalResource* resource;
833   mMap.Get(clone, &resource);
834   if (resource) {
835     return resource->mDocument;
836   }
837 
838   bool loadStartSucceeded =
839       mPendingLoads.WithEntryHandle(clone, [&](auto&& loadEntry) {
840         if (!loadEntry) {
841           loadEntry.Insert(MakeRefPtr<PendingLoad>(aDisplayDocument));
842 
843           if (NS_FAILED(loadEntry.Data()->StartLoad(clone, aReferrerInfo,
844                                                     aRequestingNode))) {
845             return false;
846           }
847         }
848 
849         RefPtr<PendingLoad> load(loadEntry.Data());
850         load.forget(aPendingLoad);
851         return true;
852       });
853   if (!loadStartSucceeded) {
854     // Make sure we don't thrash things by trying this load again, since
855     // chances are it failed for good reasons (security check, etc).
856     // This must be done outside the WithEntryHandle functor, as it accesses
857     // mPendingLoads.
858     AddExternalResource(clone, nullptr, nullptr, aDisplayDocument);
859   }
860 
861   return nullptr;
862 }
863 
EnumerateResources(SubDocEnumFunc aCallback)864 void ExternalResourceMap::EnumerateResources(SubDocEnumFunc aCallback) {
865   nsTArray<RefPtr<Document>> docs(mMap.Count());
866   for (const auto& entry : mMap.Values()) {
867     if (Document* doc = entry->mDocument) {
868       docs.AppendElement(doc);
869     }
870   }
871 
872   for (auto& doc : docs) {
873     if (aCallback(*doc) == CallState::Stop) {
874       return;
875     }
876   }
877 }
878 
Traverse(nsCycleCollectionTraversalCallback * aCallback) const879 void ExternalResourceMap::Traverse(
880     nsCycleCollectionTraversalCallback* aCallback) const {
881   // mPendingLoads will get cleared out as the requests complete, so
882   // no need to worry about those here.
883   for (const auto& entry : mMap) {
884     ExternalResourceMap::ExternalResource* resource = entry.GetWeak();
885 
886     NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*aCallback,
887                                        "mExternalResourceMap.mMap entry"
888                                        "->mDocument");
889     aCallback->NoteXPCOMChild(ToSupports(resource->mDocument));
890 
891     NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*aCallback,
892                                        "mExternalResourceMap.mMap entry"
893                                        "->mViewer");
894     aCallback->NoteXPCOMChild(resource->mViewer);
895 
896     NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*aCallback,
897                                        "mExternalResourceMap.mMap entry"
898                                        "->mLoadGroup");
899     aCallback->NoteXPCOMChild(resource->mLoadGroup);
900   }
901 }
902 
HideViewers()903 void ExternalResourceMap::HideViewers() {
904   for (const auto& entry : mMap) {
905     nsCOMPtr<nsIContentViewer> viewer = entry.GetData()->mViewer;
906     if (viewer) {
907       viewer->Hide();
908     }
909   }
910 }
911 
ShowViewers()912 void ExternalResourceMap::ShowViewers() {
913   for (const auto& entry : mMap) {
914     nsCOMPtr<nsIContentViewer> viewer = entry.GetData()->mViewer;
915     if (viewer) {
916       viewer->Show();
917     }
918   }
919 }
920 
TransferShowingState(Document * aFromDoc,Document * aToDoc)921 void TransferShowingState(Document* aFromDoc, Document* aToDoc) {
922   MOZ_ASSERT(aFromDoc && aToDoc, "transferring showing state from/to null doc");
923 
924   if (aFromDoc->IsShowing()) {
925     aToDoc->OnPageShow(true, nullptr);
926   }
927 }
928 
AddExternalResource(nsIURI * aURI,nsIContentViewer * aViewer,nsILoadGroup * aLoadGroup,Document * aDisplayDocument)929 nsresult ExternalResourceMap::AddExternalResource(nsIURI* aURI,
930                                                   nsIContentViewer* aViewer,
931                                                   nsILoadGroup* aLoadGroup,
932                                                   Document* aDisplayDocument) {
933   MOZ_ASSERT(aURI, "Unexpected call");
934   MOZ_ASSERT((aViewer && aLoadGroup) || (!aViewer && !aLoadGroup),
935              "Must have both or neither");
936 
937   RefPtr<PendingLoad> load;
938   mPendingLoads.Remove(aURI, getter_AddRefs(load));
939 
940   nsresult rv = NS_OK;
941 
942   nsCOMPtr<Document> doc;
943   if (aViewer) {
944     doc = aViewer->GetDocument();
945     NS_ASSERTION(doc, "Must have a document");
946 
947     doc->SetDisplayDocument(aDisplayDocument);
948 
949     // Make sure that hiding our viewer will tear down its presentation.
950     aViewer->SetSticky(false);
951 
952     rv = aViewer->Init(nullptr, nsIntRect(0, 0, 0, 0), nullptr);
953     if (NS_SUCCEEDED(rv)) {
954       rv = aViewer->Open(nullptr, nullptr);
955     }
956 
957     if (NS_FAILED(rv)) {
958       doc = nullptr;
959       aViewer = nullptr;
960       aLoadGroup = nullptr;
961     }
962   }
963 
964   ExternalResource* newResource =
965       mMap.InsertOrUpdate(aURI, MakeUnique<ExternalResource>()).get();
966 
967   newResource->mDocument = doc;
968   newResource->mViewer = aViewer;
969   newResource->mLoadGroup = aLoadGroup;
970   if (doc) {
971     if (nsPresContext* pc = doc->GetPresContext()) {
972       pc->RecomputeBrowsingContextDependentData();
973     }
974     TransferShowingState(aDisplayDocument, doc);
975   }
976 
977   const nsTArray<nsCOMPtr<nsIObserver>>& obs = load->Observers();
978   for (uint32_t i = 0; i < obs.Length(); ++i) {
979     obs[i]->Observe(ToSupports(doc), "external-resource-document-created",
980                     nullptr);
981   }
982 
983   return rv;
984 }
985 
NS_IMPL_ISUPPORTS(ExternalResourceMap::PendingLoad,nsIStreamListener,nsIRequestObserver)986 NS_IMPL_ISUPPORTS(ExternalResourceMap::PendingLoad, nsIStreamListener,
987                   nsIRequestObserver)
988 
989 NS_IMETHODIMP
990 ExternalResourceMap::PendingLoad::OnStartRequest(nsIRequest* aRequest) {
991   ExternalResourceMap& map = mDisplayDocument->ExternalResourceMap();
992   if (map.HaveShutDown()) {
993     return NS_BINDING_ABORTED;
994   }
995 
996   nsCOMPtr<nsIContentViewer> viewer;
997   nsCOMPtr<nsILoadGroup> loadGroup;
998   nsresult rv =
999       SetupViewer(aRequest, getter_AddRefs(viewer), getter_AddRefs(loadGroup));
1000 
1001   // Make sure to do this no matter what
1002   nsresult rv2 =
1003       map.AddExternalResource(mURI, viewer, loadGroup, mDisplayDocument);
1004   if (NS_FAILED(rv)) {
1005     return rv;
1006   }
1007   if (NS_FAILED(rv2)) {
1008     mTargetListener = nullptr;
1009     return rv2;
1010   }
1011 
1012   return mTargetListener->OnStartRequest(aRequest);
1013 }
1014 
SetupViewer(nsIRequest * aRequest,nsIContentViewer ** aViewer,nsILoadGroup ** aLoadGroup)1015 nsresult ExternalResourceMap::PendingLoad::SetupViewer(
1016     nsIRequest* aRequest, nsIContentViewer** aViewer,
1017     nsILoadGroup** aLoadGroup) {
1018   MOZ_ASSERT(!mTargetListener, "Unexpected call to OnStartRequest");
1019   *aViewer = nullptr;
1020   *aLoadGroup = nullptr;
1021 
1022   nsCOMPtr<nsIChannel> chan(do_QueryInterface(aRequest));
1023   NS_ENSURE_TRUE(chan, NS_ERROR_UNEXPECTED);
1024 
1025   nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(aRequest));
1026   if (httpChannel) {
1027     bool requestSucceeded;
1028     if (NS_FAILED(httpChannel->GetRequestSucceeded(&requestSucceeded)) ||
1029         !requestSucceeded) {
1030       // Bail out on this load, since it looks like we have an HTTP error page
1031       return NS_BINDING_ABORTED;
1032     }
1033   }
1034 
1035   nsAutoCString type;
1036   chan->GetContentType(type);
1037 
1038   nsCOMPtr<nsILoadGroup> loadGroup;
1039   chan->GetLoadGroup(getter_AddRefs(loadGroup));
1040 
1041   // Give this document its own loadgroup
1042   nsCOMPtr<nsILoadGroup> newLoadGroup =
1043       do_CreateInstance(NS_LOADGROUP_CONTRACTID);
1044   NS_ENSURE_TRUE(newLoadGroup, NS_ERROR_OUT_OF_MEMORY);
1045   newLoadGroup->SetLoadGroup(loadGroup);
1046 
1047   nsCOMPtr<nsIInterfaceRequestor> callbacks;
1048   loadGroup->GetNotificationCallbacks(getter_AddRefs(callbacks));
1049 
1050   nsCOMPtr<nsIInterfaceRequestor> newCallbacks =
1051       new LoadgroupCallbacks(callbacks);
1052   newLoadGroup->SetNotificationCallbacks(newCallbacks);
1053 
1054   // This is some serious hackery cribbed from docshell
1055   nsCOMPtr<nsICategoryManager> catMan =
1056       do_GetService(NS_CATEGORYMANAGER_CONTRACTID);
1057   NS_ENSURE_TRUE(catMan, NS_ERROR_NOT_AVAILABLE);
1058   nsCString contractId;
1059   nsresult rv =
1060       catMan->GetCategoryEntry("Gecko-Content-Viewers", type, contractId);
1061   NS_ENSURE_SUCCESS(rv, rv);
1062   nsCOMPtr<nsIDocumentLoaderFactory> docLoaderFactory =
1063       do_GetService(contractId.get());
1064   NS_ENSURE_TRUE(docLoaderFactory, NS_ERROR_NOT_AVAILABLE);
1065 
1066   nsCOMPtr<nsIContentViewer> viewer;
1067   nsCOMPtr<nsIStreamListener> listener;
1068   rv = docLoaderFactory->CreateInstance(
1069       "external-resource", chan, newLoadGroup, type, nullptr, nullptr,
1070       getter_AddRefs(listener), getter_AddRefs(viewer));
1071   NS_ENSURE_SUCCESS(rv, rv);
1072   NS_ENSURE_TRUE(viewer, NS_ERROR_UNEXPECTED);
1073 
1074   nsCOMPtr<nsIParser> parser = do_QueryInterface(listener);
1075   if (!parser) {
1076     /// We don't want to deal with the various fake documents yet
1077     return NS_ERROR_NOT_IMPLEMENTED;
1078   }
1079 
1080   // We can't handle HTML and other weird things here yet.
1081   nsIContentSink* sink = parser->GetContentSink();
1082   nsCOMPtr<nsIXMLContentSink> xmlSink = do_QueryInterface(sink);
1083   if (!xmlSink) {
1084     return NS_ERROR_NOT_IMPLEMENTED;
1085   }
1086 
1087   listener.swap(mTargetListener);
1088   viewer.forget(aViewer);
1089   newLoadGroup.forget(aLoadGroup);
1090   return NS_OK;
1091 }
1092 
1093 NS_IMETHODIMP
OnDataAvailable(nsIRequest * aRequest,nsIInputStream * aStream,uint64_t aOffset,uint32_t aCount)1094 ExternalResourceMap::PendingLoad::OnDataAvailable(nsIRequest* aRequest,
1095                                                   nsIInputStream* aStream,
1096                                                   uint64_t aOffset,
1097                                                   uint32_t aCount) {
1098   // mTargetListener might be null if SetupViewer or AddExternalResource failed.
1099   NS_ENSURE_TRUE(mTargetListener, NS_ERROR_FAILURE);
1100   if (mDisplayDocument->ExternalResourceMap().HaveShutDown()) {
1101     return NS_BINDING_ABORTED;
1102   }
1103   return mTargetListener->OnDataAvailable(aRequest, aStream, aOffset, aCount);
1104 }
1105 
1106 NS_IMETHODIMP
OnStopRequest(nsIRequest * aRequest,nsresult aStatus)1107 ExternalResourceMap::PendingLoad::OnStopRequest(nsIRequest* aRequest,
1108                                                 nsresult aStatus) {
1109   // mTargetListener might be null if SetupViewer or AddExternalResource failed
1110   if (mTargetListener) {
1111     nsCOMPtr<nsIStreamListener> listener;
1112     mTargetListener.swap(listener);
1113     return listener->OnStopRequest(aRequest, aStatus);
1114   }
1115 
1116   return NS_OK;
1117 }
1118 
StartLoad(nsIURI * aURI,nsIReferrerInfo * aReferrerInfo,nsINode * aRequestingNode)1119 nsresult ExternalResourceMap::PendingLoad::StartLoad(
1120     nsIURI* aURI, nsIReferrerInfo* aReferrerInfo, nsINode* aRequestingNode) {
1121   MOZ_ASSERT(aURI, "Must have a URI");
1122   MOZ_ASSERT(aRequestingNode, "Must have a node");
1123   MOZ_ASSERT(aReferrerInfo, "Must have a referrerInfo");
1124 
1125   nsCOMPtr<nsILoadGroup> loadGroup =
1126       aRequestingNode->OwnerDoc()->GetDocumentLoadGroup();
1127 
1128   nsresult rv = NS_OK;
1129   nsCOMPtr<nsIChannel> channel;
1130   rv = NS_NewChannel(getter_AddRefs(channel), aURI, aRequestingNode,
1131                      nsILoadInfo::SEC_REQUIRE_SAME_ORIGIN_INHERITS_SEC_CONTEXT,
1132                      nsIContentPolicy::TYPE_OTHER,
1133                      nullptr,  // aPerformanceStorage
1134                      loadGroup);
1135   NS_ENSURE_SUCCESS(rv, rv);
1136 
1137   nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(channel));
1138   if (httpChannel) {
1139     rv = httpChannel->SetReferrerInfo(aReferrerInfo);
1140     Unused << NS_WARN_IF(NS_FAILED(rv));
1141   }
1142 
1143   mURI = aURI;
1144 
1145   return channel->AsyncOpen(this);
1146 }
1147 
NS_IMPL_ISUPPORTS(ExternalResourceMap::LoadgroupCallbacks,nsIInterfaceRequestor)1148 NS_IMPL_ISUPPORTS(ExternalResourceMap::LoadgroupCallbacks,
1149                   nsIInterfaceRequestor)
1150 
1151 #define IMPL_SHIM(_i) \
1152   NS_IMPL_ISUPPORTS(ExternalResourceMap::LoadgroupCallbacks::_i##Shim, _i)
1153 
1154 IMPL_SHIM(nsILoadContext)
1155 IMPL_SHIM(nsIProgressEventSink)
1156 IMPL_SHIM(nsIChannelEventSink)
1157 
1158 #undef IMPL_SHIM
1159 
1160 #define IID_IS(_i) aIID.Equals(NS_GET_IID(_i))
1161 
1162 #define TRY_SHIM(_i)                                 \
1163   PR_BEGIN_MACRO                                     \
1164   if (IID_IS(_i)) {                                  \
1165     nsCOMPtr<_i> real = do_GetInterface(mCallbacks); \
1166     if (!real) {                                     \
1167       return NS_NOINTERFACE;                         \
1168     }                                                \
1169     nsCOMPtr<_i> shim = new _i##Shim(this, real);    \
1170     shim.forget(aSink);                              \
1171     return NS_OK;                                    \
1172   }                                                  \
1173   PR_END_MACRO
1174 
1175 NS_IMETHODIMP
1176 ExternalResourceMap::LoadgroupCallbacks::GetInterface(const nsIID& aIID,
1177                                                       void** aSink) {
1178   if (mCallbacks && (IID_IS(nsIPrompt) || IID_IS(nsIAuthPrompt) ||
1179                      IID_IS(nsIAuthPrompt2) || IID_IS(nsIBrowserChild))) {
1180     return mCallbacks->GetInterface(aIID, aSink);
1181   }
1182 
1183   *aSink = nullptr;
1184 
1185   TRY_SHIM(nsILoadContext);
1186   TRY_SHIM(nsIProgressEventSink);
1187   TRY_SHIM(nsIChannelEventSink);
1188 
1189   return NS_NOINTERFACE;
1190 }
1191 
1192 #undef TRY_SHIM
1193 #undef IID_IS
1194 
~ExternalResource()1195 ExternalResourceMap::ExternalResource::~ExternalResource() {
1196   if (mViewer) {
1197     mViewer->Close(nullptr);
1198     mViewer->Destroy();
1199   }
1200 }
1201 
1202 // ==================================================================
1203 // =
1204 // ==================================================================
1205 
1206 // If we ever have an nsIDocumentObserver notification for stylesheet title
1207 // changes we should update the list from that instead of overriding
1208 // EnsureFresh.
1209 class DOMStyleSheetSetList final : public DOMStringList {
1210  public:
1211   explicit DOMStyleSheetSetList(Document* aDocument);
1212 
Disconnect()1213   void Disconnect() { mDocument = nullptr; }
1214 
1215   virtual void EnsureFresh() override;
1216 
1217  protected:
1218   Document* mDocument;  // Our document; weak ref.  It'll let us know if it
1219                         // dies.
1220 };
1221 
DOMStyleSheetSetList(Document * aDocument)1222 DOMStyleSheetSetList::DOMStyleSheetSetList(Document* aDocument)
1223     : mDocument(aDocument) {
1224   NS_ASSERTION(mDocument, "Must have document!");
1225 }
1226 
EnsureFresh()1227 void DOMStyleSheetSetList::EnsureFresh() {
1228   MOZ_ASSERT(NS_IsMainThread());
1229 
1230   mNames.Clear();
1231 
1232   if (!mDocument) {
1233     return;  // Spec says "no exceptions", and we have no style sets if we have
1234              // no document, for sure
1235   }
1236 
1237   size_t count = mDocument->SheetCount();
1238   nsAutoString title;
1239   for (size_t index = 0; index < count; index++) {
1240     StyleSheet* sheet = mDocument->SheetAt(index);
1241     NS_ASSERTION(sheet, "Null sheet in sheet list!");
1242     sheet->GetTitle(title);
1243     if (!title.IsEmpty() && !mNames.Contains(title) && !Add(title)) {
1244       return;
1245     }
1246   }
1247 }
1248 
1249 // ==================================================================
SelectorCache(nsIEventTarget * aEventTarget)1250 Document::SelectorCache::SelectorCache(nsIEventTarget* aEventTarget)
1251     : nsExpirationTracker<SelectorCacheKey, 4>(1000, "Document::SelectorCache",
1252                                                aEventTarget) {}
1253 
~SelectorCache()1254 Document::SelectorCache::~SelectorCache() { AgeAllGenerations(); }
1255 
NotifyExpired(SelectorCacheKey * aSelector)1256 void Document::SelectorCache::NotifyExpired(SelectorCacheKey* aSelector) {
1257   MOZ_ASSERT(NS_IsMainThread());
1258   MOZ_ASSERT(aSelector);
1259 
1260   // There is no guarantee that this method won't be re-entered when selector
1261   // matching is ongoing because "memory-pressure" could be notified immediately
1262   // when OOM happens according to the design of nsExpirationTracker.
1263   // The perfect solution is to delete the |aSelector| and its
1264   // RawServoSelectorList in mTable asynchronously.
1265   // We remove these objects synchronously for now because NotifyExpired() will
1266   // never be triggered by "memory-pressure" which is not implemented yet in
1267   // the stage 2 of mozalloc_handle_oom().
1268   // Once these objects are removed asynchronously, we should update the warning
1269   // added in mozalloc_handle_oom() as well.
1270   RemoveObject(aSelector);
1271   mTable.Remove(aSelector->mKey);
1272   delete aSelector;
1273 }
1274 
1275 Document::PendingFrameStaticClone::~PendingFrameStaticClone() = default;
1276 
1277 // ==================================================================
1278 // =
1279 // ==================================================================
1280 
1281 Document::InternalCommandDataHashtable*
1282     Document::sInternalCommandDataHashtable = nullptr;
1283 
1284 // static
Shutdown()1285 void Document::Shutdown() {
1286   if (sInternalCommandDataHashtable) {
1287     sInternalCommandDataHashtable->Clear();
1288     delete sInternalCommandDataHashtable;
1289     sInternalCommandDataHashtable = nullptr;
1290   }
1291 }
1292 
Document(const char * aContentType)1293 Document::Document(const char* aContentType)
1294     : nsINode(nullptr),
1295       DocumentOrShadowRoot(this),
1296       mCharacterSet(WINDOWS_1252_ENCODING),
1297       mCharacterSetSource(0),
1298       mParentDocument(nullptr),
1299       mCachedRootElement(nullptr),
1300       mNodeInfoManager(nullptr),
1301 #ifdef DEBUG
1302       mStyledLinksCleared(false),
1303 #endif
1304       mBlockAllMixedContent(false),
1305       mBlockAllMixedContentPreloads(false),
1306       mUpgradeInsecureRequests(false),
1307       mUpgradeInsecurePreloads(false),
1308       mDevToolsWatchingDOMMutations(false),
1309       mBidiEnabled(false),
1310       mMayNeedFontPrefsUpdate(true),
1311       mMathMLEnabled(false),
1312       mIsInitialDocumentInWindow(false),
1313       mIgnoreDocGroupMismatches(false),
1314       mLoadedAsData(false),
1315       mAddedToMemoryReportingAsDataDocument(false),
1316       mMayStartLayout(true),
1317       mHaveFiredTitleChange(false),
1318       mIsShowing(false),
1319       mVisible(true),
1320       mRemovedFromDocShell(false),
1321       // mAllowDNSPrefetch starts true, so that we can always reliably && it
1322       // with various values that might disable it.  Since we never prefetch
1323       // unless we get a window, and in that case the docshell value will get
1324       // &&-ed in, this is safe.
1325       mAllowDNSPrefetch(true),
1326       mIsStaticDocument(false),
1327       mCreatingStaticClone(false),
1328       mHasPrintCallbacks(false),
1329       mInUnlinkOrDeletion(false),
1330       mHasHadScriptHandlingObject(false),
1331       mIsBeingUsedAsImage(false),
1332       mDocURISchemeIsChrome(false),
1333       mInChromeDocShell(false),
1334       mIsDevToolsDocument(false),
1335       mIsSyntheticDocument(false),
1336       mHasLinksToUpdateRunnable(false),
1337       mFlushingPendingLinkUpdates(false),
1338       mMayHaveDOMMutationObservers(false),
1339       mMayHaveAnimationObservers(false),
1340       mHasCSP(false),
1341       mHasUnsafeEvalCSP(false),
1342       mHasUnsafeInlineCSP(false),
1343       mHasCSPDeliveredThroughHeader(false),
1344       mBFCacheDisallowed(false),
1345       mHasHadDefaultView(false),
1346       mStyleSheetChangeEventsEnabled(false),
1347       mShadowRootAttachedEventEnabled(false),
1348       mIsSrcdocDocument(false),
1349       mHasDisplayDocument(false),
1350       mFontFaceSetDirty(true),
1351       mDidFireDOMContentLoaded(true),
1352       mHasScrollLinkedEffect(false),
1353       mFrameRequestCallbacksScheduled(false),
1354       mIsTopLevelContentDocument(false),
1355       mIsContentDocument(false),
1356       mDidCallBeginLoad(false),
1357       mEncodingMenuDisabled(false),
1358       mLinksEnabled(true),
1359       mIsSVGGlyphsDocument(false),
1360       mInDestructor(false),
1361       mIsGoingAway(false),
1362       mInXBLUpdate(false),
1363       mNeedsReleaseAfterStackRefCntRelease(false),
1364       mStyleSetFilled(false),
1365       mQuirkSheetAdded(false),
1366       mContentEditableSheetAdded(false),
1367       mDesignModeSheetAdded(false),
1368       mSSApplicableStateNotificationPending(false),
1369       mMayHaveTitleElement(false),
1370       mDOMLoadingSet(false),
1371       mDOMInteractiveSet(false),
1372       mDOMCompleteSet(false),
1373       mAutoFocusFired(false),
1374       mScrolledToRefAlready(false),
1375       mChangeScrollPosWhenScrollingToRef(false),
1376       mDelayFrameLoaderInitialization(false),
1377       mSynchronousDOMContentLoaded(false),
1378       mMaybeServiceWorkerControlled(false),
1379       mAllowZoom(false),
1380       mValidScaleFloat(false),
1381       mValidMinScale(false),
1382       mValidMaxScale(false),
1383       mWidthStrEmpty(false),
1384       mParserAborted(false),
1385       mReportedDocumentUseCounters(false),
1386       mHasReportedShadowDOMUsage(false),
1387       mHasDelayedRefreshEvent(false),
1388       mLoadEventFiring(false),
1389       mSkipLoadEventAfterClose(false),
1390       mDisableCookieAccess(false),
1391       mDisableDocWrite(false),
1392       mTooDeepWriteRecursion(false),
1393       mPendingMaybeEditingStateChanged(false),
1394       mHasBeenEditable(false),
1395       mHasWarnedAboutZoom(false),
1396       mIsRunningExecCommand(false),
1397       mSetCompleteAfterDOMContentLoaded(false),
1398       mDidHitCompleteSheetCache(false),
1399       mPendingFullscreenRequests(0),
1400       mXMLDeclarationBits(0),
1401       mOnloadBlockCount(0),
1402       mWriteLevel(0),
1403       mLazyLoadImageCount(0),
1404       mLazyLoadImageStarted(0),
1405       mLazyLoadImageReachViewportLoading(0),
1406       mLazyLoadImageReachViewportLoaded(0),
1407       mContentEditableCount(0),
1408       mEditingState(EditingState::eOff),
1409       mCompatMode(eCompatibility_FullStandards),
1410       mReadyState(ReadyState::READYSTATE_UNINITIALIZED),
1411       mAncestorIsLoading(false),
1412       mVisibilityState(dom::VisibilityState::Hidden),
1413       mType(eUnknown),
1414       mDefaultElementType(0),
1415       mAllowXULXBL(eTriUnset),
1416       mSkipDTDSecurityChecks(false),
1417       mBidiOptions(IBMBIDI_DEFAULT_BIDI_OPTIONS),
1418       mSandboxFlags(0),
1419       mPartID(0),
1420       mMarkedCCGeneration(0),
1421       mPresShell(nullptr),
1422       mSubtreeModifiedDepth(0),
1423       mPreloadPictureDepth(0),
1424       mEventsSuppressed(0),
1425       mIgnoreDestructiveWritesCounter(0),
1426       mStaticCloneCount(0),
1427       mWindow(nullptr),
1428       mBFCacheEntry(nullptr),
1429       mInSyncOperationCount(0),
1430       mBlockDOMContentLoaded(0),
1431       mUseCountersInitialized(false),
1432       mShouldReportUseCounters(false),
1433       mShouldSendPageUseCounters(false),
1434       mUserHasInteracted(false),
1435       mHasUserInteractionTimerScheduled(false),
1436       mStackRefCnt(0),
1437       mUpdateNestLevel(0),
1438       mHttpsOnlyStatus(nsILoadInfo::HTTPS_ONLY_UNINITIALIZED),
1439       mViewportType(Unknown),
1440       mViewportFit(ViewportFitType::Auto),
1441       mSubDocuments(nullptr),
1442       mHeaderData(nullptr),
1443       mFlashClassification(FlashClassification::Unknown),
1444       mServoRestyleRootDirtyBits(0),
1445       mThrowOnDynamicMarkupInsertionCounter(0),
1446       mIgnoreOpensDuringUnloadCounter(0),
1447       mSavedResolution(1.0f),
1448       mSavedResolutionBeforeMVM(1.0f),
1449       mGeneration(0),
1450       mCachedTabSizeGeneration(0),
1451       mNextFormNumber(0),
1452       mNextControlNumber(0),
1453       mPreloadService(this),
1454       mShouldNotifyFetchSuccess(false),
1455       mShouldNotifyFormOrPasswordRemoved(false) {
1456   MOZ_LOG(gDocumentLeakPRLog, LogLevel::Debug, ("DOCUMENT %p created", this));
1457 
1458   SetIsInDocument();
1459   SetIsConnected(true);
1460 
1461   // Create these unconditionally, they will be used to warn about the `zoom`
1462   // property, even if use counters are disabled.
1463   mStyleUseCounters = Servo_UseCounters_Create().Consume();
1464 
1465   SetContentType(nsDependentCString(aContentType));
1466 
1467   // Start out mLastStyleSheetSet as null, per spec
1468   SetDOMStringToNull(mLastStyleSheetSet);
1469 
1470   // void state used to differentiate an empty source from an unselected source
1471   mPreloadPictureFoundSource.SetIsVoid(true);
1472 
1473   RecomputeLanguageFromCharset();
1474 
1475   mPreloadReferrerInfo = new dom::ReferrerInfo(nullptr);
1476   mReferrerInfo = new dom::ReferrerInfo(nullptr);
1477 }
1478 
1479 #ifndef ANDROID
1480 // unused by GeckoView
IsAboutErrorPage(nsGlobalWindowInner * aWin,const char * aSpec)1481 static bool IsAboutErrorPage(nsGlobalWindowInner* aWin, const char* aSpec) {
1482   if (NS_WARN_IF(!aWin)) {
1483     return false;
1484   }
1485 
1486   nsIURI* uri = aWin->GetDocumentURI();
1487   if (NS_WARN_IF(!uri)) {
1488     return false;
1489   }
1490   // getSpec is an expensive operation, hence we first check the scheme
1491   // to see if the caller is actually an about: page.
1492   if (!uri->SchemeIs("about")) {
1493     return false;
1494   }
1495 
1496   nsAutoCString aboutSpec;
1497   nsresult rv = NS_GetAboutModuleName(uri, aboutSpec);
1498   NS_ENSURE_SUCCESS(rv, false);
1499 
1500   return aboutSpec.EqualsASCII(aSpec);
1501 }
1502 #endif
1503 
CallerIsTrustedAboutNetError(JSContext * aCx,JSObject * aObject)1504 bool Document::CallerIsTrustedAboutNetError(JSContext* aCx, JSObject* aObject) {
1505   nsGlobalWindowInner* win = xpc::WindowOrNull(aObject);
1506 #ifdef ANDROID
1507   // GeckoView uses data URLs for error pages, so for now just check for any
1508   // error page
1509   return win && win->GetDocument() && win->GetDocument()->IsErrorPage();
1510 #else
1511   return win && IsAboutErrorPage(win, "neterror");
1512 #endif
1513 }
1514 
CallerIsTrustedAboutHttpsOnlyError(JSContext * aCx,JSObject * aObject)1515 bool Document::CallerIsTrustedAboutHttpsOnlyError(JSContext* aCx,
1516                                                   JSObject* aObject) {
1517   nsGlobalWindowInner* win = xpc::WindowOrNull(aObject);
1518 #ifdef ANDROID
1519   // GeckoView uses data URLs for error pages, so for now just check for any
1520   // error page
1521   return win && win->GetDocument() && win->GetDocument()->IsErrorPage();
1522 #else
1523   return win && IsAboutErrorPage(win, "httpsonlyerror");
1524 #endif
1525 }
1526 
AddCertException(bool aIsTemporary)1527 already_AddRefed<mozilla::dom::Promise> Document::AddCertException(
1528     bool aIsTemporary) {
1529   nsIGlobalObject* global = GetScopeObject();
1530   if (!global) {
1531     return nullptr;
1532   }
1533 
1534   ErrorResult er;
1535   RefPtr<Promise> promise =
1536       Promise::Create(global, er, Promise::ePropagateUserInteraction);
1537   if (er.Failed()) {
1538     return nullptr;
1539   }
1540 
1541   nsCOMPtr<nsISupports> info;
1542   nsCOMPtr<nsITransportSecurityInfo> tsi;
1543   nsresult rv = NS_OK;
1544   if (NS_WARN_IF(!mFailedChannel)) {
1545     promise->MaybeReject(NS_ERROR_DOM_INVALID_STATE_ERR);
1546     return promise.forget();
1547   }
1548 
1549   rv = mFailedChannel->GetSecurityInfo(getter_AddRefs(info));
1550   if (NS_WARN_IF(NS_FAILED(rv))) {
1551     promise->MaybeReject(rv);
1552     return promise.forget();
1553   }
1554   nsCOMPtr<nsIURI> failedChannelURI;
1555   NS_GetFinalChannelURI(mFailedChannel, getter_AddRefs(failedChannelURI));
1556   if (!failedChannelURI) {
1557     promise->MaybeReject(NS_ERROR_DOM_INVALID_STATE_ERR);
1558     return promise.forget();
1559   }
1560 
1561   nsCOMPtr<nsIURI> innerURI = NS_GetInnermostURI(failedChannelURI);
1562   if (!innerURI) {
1563     promise->MaybeReject(NS_ERROR_DOM_INVALID_STATE_ERR);
1564     return promise.forget();
1565   }
1566 
1567   nsAutoCString host;
1568   innerURI->GetAsciiHost(host);
1569   int32_t port;
1570   innerURI->GetPort(&port);
1571 
1572   tsi = do_QueryInterface(info);
1573   if (NS_WARN_IF(!tsi)) {
1574     promise->MaybeReject(NS_ERROR_DOM_INVALID_STATE_ERR);
1575     return promise.forget();
1576   }
1577 
1578   bool isUntrusted = true;
1579   rv = tsi->GetIsUntrusted(&isUntrusted);
1580   if (NS_WARN_IF(NS_FAILED(rv))) {
1581     promise->MaybeReject(rv);
1582     return promise.forget();
1583   }
1584 
1585   bool isDomainMismatch = true;
1586   rv = tsi->GetIsDomainMismatch(&isDomainMismatch);
1587   if (NS_WARN_IF(NS_FAILED(rv))) {
1588     promise->MaybeReject(rv);
1589     return promise.forget();
1590   }
1591 
1592   bool isNotValidAtThisTime = true;
1593   rv = tsi->GetIsNotValidAtThisTime(&isNotValidAtThisTime);
1594   if (NS_WARN_IF(NS_FAILED(rv))) {
1595     promise->MaybeReject(rv);
1596     return promise.forget();
1597   }
1598 
1599   nsCOMPtr<nsIX509Cert> cert;
1600   rv = tsi->GetServerCert(getter_AddRefs(cert));
1601   if (NS_WARN_IF(NS_FAILED(rv))) {
1602     promise->MaybeReject(rv);
1603     return promise.forget();
1604   }
1605   if (NS_WARN_IF(!cert)) {
1606     promise->MaybeReject(NS_ERROR_DOM_INVALID_STATE_ERR);
1607     return promise.forget();
1608   }
1609 
1610   uint32_t flags = 0;
1611   if (isUntrusted) {
1612     flags |= nsICertOverrideService::ERROR_UNTRUSTED;
1613   }
1614   if (isDomainMismatch) {
1615     flags |= nsICertOverrideService::ERROR_MISMATCH;
1616   }
1617   if (isNotValidAtThisTime) {
1618     flags |= nsICertOverrideService::ERROR_TIME;
1619   }
1620 
1621   if (XRE_IsContentProcess()) {
1622     nsCOMPtr<nsISerializable> certSer = do_QueryInterface(cert);
1623     nsCString certSerialized;
1624     NS_SerializeToString(certSer, certSerialized);
1625 
1626     ContentChild* cc = ContentChild::GetSingleton();
1627     MOZ_ASSERT(cc);
1628     OriginAttributes const& attrs = NodePrincipal()->OriginAttributesRef();
1629     cc->SendAddCertException(certSerialized, flags, host, port, attrs,
1630                              aIsTemporary)
1631         ->Then(GetCurrentSerialEventTarget(), __func__,
1632                [promise](const mozilla::MozPromise<
1633                          nsresult, mozilla::ipc::ResponseRejectReason,
1634                          true>::ResolveOrRejectValue& aValue) {
1635                  if (aValue.IsResolve()) {
1636                    promise->MaybeResolve(aValue.ResolveValue());
1637                  } else {
1638                    promise->MaybeRejectWithUndefined();
1639                  }
1640                });
1641     return promise.forget();
1642   }
1643 
1644   if (XRE_IsParentProcess()) {
1645     nsCOMPtr<nsICertOverrideService> overrideService =
1646         do_GetService(NS_CERTOVERRIDE_CONTRACTID);
1647     if (!overrideService) {
1648       promise->MaybeReject(NS_ERROR_FAILURE);
1649       return promise.forget();
1650     }
1651 
1652     OriginAttributes const& attrs = NodePrincipal()->OriginAttributesRef();
1653     rv = overrideService->RememberValidityOverride(host, port, attrs, cert,
1654                                                    flags, aIsTemporary);
1655     if (NS_WARN_IF(NS_FAILED(rv))) {
1656       promise->MaybeReject(rv);
1657       return promise.forget();
1658     }
1659 
1660     promise->MaybeResolveWithUndefined();
1661     return promise.forget();
1662   }
1663 
1664   promise->MaybeReject(NS_ERROR_FAILURE);
1665   return promise.forget();
1666 }
1667 
ReloadWithHttpsOnlyException()1668 void Document::ReloadWithHttpsOnlyException() {
1669   if (WindowGlobalChild* wgc = GetWindowGlobalChild()) {
1670     wgc->SendReloadWithHttpsOnlyException();
1671   }
1672 }
1673 
GetNetErrorInfo(NetErrorInfo & aInfo,ErrorResult & aRv)1674 void Document::GetNetErrorInfo(NetErrorInfo& aInfo, ErrorResult& aRv) {
1675   nsCOMPtr<nsISupports> info;
1676   nsCOMPtr<nsITransportSecurityInfo> tsi;
1677   nsresult rv = NS_OK;
1678   if (NS_WARN_IF(!mFailedChannel)) {
1679     aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
1680     return;
1681   }
1682 
1683   rv = mFailedChannel->GetSecurityInfo(getter_AddRefs(info));
1684   if (NS_WARN_IF(NS_FAILED(rv))) {
1685     aRv.Throw(rv);
1686     return;
1687   }
1688   tsi = do_QueryInterface(info);
1689   if (NS_WARN_IF(!tsi)) {
1690     aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
1691     return;
1692   }
1693 
1694   nsAutoString errorCodeString;
1695   rv = tsi->GetErrorCodeString(errorCodeString);
1696   if (NS_WARN_IF(NS_FAILED(rv))) {
1697     aRv.Throw(rv);
1698     return;
1699   }
1700   aInfo.mErrorCodeString.Assign(errorCodeString);
1701 }
1702 
CallerIsTrustedAboutCertError(JSContext * aCx,JSObject * aObject)1703 bool Document::CallerIsTrustedAboutCertError(JSContext* aCx,
1704                                              JSObject* aObject) {
1705   nsGlobalWindowInner* win = xpc::WindowOrNull(aObject);
1706 #ifdef ANDROID
1707   // GeckoView uses data URLs for error pages, so for now just check for any
1708   // error page
1709   return win && win->GetDocument() && win->GetDocument()->IsErrorPage();
1710 #else
1711   return win && IsAboutErrorPage(win, "certerror");
1712 #endif
1713 }
1714 
CallerCanAccessPrivilegeSSA(JSContext * aCx,JSObject * aObject)1715 bool Document::CallerCanAccessPrivilegeSSA(JSContext* aCx, JSObject* aObject) {
1716   RefPtr<BasePrincipal> principal =
1717       BasePrincipal::Cast(nsContentUtils::SubjectPrincipal(aCx));
1718 
1719   if (!principal) {
1720     return false;
1721   }
1722 
1723   // We allow the privilege SSA to be called from system principal.
1724   if (principal->IsSystemPrincipal()) {
1725     return true;
1726   }
1727 
1728   // We only allow calling the privilege SSA from the content script of the
1729   // webcompat extension.
1730   if (auto* policy = principal->ContentScriptAddonPolicy()) {
1731     nsAutoString addonID;
1732     policy->GetId(addonID);
1733 
1734     return addonID.EqualsLiteral("webcompat@mozilla.org");
1735   }
1736 
1737   return false;
1738 }
1739 
IsErrorPage() const1740 bool Document::IsErrorPage() const {
1741   nsCOMPtr<nsILoadInfo> loadInfo = mChannel ? mChannel->LoadInfo() : nullptr;
1742   return loadInfo && loadInfo->GetLoadErrorPage();
1743 }
1744 
GetFailedCertSecurityInfo(FailedCertSecurityInfo & aInfo,ErrorResult & aRv)1745 void Document::GetFailedCertSecurityInfo(FailedCertSecurityInfo& aInfo,
1746                                          ErrorResult& aRv) {
1747   nsCOMPtr<nsISupports> info;
1748   nsCOMPtr<nsITransportSecurityInfo> tsi;
1749   nsresult rv = NS_OK;
1750   if (NS_WARN_IF(!mFailedChannel)) {
1751     aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
1752     return;
1753   }
1754 
1755   rv = mFailedChannel->GetSecurityInfo(getter_AddRefs(info));
1756   if (NS_WARN_IF(NS_FAILED(rv))) {
1757     aRv.Throw(rv);
1758     return;
1759   }
1760   tsi = do_QueryInterface(info);
1761   if (NS_WARN_IF(!tsi)) {
1762     aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
1763     return;
1764   }
1765 
1766   nsAutoString errorCodeString;
1767   rv = tsi->GetErrorCodeString(errorCodeString);
1768   if (NS_WARN_IF(NS_FAILED(rv))) {
1769     aRv.Throw(rv);
1770     return;
1771   }
1772   aInfo.mErrorCodeString.Assign(errorCodeString);
1773 
1774   rv = tsi->GetIsUntrusted(&aInfo.mIsUntrusted);
1775   if (NS_WARN_IF(NS_FAILED(rv))) {
1776     aRv.Throw(rv);
1777     return;
1778   }
1779 
1780   rv = tsi->GetIsDomainMismatch(&aInfo.mIsDomainMismatch);
1781   if (NS_WARN_IF(NS_FAILED(rv))) {
1782     aRv.Throw(rv);
1783     return;
1784   }
1785 
1786   rv = tsi->GetIsNotValidAtThisTime(&aInfo.mIsNotValidAtThisTime);
1787   if (NS_WARN_IF(NS_FAILED(rv))) {
1788     aRv.Throw(rv);
1789     return;
1790   }
1791 
1792   nsCOMPtr<nsIX509Cert> cert;
1793   nsCOMPtr<nsIX509CertValidity> validity;
1794   rv = tsi->GetServerCert(getter_AddRefs(cert));
1795   if (NS_WARN_IF(NS_FAILED(rv))) {
1796     aRv.Throw(rv);
1797     return;
1798   }
1799   if (NS_WARN_IF(!cert)) {
1800     aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
1801     return;
1802   }
1803 
1804   rv = cert->GetValidity(getter_AddRefs(validity));
1805   if (NS_WARN_IF(NS_FAILED(rv))) {
1806     aRv.Throw(rv);
1807     return;
1808   }
1809   if (NS_WARN_IF(!validity)) {
1810     aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
1811     return;
1812   }
1813 
1814   PRTime validityResult;
1815   rv = validity->GetNotBefore(&validityResult);
1816   if (NS_WARN_IF(NS_FAILED(rv))) {
1817     aRv.Throw(rv);
1818     return;
1819   }
1820   aInfo.mValidNotBefore = DOMTimeStamp(validityResult / PR_USEC_PER_MSEC);
1821 
1822   rv = validity->GetNotAfter(&validityResult);
1823   if (NS_WARN_IF(NS_FAILED(rv))) {
1824     aRv.Throw(rv);
1825     return;
1826   }
1827   aInfo.mValidNotAfter = DOMTimeStamp(validityResult / PR_USEC_PER_MSEC);
1828 
1829   nsAutoString issuerCommonName;
1830   nsAutoString certChainPEMString;
1831   Sequence<nsString>& certChainStrings = aInfo.mCertChainStrings.Construct();
1832   int64_t maxValidity = std::numeric_limits<int64_t>::max();
1833   int64_t minValidity = 0;
1834   PRTime notBefore, notAfter;
1835   nsTArray<RefPtr<nsIX509Cert>> failedCertArray;
1836   rv = tsi->GetFailedCertChain(failedCertArray);
1837   if (NS_WARN_IF(NS_FAILED(rv))) {
1838     aRv.Throw(rv);
1839     return;
1840   }
1841 
1842   if (NS_WARN_IF(failedCertArray.IsEmpty())) {
1843     aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
1844     return;
1845   }
1846 
1847   for (const auto& certificate : failedCertArray) {
1848     rv = certificate->GetIssuerCommonName(issuerCommonName);
1849     if (NS_WARN_IF(NS_FAILED(rv))) {
1850       aRv.Throw(rv);
1851       return;
1852     }
1853 
1854     rv = certificate->GetValidity(getter_AddRefs(validity));
1855     if (NS_WARN_IF(NS_FAILED(rv))) {
1856       aRv.Throw(rv);
1857       return;
1858     }
1859     if (NS_WARN_IF(!validity)) {
1860       aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
1861       return;
1862     }
1863 
1864     rv = validity->GetNotBefore(&notBefore);
1865     if (NS_WARN_IF(NS_FAILED(rv))) {
1866       aRv.Throw(rv);
1867       return;
1868     }
1869 
1870     rv = validity->GetNotAfter(&notAfter);
1871     if (NS_WARN_IF(NS_FAILED(rv))) {
1872       aRv.Throw(rv);
1873       return;
1874     }
1875 
1876     notBefore = std::max(minValidity, notBefore);
1877     notAfter = std::min(maxValidity, notAfter);
1878     nsTArray<uint8_t> certArray;
1879     rv = certificate->GetRawDER(certArray);
1880     if (NS_WARN_IF(NS_FAILED(rv))) {
1881       aRv.Throw(rv);
1882       return;
1883     }
1884 
1885     nsAutoString der64;
1886     rv = Base64Encode(reinterpret_cast<const char*>(certArray.Elements()),
1887                       certArray.Length(), der64);
1888     if (NS_WARN_IF(NS_FAILED(rv))) {
1889       aRv.Throw(rv);
1890       return;
1891     }
1892     if (!certChainStrings.AppendElement(der64, fallible)) {
1893       aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
1894       return;
1895     }
1896   }
1897 
1898   aInfo.mIssuerCommonName.Assign(issuerCommonName);
1899   aInfo.mCertValidityRangeNotAfter = DOMTimeStamp(notAfter / PR_USEC_PER_MSEC);
1900   aInfo.mCertValidityRangeNotBefore =
1901       DOMTimeStamp(notBefore / PR_USEC_PER_MSEC);
1902 
1903   int32_t errorCode;
1904   rv = tsi->GetErrorCode(&errorCode);
1905   if (NS_WARN_IF(NS_FAILED(rv))) {
1906     aRv.Throw(rv);
1907     return;
1908   }
1909 
1910   nsCOMPtr<nsINSSErrorsService> nsserr =
1911       do_GetService("@mozilla.org/nss_errors_service;1");
1912   if (NS_WARN_IF(!nsserr)) {
1913     aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
1914     return;
1915   }
1916   nsresult res;
1917   rv = nsserr->GetXPCOMFromNSSError(errorCode, &res);
1918   if (NS_WARN_IF(NS_FAILED(rv))) {
1919     aRv.Throw(rv);
1920     return;
1921   }
1922   rv = nsserr->GetErrorMessage(res, aInfo.mErrorMessage);
1923   if (NS_WARN_IF(NS_FAILED(rv))) {
1924     aRv.Throw(rv);
1925     return;
1926   }
1927 
1928   bool isPrivateBrowsing = nsContentUtils::IsInPrivateBrowsing(this);
1929   uint32_t flags =
1930       isPrivateBrowsing ? nsISocketProvider::NO_PERMANENT_STORAGE : 0;
1931   OriginAttributes attrs;
1932   StoragePrincipalHelper::GetRegularPrincipalOriginAttributes(this, attrs);
1933   nsCOMPtr<nsIURI> aURI;
1934   mFailedChannel->GetURI(getter_AddRefs(aURI));
1935   if (XRE_IsContentProcess()) {
1936     ContentChild* cc = ContentChild::GetSingleton();
1937     MOZ_ASSERT(cc);
1938     cc->SendIsSecureURI(aURI, flags, attrs, &aInfo.mHasHSTS);
1939   } else {
1940     nsCOMPtr<nsISiteSecurityService> sss =
1941         do_GetService(NS_SSSERVICE_CONTRACTID);
1942     if (NS_WARN_IF(!sss)) {
1943       return;
1944     }
1945     Unused << NS_WARN_IF(NS_FAILED(sss->IsSecureURI(aURI, flags, attrs, nullptr,
1946                                                     nullptr, &aInfo.mHasHSTS)));
1947   }
1948   nsCOMPtr<nsIPublicKeyPinningService> pkps =
1949       do_GetService(NS_PKPSERVICE_CONTRACTID);
1950   if (NS_WARN_IF(!pkps)) {
1951     return;
1952   }
1953   Unused << NS_WARN_IF(NS_FAILED(pkps->HostHasPins(aURI, &aInfo.mHasHPKP)));
1954 }
1955 
AllowDeprecatedTls()1956 bool Document::AllowDeprecatedTls() {
1957   return Preferences::GetBool("security.tls.version.enable-deprecated", false);
1958 }
1959 
SetAllowDeprecatedTls(bool value)1960 void Document::SetAllowDeprecatedTls(bool value) {
1961   if (!IsErrorPage()) {
1962     return;
1963   }
1964 
1965   auto docShell = GetDocShell();
1966   if (!docShell) {
1967     return;
1968   }
1969 
1970   auto child = BrowserChild::GetFrom(docShell);
1971   if (!child) {
1972     return;
1973   }
1974 
1975   child->SendSetAllowDeprecatedTls(value);
1976 }
1977 
IsAboutPage() const1978 bool Document::IsAboutPage() const {
1979   nsCOMPtr<nsIPrincipal> principal = NodePrincipal();
1980   return principal->SchemeIs("about");
1981 }
1982 
ConstructUbiNode(void * storage)1983 void Document::ConstructUbiNode(void* storage) {
1984   JS::ubi::Concrete<Document>::construct(storage, this);
1985 }
1986 
LoadEventFired()1987 void Document::LoadEventFired() {
1988   // Accumulate timing data located in each document's realm and report to
1989   // telemetry.
1990   AccumulateJSTelemetry();
1991 
1992   // Collect page load timings
1993   AccumulatePageLoadTelemetry();
1994 
1995   // Release the JS bytecode cache from its wait on the load event, and
1996   // potentially dispatch the encoding of the bytecode.
1997   if (ScriptLoader()) {
1998     ScriptLoader()->LoadEventFired();
1999   }
2000 }
2001 
AccumulatePageLoadTelemetry()2002 void Document::AccumulatePageLoadTelemetry() {
2003   // Interested only in top level documents for real websites that are in the
2004   // foreground.
2005   if (!ShouldIncludeInTelemetry(false) || !IsTopLevelContentDocument() ||
2006       !GetNavigationTiming() ||
2007       !GetNavigationTiming()->DocShellHasBeenActiveSinceNavigationStart()) {
2008     return;
2009   }
2010 
2011   if (!GetChannel()) {
2012     return;
2013   }
2014 
2015   nsCOMPtr<nsITimedChannel> timedChannel(do_QueryInterface(GetChannel()));
2016   if (!timedChannel) {
2017     return;
2018   }
2019 
2020   TimeStamp responseStart;
2021   timedChannel->GetResponseStart(&responseStart);
2022 
2023   TimeStamp navigationStart =
2024       GetNavigationTiming()->GetNavigationStartTimeStamp();
2025 
2026   if (!responseStart || !navigationStart) {
2027     return;
2028   }
2029 
2030   nsCString http3Key;
2031   nsCString http3WithPriorityKey;
2032   nsCOMPtr<nsIHttpChannelInternal> httpChannel =
2033       do_QueryInterface(GetChannel());
2034   if (httpChannel) {
2035     uint32_t major;
2036     uint32_t minor;
2037     if (NS_SUCCEEDED(httpChannel->GetResponseVersion(&major, &minor))) {
2038       if (major == 3) {
2039         http3Key = "http3"_ns;
2040         nsCOMPtr<nsIHttpChannel> httpChannel2 = do_QueryInterface(GetChannel());
2041         nsCString header;
2042         if (httpChannel2 &&
2043             NS_SUCCEEDED(
2044                 httpChannel2->GetResponseHeader("priority"_ns, header)) &&
2045             !header.IsEmpty()) {
2046           http3WithPriorityKey = "with_priority"_ns;
2047         } else {
2048           http3WithPriorityKey = "without_priority"_ns;
2049         }
2050       } else if (major == 2) {
2051         bool supportHttp3 = false;
2052         if (NS_FAILED(httpChannel->GetSupportsHTTP3(&supportHttp3))) {
2053           supportHttp3 = false;
2054         }
2055         if (supportHttp3) {
2056           http3Key = "supports_http3"_ns;
2057         }
2058       }
2059     }
2060   }
2061 
2062   // First Contentful Composite
2063   if (TimeStamp firstContentfulComposite =
2064           GetNavigationTiming()->GetFirstContentfulCompositeTimeStamp()) {
2065     Telemetry::AccumulateTimeDelta(Telemetry::PERF_FIRST_CONTENTFUL_PAINT_MS,
2066                                    navigationStart, firstContentfulComposite);
2067 
2068     if (!http3Key.IsEmpty()) {
2069       Telemetry::AccumulateTimeDelta(
2070           Telemetry::HTTP3_PERF_FIRST_CONTENTFUL_PAINT_MS, http3Key,
2071           navigationStart, firstContentfulComposite);
2072     }
2073 
2074     if (!http3WithPriorityKey.IsEmpty()) {
2075       Telemetry::AccumulateTimeDelta(
2076           Telemetry::H3P_PERF_FIRST_CONTENTFUL_PAINT_MS, http3WithPriorityKey,
2077           navigationStart, firstContentfulComposite);
2078     }
2079 
2080     Telemetry::AccumulateTimeDelta(
2081         Telemetry::PERF_FIRST_CONTENTFUL_PAINT_FROM_RESPONSESTART_MS,
2082         responseStart, firstContentfulComposite);
2083   }
2084 
2085   // DOM Content Loaded event
2086   if (TimeStamp dclEventStart =
2087           GetNavigationTiming()->GetDOMContentLoadedEventStartTimeStamp()) {
2088     Telemetry::AccumulateTimeDelta(Telemetry::PERF_DOM_CONTENT_LOADED_TIME_MS,
2089                                    navigationStart, dclEventStart);
2090     Telemetry::AccumulateTimeDelta(
2091         Telemetry::PERF_DOM_CONTENT_LOADED_TIME_FROM_RESPONSESTART_MS,
2092         responseStart, dclEventStart);
2093   }
2094 
2095   // Load event
2096   if (TimeStamp loadEventStart =
2097           GetNavigationTiming()->GetLoadEventStartTimeStamp()) {
2098     Telemetry::AccumulateTimeDelta(Telemetry::PERF_PAGE_LOAD_TIME_MS,
2099                                    navigationStart, loadEventStart);
2100     if (!http3Key.IsEmpty()) {
2101       Telemetry::AccumulateTimeDelta(Telemetry::HTTP3_PERF_PAGE_LOAD_TIME_MS,
2102                                      http3Key, navigationStart, loadEventStart);
2103     }
2104 
2105     if (!http3WithPriorityKey.IsEmpty()) {
2106       Telemetry::AccumulateTimeDelta(Telemetry::H3P_PERF_PAGE_LOAD_TIME_MS,
2107                                      http3WithPriorityKey, navigationStart,
2108                                      loadEventStart);
2109     }
2110 
2111     Telemetry::AccumulateTimeDelta(
2112         Telemetry::PERF_PAGE_LOAD_TIME_FROM_RESPONSESTART_MS, responseStart,
2113         loadEventStart);
2114   }
2115 }
2116 
AccumulateJSTelemetry()2117 void Document::AccumulateJSTelemetry() {
2118   if (!IsTopLevelContentDocument() || !ShouldIncludeInTelemetry(false)) {
2119     return;
2120   }
2121 
2122   if (!GetScopeObject() || !GetScopeObject()->GetGlobalJSObject()) {
2123     return;
2124   }
2125 
2126   AutoJSContext cx;
2127   JSObject* globalObject = GetScopeObject()->GetGlobalJSObject();
2128   JSAutoRealm ar(cx, globalObject);
2129   JS::JSTimers timers = JS::GetJSTimers(cx);
2130 
2131   if (!timers.executionTime.IsZero()) {
2132     Telemetry::Accumulate(
2133         Telemetry::JS_PAGELOAD_EXECUTION_MS,
2134         static_cast<uint32_t>(timers.executionTime.ToMilliseconds()));
2135   }
2136 
2137   if (!timers.delazificationTime.IsZero()) {
2138     Telemetry::Accumulate(
2139         Telemetry::JS_PAGELOAD_DELAZIFICATION_MS,
2140         static_cast<uint32_t>(timers.delazificationTime.ToMilliseconds()));
2141   }
2142 
2143   if (!timers.xdrEncodingTime.IsZero()) {
2144     Telemetry::Accumulate(
2145         Telemetry::JS_PAGELOAD_XDR_ENCODING_MS,
2146         static_cast<uint32_t>(timers.xdrEncodingTime.ToMilliseconds()));
2147   }
2148 
2149   if (!timers.baselineCompileTime.IsZero()) {
2150     Telemetry::Accumulate(
2151         Telemetry::JS_PAGELOAD_BASELINE_COMPILE_MS,
2152         static_cast<uint32_t>(timers.baselineCompileTime.ToMilliseconds()));
2153   }
2154 
2155   if (!timers.gcTime.IsZero()) {
2156     Telemetry::Accumulate(
2157         Telemetry::JS_PAGELOAD_GC_MS,
2158         static_cast<uint32_t>(timers.gcTime.ToMilliseconds()));
2159   }
2160 
2161   if (!timers.protectTime.IsZero()) {
2162     Telemetry::Accumulate(
2163         Telemetry::JS_PAGELOAD_PROTECT_MS,
2164         static_cast<uint32_t>(timers.protectTime.ToMilliseconds()));
2165   }
2166 }
2167 
~Document()2168 Document::~Document() {
2169   MOZ_LOG(gDocumentLeakPRLog, LogLevel::Debug, ("DOCUMENT %p destroyed", this));
2170   MOZ_ASSERT(!IsTopLevelContentDocument() || !IsResourceDoc(),
2171              "Can't be top-level and a resource doc at the same time");
2172 
2173   NS_ASSERTION(!mIsShowing, "Destroying a currently-showing document");
2174 
2175   if (IsTopLevelContentDocument()) {
2176     RemoveToplevelLoadingDocument(this);
2177 
2178     // don't report for about: pages
2179     if (!IsAboutPage()) {
2180       // record CSP telemetry on this document
2181       if (mHasCSP) {
2182         Accumulate(Telemetry::CSP_DOCUMENTS_COUNT, 1);
2183       }
2184       if (mHasUnsafeInlineCSP) {
2185         Accumulate(Telemetry::CSP_UNSAFE_INLINE_DOCUMENTS_COUNT, 1);
2186       }
2187       if (mHasUnsafeEvalCSP) {
2188         Accumulate(Telemetry::CSP_UNSAFE_EVAL_DOCUMENTS_COUNT, 1);
2189       }
2190 
2191       if (MOZ_UNLIKELY(mMathMLEnabled)) {
2192         ScalarAdd(Telemetry::ScalarID::MATHML_DOC_COUNT, 1);
2193       }
2194 
2195       if (IsHTMLDocument()) {
2196         switch (GetCompatibilityMode()) {
2197           case eCompatibility_FullStandards:
2198             Telemetry::AccumulateCategorical(
2199                 Telemetry::LABELS_QUIRKS_MODE::FullStandards);
2200             break;
2201           case eCompatibility_AlmostStandards:
2202             Telemetry::AccumulateCategorical(
2203                 Telemetry::LABELS_QUIRKS_MODE::AlmostStandards);
2204             break;
2205           case eCompatibility_NavQuirks:
2206             Telemetry::AccumulateCategorical(
2207                 Telemetry::LABELS_QUIRKS_MODE::NavQuirks);
2208             break;
2209           default:
2210             MOZ_ASSERT_UNREACHABLE("Unknown quirks mode");
2211             break;
2212         }
2213       }
2214     }
2215   }
2216 
2217   mInDestructor = true;
2218   mInUnlinkOrDeletion = true;
2219 
2220   mozilla::DropJSObjects(this);
2221 
2222   // Clear mObservers to keep it in sync with the mutationobserver list
2223   mObservers.Clear();
2224 
2225   mIntersectionObservers.Clear();
2226 
2227   if (mStyleSheetSetList) {
2228     mStyleSheetSetList->Disconnect();
2229   }
2230 
2231   if (mAnimationController) {
2232     mAnimationController->Disconnect();
2233   }
2234 
2235   MOZ_ASSERT(mTimelines.isEmpty());
2236 
2237   mParentDocument = nullptr;
2238 
2239   // Kill the subdocument map, doing this will release its strong
2240   // references, if any.
2241   delete mSubDocuments;
2242   mSubDocuments = nullptr;
2243 
2244   nsAutoScriptBlocker scriptBlocker;
2245 
2246   // Destroy link map now so we don't waste time removing
2247   // links one by one
2248   DestroyElementMaps();
2249 
2250   // Invalidate cached array of child nodes
2251   InvalidateChildNodes();
2252 
2253   // We should not have child nodes when destructor is called,
2254   // since child nodes keep their owner document alive.
2255   MOZ_ASSERT(!HasChildren());
2256 
2257   mCachedRootElement = nullptr;
2258 
2259   for (auto& sheets : mAdditionalSheets) {
2260     UnlinkStyleSheets(sheets);
2261   }
2262 
2263   if (mAttrStyleSheet) {
2264     mAttrStyleSheet->SetOwningDocument(nullptr);
2265   }
2266 
2267   if (mListenerManager) {
2268     mListenerManager->Disconnect();
2269     UnsetFlags(NODE_HAS_LISTENERMANAGER);
2270   }
2271 
2272   if (mScriptLoader) {
2273     mScriptLoader->DropDocumentReference();
2274   }
2275 
2276   if (mCSSLoader) {
2277     // Could be null here if Init() failed or if we have been unlinked.
2278     mCSSLoader->DropDocumentReference();
2279   }
2280 
2281   if (mStyleImageLoader) {
2282     mStyleImageLoader->DropDocumentReference();
2283   }
2284 
2285   if (mXULBroadcastManager) {
2286     mXULBroadcastManager->DropDocumentReference();
2287   }
2288 
2289   if (mXULPersist) {
2290     mXULPersist->DropDocumentReference();
2291   }
2292 
2293   if (mPermissionDelegateHandler) {
2294     mPermissionDelegateHandler->DropDocumentReference();
2295   }
2296 
2297   mHeaderData = nullptr;
2298 
2299   mPendingTitleChangeEvent.Revoke();
2300 
2301   MOZ_ASSERT(mDOMMediaQueryLists.isEmpty(),
2302              "must not have media query lists left");
2303 
2304   if (mNodeInfoManager) {
2305     mNodeInfoManager->DropDocumentReference();
2306   }
2307 
2308   if (mDocGroup) {
2309     MOZ_ASSERT(mDocGroup->GetBrowsingContextGroup());
2310     mDocGroup->GetBrowsingContextGroup()->RemoveDocument(this, mDocGroup);
2311   }
2312 
2313   UnlinkOriginalDocumentIfStatic();
2314 
2315   UnregisterFromMemoryReportingForDataDocument();
2316 }
2317 
NS_INTERFACE_TABLE_HEAD(Document)2318 NS_INTERFACE_TABLE_HEAD(Document)
2319   NS_WRAPPERCACHE_INTERFACE_TABLE_ENTRY
2320   NS_INTERFACE_TABLE_BEGIN
2321     NS_INTERFACE_TABLE_ENTRY_AMBIGUOUS(Document, nsISupports, nsINode)
2322     NS_INTERFACE_TABLE_ENTRY(Document, nsINode)
2323     NS_INTERFACE_TABLE_ENTRY(Document, Document)
2324     NS_INTERFACE_TABLE_ENTRY(Document, nsIScriptObjectPrincipal)
2325     NS_INTERFACE_TABLE_ENTRY(Document, EventTarget)
2326     NS_INTERFACE_TABLE_ENTRY(Document, nsISupportsWeakReference)
2327     NS_INTERFACE_TABLE_ENTRY(Document, nsIRadioGroupContainer)
2328   NS_INTERFACE_TABLE_END
2329   NS_INTERFACE_TABLE_TO_MAP_SEGUE_CYCLE_COLLECTION(Document)
2330 NS_INTERFACE_MAP_END
2331 
2332 NS_IMPL_CYCLE_COLLECTING_ADDREF(Document)
2333 NS_IMETHODIMP_(MozExternalRefCountType)
2334 Document::Release() {
2335   MOZ_ASSERT(0 != mRefCnt, "dup release");
2336   NS_ASSERT_OWNINGTHREAD(Document);
2337   nsISupports* base = NS_CYCLE_COLLECTION_CLASSNAME(Document)::Upcast(this);
2338   bool shouldDelete = false;
2339   nsrefcnt count = mRefCnt.decr(base, &shouldDelete);
2340   NS_LOG_RELEASE(this, count, "Document");
2341   if (count == 0) {
2342     if (mStackRefCnt && !mNeedsReleaseAfterStackRefCntRelease) {
2343       mNeedsReleaseAfterStackRefCntRelease = true;
2344       NS_ADDREF_THIS();
2345       return mRefCnt.get();
2346     }
2347     mRefCnt.incr(base);
2348     LastRelease();
2349     mRefCnt.decr(base);
2350     if (shouldDelete) {
2351       mRefCnt.stabilizeForDeletion();
2352       DeleteCycleCollectable();
2353     }
2354   }
2355   return count;
2356 }
2357 
NS_IMETHODIMP_(void)2358 NS_IMETHODIMP_(void)
2359 Document::DeleteCycleCollectable() { delete this; }
2360 
2361 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_BEGIN(Document)
2362   if (Element::CanSkip(tmp, aRemovingAllowed)) {
2363     EventListenerManager* elm = tmp->GetExistingListenerManager();
2364     if (elm) {
2365       elm->MarkForCC();
2366     }
2367     return true;
2368   }
2369 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_END
2370 
2371 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_BEGIN(Document)
2372   return Element::CanSkipInCC(tmp);
2373 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_END
2374 
2375 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_BEGIN(Document)
2376   return Element::CanSkipThis(tmp);
2377 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_END
2378 
2379 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INTERNAL(Document)
2380   if (MOZ_UNLIKELY(cb.WantDebugInfo())) {
2381     char name[512];
2382     nsAutoCString loadedAsData;
2383     if (tmp->IsLoadedAsData()) {
2384       loadedAsData.AssignLiteral("data");
2385     } else {
2386       loadedAsData.AssignLiteral("normal");
2387     }
2388     uint32_t nsid = tmp->GetDefaultNamespaceID();
2389     nsAutoCString uri;
2390     if (tmp->mDocumentURI) uri = tmp->mDocumentURI->GetSpecOrDefault();
2391     static const char* kNSURIs[] = {"([none])", "(xmlns)", "(xml)",
2392                                     "(xhtml)",  "(XLink)", "(XSLT)",
2393                                     "(MathML)", "(RDF)",   "(XUL)"};
2394     if (nsid < ArrayLength(kNSURIs)) {
2395       SprintfLiteral(name, "Document %s %s %s", loadedAsData.get(),
2396                      kNSURIs[nsid], uri.get());
2397     } else {
2398       SprintfLiteral(name, "Document %s %s", loadedAsData.get(), uri.get());
2399     }
2400     cb.DescribeRefCountedNode(tmp->mRefCnt.get(), name);
2401   } else {
2402     NS_IMPL_CYCLE_COLLECTION_DESCRIBE(Document, tmp->mRefCnt.get())
2403   }
2404 
2405   if (!nsINode::Traverse(tmp, cb)) {
2406     return NS_SUCCESS_INTERRUPTED_TRAVERSE;
2407   }
2408 
2409   tmp->mExternalResourceMap.Traverse(&cb);
2410 
2411   // Traverse all Document pointer members.
2412   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSecurityInfo)
2413   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDisplayDocument)
2414   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFontFaceSet)
2415   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mReadyForIdle)
2416   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocumentL10n)
2417 
2418   // Traverse all Document nsCOMPtrs.
2419   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mParser)
2420   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mScriptGlobalObject)
2421   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mListenerManager)
2422   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mStyleSheetSetList)
2423   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mScriptLoader)
2424 
2425   DocumentOrShadowRoot::Traverse(tmp, cb);
2426 
2427   for (auto& sheets : tmp->mAdditionalSheets) {
2428     tmp->TraverseStyleSheets(sheets, "mAdditionalSheets[<origin>][i]", cb);
2429   }
2430 
2431   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mOnloadBlocker)
2432   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mLazyLoadImageObserver)
2433   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mLazyLoadImageObserverViewport)
2434   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDOMImplementation)
2435   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mImageMaps)
2436   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mOrientationPendingPromise)
2437   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mOriginalDocument)
2438   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCachedEncoder)
2439   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mStateObjectCached)
2440   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocumentTimeline)
2441   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPendingAnimationTracker)
2442   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mTemplateContentsOwner)
2443   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mChildrenCollection)
2444   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mImages);
2445   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mEmbeds);
2446   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mLinks);
2447   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mForms);
2448   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mScripts);
2449   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mApplets);
2450   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mAnchors);
2451   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mAnonymousContents)
2452   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCommandDispatcher)
2453   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFeaturePolicy)
2454   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSuppressedEventListener)
2455   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPrototypeDocument)
2456   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mMidasCommandManager)
2457   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mAll)
2458   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocGroup)
2459   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFrameRequestManager)
2460 
2461   // Traverse all our nsCOMArrays.
2462   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPreloadingImages)
2463 
2464   // Traverse animation components
2465   if (tmp->mAnimationController) {
2466     tmp->mAnimationController->Traverse(&cb);
2467   }
2468 
2469   if (tmp->mSubDocuments) {
2470     for (auto iter = tmp->mSubDocuments->Iter(); !iter.Done(); iter.Next()) {
2471       auto entry = static_cast<SubDocMapEntry*>(iter.Get());
2472 
2473       NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mSubDocuments entry->mKey");
2474       cb.NoteXPCOMChild(entry->mKey);
2475       NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb,
2476                                          "mSubDocuments entry->mSubDocument");
2477       cb.NoteXPCOMChild(ToSupports(entry->mSubDocument));
2478     }
2479   }
2480 
2481   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCSSLoader)
2482 
2483   // We own only the items in mDOMMediaQueryLists that have listeners;
2484   // this reference is managed by their AddListener and RemoveListener
2485   // methods.
2486   for (MediaQueryList* mql = tmp->mDOMMediaQueryLists.getFirst(); mql;
2487        mql = static_cast<LinkedListElement<MediaQueryList>*>(mql)->getNext()) {
2488     if (mql->HasListeners() &&
2489         NS_SUCCEEDED(mql->CheckCurrentGlobalCorrectness())) {
2490       NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mDOMMediaQueryLists item");
2491       cb.NoteXPCOMChild(mql);
2492     }
2493   }
2494 
2495   // XXX: This should be not needed once bug 1569185 lands.
2496   for (const auto& entry : tmp->mL10nProtoElements) {
2497     NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mL10nProtoElements key");
2498     cb.NoteXPCOMChild(entry.GetKey());
2499     CycleCollectionNoteChild(cb, entry.GetWeak(), "mL10nProtoElements value");
2500   }
2501 
2502   for (size_t i = 0; i < tmp->mPendingFrameStaticClones.Length(); ++i) {
2503     NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPendingFrameStaticClones[i].mElement);
2504     NS_IMPL_CYCLE_COLLECTION_TRAVERSE(
2505         mPendingFrameStaticClones[i].mStaticCloneOf);
2506   }
2507 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
2508 
2509 NS_IMPL_CYCLE_COLLECTION_CLASS(Document)
2510 
2511 NS_IMPL_CYCLE_COLLECTION_TRACE_WRAPPERCACHE(Document)
2512 
2513 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(Document)
2514   tmp->mInUnlinkOrDeletion = true;
2515 
2516   // Clear out our external resources
2517   tmp->mExternalResourceMap.Shutdown();
2518 
2519   nsAutoScriptBlocker scriptBlocker;
2520 
2521   nsINode::Unlink(tmp);
2522 
2523   while (tmp->HasChildren()) {
2524     // Hold a strong ref to the node when we remove it, because we may be
2525     // the last reference to it.
2526     // If this code changes, change the corresponding code in Document's
2527     // unlink impl and ContentUnbinder::UnbindSubtree.
2528     nsCOMPtr<nsIContent> child = tmp->GetLastChild();
2529     tmp->DisconnectChild(child);
2530     child->UnbindFromTree();
2531   }
2532 
2533   tmp->UnlinkOriginalDocumentIfStatic();
2534 
2535   tmp->mCachedRootElement = nullptr;  // Avoid a dangling pointer
2536 
2537   tmp->SetScriptGlobalObject(nullptr);
2538 
2539   for (auto& sheets : tmp->mAdditionalSheets) {
2540     tmp->UnlinkStyleSheets(sheets);
2541   }
2542 
2543   NS_IMPL_CYCLE_COLLECTION_UNLINK(mSecurityInfo)
2544   NS_IMPL_CYCLE_COLLECTION_UNLINK(mDisplayDocument)
2545   NS_IMPL_CYCLE_COLLECTION_UNLINK(mLazyLoadImageObserver)
2546   NS_IMPL_CYCLE_COLLECTION_UNLINK(mLazyLoadImageObserverViewport)
2547   NS_IMPL_CYCLE_COLLECTION_UNLINK(mFontFaceSet)
2548   NS_IMPL_CYCLE_COLLECTION_UNLINK(mReadyForIdle)
2549   NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocumentL10n)
2550   NS_IMPL_CYCLE_COLLECTION_UNLINK(mParser)
2551   NS_IMPL_CYCLE_COLLECTION_UNLINK(mOnloadBlocker)
2552   NS_IMPL_CYCLE_COLLECTION_UNLINK(mDOMImplementation)
2553   NS_IMPL_CYCLE_COLLECTION_UNLINK(mImageMaps)
2554   NS_IMPL_CYCLE_COLLECTION_UNLINK(mOrientationPendingPromise)
2555   NS_IMPL_CYCLE_COLLECTION_UNLINK(mOriginalDocument)
2556   NS_IMPL_CYCLE_COLLECTION_UNLINK(mCachedEncoder)
2557   NS_IMPL_CYCLE_COLLECTION_UNLINK(mStateObjectCached)
2558   NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocumentTimeline)
2559   NS_IMPL_CYCLE_COLLECTION_UNLINK(mPendingAnimationTracker)
2560   NS_IMPL_CYCLE_COLLECTION_UNLINK(mTemplateContentsOwner)
2561   NS_IMPL_CYCLE_COLLECTION_UNLINK(mChildrenCollection)
2562   NS_IMPL_CYCLE_COLLECTION_UNLINK(mImages);
2563   NS_IMPL_CYCLE_COLLECTION_UNLINK(mEmbeds);
2564   NS_IMPL_CYCLE_COLLECTION_UNLINK(mLinks);
2565   NS_IMPL_CYCLE_COLLECTION_UNLINK(mForms);
2566   NS_IMPL_CYCLE_COLLECTION_UNLINK(mScripts);
2567   NS_IMPL_CYCLE_COLLECTION_UNLINK(mApplets);
2568   NS_IMPL_CYCLE_COLLECTION_UNLINK(mAnchors);
2569   NS_IMPL_CYCLE_COLLECTION_UNLINK(mAnonymousContents)
2570   NS_IMPL_CYCLE_COLLECTION_UNLINK(mCommandDispatcher)
2571   NS_IMPL_CYCLE_COLLECTION_UNLINK(mFeaturePolicy)
2572   NS_IMPL_CYCLE_COLLECTION_UNLINK(mSuppressedEventListener)
2573   NS_IMPL_CYCLE_COLLECTION_UNLINK(mPrototypeDocument)
2574   NS_IMPL_CYCLE_COLLECTION_UNLINK(mMidasCommandManager)
2575   NS_IMPL_CYCLE_COLLECTION_UNLINK(mAll)
2576   NS_IMPL_CYCLE_COLLECTION_UNLINK(mReferrerInfo)
2577   NS_IMPL_CYCLE_COLLECTION_UNLINK(mPreloadReferrerInfo)
2578 
2579   if (tmp->mDocGroup && tmp->mDocGroup->GetBrowsingContextGroup()) {
2580     tmp->mDocGroup->GetBrowsingContextGroup()->RemoveDocument(tmp,
2581                                                               tmp->mDocGroup);
2582   }
2583   tmp->mDocGroup = nullptr;
2584 
2585   if (tmp->IsTopLevelContentDocument()) {
2586     RemoveToplevelLoadingDocument(tmp);
2587   }
2588 
2589   tmp->mParentDocument = nullptr;
2590 
2591   NS_IMPL_CYCLE_COLLECTION_UNLINK(mPreloadingImages)
2592 
2593   NS_IMPL_CYCLE_COLLECTION_UNLINK(mIntersectionObservers)
2594 
2595   if (tmp->mListenerManager) {
2596     tmp->mListenerManager->Disconnect();
2597     tmp->UnsetFlags(NODE_HAS_LISTENERMANAGER);
2598     tmp->mListenerManager = nullptr;
2599   }
2600 
2601   if (tmp->mStyleSheetSetList) {
2602     tmp->mStyleSheetSetList->Disconnect();
2603     tmp->mStyleSheetSetList = nullptr;
2604   }
2605 
2606   delete tmp->mSubDocuments;
2607   tmp->mSubDocuments = nullptr;
2608 
2609   NS_IMPL_CYCLE_COLLECTION_UNLINK(mFrameRequestManager)
2610   MOZ_RELEASE_ASSERT(!tmp->mFrameRequestCallbacksScheduled,
2611                      "How did we get here without our presshell going away "
2612                      "first?");
2613 
2614   DocumentOrShadowRoot::Unlink(tmp);
2615 
2616   // Document has a pretty complex destructor, so we're going to
2617   // assume that *most* cycles you actually want to break somewhere
2618   // else, and not unlink an awful lot here.
2619 
2620   tmp->mExpandoAndGeneration.OwnerUnlinked();
2621 
2622   if (tmp->mAnimationController) {
2623     tmp->mAnimationController->Unlink();
2624   }
2625 
2626   tmp->mPendingTitleChangeEvent.Revoke();
2627 
2628   if (tmp->mCSSLoader) {
2629     tmp->mCSSLoader->DropDocumentReference();
2630     NS_IMPL_CYCLE_COLLECTION_UNLINK(mCSSLoader)
2631   }
2632 
2633   // We own only the items in mDOMMediaQueryLists that have listeners;
2634   // this reference is managed by their AddListener and RemoveListener
2635   // methods.
2636   for (MediaQueryList* mql = tmp->mDOMMediaQueryLists.getFirst(); mql;) {
2637     MediaQueryList* next =
2638         static_cast<LinkedListElement<MediaQueryList>*>(mql)->getNext();
2639     mql->Disconnect();
2640     mql = next;
2641   }
2642 
2643   tmp->mPendingFrameStaticClones.Clear();
2644 
2645   tmp->mInUnlinkOrDeletion = false;
2646 
2647   tmp->UnregisterFromMemoryReportingForDataDocument();
2648 
NS_IMPL_CYCLE_COLLECTION_UNLINK(mL10nProtoElements)2649   NS_IMPL_CYCLE_COLLECTION_UNLINK(mL10nProtoElements)
2650   NS_IMPL_CYCLE_COLLECTION_UNLINK_WEAK_PTR
2651   NS_IMPL_CYCLE_COLLECTION_UNLINK_WEAK_REFERENCE
2652 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
2653 
2654 nsresult Document::Init() {
2655   if (mCSSLoader || mStyleImageLoader || mNodeInfoManager || mScriptLoader) {
2656     return NS_ERROR_ALREADY_INITIALIZED;
2657   }
2658 
2659   // Force initialization.
2660   mOnloadBlocker = new OnloadBlocker();
2661   mStyleImageLoader = new css::ImageLoader(this);
2662 
2663   mNodeInfoManager = new nsNodeInfoManager();
2664   nsresult rv = mNodeInfoManager->Init(this);
2665   NS_ENSURE_SUCCESS(rv, rv);
2666 
2667   // mNodeInfo keeps NodeInfoManager alive!
2668   mNodeInfo = mNodeInfoManager->GetDocumentNodeInfo();
2669   NS_ENSURE_TRUE(mNodeInfo, NS_ERROR_OUT_OF_MEMORY);
2670   MOZ_ASSERT(mNodeInfo->NodeType() == DOCUMENT_NODE,
2671              "Bad NodeType in aNodeInfo");
2672 
2673   NS_ASSERTION(OwnerDoc() == this, "Our nodeinfo is busted!");
2674 
2675   mCSSLoader = new css::Loader(this);
2676   // Assume we're not quirky, until we know otherwise
2677   mCSSLoader->SetCompatibilityMode(eCompatibility_FullStandards);
2678 
2679   // If after creation the owner js global is not set for a document
2680   // we use the default compartment for this document, instead of creating
2681   // wrapper in some random compartment when the document is exposed to js
2682   // via some events.
2683   nsCOMPtr<nsIGlobalObject> global =
2684       xpc::NativeGlobal(xpc::PrivilegedJunkScope());
2685   NS_ENSURE_TRUE(global, NS_ERROR_FAILURE);
2686   mScopeObject = do_GetWeakReference(global);
2687   MOZ_ASSERT(mScopeObject);
2688 
2689   mScriptLoader = new dom::ScriptLoader(this);
2690 
2691   // we need to create a policy here so getting the policy within
2692   // ::Policy() can *always* return a non null policy
2693   mFeaturePolicy = new dom::FeaturePolicy(this);
2694   mFeaturePolicy->SetDefaultOrigin(NodePrincipal());
2695 
2696   mStyleSet = MakeUnique<ServoStyleSet>(*this);
2697 
2698   mozilla::HoldJSObjects(this);
2699 
2700   return NS_OK;
2701 }
2702 
RemoveAllProperties()2703 void Document::RemoveAllProperties() { PropertyTable().RemoveAllProperties(); }
2704 
RemoveAllPropertiesFor(nsINode * aNode)2705 void Document::RemoveAllPropertiesFor(nsINode* aNode) {
2706   PropertyTable().RemoveAllPropertiesFor(aNode);
2707 }
2708 
Reset(nsIChannel * aChannel,nsILoadGroup * aLoadGroup)2709 void Document::Reset(nsIChannel* aChannel, nsILoadGroup* aLoadGroup) {
2710   nsCOMPtr<nsIURI> uri;
2711   nsCOMPtr<nsIPrincipal> principal;
2712   nsCOMPtr<nsIPrincipal> partitionedPrincipal;
2713   if (aChannel) {
2714     // Note: this code is duplicated in PrototypeDocumentContentSink::Init and
2715     // nsScriptSecurityManager::GetChannelResultPrincipals.
2716     // Note: this should match the uri used for the OnNewURI call in
2717     //       nsDocShell::CreateContentViewer.
2718     NS_GetFinalChannelURI(aChannel, getter_AddRefs(uri));
2719 
2720     nsIScriptSecurityManager* securityManager =
2721         nsContentUtils::GetSecurityManager();
2722     if (securityManager) {
2723       securityManager->GetChannelResultPrincipals(
2724           aChannel, getter_AddRefs(principal),
2725           getter_AddRefs(partitionedPrincipal));
2726     }
2727   }
2728 
2729   bool equal = principal->Equals(partitionedPrincipal);
2730 
2731   principal = MaybeDowngradePrincipal(principal);
2732   if (equal) {
2733     partitionedPrincipal = principal;
2734   } else {
2735     partitionedPrincipal = MaybeDowngradePrincipal(partitionedPrincipal);
2736   }
2737 
2738   ResetToURI(uri, aLoadGroup, principal, partitionedPrincipal);
2739 
2740   // Note that, since mTiming does not change during a reset, the
2741   // navigationStart time remains unchanged and therefore any future new
2742   // timeline will have the same global clock time as the old one.
2743   mDocumentTimeline = nullptr;
2744 
2745   if (nsCOMPtr<nsIPropertyBag2> bag = do_QueryInterface(aChannel)) {
2746     if (nsCOMPtr<nsIURI> baseURI = do_GetProperty(bag, u"baseURI"_ns)) {
2747       mDocumentBaseURI = baseURI.forget();
2748       mChromeXHRDocBaseURI = nullptr;
2749     }
2750   }
2751 
2752   mChannel = aChannel;
2753 }
2754 
DisconnectNodeTree()2755 void Document::DisconnectNodeTree() {
2756   // Delete references to sub-documents and kill the subdocument map,
2757   // if any. This is not strictly needed, but makes the node tree
2758   // teardown a bit faster.
2759   delete mSubDocuments;
2760   mSubDocuments = nullptr;
2761 
2762   bool oldVal = mInUnlinkOrDeletion;
2763   mInUnlinkOrDeletion = true;
2764   {  // Scope for update
2765     MOZ_AUTO_DOC_UPDATE(this, true);
2766 
2767     // Destroy link map now so we don't waste time removing
2768     // links one by one
2769     DestroyElementMaps();
2770 
2771     // Invalidate cached array of child nodes
2772     InvalidateChildNodes();
2773 
2774     while (HasChildren()) {
2775       nsMutationGuard::DidMutate();
2776       nsCOMPtr<nsIContent> content = GetLastChild();
2777       nsIContent* previousSibling = content->GetPreviousSibling();
2778       DisconnectChild(content);
2779       if (content == mCachedRootElement) {
2780         // Immediately clear mCachedRootElement, now that it's been removed
2781         // from mChildren, so that GetRootElement() will stop returning this
2782         // now-stale value.
2783         mCachedRootElement = nullptr;
2784       }
2785       MutationObservers::NotifyContentRemoved(this, content, previousSibling);
2786       content->UnbindFromTree();
2787     }
2788     MOZ_ASSERT(!mCachedRootElement,
2789                "After removing all children, there should be no root elem");
2790   }
2791   mInUnlinkOrDeletion = oldVal;
2792 }
2793 
ResetToURI(nsIURI * aURI,nsILoadGroup * aLoadGroup,nsIPrincipal * aPrincipal,nsIPrincipal * aPartitionedPrincipal)2794 void Document::ResetToURI(nsIURI* aURI, nsILoadGroup* aLoadGroup,
2795                           nsIPrincipal* aPrincipal,
2796                           nsIPrincipal* aPartitionedPrincipal) {
2797   MOZ_ASSERT(aURI, "Null URI passed to ResetToURI");
2798   MOZ_ASSERT(!!aPrincipal == !!aPartitionedPrincipal);
2799 
2800   MOZ_LOG(gDocumentLeakPRLog, LogLevel::Debug,
2801           ("DOCUMENT %p ResetToURI %s", this, aURI->GetSpecOrDefault().get()));
2802 
2803   mSecurityInfo = nullptr;
2804 
2805   nsCOMPtr<nsILoadGroup> group = do_QueryReferent(mDocumentLoadGroup);
2806   if (!aLoadGroup || group != aLoadGroup) {
2807     mDocumentLoadGroup = nullptr;
2808   }
2809 
2810   DisconnectNodeTree();
2811 
2812   // Reset our stylesheets
2813   ResetStylesheetsToURI(aURI);
2814 
2815   // Release the listener manager
2816   if (mListenerManager) {
2817     mListenerManager->Disconnect();
2818     mListenerManager = nullptr;
2819   }
2820 
2821   // Release the stylesheets list.
2822   mDOMStyleSheets = nullptr;
2823 
2824   // Release our principal after tearing down the document, rather than before.
2825   // This ensures that, during teardown, the document and the dying window
2826   // (which already nulled out its document pointer and cached the principal)
2827   // have matching principals.
2828   SetPrincipals(nullptr, nullptr);
2829 
2830   // Clear the original URI so SetDocumentURI sets it.
2831   mOriginalURI = nullptr;
2832 
2833   SetDocumentURI(aURI);
2834   mChromeXHRDocURI = nullptr;
2835   // If mDocumentBaseURI is null, Document::GetBaseURI() returns
2836   // mDocumentURI.
2837   mDocumentBaseURI = nullptr;
2838   mChromeXHRDocBaseURI = nullptr;
2839 
2840   // Check if the current document is the top-level DevTools document.
2841   // For inner DevTools frames, mIsDevToolsDocument will be set when
2842   // calling SetDocumentParent.
2843   if (aURI && aURI->SchemeIs("about") &&
2844       aURI->GetSpecOrDefault().EqualsLiteral("about:devtools-toolbox")) {
2845     mIsDevToolsDocument = true;
2846   }
2847 
2848   if (aLoadGroup) {
2849     mDocumentLoadGroup = do_GetWeakReference(aLoadGroup);
2850     // there was an assertion here that aLoadGroup was not null.  This
2851     // is no longer valid: nsDocShell::SetDocument does not create a
2852     // load group, and it works just fine
2853 
2854     // XXXbz what does "just fine" mean exactly?  And given that there
2855     // is no nsDocShell::SetDocument, what is this talking about?
2856 
2857     if (IsContentDocument()) {
2858       // Inform the associated request context about this load start so
2859       // any of its internal load progress flags gets reset.
2860       nsCOMPtr<nsIRequestContextService> rcsvc =
2861           net::RequestContextService::GetOrCreate();
2862       if (rcsvc) {
2863         nsCOMPtr<nsIRequestContext> rc;
2864         rcsvc->GetRequestContextFromLoadGroup(aLoadGroup, getter_AddRefs(rc));
2865         if (rc) {
2866           rc->BeginLoad();
2867         }
2868       }
2869     }
2870   }
2871 
2872   mLastModified.Truncate();
2873   // XXXbz I guess we're assuming that the caller will either pass in
2874   // a channel with a useful type or call SetContentType?
2875   SetContentType(""_ns);
2876   mContentLanguage.Truncate();
2877   mBaseTarget.Truncate();
2878 
2879   mXMLDeclarationBits = 0;
2880 
2881   // Now get our new principal
2882   if (aPrincipal) {
2883     SetPrincipals(aPrincipal, aPartitionedPrincipal);
2884   } else {
2885     nsIScriptSecurityManager* securityManager =
2886         nsContentUtils::GetSecurityManager();
2887     if (securityManager) {
2888       nsCOMPtr<nsILoadContext> loadContext(mDocumentContainer);
2889 
2890       if (!loadContext && aLoadGroup) {
2891         nsCOMPtr<nsIInterfaceRequestor> cbs;
2892         aLoadGroup->GetNotificationCallbacks(getter_AddRefs(cbs));
2893         loadContext = do_GetInterface(cbs);
2894       }
2895 
2896       MOZ_ASSERT(loadContext,
2897                  "must have a load context or pass in an explicit principal");
2898 
2899       nsCOMPtr<nsIPrincipal> principal;
2900       nsresult rv = securityManager->GetLoadContextContentPrincipal(
2901           mDocumentURI, loadContext, getter_AddRefs(principal));
2902       if (NS_SUCCEEDED(rv)) {
2903         SetPrincipals(principal, principal);
2904       }
2905     }
2906   }
2907 
2908   if (mFontFaceSet) {
2909     mFontFaceSet->RefreshStandardFontLoadPrincipal();
2910   }
2911 
2912   // Refresh the principal on the realm.
2913   if (nsPIDOMWindowInner* win = GetInnerWindow()) {
2914     nsGlobalWindowInner::Cast(win)->RefreshRealmPrincipal();
2915   }
2916 }
2917 
MaybeDowngradePrincipal(nsIPrincipal * aPrincipal)2918 already_AddRefed<nsIPrincipal> Document::MaybeDowngradePrincipal(
2919     nsIPrincipal* aPrincipal) {
2920   if (!aPrincipal) {
2921     return nullptr;
2922   }
2923 
2924   // We can't load a document with an expanded principal. If we're given one,
2925   // automatically downgrade it to the last principal it subsumes (which is the
2926   // extension principal, in the case of extension content scripts).
2927   auto* basePrin = BasePrincipal::Cast(aPrincipal);
2928   if (basePrin->Is<ExpandedPrincipal>()) {
2929     MOZ_DIAGNOSTIC_ASSERT(false,
2930                           "Should never try to create a document with "
2931                           "an expanded principal");
2932 
2933     auto* expanded = basePrin->As<ExpandedPrincipal>();
2934     return do_AddRef(expanded->AllowList().LastElement());
2935   }
2936 
2937   if (aPrincipal->IsSystemPrincipal() && mDocumentContainer) {
2938     // We basically want the parent document here, but because this is very
2939     // early in the load, GetInProcessParentDocument() returns null, so we use
2940     // the docshell hierarchy to get this information instead.
2941     if (RefPtr<BrowsingContext> parent =
2942             mDocumentContainer->GetBrowsingContext()->GetParent()) {
2943       auto* parentWin = nsGlobalWindowOuter::Cast(parent->GetDOMWindow());
2944       if (!parentWin || !parentWin->GetPrincipal()->IsSystemPrincipal()) {
2945         nsCOMPtr<nsIPrincipal> nullPrincipal =
2946             NullPrincipal::CreateWithoutOriginAttributes();
2947         return nullPrincipal.forget();
2948       }
2949     }
2950   }
2951   nsCOMPtr<nsIPrincipal> principal(aPrincipal);
2952   return principal.forget();
2953 }
2954 
FindDocStyleSheetInsertionPoint(const StyleSheet & aSheet)2955 size_t Document::FindDocStyleSheetInsertionPoint(const StyleSheet& aSheet) {
2956   nsStyleSheetService* sheetService = nsStyleSheetService::GetInstance();
2957 
2958   // lowest index first
2959   int32_t newDocIndex = StyleOrderIndexOfSheet(aSheet);
2960 
2961   size_t count = mStyleSet->SheetCount(StyleOrigin::Author);
2962   size_t index = 0;
2963   for (; index < count; index++) {
2964     auto* sheet = mStyleSet->SheetAt(StyleOrigin::Author, index);
2965     MOZ_ASSERT(sheet);
2966     int32_t sheetDocIndex = StyleOrderIndexOfSheet(*sheet);
2967     if (sheetDocIndex > newDocIndex) {
2968       break;
2969     }
2970 
2971     // If the sheet is not owned by the document it can be an author
2972     // sheet registered at nsStyleSheetService or an additional author
2973     // sheet on the document, which means the new
2974     // doc sheet should end up before it.
2975     if (sheetDocIndex < 0) {
2976       if (sheetService) {
2977         auto& authorSheets = *sheetService->AuthorStyleSheets();
2978         if (authorSheets.IndexOf(sheet) != authorSheets.NoIndex) {
2979           break;
2980         }
2981       }
2982       if (sheet == GetFirstAdditionalAuthorSheet()) {
2983         break;
2984       }
2985     }
2986   }
2987 
2988   return index;
2989 }
2990 
ResetStylesheetsToURI(nsIURI * aURI)2991 void Document::ResetStylesheetsToURI(nsIURI* aURI) {
2992   MOZ_ASSERT(aURI);
2993 
2994   ClearAdoptedStyleSheets();
2995 
2996   auto ClearSheetList = [&](nsTArray<RefPtr<StyleSheet>>& aSheetList) {
2997     for (auto& sheet : Reversed(aSheetList)) {
2998       sheet->ClearAssociatedDocumentOrShadowRoot();
2999       if (mStyleSetFilled) {
3000         mStyleSet->RemoveStyleSheet(*sheet);
3001       }
3002     }
3003     aSheetList.Clear();
3004   };
3005   ClearSheetList(mStyleSheets);
3006   for (auto& sheets : mAdditionalSheets) {
3007     ClearSheetList(sheets);
3008   }
3009   if (mStyleSetFilled) {
3010     if (auto* ss = nsStyleSheetService::GetInstance()) {
3011       for (auto& sheet : Reversed(*ss->AuthorStyleSheets())) {
3012         MOZ_ASSERT(!sheet->GetAssociatedDocumentOrShadowRoot());
3013         if (sheet->IsApplicable()) {
3014           mStyleSet->RemoveStyleSheet(*sheet);
3015         }
3016       }
3017     }
3018   }
3019 
3020   // Now reset our inline style and attribute sheets.
3021   if (mAttrStyleSheet) {
3022     mAttrStyleSheet->Reset();
3023     mAttrStyleSheet->SetOwningDocument(this);
3024   } else {
3025     mAttrStyleSheet = new nsHTMLStyleSheet(this);
3026   }
3027 
3028   if (!mStyleAttrStyleSheet) {
3029     mStyleAttrStyleSheet = new nsHTMLCSSStyleSheet();
3030   }
3031 
3032   if (mStyleSetFilled) {
3033     FillStyleSetDocumentSheets();
3034 
3035     if (mStyleSet->StyleSheetsHaveChanged()) {
3036       ApplicableStylesChanged();
3037     }
3038   }
3039 }
3040 
AppendSheetsToStyleSet(ServoStyleSet * aStyleSet,const nsTArray<RefPtr<StyleSheet>> & aSheets)3041 static void AppendSheetsToStyleSet(
3042     ServoStyleSet* aStyleSet, const nsTArray<RefPtr<StyleSheet>>& aSheets) {
3043   for (StyleSheet* sheet : Reversed(aSheets)) {
3044     aStyleSet->AppendStyleSheet(*sheet);
3045   }
3046 }
3047 
FillStyleSetUserAndUASheets()3048 void Document::FillStyleSetUserAndUASheets() {
3049   // Make sure this does the same thing as PresShell::Add{User,Agent}Sheet wrt
3050   // ordering.
3051 
3052   // The document will fill in the document sheets when we create the presshell
3053   auto* cache = GlobalStyleSheetCache::Singleton();
3054 
3055   nsStyleSheetService* sheetService = nsStyleSheetService::GetInstance();
3056   MOZ_ASSERT(sheetService,
3057              "should never be creating a StyleSet after the style sheet "
3058              "service has gone");
3059 
3060   for (StyleSheet* sheet : *sheetService->UserStyleSheets()) {
3061     mStyleSet->AppendStyleSheet(*sheet);
3062   }
3063 
3064   StyleSheet* sheet = IsInChromeDocShell() ? cache->GetUserChromeSheet()
3065                                            : cache->GetUserContentSheet();
3066   if (sheet) {
3067     mStyleSet->AppendStyleSheet(*sheet);
3068   }
3069 
3070   mStyleSet->AppendStyleSheet(*cache->UASheet());
3071 
3072   if (MOZ_LIKELY(NodeInfoManager()->MathMLEnabled())) {
3073     mStyleSet->AppendStyleSheet(*cache->MathMLSheet());
3074   }
3075 
3076   if (MOZ_LIKELY(NodeInfoManager()->SVGEnabled())) {
3077     mStyleSet->AppendStyleSheet(*cache->SVGSheet());
3078   }
3079 
3080   mStyleSet->AppendStyleSheet(*cache->HTMLSheet());
3081 
3082   if (nsLayoutUtils::ShouldUseNoFramesSheet(this)) {
3083     mStyleSet->AppendStyleSheet(*cache->NoFramesSheet());
3084   }
3085 
3086   if (nsLayoutUtils::ShouldUseNoScriptSheet(this)) {
3087     mStyleSet->AppendStyleSheet(*cache->NoScriptSheet());
3088   }
3089 
3090   mStyleSet->AppendStyleSheet(*cache->CounterStylesSheet());
3091 
3092   // Load the minimal XUL rules for scrollbars and a few other XUL things
3093   // that non-XUL (typically HTML) documents commonly use.
3094   mStyleSet->AppendStyleSheet(*cache->MinimalXULSheet());
3095 
3096   // Only load the full XUL sheet if we'll need it.
3097   if (LoadsFullXULStyleSheetUpFront()) {
3098     mStyleSet->AppendStyleSheet(*cache->XULSheet());
3099   }
3100 
3101   mStyleSet->AppendStyleSheet(*cache->FormsSheet());
3102   mStyleSet->AppendStyleSheet(*cache->ScrollbarsSheet());
3103 
3104   for (StyleSheet* sheet : *sheetService->AgentStyleSheets()) {
3105     mStyleSet->AppendStyleSheet(*sheet);
3106   }
3107 
3108   MOZ_ASSERT(!mQuirkSheetAdded);
3109   if (NeedsQuirksSheet()) {
3110     mStyleSet->AppendStyleSheet(*cache->QuirkSheet());
3111     mQuirkSheetAdded = true;
3112   }
3113 }
3114 
FillStyleSet()3115 void Document::FillStyleSet() {
3116   MOZ_ASSERT(!mStyleSetFilled);
3117   FillStyleSetUserAndUASheets();
3118   FillStyleSetDocumentSheets();
3119   mStyleSetFilled = true;
3120 }
3121 
RemoveContentEditableStyleSheets()3122 void Document::RemoveContentEditableStyleSheets() {
3123   MOZ_ASSERT(IsHTMLOrXHTML());
3124 
3125   auto* cache = GlobalStyleSheetCache::Singleton();
3126   bool changed = false;
3127   if (mDesignModeSheetAdded) {
3128     mStyleSet->RemoveStyleSheet(*cache->DesignModeSheet());
3129     mDesignModeSheetAdded = false;
3130     changed = true;
3131   }
3132   if (mContentEditableSheetAdded) {
3133     mStyleSet->RemoveStyleSheet(*cache->ContentEditableSheet());
3134     mContentEditableSheetAdded = false;
3135     changed = true;
3136   }
3137   if (changed) {
3138     MOZ_ASSERT(mStyleSetFilled);
3139     ApplicableStylesChanged();
3140   }
3141 }
3142 
AddContentEditableStyleSheetsToStyleSet(bool aDesignMode)3143 void Document::AddContentEditableStyleSheetsToStyleSet(bool aDesignMode) {
3144   MOZ_ASSERT(IsHTMLOrXHTML());
3145   MOZ_DIAGNOSTIC_ASSERT(mStyleSetFilled,
3146                         "Caller should ensure we're being rendered");
3147 
3148   auto* cache = GlobalStyleSheetCache::Singleton();
3149   bool changed = false;
3150   if (!mContentEditableSheetAdded) {
3151     mStyleSet->AppendStyleSheet(*cache->ContentEditableSheet());
3152     mContentEditableSheetAdded = true;
3153     changed = true;
3154   }
3155   if (mDesignModeSheetAdded != aDesignMode) {
3156     if (mDesignModeSheetAdded) {
3157       mStyleSet->RemoveStyleSheet(*cache->DesignModeSheet());
3158     } else {
3159       mStyleSet->AppendStyleSheet(*cache->DesignModeSheet());
3160     }
3161     mDesignModeSheetAdded = !mDesignModeSheetAdded;
3162     changed = true;
3163   }
3164   if (changed) {
3165     ApplicableStylesChanged();
3166   }
3167 }
3168 
FillStyleSetDocumentSheets()3169 void Document::FillStyleSetDocumentSheets() {
3170   MOZ_ASSERT(mStyleSet->SheetCount(StyleOrigin::Author) == 0,
3171              "Style set already has document sheets?");
3172 
3173   // Sheets are added in reverse order to avoid worst-case time complexity when
3174   // looking up the index of a sheet.
3175   //
3176   // Note that usually appending is faster (rebuilds less stuff in the
3177   // styleset), but in this case it doesn't matter since we're filling the
3178   // styleset from scratch anyway.
3179   for (StyleSheet* sheet : Reversed(mStyleSheets)) {
3180     if (sheet->IsApplicable()) {
3181       mStyleSet->AddDocStyleSheet(*sheet);
3182     }
3183   }
3184 
3185   EnumerateUniqueAdoptedStyleSheetsBackToFront([&](StyleSheet& aSheet) {
3186     if (aSheet.IsApplicable()) {
3187       mStyleSet->AddDocStyleSheet(aSheet);
3188     }
3189   });
3190 
3191   nsStyleSheetService* sheetService = nsStyleSheetService::GetInstance();
3192   for (StyleSheet* sheet : *sheetService->AuthorStyleSheets()) {
3193     mStyleSet->AppendStyleSheet(*sheet);
3194   }
3195 
3196   AppendSheetsToStyleSet(mStyleSet.get(), mAdditionalSheets[eAgentSheet]);
3197   AppendSheetsToStyleSet(mStyleSet.get(), mAdditionalSheets[eUserSheet]);
3198   AppendSheetsToStyleSet(mStyleSet.get(), mAdditionalSheets[eAuthorSheet]);
3199 }
3200 
CompatibilityModeChanged()3201 void Document::CompatibilityModeChanged() {
3202   MOZ_ASSERT(IsHTMLOrXHTML());
3203   CSSLoader()->SetCompatibilityMode(mCompatMode);
3204   mStyleSet->CompatibilityModeChanged();
3205   if (PresShell* presShell = GetPresShell()) {
3206     // Selectors may have become case-sensitive / case-insensitive, the stylist
3207     // has already performed the relevant invalidation.
3208     presShell->EnsureStyleFlush();
3209   }
3210   if (!mStyleSetFilled) {
3211     MOZ_ASSERT(!mQuirkSheetAdded);
3212     return;
3213   }
3214   if (mQuirkSheetAdded == NeedsQuirksSheet()) {
3215     return;
3216   }
3217   auto* cache = GlobalStyleSheetCache::Singleton();
3218   StyleSheet* sheet = cache->QuirkSheet();
3219   if (mQuirkSheetAdded) {
3220     mStyleSet->RemoveStyleSheet(*sheet);
3221   } else {
3222     mStyleSet->AppendStyleSheet(*sheet);
3223   }
3224   mQuirkSheetAdded = !mQuirkSheetAdded;
3225   ApplicableStylesChanged();
3226 }
3227 
SetCompatibilityMode(nsCompatibility aMode)3228 void Document::SetCompatibilityMode(nsCompatibility aMode) {
3229   NS_ASSERTION(IsHTMLDocument() || aMode == eCompatibility_FullStandards,
3230                "Bad compat mode for XHTML document!");
3231 
3232   if (mCompatMode == aMode) {
3233     return;
3234   }
3235   mCompatMode = aMode;
3236   CompatibilityModeChanged();
3237   // Trigger recomputation of the nsViewportInfo the next time it's queried.
3238   mViewportType = Unknown;
3239 }
3240 
WarnIfSandboxIneffective(nsIDocShell * aDocShell,uint32_t aSandboxFlags,nsIChannel * aChannel)3241 static void WarnIfSandboxIneffective(nsIDocShell* aDocShell,
3242                                      uint32_t aSandboxFlags,
3243                                      nsIChannel* aChannel) {
3244   // If the document permits allow-top-navigation and
3245   // allow-top-navigation-by-user-activation this will permit all top
3246   // navigation.
3247   if (aSandboxFlags != SANDBOXED_NONE &&
3248       !(aSandboxFlags & SANDBOXED_TOPLEVEL_NAVIGATION) &&
3249       !(aSandboxFlags & SANDBOXED_TOPLEVEL_NAVIGATION_USER_ACTIVATION)) {
3250     nsContentUtils::ReportToConsole(
3251         nsIScriptError::warningFlag, "Iframe Sandbox"_ns,
3252         aDocShell->GetDocument(), nsContentUtils::eSECURITY_PROPERTIES,
3253         "BothAllowTopNavigationAndUserActivationPresent");
3254   }
3255   // If the document is sandboxed (via the HTML5 iframe sandbox
3256   // attribute) and both the allow-scripts and allow-same-origin
3257   // keywords are supplied, the sandboxed document can call into its
3258   // parent document and remove its sandboxing entirely - we print a
3259   // warning to the web console in this case.
3260   if (aSandboxFlags & SANDBOXED_NAVIGATION &&
3261       !(aSandboxFlags & SANDBOXED_SCRIPTS) &&
3262       !(aSandboxFlags & SANDBOXED_ORIGIN)) {
3263     RefPtr<BrowsingContext> bc = aDocShell->GetBrowsingContext();
3264     MOZ_ASSERT(bc->IsInProcess());
3265 
3266     RefPtr<BrowsingContext> parentBC = bc->GetParent();
3267     if (!parentBC || !parentBC->IsInProcess()) {
3268       // If parent document is not in process, then by construction it
3269       // cannot be same origin.
3270       return;
3271     }
3272 
3273     // Don't warn if our parent is not the top-level document.
3274     if (!parentBC->IsTopContent()) {
3275       return;
3276     }
3277 
3278     nsCOMPtr<nsIDocShell> parentDocShell = parentBC->GetDocShell();
3279     MOZ_ASSERT(parentDocShell);
3280 
3281     nsCOMPtr<nsIChannel> parentChannel;
3282     parentDocShell->GetCurrentDocumentChannel(getter_AddRefs(parentChannel));
3283     if (!parentChannel) {
3284       return;
3285     }
3286     nsresult rv = nsContentUtils::CheckSameOrigin(aChannel, parentChannel);
3287     if (NS_FAILED(rv)) {
3288       return;
3289     }
3290 
3291     nsCOMPtr<Document> parentDocument = parentDocShell->GetDocument();
3292     nsCOMPtr<nsIURI> iframeUri;
3293     parentChannel->GetURI(getter_AddRefs(iframeUri));
3294     nsContentUtils::ReportToConsole(nsIScriptError::warningFlag,
3295                                     "Iframe Sandbox"_ns, parentDocument,
3296                                     nsContentUtils::eSECURITY_PROPERTIES,
3297                                     "BothAllowScriptsAndSameOriginPresent",
3298                                     nsTArray<nsString>(), iframeUri);
3299   }
3300 }
3301 
IsSynthesized()3302 bool Document::IsSynthesized() {
3303   nsCOMPtr<nsILoadInfo> loadInfo = mChannel ? mChannel->LoadInfo() : nullptr;
3304   return loadInfo && loadInfo->GetServiceWorkerTaintingSynthesized();
3305 }
3306 
3307 // static
IsCallerChromeOrAddon(JSContext * aCx,JSObject * aObject)3308 bool Document::IsCallerChromeOrAddon(JSContext* aCx, JSObject* aObject) {
3309   nsIPrincipal* principal = nsContentUtils::SubjectPrincipal(aCx);
3310   return principal && (principal->IsSystemPrincipal() ||
3311                        principal->GetIsAddonOrExpandedAddonPrincipal());
3312 }
3313 
StartDocumentLoad(const char * aCommand,nsIChannel * aChannel,nsILoadGroup * aLoadGroup,nsISupports * aContainer,nsIStreamListener ** aDocListener,bool aReset)3314 nsresult Document::StartDocumentLoad(const char* aCommand, nsIChannel* aChannel,
3315                                      nsILoadGroup* aLoadGroup,
3316                                      nsISupports* aContainer,
3317                                      nsIStreamListener** aDocListener,
3318                                      bool aReset) {
3319   if (MOZ_LOG_TEST(gDocumentLeakPRLog, LogLevel::Debug)) {
3320     nsCOMPtr<nsIURI> uri;
3321     aChannel->GetURI(getter_AddRefs(uri));
3322     MOZ_LOG(gDocumentLeakPRLog, LogLevel::Debug,
3323             ("DOCUMENT %p StartDocumentLoad %s", this,
3324              uri ? uri->GetSpecOrDefault().get() : ""));
3325   }
3326 
3327   MOZ_ASSERT(GetReadyStateEnum() == Document::READYSTATE_UNINITIALIZED,
3328              "Bad readyState");
3329   SetReadyStateInternal(READYSTATE_LOADING);
3330 
3331   if (nsCRT::strcmp(kLoadAsData, aCommand) == 0) {
3332     mLoadedAsData = true;
3333     SetLoadedAsData(true, /* aConsiderForMemoryReporting */ true);
3334     // We need to disable script & style loading in this case.
3335     // We leave them disabled even in EndLoad(), and let anyone
3336     // who puts the document on display to worry about enabling.
3337 
3338     // Do not load/process scripts when loading as data
3339     ScriptLoader()->SetEnabled(false);
3340 
3341     // styles
3342     CSSLoader()->SetEnabled(
3343         false);  // Do not load/process styles when loading as data
3344   } else if (nsCRT::strcmp("external-resource", aCommand) == 0) {
3345     // Allow CSS, but not scripts
3346     ScriptLoader()->SetEnabled(false);
3347   }
3348 
3349   mMayStartLayout = false;
3350   MOZ_ASSERT(!mReadyForIdle,
3351              "We should never hit DOMContentLoaded before this point");
3352 
3353   if (aReset) {
3354     Reset(aChannel, aLoadGroup);
3355   }
3356 
3357   nsAutoCString contentType;
3358   nsCOMPtr<nsIPropertyBag2> bag = do_QueryInterface(aChannel);
3359   if ((bag && NS_SUCCEEDED(bag->GetPropertyAsACString(u"contentType"_ns,
3360                                                       contentType))) ||
3361       NS_SUCCEEDED(aChannel->GetContentType(contentType))) {
3362     // XXX this is only necessary for viewsource:
3363     nsACString::const_iterator start, end, semicolon;
3364     contentType.BeginReading(start);
3365     contentType.EndReading(end);
3366     semicolon = start;
3367     FindCharInReadable(';', semicolon, end);
3368     SetContentType(Substring(start, semicolon));
3369   }
3370 
3371   RetrieveRelevantHeaders(aChannel);
3372 
3373   mChannel = aChannel;
3374   nsCOMPtr<nsIInputStreamChannel> inStrmChan = do_QueryInterface(mChannel);
3375   if (inStrmChan) {
3376     bool isSrcdocChannel;
3377     inStrmChan->GetIsSrcdocChannel(&isSrcdocChannel);
3378     if (isSrcdocChannel) {
3379       mIsSrcdocDocument = true;
3380     }
3381   }
3382 
3383   if (mChannel) {
3384     nsLoadFlags loadFlags;
3385     mChannel->GetLoadFlags(&loadFlags);
3386     bool isDocument = false;
3387     mChannel->GetIsDocument(&isDocument);
3388     if (loadFlags & nsIRequest::LOAD_DOCUMENT_NEEDS_COOKIE && isDocument &&
3389         IsSynthesized() && XRE_IsContentProcess()) {
3390       ContentChild::UpdateCookieStatus(mChannel);
3391     }
3392 
3393     // Store the security info for future use.
3394     mChannel->GetSecurityInfo(getter_AddRefs(mSecurityInfo));
3395   }
3396 
3397   // If this document is being loaded by a docshell, copy its sandbox flags
3398   // to the document, and store the fullscreen enabled flag. These are
3399   // immutable after being set here.
3400   nsCOMPtr<nsIDocShell> docShell = do_QueryInterface(aContainer);
3401 
3402   // If this is an error page, don't inherit sandbox flags
3403   nsCOMPtr<nsILoadInfo> loadInfo = aChannel->LoadInfo();
3404   if (docShell && !loadInfo->GetLoadErrorPage()) {
3405     mSandboxFlags = loadInfo->GetSandboxFlags();
3406     WarnIfSandboxIneffective(docShell, mSandboxFlags, GetChannel());
3407   }
3408 
3409   // Set the opener policy for the top level content document.
3410   nsCOMPtr<nsIHttpChannelInternal> httpChan = do_QueryInterface(mChannel);
3411   nsILoadInfo::CrossOriginOpenerPolicy policy =
3412       nsILoadInfo::OPENER_POLICY_UNSAFE_NONE;
3413   if (IsTopLevelContentDocument() && httpChan &&
3414       NS_SUCCEEDED(httpChan->GetCrossOriginOpenerPolicy(&policy)) && docShell &&
3415       docShell->GetBrowsingContext()) {
3416     // Setting the opener policy on a discarded context has no effect.
3417     Unused << docShell->GetBrowsingContext()->SetOpenerPolicy(policy);
3418   }
3419 
3420   // The CSP directives upgrade-insecure-requests as well as
3421   // block-all-mixed-content not only apply to the toplevel document,
3422   // but also to nested documents. The loadInfo of a subdocument
3423   // load already holds the correct flag, so let's just set it here
3424   // on the document. Please note that we set the appropriate preload
3425   // bits just for the sake of completeness here, because the preloader
3426   // does not reach into subdocuments.
3427   mUpgradeInsecureRequests = loadInfo->GetUpgradeInsecureRequests();
3428   mUpgradeInsecurePreloads = mUpgradeInsecureRequests;
3429   mBlockAllMixedContent = loadInfo->GetBlockAllMixedContent();
3430   mBlockAllMixedContentPreloads = mBlockAllMixedContent;
3431 
3432   // HTTPS-Only Mode flags
3433   // The HTTPS_ONLY_EXEMPT flag of the HTTPS-Only state gets propagated to all
3434   // sub-resources and sub-documents.
3435   mHttpsOnlyStatus = loadInfo->GetHttpsOnlyStatus();
3436 
3437   nsresult rv = InitReferrerInfo(aChannel);
3438   NS_ENSURE_SUCCESS(rv, rv);
3439 
3440   rv = InitCOEP(aChannel);
3441   NS_ENSURE_SUCCESS(rv, rv);
3442 
3443   // Check CSP navigate-to
3444   // We need to enforce the CSP of the document that initiated the load,
3445   // which is the CSP to inherit.
3446   nsCOMPtr<nsIContentSecurityPolicy> cspToInherit = loadInfo->GetCspToInherit();
3447   if (cspToInherit) {
3448     bool allowsNavigateTo = false;
3449     rv = cspToInherit->GetAllowsNavigateTo(
3450         mDocumentURI, loadInfo->GetIsFormSubmission(),
3451         !loadInfo->RedirectChain().IsEmpty(), /* aWasRedirected */
3452         true,                                 /* aEnforceWhitelist */
3453         &allowsNavigateTo);
3454     NS_ENSURE_SUCCESS(rv, rv);
3455 
3456     if (!allowsNavigateTo) {
3457       aChannel->Cancel(NS_ERROR_CSP_NAVIGATE_TO_VIOLATION);
3458       return NS_OK;
3459     }
3460   }
3461 
3462   rv = InitCSP(aChannel);
3463   NS_ENSURE_SUCCESS(rv, rv);
3464 
3465   // Initialize FeaturePolicy
3466   rv = InitFeaturePolicy(aChannel);
3467   NS_ENSURE_SUCCESS(rv, rv);
3468 
3469   rv = loadInfo->GetCookieJarSettings(getter_AddRefs(mCookieJarSettings));
3470   NS_ENSURE_SUCCESS(rv, rv);
3471 
3472   // Generally XFO and CSP frame-ancestors is handled within
3473   // DocumentLoadListener. However, the DocumentLoadListener can not handle
3474   // object and embed. Until then we have to enforce it here (See Bug 1646899).
3475   nsContentPolicyType internalContentType =
3476       loadInfo->InternalContentPolicyType();
3477   if (internalContentType == nsIContentPolicy::TYPE_INTERNAL_OBJECT ||
3478       internalContentType == nsIContentPolicy::TYPE_INTERNAL_EMBED) {
3479     nsContentSecurityUtils::PerformCSPFrameAncestorAndXFOCheck(aChannel);
3480 
3481     nsresult status;
3482     aChannel->GetStatus(&status);
3483     if (status == NS_ERROR_XFO_VIOLATION) {
3484       // stop!  ERROR page!
3485       // But before we have to reset the principal of the document
3486       // because the onload() event fires before the error page
3487       // is displayed and we do not want the enclosing document
3488       // to access the contentDocument.
3489       RefPtr<NullPrincipal> nullPrincipal =
3490           NullPrincipal::CreateWithInheritedAttributes(NodePrincipal());
3491       // Before calling SetPrincipals() we should ensure that mFontFaceSet
3492       // and also GetInnerWindow() is still null at this point, before
3493       // we can fix Bug 1614735: Evaluate calls to SetPrincipal
3494       // within Document.cpp
3495       MOZ_ASSERT(!mFontFaceSet && !GetInnerWindow());
3496       SetPrincipals(nullPrincipal, nullPrincipal);
3497     }
3498   }
3499 
3500   return NS_OK;
3501 }
3502 
SetLoadedAsData(bool aLoadedAsData,bool aConsiderForMemoryReporting)3503 void Document::SetLoadedAsData(bool aLoadedAsData,
3504                                bool aConsiderForMemoryReporting) {
3505   mLoadedAsData = aLoadedAsData;
3506   if (aConsiderForMemoryReporting) {
3507     nsIGlobalObject* global = GetScopeObject();
3508     if (global) {
3509       if (nsPIDOMWindowInner* window = global->AsInnerWindow()) {
3510         nsGlobalWindowInner::Cast(window)
3511             ->RegisterDataDocumentForMemoryReporting(this);
3512       }
3513     }
3514   }
3515 }
3516 
GetCsp() const3517 nsIContentSecurityPolicy* Document::GetCsp() const { return mCSP; }
3518 
SetCsp(nsIContentSecurityPolicy * aCSP)3519 void Document::SetCsp(nsIContentSecurityPolicy* aCSP) { mCSP = aCSP; }
3520 
GetPreloadCsp() const3521 nsIContentSecurityPolicy* Document::GetPreloadCsp() const {
3522   return mPreloadCSP;
3523 }
3524 
SetPreloadCsp(nsIContentSecurityPolicy * aPreloadCSP)3525 void Document::SetPreloadCsp(nsIContentSecurityPolicy* aPreloadCSP) {
3526   mPreloadCSP = aPreloadCSP;
3527 }
3528 
GetCspJSON(nsString & aJSON)3529 void Document::GetCspJSON(nsString& aJSON) {
3530   aJSON.Truncate();
3531 
3532   if (!mCSP) {
3533     dom::CSPPolicies jsonPolicies;
3534     jsonPolicies.ToJSON(aJSON);
3535     return;
3536   }
3537   mCSP->ToJSON(aJSON);
3538 }
3539 
SendToConsole(nsCOMArray<nsISecurityConsoleMessage> & aMessages)3540 void Document::SendToConsole(nsCOMArray<nsISecurityConsoleMessage>& aMessages) {
3541   for (uint32_t i = 0; i < aMessages.Length(); ++i) {
3542     nsAutoString messageTag;
3543     aMessages[i]->GetTag(messageTag);
3544 
3545     nsAutoString category;
3546     aMessages[i]->GetCategory(category);
3547 
3548     nsContentUtils::ReportToConsole(nsIScriptError::warningFlag,
3549                                     NS_ConvertUTF16toUTF8(category), this,
3550                                     nsContentUtils::eSECURITY_PROPERTIES,
3551                                     NS_ConvertUTF16toUTF8(messageTag).get());
3552   }
3553 }
3554 
ApplySettingsFromCSP(bool aSpeculative)3555 void Document::ApplySettingsFromCSP(bool aSpeculative) {
3556   nsresult rv = NS_OK;
3557   if (!aSpeculative) {
3558     // 1) apply settings from regular CSP
3559     if (mCSP) {
3560       // Set up 'block-all-mixed-content' if not already inherited
3561       // from the parent context or set by any other CSP.
3562       if (!mBlockAllMixedContent) {
3563         bool block = false;
3564         rv = mCSP->GetBlockAllMixedContent(&block);
3565         NS_ENSURE_SUCCESS_VOID(rv);
3566         mBlockAllMixedContent = block;
3567       }
3568       if (!mBlockAllMixedContentPreloads) {
3569         mBlockAllMixedContentPreloads = mBlockAllMixedContent;
3570       }
3571 
3572       // Set up 'upgrade-insecure-requests' if not already inherited
3573       // from the parent context or set by any other CSP.
3574       if (!mUpgradeInsecureRequests) {
3575         bool upgrade = false;
3576         rv = mCSP->GetUpgradeInsecureRequests(&upgrade);
3577         NS_ENSURE_SUCCESS_VOID(rv);
3578         mUpgradeInsecureRequests = upgrade;
3579       }
3580       if (!mUpgradeInsecurePreloads) {
3581         mUpgradeInsecurePreloads = mUpgradeInsecureRequests;
3582       }
3583       // Update csp settings in the parent process
3584       if (auto* wgc = GetWindowGlobalChild()) {
3585         wgc->SendUpdateDocumentCspSettings(mBlockAllMixedContent,
3586                                            mUpgradeInsecureRequests);
3587       }
3588     }
3589     return;
3590   }
3591 
3592   // 2) apply settings from speculative csp
3593   if (mPreloadCSP) {
3594     if (!mBlockAllMixedContentPreloads) {
3595       bool block = false;
3596       rv = mPreloadCSP->GetBlockAllMixedContent(&block);
3597       NS_ENSURE_SUCCESS_VOID(rv);
3598       mBlockAllMixedContent = block;
3599     }
3600     if (!mUpgradeInsecurePreloads) {
3601       bool upgrade = false;
3602       rv = mPreloadCSP->GetUpgradeInsecureRequests(&upgrade);
3603       NS_ENSURE_SUCCESS_VOID(rv);
3604       mUpgradeInsecurePreloads = upgrade;
3605     }
3606   }
3607 }
3608 
InitCSP(nsIChannel * aChannel)3609 nsresult Document::InitCSP(nsIChannel* aChannel) {
3610   MOZ_ASSERT(!mScriptGlobalObject,
3611              "CSP must be initialized before mScriptGlobalObject is set!");
3612 
3613   // If this is a data document - no need to set CSP.
3614   if (mLoadedAsData) {
3615     return NS_OK;
3616   }
3617 
3618   // If this is an image, no need to set a CSP. Otherwise SVG images
3619   // served with a CSP might block internally applied inline styles.
3620   nsCOMPtr<nsILoadInfo> loadInfo = aChannel->LoadInfo();
3621   if (loadInfo->GetExternalContentPolicyType() ==
3622       ExtContentPolicy::TYPE_IMAGE) {
3623     return NS_OK;
3624   }
3625 
3626   MOZ_ASSERT(!mCSP, "where did mCSP get set if not here?");
3627 
3628   // If there is a CSP that needs to be inherited from whatever
3629   // global is considered the client of the document fetch then
3630   // we query it here from the loadinfo in case the newly created
3631   // document needs to inherit the CSP. See:
3632   // https://w3c.github.io/webappsec-csp/#initialize-document-csp
3633   bool inheritedCSP = CSP_ShouldResponseInheritCSP(aChannel);
3634   if (inheritedCSP) {
3635     mCSP = loadInfo->GetCspToInherit();
3636   }
3637 
3638   // If there is no CSP to inherit, then we create a new CSP here so
3639   // that history entries always have the right reference in case a
3640   // Meta CSP gets dynamically added after the history entry has
3641   // already been created.
3642   if (!mCSP) {
3643     mCSP = new nsCSPContext();
3644   }
3645 
3646   // Always overwrite the requesting context of the CSP so that any new
3647   // 'self' keyword added to an inherited CSP translates correctly.
3648   nsresult rv = mCSP->SetRequestContextWithDocument(this);
3649   if (NS_WARN_IF(NS_FAILED(rv))) {
3650     return rv;
3651   }
3652 
3653   nsAutoCString tCspHeaderValue, tCspROHeaderValue;
3654 
3655   nsCOMPtr<nsIHttpChannel> httpChannel;
3656   rv = GetHttpChannelHelper(aChannel, getter_AddRefs(httpChannel));
3657   if (NS_WARN_IF(NS_FAILED(rv))) {
3658     return rv;
3659   }
3660 
3661   if (httpChannel) {
3662     Unused << httpChannel->GetResponseHeader("content-security-policy"_ns,
3663                                              tCspHeaderValue);
3664 
3665     Unused << httpChannel->GetResponseHeader(
3666         "content-security-policy-report-only"_ns, tCspROHeaderValue);
3667   }
3668   NS_ConvertASCIItoUTF16 cspHeaderValue(tCspHeaderValue);
3669   NS_ConvertASCIItoUTF16 cspROHeaderValue(tCspROHeaderValue);
3670 
3671   // Check if this is a document from a WebExtension.
3672   nsCOMPtr<nsIPrincipal> principal = NodePrincipal();
3673   auto addonPolicy = BasePrincipal::Cast(principal)->AddonPolicy();
3674 
3675   // If there's no CSP to apply, go ahead and return early
3676   if (!inheritedCSP && !addonPolicy && cspHeaderValue.IsEmpty() &&
3677       cspROHeaderValue.IsEmpty()) {
3678     if (MOZ_LOG_TEST(gCspPRLog, LogLevel::Debug)) {
3679       nsCOMPtr<nsIURI> chanURI;
3680       aChannel->GetURI(getter_AddRefs(chanURI));
3681       nsAutoCString aspec;
3682       chanURI->GetAsciiSpec(aspec);
3683       MOZ_LOG(gCspPRLog, LogLevel::Debug,
3684               ("no CSP for document, %s", aspec.get()));
3685     }
3686 
3687     return NS_OK;
3688   }
3689 
3690   MOZ_LOG(gCspPRLog, LogLevel::Debug,
3691           ("Document is an add-on or CSP header specified %p", this));
3692 
3693   // ----- if the doc is an addon, apply its CSP.
3694   if (addonPolicy) {
3695     mCSP->AppendPolicy(addonPolicy->BaseCSP(), false, false);
3696 
3697     mCSP->AppendPolicy(addonPolicy->ExtensionPageCSP(), false, false);
3698     // Bug 1548468: Move CSP off ExpandedPrincipal
3699     // Currently the LoadInfo holds the source of truth for every resource load
3700     // because LoadInfo::GetCSP() queries the CSP from an ExpandedPrincipal
3701     // (and not from the Client) if the load was triggered by an extension.
3702     auto* basePrin = BasePrincipal::Cast(principal);
3703     if (basePrin->Is<ExpandedPrincipal>()) {
3704       basePrin->As<ExpandedPrincipal>()->SetCsp(mCSP);
3705     }
3706   }
3707 
3708   // ----- if there's a full-strength CSP header, apply it.
3709   if (!cspHeaderValue.IsEmpty()) {
3710     mHasCSPDeliveredThroughHeader = true;
3711     rv = CSP_AppendCSPFromHeader(mCSP, cspHeaderValue, false);
3712     NS_ENSURE_SUCCESS(rv, rv);
3713   }
3714 
3715   // ----- if there's a report-only CSP header, apply it.
3716   if (!cspROHeaderValue.IsEmpty()) {
3717     rv = CSP_AppendCSPFromHeader(mCSP, cspROHeaderValue, true);
3718     NS_ENSURE_SUCCESS(rv, rv);
3719   }
3720 
3721   // ----- Enforce sandbox policy if supplied in CSP header
3722   // The document may already have some sandbox flags set (e.g. if the document
3723   // is an iframe with the sandbox attribute set). If we have a CSP sandbox
3724   // directive, intersect the CSP sandbox flags with the existing flags. This
3725   // corresponds to the _least_ permissive policy.
3726   uint32_t cspSandboxFlags = SANDBOXED_NONE;
3727   rv = mCSP->GetCSPSandboxFlags(&cspSandboxFlags);
3728   NS_ENSURE_SUCCESS(rv, rv);
3729 
3730   // Probably the iframe sandbox attribute already caused the creation of a
3731   // new NullPrincipal. Only create a new NullPrincipal if CSP requires so
3732   // and no one has been created yet.
3733   bool needNewNullPrincipal = (cspSandboxFlags & SANDBOXED_ORIGIN) &&
3734                               !(mSandboxFlags & SANDBOXED_ORIGIN);
3735 
3736   mSandboxFlags |= cspSandboxFlags;
3737 
3738   if (needNewNullPrincipal) {
3739     principal = NullPrincipal::CreateWithInheritedAttributes(principal);
3740     // Skip setting the content blocking allowlist principal to NullPrincipal.
3741     // The principal is only used to enable/disable trackingprotection via
3742     // permission and can be shared with the top level sandboxed site.
3743     // See Bug 1654546.
3744     SetPrincipals(principal, principal);
3745   }
3746 
3747   ApplySettingsFromCSP(false);
3748   return NS_OK;
3749 }
3750 
GetParentFeaturePolicy()3751 already_AddRefed<dom::FeaturePolicy> Document::GetParentFeaturePolicy() {
3752   BrowsingContext* browsingContext = GetBrowsingContext();
3753   if (!browsingContext) {
3754     return nullptr;
3755   }
3756   if (!browsingContext->IsContentSubframe()) {
3757     return nullptr;
3758   }
3759 
3760   HTMLIFrameElement* iframe =
3761       HTMLIFrameElement::FromNodeOrNull(browsingContext->GetEmbedderElement());
3762   if (iframe) {
3763     return do_AddRef(iframe->FeaturePolicy());
3764   }
3765 
3766   if (XRE_IsParentProcess()) {
3767     return do_AddRef(browsingContext->Canonical()->GetContainerFeaturePolicy());
3768   }
3769 
3770   WindowContext* windowContext = browsingContext->GetCurrentWindowContext();
3771   if (!windowContext) {
3772     return nullptr;
3773   }
3774 
3775   WindowGlobalChild* child = windowContext->GetWindowGlobalChild();
3776   if (!child) {
3777     return nullptr;
3778   }
3779 
3780   return do_AddRef(child->GetContainerFeaturePolicy());
3781 }
3782 
InitFeaturePolicy(nsIChannel * aChannel)3783 nsresult Document::InitFeaturePolicy(nsIChannel* aChannel) {
3784   MOZ_ASSERT(mFeaturePolicy, "we should only call init once");
3785 
3786   mFeaturePolicy->ResetDeclaredPolicy();
3787 
3788   mFeaturePolicy->SetDefaultOrigin(NodePrincipal());
3789 
3790   RefPtr<mozilla::dom::FeaturePolicy> parentPolicy = GetParentFeaturePolicy();
3791   if (parentPolicy) {
3792     // Let's inherit the policy from the parent HTMLIFrameElement if it exists.
3793     mFeaturePolicy->InheritPolicy(parentPolicy);
3794     mFeaturePolicy->SetSrcOrigin(parentPolicy->GetSrcOrigin());
3795   }
3796 
3797   // We don't want to parse the http Feature-Policy header if this pref is off.
3798   if (!StaticPrefs::dom_security_featurePolicy_header_enabled()) {
3799     return NS_OK;
3800   }
3801 
3802   nsCOMPtr<nsIHttpChannel> httpChannel;
3803   nsresult rv = GetHttpChannelHelper(aChannel, getter_AddRefs(httpChannel));
3804   if (NS_WARN_IF(NS_FAILED(rv))) {
3805     return rv;
3806   }
3807 
3808   if (!httpChannel) {
3809     return NS_OK;
3810   }
3811 
3812   // query the policy from the header
3813   nsAutoCString value;
3814   rv = httpChannel->GetResponseHeader("Feature-Policy"_ns, value);
3815   if (NS_SUCCEEDED(rv)) {
3816     mFeaturePolicy->SetDeclaredPolicy(this, NS_ConvertUTF8toUTF16(value),
3817                                       NodePrincipal(), nullptr);
3818   }
3819 
3820   return NS_OK;
3821 }
3822 
InitReferrerInfo(nsIChannel * aChannel)3823 nsresult Document::InitReferrerInfo(nsIChannel* aChannel) {
3824   MOZ_ASSERT(mReferrerInfo);
3825   MOZ_ASSERT(mPreloadReferrerInfo);
3826 
3827   if (ReferrerInfo::ShouldResponseInheritReferrerInfo(aChannel)) {
3828     // The channel is loading `about:srcdoc`. Srcdoc loads should respond with
3829     // their parent's ReferrerInfo when asked for their ReferrerInfo, unless
3830     // they have an opaque origin.
3831     // https://w3c.github.io/webappsec-referrer-policy/#determine-requests-referrer
3832     if (BrowsingContext* bc = GetBrowsingContext()) {
3833       // At this point the document is not fully created and mParentDocument has
3834       // not been set yet,
3835       Document* parentDoc = bc->GetEmbedderElement()
3836                                 ? bc->GetEmbedderElement()->OwnerDoc()
3837                                 : nullptr;
3838       if (parentDoc) {
3839         mReferrerInfo = parentDoc->GetReferrerInfo();
3840         mPreloadReferrerInfo = mReferrerInfo;
3841         return NS_OK;
3842       }
3843 
3844       MOZ_ASSERT(bc->IsInProcess() || NodePrincipal()->GetIsNullPrincipal(),
3845                  "srcdoc without null principal as toplevel!");
3846     }
3847   }
3848 
3849   nsCOMPtr<nsIHttpChannel> httpChannel;
3850   nsresult rv = GetHttpChannelHelper(aChannel, getter_AddRefs(httpChannel));
3851   if (NS_WARN_IF(NS_FAILED(rv))) {
3852     return rv;
3853   }
3854 
3855   if (!httpChannel) {
3856     return NS_OK;
3857   }
3858 
3859   nsCOMPtr<nsIReferrerInfo> referrerInfo = httpChannel->GetReferrerInfo();
3860   if (referrerInfo) {
3861     mReferrerInfo = referrerInfo;
3862   }
3863 
3864   // Override policy if we get one from Referrerr-Policy header
3865   mozilla::dom::ReferrerPolicy policy =
3866       nsContentUtils::GetReferrerPolicyFromChannel(aChannel);
3867   mReferrerInfo = static_cast<dom::ReferrerInfo*>(mReferrerInfo.get())
3868                       ->CloneWithNewPolicy(policy);
3869 
3870   mPreloadReferrerInfo = mReferrerInfo;
3871   return NS_OK;
3872 }
3873 
InitCOEP(nsIChannel * aChannel)3874 nsresult Document::InitCOEP(nsIChannel* aChannel) {
3875   nsCOMPtr<nsIHttpChannel> httpChannel;
3876   nsresult rv = GetHttpChannelHelper(aChannel, getter_AddRefs(httpChannel));
3877   if (NS_FAILED(rv)) {
3878     return NS_OK;
3879   }
3880 
3881   nsCOMPtr<nsIHttpChannelInternal> intChannel = do_QueryInterface(httpChannel);
3882 
3883   if (!intChannel) {
3884     return NS_OK;
3885   }
3886 
3887   nsILoadInfo::CrossOriginEmbedderPolicy policy =
3888       nsILoadInfo::EMBEDDER_POLICY_NULL;
3889   if (NS_SUCCEEDED(intChannel->GetResponseEmbedderPolicy(&policy))) {
3890     mEmbedderPolicy = Some(policy);
3891   }
3892 
3893   return NS_OK;
3894 }
3895 
StopDocumentLoad()3896 void Document::StopDocumentLoad() {
3897   if (mParser) {
3898     mParserAborted = true;
3899     mParser->Terminate();
3900   }
3901 }
3902 
SetDocumentURI(nsIURI * aURI)3903 void Document::SetDocumentURI(nsIURI* aURI) {
3904   nsCOMPtr<nsIURI> oldBase = GetDocBaseURI();
3905   mDocumentURI = aURI;
3906   nsIURI* newBase = GetDocBaseURI();
3907 
3908   mDocURISchemeIsChrome = aURI && IsChromeURI(aURI);
3909 
3910   bool equalBases = false;
3911   // Changing just the ref of a URI does not change how relative URIs would
3912   // resolve wrt to it, so we can treat the bases as equal as long as they're
3913   // equal ignoring the ref.
3914   if (oldBase && newBase) {
3915     oldBase->EqualsExceptRef(newBase, &equalBases);
3916   } else {
3917     equalBases = !oldBase && !newBase;
3918   }
3919 
3920   // If this is the first time we're setting the document's URI, set the
3921   // document's original URI.
3922   if (!mOriginalURI) mOriginalURI = mDocumentURI;
3923 
3924   // If changing the document's URI changed the base URI of the document, we
3925   // need to refresh the hrefs of all the links on the page.
3926   if (!equalBases) {
3927     RefreshLinkHrefs();
3928   }
3929 
3930   // Recalculate our base domain
3931   mBaseDomain.Truncate();
3932   ThirdPartyUtil* thirdPartyUtil = ThirdPartyUtil::GetInstance();
3933   if (thirdPartyUtil) {
3934     Unused << thirdPartyUtil->GetBaseDomain(mDocumentURI, mBaseDomain);
3935   }
3936 
3937   // Tell our WindowGlobalParent that the document's URI has been changed.
3938   nsPIDOMWindowInner* inner = GetInnerWindow();
3939   if (inner && inner->GetWindowGlobalChild()) {
3940     inner->GetWindowGlobalChild()->SetDocumentURI(mDocumentURI);
3941   }
3942 }
3943 
GetFormattedTimeString(PRTime aTime,nsAString & aFormattedTimeString)3944 static void GetFormattedTimeString(PRTime aTime,
3945                                    nsAString& aFormattedTimeString) {
3946   PRExplodedTime prtime;
3947   PR_ExplodeTime(aTime, PR_LocalTimeParameters, &prtime);
3948   // "MM/DD/YYYY hh:mm:ss"
3949   char formatedTime[24];
3950   if (SprintfLiteral(formatedTime, "%02d/%02d/%04d %02d:%02d:%02d",
3951                      prtime.tm_month + 1, prtime.tm_mday, int(prtime.tm_year),
3952                      prtime.tm_hour, prtime.tm_min, prtime.tm_sec)) {
3953     CopyASCIItoUTF16(nsDependentCString(formatedTime), aFormattedTimeString);
3954   } else {
3955     // If we for whatever reason failed to find the last modified time
3956     // (or even the current time), fall back to what NS4.x returned.
3957     aFormattedTimeString.AssignLiteral(u"01/01/1970 00:00:00");
3958   }
3959 }
3960 
GetLastModified(nsAString & aLastModified) const3961 void Document::GetLastModified(nsAString& aLastModified) const {
3962   if (!mLastModified.IsEmpty()) {
3963     aLastModified.Assign(mLastModified);
3964   } else {
3965     GetFormattedTimeString(PR_Now(), aLastModified);
3966   }
3967 }
3968 
IncrementExpandoGeneration(Document & aDoc)3969 static void IncrementExpandoGeneration(Document& aDoc) {
3970   ++aDoc.mExpandoAndGeneration.generation;
3971 }
3972 
AddToNameTable(Element * aElement,nsAtom * aName)3973 void Document::AddToNameTable(Element* aElement, nsAtom* aName) {
3974   MOZ_ASSERT(
3975       nsGenericHTMLElement::ShouldExposeNameAsHTMLDocumentProperty(aElement),
3976       "Only put elements that need to be exposed as document['name'] in "
3977       "the named table.");
3978 
3979   IdentifierMapEntry* entry = mIdentifierMap.PutEntry(aName);
3980 
3981   // Null for out-of-memory
3982   if (entry) {
3983     if (!entry->HasNameElement() &&
3984         !entry->HasIdElementExposedAsHTMLDocumentProperty()) {
3985       IncrementExpandoGeneration(*this);
3986     }
3987     entry->AddNameElement(this, aElement);
3988   }
3989 }
3990 
RemoveFromNameTable(Element * aElement,nsAtom * aName)3991 void Document::RemoveFromNameTable(Element* aElement, nsAtom* aName) {
3992   // Speed up document teardown
3993   if (mIdentifierMap.Count() == 0) return;
3994 
3995   IdentifierMapEntry* entry = mIdentifierMap.GetEntry(aName);
3996   if (!entry)  // Could be false if the element was anonymous, hence never added
3997     return;
3998 
3999   entry->RemoveNameElement(aElement);
4000   if (!entry->HasNameElement() &&
4001       !entry->HasIdElementExposedAsHTMLDocumentProperty()) {
4002     IncrementExpandoGeneration(*this);
4003   }
4004 }
4005 
AddToIdTable(Element * aElement,nsAtom * aId)4006 void Document::AddToIdTable(Element* aElement, nsAtom* aId) {
4007   IdentifierMapEntry* entry = mIdentifierMap.PutEntry(aId);
4008 
4009   if (entry) { /* True except on OOM */
4010     if (nsGenericHTMLElement::ShouldExposeIdAsHTMLDocumentProperty(aElement) &&
4011         !entry->HasNameElement() &&
4012         !entry->HasIdElementExposedAsHTMLDocumentProperty()) {
4013       IncrementExpandoGeneration(*this);
4014     }
4015     entry->AddIdElement(aElement);
4016   }
4017 }
4018 
RemoveFromIdTable(Element * aElement,nsAtom * aId)4019 void Document::RemoveFromIdTable(Element* aElement, nsAtom* aId) {
4020   NS_ASSERTION(aId, "huhwhatnow?");
4021 
4022   // Speed up document teardown
4023   if (mIdentifierMap.Count() == 0) {
4024     return;
4025   }
4026 
4027   IdentifierMapEntry* entry = mIdentifierMap.GetEntry(aId);
4028   if (!entry)  // Can be null for XML elements with changing ids.
4029     return;
4030 
4031   entry->RemoveIdElement(aElement);
4032   if (nsGenericHTMLElement::ShouldExposeIdAsHTMLDocumentProperty(aElement) &&
4033       !entry->HasNameElement() &&
4034       !entry->HasIdElementExposedAsHTMLDocumentProperty()) {
4035     IncrementExpandoGeneration(*this);
4036   }
4037   if (entry->IsEmpty()) {
4038     mIdentifierMap.RemoveEntry(entry);
4039   }
4040 }
4041 
UpdateReferrerInfoFromMeta(const nsAString & aMetaReferrer,bool aPreload)4042 void Document::UpdateReferrerInfoFromMeta(const nsAString& aMetaReferrer,
4043                                           bool aPreload) {
4044   ReferrerPolicyEnum policy =
4045       ReferrerInfo::ReferrerPolicyFromMetaString(aMetaReferrer);
4046   // The empty string "" corresponds to no referrer policy, causing a fallback
4047   // to a referrer policy defined elsewhere.
4048   if (policy == ReferrerPolicy::_empty) {
4049     return;
4050   }
4051 
4052   MOZ_ASSERT(mReferrerInfo);
4053   MOZ_ASSERT(mPreloadReferrerInfo);
4054 
4055   if (aPreload) {
4056     mPreloadReferrerInfo =
4057         static_cast<mozilla::dom::ReferrerInfo*>((mPreloadReferrerInfo).get())
4058             ->CloneWithNewPolicy(policy);
4059   } else {
4060     mReferrerInfo =
4061         static_cast<mozilla::dom::ReferrerInfo*>((mReferrerInfo).get())
4062             ->CloneWithNewPolicy(policy);
4063   }
4064 }
4065 
SetPrincipals(nsIPrincipal * aNewPrincipal,nsIPrincipal * aNewPartitionedPrincipal)4066 void Document::SetPrincipals(nsIPrincipal* aNewPrincipal,
4067                              nsIPrincipal* aNewPartitionedPrincipal) {
4068   MOZ_ASSERT(!!aNewPrincipal == !!aNewPartitionedPrincipal);
4069   if (aNewPrincipal && mAllowDNSPrefetch &&
4070       StaticPrefs::network_dns_disablePrefetchFromHTTPS()) {
4071     if (aNewPrincipal->SchemeIs("https")) {
4072       mAllowDNSPrefetch = false;
4073     }
4074   }
4075 
4076   mCSSLoader->DeregisterFromSheetCache();
4077 
4078   mNodeInfoManager->SetDocumentPrincipal(aNewPrincipal);
4079   mPartitionedPrincipal = aNewPartitionedPrincipal;
4080 
4081   mCSSLoader->RegisterInSheetCache();
4082 
4083 #ifdef DEBUG
4084   // Validate that the docgroup is set correctly by calling its getter and
4085   // triggering its sanity check.
4086   //
4087   // If we're setting the principal to null, we don't want to perform the check,
4088   // as the document is entering an intermediate state where it does not have a
4089   // principal. It will be given another real principal shortly which we will
4090   // check. It's not unsafe to have a document which has a null principal in the
4091   // same docgroup as another document, so this should not be a problem.
4092   if (aNewPrincipal) {
4093     GetDocGroup();
4094   }
4095 #endif
4096 }
4097 
4098 #ifdef DEBUG
AssertDocGroupMatchesKey() const4099 void Document::AssertDocGroupMatchesKey() const {
4100   // Sanity check that we have an up-to-date and accurate docgroup
4101   // We only check if the principal when we can get the browsing context.
4102   if (!GetBrowsingContext()) {
4103     return;
4104   }
4105 
4106   if (mDocGroup) {
4107     nsAutoCString docGroupKey;
4108 
4109     // GetKey() can fail, e.g. after the TLD service has shut down.
4110     nsresult rv = mozilla::dom::DocGroup::GetKey(
4111         NodePrincipal(), CrossOriginIsolated(), docGroupKey);
4112     if (NS_SUCCEEDED(rv)) {
4113       MOZ_ASSERT(mDocGroup->MatchesKey(docGroupKey));
4114     }
4115   }
4116 }
4117 #endif
4118 
Dispatch(TaskCategory aCategory,already_AddRefed<nsIRunnable> && aRunnable)4119 nsresult Document::Dispatch(TaskCategory aCategory,
4120                             already_AddRefed<nsIRunnable>&& aRunnable) {
4121   // Note that this method may be called off the main thread.
4122   if (mDocGroup) {
4123     return mDocGroup->Dispatch(aCategory, std::move(aRunnable));
4124   }
4125   return DispatcherTrait::Dispatch(aCategory, std::move(aRunnable));
4126 }
4127 
EventTargetFor(TaskCategory aCategory) const4128 nsISerialEventTarget* Document::EventTargetFor(TaskCategory aCategory) const {
4129   if (mDocGroup) {
4130     return mDocGroup->EventTargetFor(aCategory);
4131   }
4132   return DispatcherTrait::EventTargetFor(aCategory);
4133 }
4134 
AbstractMainThreadFor(mozilla::TaskCategory aCategory)4135 AbstractThread* Document::AbstractMainThreadFor(
4136     mozilla::TaskCategory aCategory) {
4137   MOZ_ASSERT(NS_IsMainThread());
4138   if (mDocGroup) {
4139     return mDocGroup->AbstractMainThreadFor(aCategory);
4140   }
4141   return DispatcherTrait::AbstractMainThreadFor(aCategory);
4142 }
4143 
NoteScriptTrackingStatus(const nsACString & aURL,bool aIsTracking)4144 void Document::NoteScriptTrackingStatus(const nsACString& aURL,
4145                                         bool aIsTracking) {
4146   if (aIsTracking) {
4147     mTrackingScripts.Insert(aURL);
4148   } else {
4149     MOZ_ASSERT(!mTrackingScripts.Contains(aURL));
4150   }
4151 }
4152 
IsScriptTracking(JSContext * aCx) const4153 bool Document::IsScriptTracking(JSContext* aCx) const {
4154   JS::AutoFilename filename;
4155   uint32_t line = 0;
4156   uint32_t column = 0;
4157   if (!JS::DescribeScriptedCaller(aCx, &filename, &line, &column)) {
4158     return false;
4159   }
4160   return mTrackingScripts.Contains(nsDependentCString(filename.get()));
4161 }
4162 
GetContentType(nsAString & aContentType)4163 void Document::GetContentType(nsAString& aContentType) {
4164   CopyUTF8toUTF16(GetContentTypeInternal(), aContentType);
4165 }
4166 
SetContentType(const nsACString & aContentType)4167 void Document::SetContentType(const nsACString& aContentType) {
4168   if (!IsHTMLOrXHTML() && mDefaultElementType == kNameSpaceID_None &&
4169       aContentType.EqualsLiteral("application/xhtml+xml")) {
4170     mDefaultElementType = kNameSpaceID_XHTML;
4171   }
4172 
4173   mCachedEncoder = nullptr;
4174   mContentType = aContentType;
4175 }
4176 
GetAllowPlugins()4177 bool Document::GetAllowPlugins() {
4178   // First, we ask our docshell if it allows plugins.
4179   auto* browsingContext = GetBrowsingContext();
4180 
4181   if (browsingContext) {
4182     if (!browsingContext->GetAllowPlugins()) {
4183       return false;
4184     }
4185 
4186     // If the docshell allows plugins, we check whether
4187     // we are sandboxed and plugins should not be allowed.
4188     if (mSandboxFlags & SANDBOXED_PLUGINS) {
4189       return false;
4190     }
4191   }
4192 
4193   FlashClassification classification = DocumentFlashClassification();
4194   if (classification == FlashClassification::Denied) {
4195     return false;
4196   }
4197 
4198   return true;
4199 }
4200 
HasPendingInitialTranslation()4201 bool Document::HasPendingInitialTranslation() {
4202   return mDocumentL10n && mDocumentL10n->GetState() != DocumentL10nState::Ready;
4203 }
4204 
GetL10n()4205 DocumentL10n* Document::GetL10n() { return mDocumentL10n; }
4206 
DocumentSupportsL10n(JSContext * aCx,JSObject * aObject)4207 bool Document::DocumentSupportsL10n(JSContext* aCx, JSObject* aObject) {
4208   JS::Rooted<JSObject*> object(aCx, aObject);
4209   nsCOMPtr<nsIPrincipal> callerPrincipal =
4210       nsContentUtils::SubjectPrincipal(aCx);
4211   nsGlobalWindowInner* win = xpc::WindowOrNull(object);
4212   bool allowed = false;
4213   callerPrincipal->IsL10nAllowed(win ? win->GetDocumentURI() : nullptr,
4214                                  &allowed);
4215   return allowed;
4216 }
4217 
LocalizationLinkAdded(Element * aLinkElement)4218 void Document::LocalizationLinkAdded(Element* aLinkElement) {
4219   if (!AllowsL10n()) {
4220     return;
4221   }
4222 
4223   nsAutoString href;
4224   aLinkElement->GetAttr(kNameSpaceID_None, nsGkAtoms::href, href);
4225 
4226   if (!mDocumentL10n) {
4227     Element* elem = GetDocumentElement();
4228     MOZ_DIAGNOSTIC_ASSERT(elem);
4229 
4230     bool isSync = elem->HasAttr(nsGkAtoms::datal10nsync);
4231     mDocumentL10n = DocumentL10n::Create(this, isSync);
4232     if (NS_WARN_IF(!mDocumentL10n)) {
4233       return;
4234     }
4235   }
4236 
4237   mDocumentL10n->AddResourceId(NS_ConvertUTF16toUTF8(href));
4238 
4239   if (mReadyState >= READYSTATE_INTERACTIVE) {
4240     nsContentUtils::AddScriptRunner(NewRunnableMethod(
4241         "DocumentL10n::TriggerInitialTranslation()", mDocumentL10n,
4242         &DocumentL10n::TriggerInitialTranslation));
4243   } else {
4244     if (!mDocumentL10n->mBlockingLayout) {
4245       // Our initial translation is going to block layout start.  Make sure
4246       // we don't fire the load event until after that stops happening and
4247       // layout has a chance to start.
4248       BlockOnload();
4249       mDocumentL10n->mBlockingLayout = true;
4250     }
4251   }
4252 }
4253 
LocalizationLinkRemoved(Element * aLinkElement)4254 void Document::LocalizationLinkRemoved(Element* aLinkElement) {
4255   if (!AllowsL10n()) {
4256     return;
4257   }
4258 
4259   if (mDocumentL10n) {
4260     nsAutoString href;
4261     aLinkElement->GetAttr(kNameSpaceID_None, nsGkAtoms::href, href);
4262     uint32_t remaining =
4263         mDocumentL10n->RemoveResourceId(NS_ConvertUTF16toUTF8(href));
4264     if (remaining == 0) {
4265       if (mDocumentL10n->mBlockingLayout) {
4266         mDocumentL10n->mBlockingLayout = false;
4267         UnblockOnload(/* aFireSync = */ false);
4268       }
4269       mDocumentL10n = nullptr;
4270     }
4271   }
4272 }
4273 
4274 /**
4275  * This method should be called once the end of the l10n
4276  * resource container has been parsed.
4277  *
4278  * In XUL this is the end of the first </linkset>,
4279  * In XHTML/HTML this is the end of </head>.
4280  *
4281  * This milestone is used to allow for batch
4282  * localization context I/O and building done
4283  * once when all resources in the document have been
4284  * collected.
4285  */
OnL10nResourceContainerParsed()4286 void Document::OnL10nResourceContainerParsed() {
4287   // XXX: This is a scaffolding for where we might inject prefetch
4288   // in bug 1717241.
4289 }
4290 
OnParsingCompleted()4291 void Document::OnParsingCompleted() {
4292   // Let's call it again, in case the resource
4293   // container has not been closed, and only
4294   // now we're closing the document.
4295   OnL10nResourceContainerParsed();
4296 
4297   if (mDocumentL10n) {
4298     RefPtr<DocumentL10n> l10n = mDocumentL10n;
4299     l10n->TriggerInitialTranslation();
4300   }
4301 }
4302 
InitialTranslationCompleted(bool aL10nCached)4303 void Document::InitialTranslationCompleted(bool aL10nCached) {
4304   if (mDocumentL10n && mDocumentL10n->mBlockingLayout) {
4305     // This means we blocked the load event in LocalizationLinkAdded.  It's
4306     // important that the load blocker removal here be async, because our caller
4307     // will notify the content sink after us, and we want the content sync's
4308     // work to happen before the load event fires.
4309     mDocumentL10n->mBlockingLayout = false;
4310     UnblockOnload(/* aFireSync = */ false);
4311   }
4312 
4313   mL10nProtoElements.Clear();
4314 
4315   nsXULPrototypeDocument* proto = GetPrototype();
4316   if (proto) {
4317     proto->SetIsL10nCached(aL10nCached);
4318   }
4319 }
4320 
AllowsL10n() const4321 bool Document::AllowsL10n() const {
4322   if (IsStaticDocument()) {
4323     // We don't allow l10n on static documents, because the nodes are already
4324     // cloned translated, and static docs don't get parsed so we never
4325     // TriggerInitialTranslation, etc, so a load blocker would keep hanging
4326     // forever.
4327     return false;
4328   }
4329   bool allowed = false;
4330   NodePrincipal()->IsL10nAllowed(GetDocumentURI(), &allowed);
4331   return allowed;
4332 }
4333 
IsWebAnimationsEnabled(JSContext * aCx,JSObject *)4334 bool Document::IsWebAnimationsEnabled(JSContext* aCx, JSObject* /*unused*/) {
4335   MOZ_ASSERT(NS_IsMainThread());
4336 
4337   return nsContentUtils::IsSystemCaller(aCx) ||
4338          StaticPrefs::dom_animations_api_core_enabled();
4339 }
4340 
IsWebAnimationsEnabled(CallerType aCallerType)4341 bool Document::IsWebAnimationsEnabled(CallerType aCallerType) {
4342   MOZ_ASSERT(NS_IsMainThread());
4343 
4344   return aCallerType == dom::CallerType::System ||
4345          StaticPrefs::dom_animations_api_core_enabled();
4346 }
4347 
IsWebAnimationsGetAnimationsEnabled(JSContext * aCx,JSObject *)4348 bool Document::IsWebAnimationsGetAnimationsEnabled(JSContext* aCx,
4349                                                    JSObject* /*unused*/
4350 ) {
4351   MOZ_ASSERT(NS_IsMainThread());
4352 
4353   return nsContentUtils::IsSystemCaller(aCx) ||
4354          StaticPrefs::dom_animations_api_getAnimations_enabled();
4355 }
4356 
AreWebAnimationsImplicitKeyframesEnabled(JSContext * aCx,JSObject *)4357 bool Document::AreWebAnimationsImplicitKeyframesEnabled(JSContext* aCx,
4358                                                         JSObject* /*unused*/
4359 ) {
4360   MOZ_ASSERT(NS_IsMainThread());
4361 
4362   return nsContentUtils::IsSystemCaller(aCx) ||
4363          StaticPrefs::dom_animations_api_implicit_keyframes_enabled();
4364 }
4365 
AreWebAnimationsTimelinesEnabled(JSContext * aCx,JSObject *)4366 bool Document::AreWebAnimationsTimelinesEnabled(JSContext* aCx,
4367                                                 JSObject* /*unused*/
4368 ) {
4369   MOZ_ASSERT(NS_IsMainThread());
4370 
4371   return nsContentUtils::IsSystemCaller(aCx) ||
4372          StaticPrefs::dom_animations_api_timelines_enabled();
4373 }
4374 
Timeline()4375 DocumentTimeline* Document::Timeline() {
4376   if (!mDocumentTimeline) {
4377     mDocumentTimeline = new DocumentTimeline(this, TimeDuration(0));
4378   }
4379 
4380   return mDocumentTimeline;
4381 }
4382 
GetSVGRootElement() const4383 SVGSVGElement* Document::GetSVGRootElement() const {
4384   Element* root = GetRootElement();
4385   if (!root || !root->IsSVGElement(nsGkAtoms::svg)) {
4386     return nullptr;
4387   }
4388   return static_cast<SVGSVGElement*>(root);
4389 }
4390 
4391 /* Return true if the document is in the focused top-level window, and is an
4392  * ancestor of the focused DOMWindow. */
HasFocus(ErrorResult & rv) const4393 bool Document::HasFocus(ErrorResult& rv) const {
4394   nsFocusManager* fm = nsFocusManager::GetFocusManager();
4395   if (!fm) {
4396     rv.Throw(NS_ERROR_NOT_AVAILABLE);
4397     return false;
4398   }
4399 
4400   BrowsingContext* bc = GetBrowsingContext();
4401   if (!bc) {
4402     return false;
4403   }
4404 
4405   if (!fm->IsInActiveWindow(bc)) {
4406     return false;
4407   }
4408 
4409   return fm->IsSameOrAncestor(bc, fm->GetFocusedBrowsingContext());
4410 }
4411 
GetDesignMode(nsAString & aDesignMode)4412 void Document::GetDesignMode(nsAString& aDesignMode) {
4413   if (IsInDesignMode()) {
4414     aDesignMode.AssignLiteral("on");
4415   } else {
4416     aDesignMode.AssignLiteral("off");
4417   }
4418 }
4419 
SetDesignMode(const nsAString & aDesignMode,nsIPrincipal & aSubjectPrincipal,ErrorResult & rv)4420 void Document::SetDesignMode(const nsAString& aDesignMode,
4421                              nsIPrincipal& aSubjectPrincipal, ErrorResult& rv) {
4422   SetDesignMode(aDesignMode, Some(&aSubjectPrincipal), rv);
4423 }
4424 
NotifyEditableStateChange(Document & aDoc)4425 static void NotifyEditableStateChange(Document& aDoc) {
4426 #ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
4427   nsMutationGuard g;
4428 #endif
4429   for (nsIContent* node = aDoc.GetNextNode(&aDoc); node;
4430        node = node->GetNextNode(&aDoc)) {
4431     if (auto* element = Element::FromNode(node)) {
4432       element->UpdateState(true);
4433     }
4434   }
4435   MOZ_DIAGNOSTIC_ASSERT(!g.Mutated(0));
4436 }
4437 
SetDesignMode(const nsAString & aDesignMode,const Maybe<nsIPrincipal * > & aSubjectPrincipal,ErrorResult & rv)4438 void Document::SetDesignMode(const nsAString& aDesignMode,
4439                              const Maybe<nsIPrincipal*>& aSubjectPrincipal,
4440                              ErrorResult& rv) {
4441   if (aSubjectPrincipal.isSome() &&
4442       !aSubjectPrincipal.value()->Subsumes(NodePrincipal())) {
4443     rv.Throw(NS_ERROR_DOM_PROP_ACCESS_DENIED);
4444     return;
4445   }
4446   const bool editableMode = IsInDesignMode();
4447   if (aDesignMode.LowerCaseEqualsASCII(editableMode ? "off" : "on")) {
4448     SetEditableFlag(!editableMode);
4449     // Changing the NODE_IS_EDITABLE flags on document changes the intrinsic
4450     // state of all descendant elements of it. Update that now.
4451     NotifyEditableStateChange(*this);
4452     rv = EditingStateChanged();
4453   }
4454 }
4455 
GetMidasCommandManager()4456 nsCommandManager* Document::GetMidasCommandManager() {
4457   // check if we have it cached
4458   if (mMidasCommandManager) {
4459     return mMidasCommandManager;
4460   }
4461 
4462   nsPIDOMWindowOuter* window = GetWindow();
4463   if (!window) {
4464     return nullptr;
4465   }
4466 
4467   nsIDocShell* docshell = window->GetDocShell();
4468   if (!docshell) {
4469     return nullptr;
4470   }
4471 
4472   mMidasCommandManager = docshell->GetCommandManager();
4473   return mMidasCommandManager;
4474 }
4475 
4476 // static
EnsureInitializeInternalCommandDataHashtable()4477 void Document::EnsureInitializeInternalCommandDataHashtable() {
4478   if (sInternalCommandDataHashtable) {
4479     return;
4480   }
4481   using CommandOnTextEditor = InternalCommandData::CommandOnTextEditor;
4482   sInternalCommandDataHashtable = new InternalCommandDataHashtable();
4483   // clang-format off
4484   sInternalCommandDataHashtable->InsertOrUpdate(
4485       u"bold"_ns,
4486       InternalCommandData(
4487           "cmd_bold",
4488           Command::FormatBold,
4489           ExecCommandParam::Ignore,
4490           StyleUpdatingCommand::GetInstance,
4491           CommandOnTextEditor::Disabled));
4492   sInternalCommandDataHashtable->InsertOrUpdate(
4493       u"italic"_ns,
4494       InternalCommandData(
4495           "cmd_italic",
4496           Command::FormatItalic,
4497           ExecCommandParam::Ignore,
4498           StyleUpdatingCommand::GetInstance,
4499           CommandOnTextEditor::Disabled));
4500   sInternalCommandDataHashtable->InsertOrUpdate(
4501       u"underline"_ns,
4502       InternalCommandData(
4503           "cmd_underline",
4504           Command::FormatUnderline,
4505           ExecCommandParam::Ignore,
4506           StyleUpdatingCommand::GetInstance,
4507           CommandOnTextEditor::Disabled));
4508   sInternalCommandDataHashtable->InsertOrUpdate(
4509       u"strikethrough"_ns,
4510       InternalCommandData(
4511           "cmd_strikethrough",
4512           Command::FormatStrikeThrough,
4513           ExecCommandParam::Ignore,
4514           StyleUpdatingCommand::GetInstance,
4515           CommandOnTextEditor::Disabled));
4516   sInternalCommandDataHashtable->InsertOrUpdate(
4517       u"subscript"_ns,
4518       InternalCommandData(
4519           "cmd_subscript",
4520           Command::FormatSubscript,
4521           ExecCommandParam::Ignore,
4522           StyleUpdatingCommand::GetInstance,
4523           CommandOnTextEditor::Disabled));
4524   sInternalCommandDataHashtable->InsertOrUpdate(
4525       u"superscript"_ns,
4526       InternalCommandData(
4527           "cmd_superscript",
4528           Command::FormatSuperscript,
4529           ExecCommandParam::Ignore,
4530           StyleUpdatingCommand::GetInstance,
4531           CommandOnTextEditor::Disabled));
4532   sInternalCommandDataHashtable->InsertOrUpdate(
4533       u"cut"_ns,
4534       InternalCommandData(
4535           "cmd_cut",
4536           Command::Cut,
4537           ExecCommandParam::Ignore,
4538           CutCommand::GetInstance,
4539           CommandOnTextEditor::Enabled));
4540   sInternalCommandDataHashtable->InsertOrUpdate(
4541       u"copy"_ns,
4542       InternalCommandData(
4543           "cmd_copy",
4544           Command::Copy,
4545           ExecCommandParam::Ignore,
4546           CopyCommand::GetInstance,
4547           CommandOnTextEditor::Enabled));
4548   sInternalCommandDataHashtable->InsertOrUpdate(
4549       u"paste"_ns,
4550       InternalCommandData(
4551           "cmd_paste",
4552           Command::Paste,
4553           ExecCommandParam::Ignore,
4554           PasteCommand::GetInstance,
4555           CommandOnTextEditor::Enabled));
4556   sInternalCommandDataHashtable->InsertOrUpdate(
4557       u"delete"_ns,
4558       InternalCommandData(
4559           "cmd_deleteCharBackward",
4560           Command::DeleteCharBackward,
4561           ExecCommandParam::Ignore,
4562           DeleteCommand::GetInstance,
4563           CommandOnTextEditor::Enabled));
4564   sInternalCommandDataHashtable->InsertOrUpdate(
4565       u"forwarddelete"_ns,
4566       InternalCommandData(
4567           "cmd_deleteCharForward",
4568           Command::DeleteCharForward,
4569           ExecCommandParam::Ignore,
4570           DeleteCommand::GetInstance,
4571           CommandOnTextEditor::Enabled));
4572   sInternalCommandDataHashtable->InsertOrUpdate(
4573       u"selectall"_ns,
4574       InternalCommandData(
4575           "cmd_selectAll",
4576           Command::SelectAll,
4577           ExecCommandParam::Ignore,
4578           SelectAllCommand::GetInstance,
4579           CommandOnTextEditor::Enabled));
4580   sInternalCommandDataHashtable->InsertOrUpdate(
4581       u"undo"_ns,
4582       InternalCommandData(
4583           "cmd_undo",
4584           Command::HistoryUndo,
4585           ExecCommandParam::Ignore,
4586           UndoCommand::GetInstance,
4587           CommandOnTextEditor::Enabled));
4588   sInternalCommandDataHashtable->InsertOrUpdate(
4589       u"redo"_ns,
4590       InternalCommandData(
4591           "cmd_redo",
4592           Command::HistoryRedo,
4593           ExecCommandParam::Ignore,
4594           RedoCommand::GetInstance,
4595           CommandOnTextEditor::Enabled));
4596   sInternalCommandDataHashtable->InsertOrUpdate(
4597       u"indent"_ns,
4598       InternalCommandData("cmd_indent",
4599           Command::FormatIndent,
4600           ExecCommandParam::Ignore,
4601           IndentCommand::GetInstance,
4602           CommandOnTextEditor::Disabled));
4603   sInternalCommandDataHashtable->InsertOrUpdate(
4604       u"outdent"_ns,
4605       InternalCommandData(
4606           "cmd_outdent",
4607           Command::FormatOutdent,
4608           ExecCommandParam::Ignore,
4609           OutdentCommand::GetInstance,
4610           CommandOnTextEditor::Disabled));
4611   sInternalCommandDataHashtable->InsertOrUpdate(
4612       u"backcolor"_ns,
4613       InternalCommandData(
4614           "cmd_highlight",
4615           Command::FormatBackColor,
4616           ExecCommandParam::String,
4617           HighlightColorStateCommand::GetInstance,
4618           CommandOnTextEditor::Disabled));
4619   sInternalCommandDataHashtable->InsertOrUpdate(
4620       u"hilitecolor"_ns,
4621       InternalCommandData(
4622           "cmd_highlight",
4623           Command::FormatBackColor,
4624           ExecCommandParam::String,
4625           HighlightColorStateCommand::GetInstance,
4626           CommandOnTextEditor::Disabled));
4627   sInternalCommandDataHashtable->InsertOrUpdate(
4628       u"forecolor"_ns,
4629       InternalCommandData(
4630           "cmd_fontColor",
4631           Command::FormatFontColor,
4632           ExecCommandParam::String,
4633           FontColorStateCommand::GetInstance,
4634           CommandOnTextEditor::Disabled));
4635   sInternalCommandDataHashtable->InsertOrUpdate(
4636       u"fontname"_ns,
4637       InternalCommandData(
4638           "cmd_fontFace",
4639           Command::FormatFontName,
4640           ExecCommandParam::String,
4641           FontFaceStateCommand::GetInstance,
4642           CommandOnTextEditor::Disabled));
4643   sInternalCommandDataHashtable->InsertOrUpdate(
4644       u"fontsize"_ns,
4645       InternalCommandData(
4646           "cmd_fontSize",
4647           Command::FormatFontSize,
4648           ExecCommandParam::String,
4649           FontSizeStateCommand::GetInstance,
4650           CommandOnTextEditor::Disabled));
4651   sInternalCommandDataHashtable->InsertOrUpdate(
4652       u"increasefontsize"_ns,
4653       InternalCommandData(
4654           "cmd_increaseFont",
4655           Command::FormatIncreaseFontSize,
4656           ExecCommandParam::Ignore,
4657           IncreaseFontSizeCommand::GetInstance,
4658           CommandOnTextEditor::Disabled));
4659   sInternalCommandDataHashtable->InsertOrUpdate(
4660       u"decreasefontsize"_ns,
4661       InternalCommandData(
4662           "cmd_decreaseFont",
4663           Command::FormatDecreaseFontSize,
4664           ExecCommandParam::Ignore,
4665           DecreaseFontSizeCommand::GetInstance,
4666           CommandOnTextEditor::Disabled));
4667   sInternalCommandDataHashtable->InsertOrUpdate(
4668       u"inserthorizontalrule"_ns,
4669       InternalCommandData(
4670           "cmd_insertHR",
4671           Command::InsertHorizontalRule,
4672           ExecCommandParam::Ignore,
4673           InsertTagCommand::GetInstance,
4674           CommandOnTextEditor::Disabled));
4675   sInternalCommandDataHashtable->InsertOrUpdate(
4676       u"createlink"_ns,
4677       InternalCommandData(
4678           "cmd_insertLinkNoUI",
4679           Command::InsertLink,
4680           ExecCommandParam::String,
4681           InsertTagCommand::GetInstance,
4682           CommandOnTextEditor::Disabled));
4683   sInternalCommandDataHashtable->InsertOrUpdate(
4684       u"insertimage"_ns,
4685       InternalCommandData(
4686           "cmd_insertImageNoUI",
4687           Command::InsertImage,
4688           ExecCommandParam::String,
4689           InsertTagCommand::GetInstance,
4690           CommandOnTextEditor::Disabled));
4691   sInternalCommandDataHashtable->InsertOrUpdate(
4692       u"inserthtml"_ns,
4693       InternalCommandData(
4694           "cmd_insertHTML",
4695           Command::InsertHTML,
4696           ExecCommandParam::String,
4697           InsertHTMLCommand::GetInstance,
4698           // TODO: Chromium inserts text content of the document fragment
4699           //       created from the param.
4700           //       https://source.chromium.org/chromium/chromium/src/+/master:third_party/blink/renderer/core/editing/commands/insert_commands.cc;l=105;drc=a4708b724062f17824815b896c3aaa43825128f8
4701           CommandOnTextEditor::Disabled));
4702   sInternalCommandDataHashtable->InsertOrUpdate(
4703       u"inserttext"_ns,
4704       InternalCommandData(
4705           "cmd_insertText",
4706           Command::InsertText,
4707           ExecCommandParam::String,
4708           InsertPlaintextCommand::GetInstance,
4709           CommandOnTextEditor::Enabled));
4710   sInternalCommandDataHashtable->InsertOrUpdate(
4711       u"gethtml"_ns,
4712       InternalCommandData(
4713           "cmd_getContents",
4714           Command::GetHTML,
4715           ExecCommandParam::Ignore,
4716           nullptr,  // Not defined in EditorCommands.h
4717           // getHTML command is not supported by Chromium, and we return HTML
4718           // source code at selected range.  So, let's return selected text
4719           // when `<input>` or `<textarea>` has focus.
4720           CommandOnTextEditor::Enabled));
4721   sInternalCommandDataHashtable->InsertOrUpdate(
4722       u"justifyleft"_ns,
4723       InternalCommandData(
4724           "cmd_align",
4725           Command::FormatJustifyLeft,
4726           ExecCommandParam::Ignore,  // Will be set to "left"
4727           AlignCommand::GetInstance,
4728           CommandOnTextEditor::Disabled));
4729   sInternalCommandDataHashtable->InsertOrUpdate(
4730       u"justifyright"_ns,
4731       InternalCommandData(
4732           "cmd_align",
4733           Command::FormatJustifyRight,
4734           ExecCommandParam::Ignore,  // Will be set to "right"
4735           AlignCommand::GetInstance,
4736           CommandOnTextEditor::Disabled));
4737   sInternalCommandDataHashtable->InsertOrUpdate(
4738       u"justifycenter"_ns,
4739       InternalCommandData(
4740           "cmd_align",
4741           Command::FormatJustifyCenter,
4742           ExecCommandParam::Ignore,  // Will be set to "center"
4743           AlignCommand::GetInstance,
4744           CommandOnTextEditor::Disabled));
4745   sInternalCommandDataHashtable->InsertOrUpdate(
4746       u"justifyfull"_ns,
4747       InternalCommandData(
4748           "cmd_align",
4749           Command::FormatJustifyFull,
4750           ExecCommandParam::Ignore,  // Will be set to "justify"
4751           AlignCommand::GetInstance,
4752           CommandOnTextEditor::Disabled));
4753   sInternalCommandDataHashtable->InsertOrUpdate(
4754       u"removeformat"_ns,
4755       InternalCommandData(
4756           "cmd_removeStyles",
4757           Command::FormatRemove,
4758           ExecCommandParam::Ignore,
4759           RemoveStylesCommand::GetInstance,
4760           CommandOnTextEditor::Disabled));
4761   sInternalCommandDataHashtable->InsertOrUpdate(
4762       u"unlink"_ns,
4763       InternalCommandData(
4764           "cmd_removeLinks",
4765           Command::FormatRemoveLink,
4766           ExecCommandParam::Ignore,
4767           StyleUpdatingCommand::GetInstance,
4768           CommandOnTextEditor::Disabled));
4769   sInternalCommandDataHashtable->InsertOrUpdate(
4770       u"insertorderedlist"_ns,
4771       InternalCommandData(
4772           "cmd_ol",
4773           Command::InsertOrderedList,
4774           ExecCommandParam::Ignore,
4775           ListCommand::GetInstance,
4776           CommandOnTextEditor::Disabled));
4777   sInternalCommandDataHashtable->InsertOrUpdate(
4778       u"insertunorderedlist"_ns,
4779       InternalCommandData(
4780           "cmd_ul",
4781           Command::InsertUnorderedList,
4782           ExecCommandParam::Ignore,
4783           ListCommand::GetInstance,
4784           CommandOnTextEditor::Disabled));
4785   sInternalCommandDataHashtable->InsertOrUpdate(
4786       u"insertparagraph"_ns,
4787       InternalCommandData(
4788           "cmd_insertParagraph",
4789           Command::InsertParagraph,
4790           ExecCommandParam::Ignore,
4791           InsertParagraphCommand::GetInstance,
4792           CommandOnTextEditor::Enabled));
4793   sInternalCommandDataHashtable->InsertOrUpdate(
4794       u"insertlinebreak"_ns,
4795       InternalCommandData(
4796           "cmd_insertLineBreak",
4797           Command::InsertLineBreak,
4798           ExecCommandParam::Ignore,
4799           InsertLineBreakCommand::GetInstance,
4800           CommandOnTextEditor::Enabled));
4801   sInternalCommandDataHashtable->InsertOrUpdate(
4802       u"formatblock"_ns,
4803       InternalCommandData(
4804           "cmd_paragraphState",
4805           Command::FormatBlock,
4806           ExecCommandParam::String,
4807           ParagraphStateCommand::GetInstance,
4808           CommandOnTextEditor::Disabled));
4809   sInternalCommandDataHashtable->InsertOrUpdate(
4810       u"heading"_ns,
4811       InternalCommandData(
4812           "cmd_paragraphState",
4813           Command::FormatBlock,
4814           ExecCommandParam::String,
4815           ParagraphStateCommand::GetInstance,
4816           CommandOnTextEditor::Disabled));
4817   sInternalCommandDataHashtable->InsertOrUpdate(
4818       u"styleWithCSS"_ns,
4819       InternalCommandData(
4820           "cmd_setDocumentUseCSS",
4821           Command::SetDocumentUseCSS,
4822           ExecCommandParam::Boolean,
4823           SetDocumentStateCommand::GetInstance,
4824           CommandOnTextEditor::FallThrough));
4825   sInternalCommandDataHashtable->InsertOrUpdate(
4826       u"usecss"_ns,  // Legacy command
4827       InternalCommandData(
4828           "cmd_setDocumentUseCSS",
4829           Command::SetDocumentUseCSS,
4830           ExecCommandParam::InvertedBoolean,
4831           SetDocumentStateCommand::GetInstance,
4832           CommandOnTextEditor::FallThrough));
4833   sInternalCommandDataHashtable->InsertOrUpdate(
4834       u"contentReadOnly"_ns,
4835       InternalCommandData(
4836           "cmd_setDocumentReadOnly",
4837           Command::SetDocumentReadOnly,
4838           ExecCommandParam::Boolean,
4839           SetDocumentStateCommand::GetInstance,
4840           CommandOnTextEditor::Enabled));
4841   sInternalCommandDataHashtable->InsertOrUpdate(
4842       u"readonly"_ns,  // Legacy command
4843       InternalCommandData(
4844           "cmd_setDocumentReadOnly",
4845           Command::SetDocumentReadOnly,
4846           ExecCommandParam::InvertedBoolean,
4847           SetDocumentStateCommand::GetInstance,
4848           CommandOnTextEditor::Enabled));
4849   sInternalCommandDataHashtable->InsertOrUpdate(
4850       u"insertBrOnReturn"_ns,
4851       InternalCommandData(
4852           "cmd_insertBrOnReturn",
4853           Command::SetDocumentInsertBROnEnterKeyPress,
4854           ExecCommandParam::Boolean,
4855           SetDocumentStateCommand::GetInstance,
4856           CommandOnTextEditor::FallThrough));
4857   sInternalCommandDataHashtable->InsertOrUpdate(
4858       u"defaultParagraphSeparator"_ns,
4859       InternalCommandData(
4860           "cmd_defaultParagraphSeparator",
4861           Command::SetDocumentDefaultParagraphSeparator,
4862           ExecCommandParam::String,
4863           SetDocumentStateCommand::GetInstance,
4864           CommandOnTextEditor::FallThrough));
4865   sInternalCommandDataHashtable->InsertOrUpdate(
4866       u"enableObjectResizing"_ns,
4867       InternalCommandData(
4868           "cmd_enableObjectResizing",
4869           Command::ToggleObjectResizers,
4870           ExecCommandParam::Boolean,
4871           SetDocumentStateCommand::GetInstance,
4872           CommandOnTextEditor::FallThrough));
4873   sInternalCommandDataHashtable->InsertOrUpdate(
4874       u"enableInlineTableEditing"_ns,
4875       InternalCommandData(
4876           "cmd_enableInlineTableEditing",
4877           Command::ToggleInlineTableEditor,
4878           ExecCommandParam::Boolean,
4879           SetDocumentStateCommand::GetInstance,
4880           CommandOnTextEditor::FallThrough));
4881   sInternalCommandDataHashtable->InsertOrUpdate(
4882       u"enableAbsolutePositionEditing"_ns,
4883       InternalCommandData(
4884           "cmd_enableAbsolutePositionEditing",
4885           Command::ToggleAbsolutePositionEditor,
4886           ExecCommandParam::Boolean,
4887           SetDocumentStateCommand::GetInstance,
4888           CommandOnTextEditor::FallThrough));
4889 #if 0
4890   // with empty string
4891   sInternalCommandDataHashtable->InsertOrUpdate(
4892       u"justifynone"_ns,
4893       InternalCommandData(
4894           "cmd_align",
4895           Command::Undefined,
4896           ExecCommandParam::Ignore,
4897           nullptr,
4898           CommandOnTextEditor::Disabled));  // Not implemented yet.
4899   // REQUIRED SPECIAL REVIEW special review
4900   sInternalCommandDataHashtable->InsertOrUpdate(
4901       u"saveas"_ns,
4902       InternalCommandData(
4903           "cmd_saveAs",
4904           Command::Undefined,
4905           ExecCommandParam::Boolean,
4906           nullptr,
4907           CommandOnTextEditor::FallThrough));  // Not implemented yet.
4908   // REQUIRED SPECIAL REVIEW special review
4909   sInternalCommandDataHashtable->InsertOrUpdate(
4910       u"print"_ns,
4911       InternalCommandData(
4912           "cmd_print",
4913           Command::Undefined,
4914           ExecCommandParam::Boolean,
4915           nullptr,
4916           CommandOnTextEditor::FallThrough));  // Not implemented yet.
4917 #endif  // #if 0
4918   // clang-format on
4919 }
4920 
ConvertToInternalCommand(const nsAString & aHTMLCommandName,const nsAString & aValue,nsAString * aAdjustedValue)4921 Document::InternalCommandData Document::ConvertToInternalCommand(
4922     const nsAString& aHTMLCommandName, const nsAString& aValue /* = u""_ns */,
4923     nsAString* aAdjustedValue /* = nullptr */) {
4924   MOZ_ASSERT(!aAdjustedValue || aAdjustedValue->IsEmpty());
4925   EnsureInitializeInternalCommandDataHashtable();
4926   InternalCommandData commandData;
4927   if (!sInternalCommandDataHashtable->Get(aHTMLCommandName, &commandData)) {
4928     return InternalCommandData();
4929   }
4930   // Ignore if the command is disabled by a corresponding pref due to Gecko
4931   // specific.
4932   switch (commandData.mCommand) {
4933     case Command::FormatIncreaseFontSize:
4934       MOZ_DIAGNOSTIC_ASSERT(
4935           aHTMLCommandName.LowerCaseEqualsLiteral("increasefontsize"));
4936       if (!StaticPrefs::dom_document_edit_command_increasefontsize_enabled()) {
4937         return InternalCommandData();
4938       }
4939       break;
4940     case Command::FormatDecreaseFontSize:
4941       MOZ_DIAGNOSTIC_ASSERT(
4942           aHTMLCommandName.LowerCaseEqualsLiteral("decreasefontsize"));
4943       if (!StaticPrefs::dom_document_edit_command_decreasefontsize_enabled()) {
4944         return InternalCommandData();
4945       }
4946       break;
4947     case Command::GetHTML:
4948       MOZ_DIAGNOSTIC_ASSERT(aHTMLCommandName.LowerCaseEqualsLiteral("gethtml"));
4949       if (!StaticPrefs::dom_document_edit_command_gethtml_enabled()) {
4950         return InternalCommandData();
4951       }
4952       break;
4953     case Command::FormatBlock:
4954       if (!StaticPrefs::dom_document_edit_command_heading_enabled() &&
4955           aHTMLCommandName.LowerCaseEqualsLiteral("heading")) {
4956         return InternalCommandData();
4957       }
4958       break;
4959     case Command::SetDocumentReadOnly:
4960       if (!StaticPrefs::dom_document_edit_command_contentReadOnly_enabled() &&
4961           aHTMLCommandName.LowerCaseEqualsLiteral("contentreadonly")) {
4962         return InternalCommandData();
4963       }
4964       if (!StaticPrefs::dom_document_edit_command_readonly_enabled() &&
4965           aHTMLCommandName.LowerCaseEqualsLiteral("readonly")) {
4966         return InternalCommandData();
4967       }
4968       break;
4969     case Command::SetDocumentInsertBROnEnterKeyPress:
4970       MOZ_DIAGNOSTIC_ASSERT(
4971           aHTMLCommandName.LowerCaseEqualsLiteral("insertbronreturn"));
4972       if (!StaticPrefs::dom_document_edit_command_insertBrOnReturn_enabled()) {
4973         return InternalCommandData();
4974       }
4975       break;
4976     default:
4977       break;
4978   }
4979   if (!aAdjustedValue) {
4980     // No further work to do
4981     return commandData;
4982   }
4983   switch (commandData.mExecCommandParam) {
4984     case ExecCommandParam::Ignore:
4985       // Just have to copy it, no checking
4986       switch (commandData.mCommand) {
4987         case Command::FormatJustifyLeft:
4988           aAdjustedValue->AssignLiteral("left");
4989           break;
4990         case Command::FormatJustifyRight:
4991           aAdjustedValue->AssignLiteral("right");
4992           break;
4993         case Command::FormatJustifyCenter:
4994           aAdjustedValue->AssignLiteral("center");
4995           break;
4996         case Command::FormatJustifyFull:
4997           aAdjustedValue->AssignLiteral("justify");
4998           break;
4999         default:
5000           MOZ_ASSERT(EditorCommand::GetParamType(commandData.mCommand) ==
5001                      EditorCommandParamType::None);
5002           break;
5003       }
5004       return commandData;
5005 
5006     case ExecCommandParam::Boolean:
5007       MOZ_ASSERT(!!(EditorCommand::GetParamType(commandData.mCommand) &
5008                     EditorCommandParamType::Bool));
5009       // If this is a boolean value and it's not explicitly false (e.g. no
5010       // value).  We default to "true" (see bug 301490).
5011       if (!aValue.LowerCaseEqualsLiteral("false")) {
5012         aAdjustedValue->AssignLiteral("true");
5013       } else {
5014         aAdjustedValue->AssignLiteral("false");
5015       }
5016       return commandData;
5017 
5018     case ExecCommandParam::InvertedBoolean:
5019       MOZ_ASSERT(!!(EditorCommand::GetParamType(commandData.mCommand) &
5020                     EditorCommandParamType::Bool));
5021       // For old backwards commands we invert the check.
5022       if (aValue.LowerCaseEqualsLiteral("false")) {
5023         aAdjustedValue->AssignLiteral("true");
5024       } else {
5025         aAdjustedValue->AssignLiteral("false");
5026       }
5027       return commandData;
5028 
5029     case ExecCommandParam::String:
5030       MOZ_ASSERT(!!(
5031           EditorCommand::GetParamType(commandData.mCommand) &
5032           (EditorCommandParamType::String | EditorCommandParamType::CString)));
5033       switch (commandData.mCommand) {
5034         case Command::FormatBlock: {
5035           const char16_t* start = aValue.BeginReading();
5036           const char16_t* end = aValue.EndReading();
5037           if (start != end && *start == '<' && *(end - 1) == '>') {
5038             ++start;
5039             --end;
5040           }
5041           // XXX Should we reorder this array with actual usage?
5042           static const nsStaticAtom* kFormattableBlockTags[] = {
5043               // clang-format off
5044             nsGkAtoms::address,
5045             nsGkAtoms::blockquote,
5046             nsGkAtoms::dd,
5047             nsGkAtoms::div,
5048             nsGkAtoms::dl,
5049             nsGkAtoms::dt,
5050             nsGkAtoms::h1,
5051             nsGkAtoms::h2,
5052             nsGkAtoms::h3,
5053             nsGkAtoms::h4,
5054             nsGkAtoms::h5,
5055             nsGkAtoms::h6,
5056             nsGkAtoms::p,
5057             nsGkAtoms::pre,
5058               // clang-format on
5059           };
5060           nsAutoString value(nsDependentSubstring(start, end));
5061           ToLowerCase(value);
5062           const nsStaticAtom* valueAtom = NS_GetStaticAtom(value);
5063           for (const nsStaticAtom* kTag : kFormattableBlockTags) {
5064             if (valueAtom == kTag) {
5065               kTag->ToString(*aAdjustedValue);
5066               return commandData;
5067             }
5068           }
5069           return InternalCommandData();
5070         }
5071         case Command::FormatFontSize: {
5072           // Per editing spec as of April 23, 2012, we need to reject the value
5073           // if it's not a valid floating-point number surrounded by optional
5074           // whitespace.  Otherwise, we parse it as a legacy font size.  For
5075           // now, we just parse as a legacy font size regardless (matching
5076           // WebKit) -- bug 747879.
5077           int32_t size = nsContentUtils::ParseLegacyFontSize(aValue);
5078           if (!size) {
5079             return InternalCommandData();
5080           }
5081           MOZ_ASSERT(aAdjustedValue->IsEmpty());
5082           aAdjustedValue->AppendInt(size);
5083           return commandData;
5084         }
5085         case Command::InsertImage:
5086         case Command::InsertLink:
5087           if (aValue.IsEmpty()) {
5088             // Invalid value, return false
5089             return InternalCommandData();
5090           }
5091           aAdjustedValue->Assign(aValue);
5092           return commandData;
5093         case Command::SetDocumentDefaultParagraphSeparator:
5094           if (!aValue.LowerCaseEqualsLiteral("div") &&
5095               !aValue.LowerCaseEqualsLiteral("p") &&
5096               !aValue.LowerCaseEqualsLiteral("br")) {
5097             // Invalid value
5098             return InternalCommandData();
5099           }
5100           aAdjustedValue->Assign(aValue);
5101           return commandData;
5102         default:
5103           aAdjustedValue->Assign(aValue);
5104           return commandData;
5105       }
5106 
5107     default:
5108       MOZ_ASSERT_UNREACHABLE("New ExecCommandParam value hasn't been handled");
5109       return InternalCommandData();
5110   }
5111 }
5112 
AutoEditorCommandTarget(Document & aDocument,const InternalCommandData & aCommandData)5113 Document::AutoEditorCommandTarget::AutoEditorCommandTarget(
5114     Document& aDocument, const InternalCommandData& aCommandData)
5115     : mCommandData(aCommandData) {
5116   // We'll retrieve an editor with current DOM tree and layout information.
5117   // However, JS may have already hidden or remove exposed root content of
5118   // the editor.  Therefore, we need the latest layout information here.
5119   aDocument.FlushPendingNotifications(FlushType::Layout);
5120   if (!aDocument.GetPresShell() || aDocument.GetPresShell()->IsDestroying()) {
5121     mDoNothing = true;
5122     return;
5123   }
5124 
5125   if (nsPresContext* presContext = aDocument.GetPresContext()) {
5126     // Consider context of command handling which is automatically resolved
5127     // by order of controllers in `nsCommandManager::GetControllerForCommand()`.
5128     // The order is:
5129     //   1. TextEditor if there is an active element and it has TextEditor like
5130     //      <input type="text"> or <textarea>.
5131     //   2. HTMLEditor for the document, if there is.
5132     //   3. Retarget to the DocShell or nsCommandManager as what we've done.
5133     if (aCommandData.IsCutOrCopyCommand()) {
5134       // Note that we used to use DocShell to handle `cut` and `copy` command
5135       // for dispatching corresponding events for making possible web apps to
5136       // implement their own editor without editable elements but supports
5137       // standard shortcut keys, etc.  In this case, we prefer to use active
5138       // element's editor to keep same behavior.
5139       mActiveEditor = nsContentUtils::GetActiveEditor(presContext);
5140     } else {
5141       mActiveEditor = nsContentUtils::GetActiveEditor(presContext);
5142       mHTMLEditor = nsContentUtils::GetHTMLEditor(presContext);
5143       if (!mActiveEditor) {
5144         mActiveEditor = mHTMLEditor;
5145       }
5146     }
5147   }
5148 
5149   // Then, retrieve editor command class instance which should handle it
5150   // and can handle it now.
5151   if (!mActiveEditor) {
5152     // If the command is available without editor, we should redirect the
5153     // command to focused descendant with DocShell.
5154     if (aCommandData.IsAvailableOnlyWhenEditable()) {
5155       mDoNothing = true;
5156       return;
5157     }
5158     return;
5159   }
5160 
5161   // Otherwise, we should use EditorCommand instance (which is singleton
5162   // instance) when it's enabled.
5163   mEditorCommand = aCommandData.mGetEditorCommandFunc
5164                        ? aCommandData.mGetEditorCommandFunc()
5165                        : nullptr;
5166   if (!mEditorCommand) {
5167     mDoNothing = true;
5168     mActiveEditor = nullptr;
5169     mHTMLEditor = nullptr;
5170     return;
5171   }
5172 
5173   if (IsCommandEnabled()) {
5174     return;
5175   }
5176 
5177   // If the EditorCommand instance is disabled, we should do nothing if
5178   // the command requires an editor.
5179   if (aCommandData.IsAvailableOnlyWhenEditable()) {
5180     // Do nothing if editor specific commands is disabled (bug 760052).
5181     mDoNothing = true;
5182     return;
5183   }
5184 
5185   // Otherwise, we should redirect it to focused descendant with DocShell.
5186   mEditorCommand = nullptr;
5187   mActiveEditor = nullptr;
5188   mHTMLEditor = nullptr;
5189 }
5190 
GetTargetEditor() const5191 EditorBase* Document::AutoEditorCommandTarget::GetTargetEditor() const {
5192   using CommandOnTextEditor = InternalCommandData::CommandOnTextEditor;
5193   switch (mCommandData.mCommandOnTextEditor) {
5194     case CommandOnTextEditor::Enabled:
5195       return mActiveEditor;
5196     case CommandOnTextEditor::Disabled:
5197       return mActiveEditor && mActiveEditor->IsTextEditor()
5198                  ? nullptr
5199                  : mActiveEditor.get();
5200     case CommandOnTextEditor::FallThrough:
5201       return mHTMLEditor;
5202   }
5203   return nullptr;
5204 }
5205 
IsEditable(Document * aDocument) const5206 bool Document::AutoEditorCommandTarget::IsEditable(Document* aDocument) const {
5207   if (RefPtr<Document> doc = aDocument->GetInProcessParentDocument()) {
5208     // Make sure frames are up to date, since that can affect whether
5209     // we're editable.
5210     doc->FlushPendingNotifications(FlushType::Frames);
5211   }
5212   EditorBase* targetEditor = GetTargetEditor();
5213   if (targetEditor && targetEditor->IsTextEditor()) {
5214     // FYI: When `disabled` attribute is set, `TextEditor` treats it as
5215     //      "readonly" too.
5216     return !targetEditor->IsReadonly();
5217   }
5218   return aDocument->IsEditingOn();
5219 }
5220 
IsCommandEnabled() const5221 bool Document::AutoEditorCommandTarget::IsCommandEnabled() const {
5222   EditorBase* targetEditor = GetTargetEditor();
5223   if (!targetEditor) {
5224     return false;
5225   }
5226   MOZ_ASSERT(targetEditor == mActiveEditor || targetEditor == mHTMLEditor);
5227   return MOZ_KnownLive(mEditorCommand)
5228       ->IsCommandEnabled(mCommandData.mCommand, MOZ_KnownLive(targetEditor));
5229 }
5230 
DoCommand(nsIPrincipal * aPrincipal) const5231 nsresult Document::AutoEditorCommandTarget::DoCommand(
5232     nsIPrincipal* aPrincipal) const {
5233   MOZ_ASSERT(!DoNothing());
5234   MOZ_ASSERT(mEditorCommand);
5235   EditorBase* targetEditor = GetTargetEditor();
5236   if (!targetEditor) {
5237     return NS_SUCCESS_DOM_NO_OPERATION;
5238   }
5239   MOZ_ASSERT(targetEditor == mActiveEditor || targetEditor == mHTMLEditor);
5240   return MOZ_KnownLive(mEditorCommand)
5241       ->DoCommand(mCommandData.mCommand, MOZ_KnownLive(*targetEditor),
5242                   aPrincipal);
5243 }
5244 
5245 template <typename ParamType>
DoCommandParam(const ParamType & aParam,nsIPrincipal * aPrincipal) const5246 nsresult Document::AutoEditorCommandTarget::DoCommandParam(
5247     const ParamType& aParam, nsIPrincipal* aPrincipal) const {
5248   MOZ_ASSERT(!DoNothing());
5249   MOZ_ASSERT(mEditorCommand);
5250   EditorBase* targetEditor = GetTargetEditor();
5251   if (!targetEditor) {
5252     return NS_SUCCESS_DOM_NO_OPERATION;
5253   }
5254   MOZ_ASSERT(targetEditor == mActiveEditor || targetEditor == mHTMLEditor);
5255   return MOZ_KnownLive(mEditorCommand)
5256       ->DoCommandParam(mCommandData.mCommand, aParam,
5257                        MOZ_KnownLive(*targetEditor), aPrincipal);
5258 }
5259 
GetCommandStateParams(nsCommandParams & aParams) const5260 nsresult Document::AutoEditorCommandTarget::GetCommandStateParams(
5261     nsCommandParams& aParams) const {
5262   MOZ_ASSERT(mEditorCommand);
5263   EditorBase* targetEditor = GetTargetEditor();
5264   if (!targetEditor) {
5265     return NS_OK;
5266   }
5267   MOZ_ASSERT(targetEditor == mActiveEditor || targetEditor == mHTMLEditor);
5268   return MOZ_KnownLive(mEditorCommand)
5269       ->GetCommandStateParams(mCommandData.mCommand, MOZ_KnownLive(aParams),
5270                               MOZ_KnownLive(targetEditor), nullptr);
5271 }
5272 
ExecCommand(const nsAString & aHTMLCommandName,bool aShowUI,const nsAString & aValue,nsIPrincipal & aSubjectPrincipal,ErrorResult & aRv)5273 bool Document::ExecCommand(const nsAString& aHTMLCommandName, bool aShowUI,
5274                            const nsAString& aValue,
5275                            nsIPrincipal& aSubjectPrincipal, ErrorResult& aRv) {
5276   // Only allow on HTML documents.
5277   if (!IsHTMLOrXHTML()) {
5278     aRv.ThrowInvalidStateError(
5279         "execCommand is only supported on HTML documents");
5280     return false;
5281   }
5282   // Otherwise, don't throw exception for compatibility with Chrome.
5283 
5284   // if they are requesting UI from us, let's fail since we have no UI
5285   if (aShowUI) {
5286     return false;
5287   }
5288 
5289   // If we're running an execCommand, we should just return false.
5290   // https://github.com/w3c/editing/issues/200#issuecomment-575241816
5291   if (!StaticPrefs::dom_document_exec_command_nested_calls_allowed() &&
5292       mIsRunningExecCommand) {
5293     return false;
5294   }
5295 
5296   //  for optional parameters see dom/src/base/nsHistory.cpp: HistoryImpl::Go()
5297   //  this might add some ugly JS dependencies?
5298 
5299   nsAutoString adjustedValue;
5300   InternalCommandData commandData =
5301       ConvertToInternalCommand(aHTMLCommandName, aValue, &adjustedValue);
5302   switch (commandData.mCommand) {
5303     case Command::DoNothing:
5304     // "gethtml" command is a command to retrieve a string value, not executing
5305     // anything and not enough the `bool` value of `execCommand`.  So, at here,
5306     // we do nothing for "gethtml" command.
5307     case Command::GetHTML:
5308       return false;
5309     case Command::FormatIncreaseFontSize:
5310       SetUseCounter(eUseCounter_custom_DocumentExecCommandIncreaseFontSize);
5311       break;
5312     case Command::FormatDecreaseFontSize:
5313       SetUseCounter(eUseCounter_custom_DocumentExecCommandDecreaseFontSize);
5314       break;
5315     case Command::FormatBlock:
5316       if (aHTMLCommandName.LowerCaseEqualsLiteral("heading")) {
5317         SetUseCounter(eUseCounter_custom_DocumentExecCommandHeading);
5318       }
5319       break;
5320     case Command::SetDocumentReadOnly:
5321       SetUseCounter(aHTMLCommandName.LowerCaseEqualsLiteral("contentreadonly")
5322                         ? eUseCounter_custom_DocumentExecCommandContentReadOnly
5323                         : eUseCounter_custom_DocumentExecCommandReadOnly);
5324       break;
5325     default:
5326       break;
5327   }
5328 
5329   // Do security check first.
5330   if (commandData.IsCutOrCopyCommand()) {
5331     if (!nsContentUtils::IsCutCopyAllowed(this, aSubjectPrincipal)) {
5332       // We have rejected the event due to it not being performed in an
5333       // input-driven context therefore, we report the error to the console.
5334       nsContentUtils::ReportToConsole(nsIScriptError::warningFlag, "DOM"_ns,
5335                                       this, nsContentUtils::eDOM_PROPERTIES,
5336                                       "ExecCommandCutCopyDeniedNotInputDriven");
5337       return false;
5338     }
5339   } else if (commandData.IsPasteCommand()) {
5340     if (!nsContentUtils::PrincipalHasPermission(aSubjectPrincipal,
5341                                                 nsGkAtoms::clipboardRead)) {
5342       return false;
5343     }
5344   }
5345 
5346   AutoRunningExecCommandMarker markRunningExecCommand(*this);
5347 
5348   // Next, consider context of command handling which is automatically resolved
5349   // by order of controllers in `nsCommandManager::GetControllerForCommand()`.
5350   AutoEditorCommandTarget editCommandTarget(*this, commandData);
5351   if (commandData.IsAvailableOnlyWhenEditable() &&
5352       !editCommandTarget.IsEditable(this)) {
5353     return false;
5354   }
5355 
5356   if (editCommandTarget.DoNothing()) {
5357     return false;
5358   }
5359 
5360   // If we cannot use EditorCommand instance directly, we need to handle the
5361   // command with traditional path (i.e., with DocShell or nsCommandManager).
5362   if (!editCommandTarget.IsEditor()) {
5363     MOZ_ASSERT(!commandData.IsAvailableOnlyWhenEditable());
5364 
5365     // Special case clipboard write commands like Command::Cut and
5366     // Command::Copy.  For such commands, we need the behaviour from
5367     // nsWindowRoot::GetControllers() which is to look at the focused element,
5368     // and defer to a focused textbox's controller.  The code past taken by
5369     // other commands in ExecCommand() always uses the window directly, rather
5370     // than deferring to the textbox, which is desireable for most editor
5371     // commands, but not these commands (as those should allow copying out of
5372     // embedded editors). This behaviour is invoked if we call DoCommand()
5373     // directly on the docShell.
5374     // XXX This means that we allow web app to pick up selected content in
5375     //     descendant document and write it into the clipboard when a
5376     //     descendant document has focus.  However, Chromium does not allow
5377     //     this and this seems that it's not good behavior from point of view
5378     //     of security.  We should treat this issue in another bug.
5379     if (commandData.IsCutOrCopyCommand()) {
5380       nsCOMPtr<nsIDocShell> docShell(mDocumentContainer);
5381       if (!docShell) {
5382         return false;
5383       }
5384       nsresult rv = docShell->DoCommand(commandData.mXULCommandName);
5385       if (rv == NS_SUCCESS_DOM_NO_OPERATION) {
5386         return false;
5387       }
5388       return NS_SUCCEEDED(rv);
5389     }
5390 
5391     // Otherwise (currently, only clipboard read commands like Command::Paste),
5392     // we don't need to redirect the command to focused subdocument.
5393     // Therefore, we should handle it with nsCommandManager as used to be.
5394     // It may dispatch only preceding event of editing on non-editable element
5395     // to make web apps possible to handle standard shortcut key, etc in
5396     // their own editor.
5397     RefPtr<nsCommandManager> commandManager = GetMidasCommandManager();
5398     if (!commandManager) {
5399       return false;
5400     }
5401 
5402     nsCOMPtr<nsPIDOMWindowOuter> window = GetWindow();
5403     if (!window) {
5404       return false;
5405     }
5406 
5407     // Return false for disabled commands (bug 760052)
5408     if (!commandManager->IsCommandEnabled(
5409             nsDependentCString(commandData.mXULCommandName), window)) {
5410       return false;
5411     }
5412 
5413     MOZ_ASSERT(commandData.IsPasteCommand() ||
5414                commandData.mCommand == Command::SelectAll);
5415     nsresult rv =
5416         commandManager->DoCommand(commandData.mXULCommandName, nullptr, window);
5417     return NS_SUCCEEDED(rv) && rv != NS_SUCCESS_DOM_NO_OPERATION;
5418   }
5419 
5420   // Now, our target is fixed to the editor.  So, we can use EditorCommand
5421   // in EditorCommandTarget directly.
5422 
5423   EditorCommandParamType paramType =
5424       EditorCommand::GetParamType(commandData.mCommand);
5425 
5426   // If we don't have meaningful parameter or the EditorCommand does not
5427   // require additional parameter, we can use `DoCommand()`.
5428   if (adjustedValue.IsEmpty() || paramType == EditorCommandParamType::None) {
5429     MOZ_ASSERT(!(paramType & EditorCommandParamType::Bool));
5430     nsresult rv = editCommandTarget.DoCommand(&aSubjectPrincipal);
5431     return NS_SUCCEEDED(rv) && rv != NS_SUCCESS_DOM_NO_OPERATION;
5432   }
5433 
5434   // If the EditorCommand requires `bool` parameter, `adjustedValue` must be
5435   // "true" or "false" here.  So, we can use `DoCommandParam()` which takes
5436   // a `bool` value.
5437   if (!!(paramType & EditorCommandParamType::Bool)) {
5438     MOZ_ASSERT(adjustedValue.EqualsLiteral("true") ||
5439                adjustedValue.EqualsLiteral("false"));
5440     nsresult rv = editCommandTarget.DoCommandParam(
5441         Some(adjustedValue.EqualsLiteral("true")), &aSubjectPrincipal);
5442     return NS_SUCCEEDED(rv) && rv != NS_SUCCESS_DOM_NO_OPERATION;
5443   }
5444 
5445   // Now, the EditorCommand requires `nsAString` or `nsACString` parameter
5446   // in this case.  However, `paramType` may contain both `String` and
5447   // `CString` but in such case, we should use `DoCommandParam()` which
5448   // takes `nsAString`.  So, we should check whether `paramType` contains
5449   // `String` or not first.
5450   if (!!(paramType & EditorCommandParamType::String)) {
5451     MOZ_ASSERT(!adjustedValue.IsVoid());
5452     nsresult rv =
5453         editCommandTarget.DoCommandParam(adjustedValue, &aSubjectPrincipal);
5454     return NS_SUCCEEDED(rv) && rv != NS_SUCCESS_DOM_NO_OPERATION;
5455   }
5456 
5457   // Finally, `paramType` should have `CString`.  We should use
5458   // `DoCommandParam()` which takes `nsACString`.
5459   if (!!(paramType & EditorCommandParamType::CString)) {
5460     NS_ConvertUTF16toUTF8 utf8Value(adjustedValue);
5461     MOZ_ASSERT(!utf8Value.IsVoid());
5462     nsresult rv =
5463         editCommandTarget.DoCommandParam(utf8Value, &aSubjectPrincipal);
5464     return NS_SUCCEEDED(rv) && rv != NS_SUCCESS_DOM_NO_OPERATION;
5465   }
5466 
5467   MOZ_ASSERT_UNREACHABLE(
5468       "Not yet implemented to handle new EditorCommandParamType");
5469   return false;
5470 }
5471 
QueryCommandEnabled(const nsAString & aHTMLCommandName,nsIPrincipal & aSubjectPrincipal,ErrorResult & aRv)5472 bool Document::QueryCommandEnabled(const nsAString& aHTMLCommandName,
5473                                    nsIPrincipal& aSubjectPrincipal,
5474                                    ErrorResult& aRv) {
5475   // Only allow on HTML documents.
5476   if (!IsHTMLOrXHTML()) {
5477     aRv.ThrowInvalidStateError(
5478         "queryCommandEnabled is only supported on HTML documents");
5479     return false;
5480   }
5481   // Otherwise, don't throw exception for compatibility with Chrome.
5482 
5483   InternalCommandData commandData = ConvertToInternalCommand(aHTMLCommandName);
5484   switch (commandData.mCommand) {
5485     case Command::DoNothing:
5486       return false;
5487     case Command::FormatIncreaseFontSize:
5488       SetUseCounter(
5489           eUseCounter_custom_DocumentQueryCommandSupportedOrEnabledIncreaseFontSize);
5490       break;
5491     case Command::FormatDecreaseFontSize:
5492       SetUseCounter(
5493           eUseCounter_custom_DocumentQueryCommandSupportedOrEnabledDecreaseFontSize);
5494       break;
5495     case Command::GetHTML:
5496       SetUseCounter(
5497           eUseCounter_custom_DocumentQueryCommandSupportedOrEnabledGetHTML);
5498       break;
5499     case Command::FormatBlock:
5500       if (aHTMLCommandName.LowerCaseEqualsLiteral("heading")) {
5501         SetUseCounter(
5502             eUseCounter_custom_DocumentQueryCommandSupportedOrEnabledHeading);
5503       }
5504       break;
5505     case Command::SetDocumentReadOnly:
5506       SetUseCounter(
5507           aHTMLCommandName.LowerCaseEqualsLiteral("contentreadonly")
5508               ? eUseCounter_custom_DocumentQueryCommandSupportedOrEnabledContentReadOnly
5509               : eUseCounter_custom_DocumentQueryCommandSupportedOrEnabledReadOnly);
5510       break;
5511     case Command::SetDocumentInsertBROnEnterKeyPress:
5512       SetUseCounter(
5513           eUseCounter_custom_DocumentQueryCommandSupportedOrEnabledInsertBrOnReturn);
5514       break;
5515     default:
5516       break;
5517   }
5518 
5519   // cut & copy are always allowed
5520   if (commandData.IsCutOrCopyCommand()) {
5521     return nsContentUtils::IsCutCopyAllowed(this, aSubjectPrincipal);
5522   }
5523 
5524   // Report false for restricted commands
5525   if (commandData.IsPasteCommand() && !aSubjectPrincipal.IsSystemPrincipal()) {
5526     return false;
5527   }
5528 
5529   AutoEditorCommandTarget editCommandTarget(*this, commandData);
5530   if (commandData.IsAvailableOnlyWhenEditable() &&
5531       !editCommandTarget.IsEditable(this)) {
5532     return false;
5533   }
5534 
5535   if (editCommandTarget.IsEditor()) {
5536     return editCommandTarget.IsCommandEnabled();
5537   }
5538 
5539   // get command manager and dispatch command to our window if it's acceptable
5540   RefPtr<nsCommandManager> commandManager = GetMidasCommandManager();
5541   if (!commandManager) {
5542     return false;
5543   }
5544 
5545   nsPIDOMWindowOuter* window = GetWindow();
5546   if (!window) {
5547     return false;
5548   }
5549 
5550   return commandManager->IsCommandEnabled(
5551       nsDependentCString(commandData.mXULCommandName), window);
5552 }
5553 
QueryCommandIndeterm(const nsAString & aHTMLCommandName,ErrorResult & aRv)5554 bool Document::QueryCommandIndeterm(const nsAString& aHTMLCommandName,
5555                                     ErrorResult& aRv) {
5556   // Only allow on HTML documents.
5557   if (!IsHTMLOrXHTML()) {
5558     aRv.ThrowInvalidStateError(
5559         "queryCommandIndeterm is only supported on HTML documents");
5560     return false;
5561   }
5562   // Otherwise, don't throw exception for compatibility with Chrome.
5563 
5564   InternalCommandData commandData = ConvertToInternalCommand(aHTMLCommandName);
5565   if (commandData.mCommand == Command::DoNothing) {
5566     return false;
5567   }
5568 
5569   AutoEditorCommandTarget editCommandTarget(*this, commandData);
5570   if (commandData.IsAvailableOnlyWhenEditable() &&
5571       !editCommandTarget.IsEditable(this)) {
5572     return false;
5573   }
5574   RefPtr<nsCommandParams> params = new nsCommandParams();
5575   if (editCommandTarget.IsEditor()) {
5576     if (NS_FAILED(editCommandTarget.GetCommandStateParams(*params))) {
5577       return false;
5578     }
5579   } else {
5580     // get command manager and dispatch command to our window if it's acceptable
5581     RefPtr<nsCommandManager> commandManager = GetMidasCommandManager();
5582     if (!commandManager) {
5583       return false;
5584     }
5585 
5586     nsPIDOMWindowOuter* window = GetWindow();
5587     if (!window) {
5588       return false;
5589     }
5590 
5591     if (NS_FAILED(commandManager->GetCommandState(commandData.mXULCommandName,
5592                                                   window, params))) {
5593       return false;
5594     }
5595   }
5596 
5597   // If command does not have a state_mixed value, this call fails and sets
5598   // retval to false.  This is fine -- we want to return false in that case
5599   // anyway (bug 738385), so we just don't throw regardless.
5600   return params->GetBool("state_mixed");
5601 }
5602 
QueryCommandState(const nsAString & aHTMLCommandName,ErrorResult & aRv)5603 bool Document::QueryCommandState(const nsAString& aHTMLCommandName,
5604                                  ErrorResult& aRv) {
5605   // Only allow on HTML documents.
5606   if (!IsHTMLOrXHTML()) {
5607     aRv.ThrowInvalidStateError(
5608         "queryCommandState is only supported on HTML documents");
5609     return false;
5610   }
5611   // Otherwise, don't throw exception for compatibility with Chrome.
5612 
5613   InternalCommandData commandData = ConvertToInternalCommand(aHTMLCommandName);
5614   switch (commandData.mCommand) {
5615     case Command::DoNothing:
5616       return false;
5617     case Command::GetHTML:
5618       SetUseCounter(eUseCounter_custom_DocumentQueryCommandStateOrValueGetHTML);
5619       break;
5620     case Command::FormatBlock:
5621       if (aHTMLCommandName.LowerCaseEqualsLiteral("heading")) {
5622         SetUseCounter(
5623             eUseCounter_custom_DocumentQueryCommandStateOrValueHeading);
5624       }
5625       break;
5626     case Command::SetDocumentReadOnly:
5627       SetUseCounter(
5628           aHTMLCommandName.LowerCaseEqualsLiteral("contentreadonly")
5629               ? eUseCounter_custom_DocumentQueryCommandStateOrValueContentReadOnly
5630               : eUseCounter_custom_DocumentQueryCommandStateOrValueReadOnly);
5631       break;
5632     case Command::SetDocumentInsertBROnEnterKeyPress:
5633       SetUseCounter(
5634           eUseCounter_custom_DocumentQueryCommandStateOrValueInsertBrOnReturn);
5635       break;
5636     default:
5637       break;
5638   }
5639 
5640   if (aHTMLCommandName.LowerCaseEqualsLiteral("usecss")) {
5641     // Per spec, state is supported for styleWithCSS but not useCSS, so we just
5642     // return false always.
5643     return false;
5644   }
5645 
5646   AutoEditorCommandTarget editCommandTarget(*this, commandData);
5647   if (commandData.IsAvailableOnlyWhenEditable() &&
5648       !editCommandTarget.IsEditable(this)) {
5649     return false;
5650   }
5651   RefPtr<nsCommandParams> params = new nsCommandParams();
5652   if (editCommandTarget.IsEditor()) {
5653     if (NS_FAILED(editCommandTarget.GetCommandStateParams(*params))) {
5654       return false;
5655     }
5656   } else {
5657     // get command manager and dispatch command to our window if it's acceptable
5658     RefPtr<nsCommandManager> commandManager = GetMidasCommandManager();
5659     if (!commandManager) {
5660       return false;
5661     }
5662 
5663     nsPIDOMWindowOuter* window = GetWindow();
5664     if (!window) {
5665       return false;
5666     }
5667 
5668     if (NS_FAILED(commandManager->GetCommandState(commandData.mXULCommandName,
5669                                                   window, params))) {
5670       return false;
5671     }
5672   }
5673 
5674   // handle alignment as a special case (possibly other commands too?)
5675   // Alignment is special because the external api is individual
5676   // commands but internally we use cmd_align with different
5677   // parameters.  When getting the state of this command, we need to
5678   // return the boolean for this particular alignment rather than the
5679   // string of 'which alignment is this?'
5680   switch (commandData.mCommand) {
5681     case Command::FormatJustifyLeft: {
5682       nsAutoCString currentValue;
5683       nsresult rv = params->GetCString("state_attribute", currentValue);
5684       if (NS_FAILED(rv)) {
5685         return false;
5686       }
5687       return currentValue.EqualsLiteral("left");
5688     }
5689     case Command::FormatJustifyRight: {
5690       nsAutoCString currentValue;
5691       nsresult rv = params->GetCString("state_attribute", currentValue);
5692       if (NS_FAILED(rv)) {
5693         return false;
5694       }
5695       return currentValue.EqualsLiteral("right");
5696     }
5697     case Command::FormatJustifyCenter: {
5698       nsAutoCString currentValue;
5699       nsresult rv = params->GetCString("state_attribute", currentValue);
5700       if (NS_FAILED(rv)) {
5701         return false;
5702       }
5703       return currentValue.EqualsLiteral("center");
5704     }
5705     case Command::FormatJustifyFull: {
5706       nsAutoCString currentValue;
5707       nsresult rv = params->GetCString("state_attribute", currentValue);
5708       if (NS_FAILED(rv)) {
5709         return false;
5710       }
5711       return currentValue.EqualsLiteral("justify");
5712     }
5713     default:
5714       break;
5715   }
5716 
5717   // If command does not have a state_all value, this call fails and sets
5718   // retval to false.  This is fine -- we want to return false in that case
5719   // anyway (bug 738385), so we just succeed and return false regardless.
5720   return params->GetBool("state_all");
5721 }
5722 
QueryCommandSupported(const nsAString & aHTMLCommandName,CallerType aCallerType,ErrorResult & aRv)5723 bool Document::QueryCommandSupported(const nsAString& aHTMLCommandName,
5724                                      CallerType aCallerType, ErrorResult& aRv) {
5725   // Only allow on HTML documents.
5726   if (!IsHTMLOrXHTML()) {
5727     aRv.ThrowInvalidStateError(
5728         "queryCommandSupported is only supported on HTML documents");
5729     return false;
5730   }
5731   // Otherwise, don't throw exception for compatibility with Chrome.
5732 
5733   InternalCommandData commandData = ConvertToInternalCommand(aHTMLCommandName);
5734   switch (commandData.mCommand) {
5735     case Command::DoNothing:
5736       return false;
5737     case Command::FormatIncreaseFontSize:
5738       SetUseCounter(
5739           eUseCounter_custom_DocumentQueryCommandSupportedOrEnabledIncreaseFontSize);
5740       break;
5741     case Command::FormatDecreaseFontSize:
5742       SetUseCounter(
5743           eUseCounter_custom_DocumentQueryCommandSupportedOrEnabledDecreaseFontSize);
5744       break;
5745     case Command::GetHTML:
5746       SetUseCounter(
5747           eUseCounter_custom_DocumentQueryCommandSupportedOrEnabledGetHTML);
5748       break;
5749     case Command::FormatBlock:
5750       if (aHTMLCommandName.LowerCaseEqualsLiteral("heading")) {
5751         SetUseCounter(
5752             eUseCounter_custom_DocumentQueryCommandSupportedOrEnabledHeading);
5753       }
5754       break;
5755     case Command::SetDocumentReadOnly:
5756       SetUseCounter(
5757           aHTMLCommandName.LowerCaseEqualsLiteral("contentreadonly")
5758               ? eUseCounter_custom_DocumentQueryCommandSupportedOrEnabledContentReadOnly
5759               : eUseCounter_custom_DocumentQueryCommandSupportedOrEnabledReadOnly);
5760       break;
5761     case Command::SetDocumentInsertBROnEnterKeyPress:
5762       SetUseCounter(
5763           eUseCounter_custom_DocumentQueryCommandSupportedOrEnabledInsertBrOnReturn);
5764       break;
5765     default:
5766       break;
5767   }
5768 
5769   // Gecko technically supports all the clipboard commands including
5770   // cut/copy/paste, but non-privileged content will be unable to call
5771   // paste, and depending on the pref "dom.allow_cut_copy", cut and copy
5772   // may also be disallowed to be called from non-privileged content.
5773   // For that reason, we report the support status of corresponding
5774   // command accordingly.
5775   if (aCallerType != CallerType::System) {
5776     if (commandData.IsPasteCommand()) {
5777       return false;
5778     }
5779     if (commandData.IsCutOrCopyCommand() &&
5780         !StaticPrefs::dom_allow_cut_copy()) {
5781       // XXXbz should we worry about correctly reporting "true" in the
5782       // "restricted, but we're an addon with clipboardWrite permissions" case?
5783       // See also nsContentUtils::IsCutCopyAllowed.
5784       return false;
5785     }
5786   }
5787 
5788   // aHTMLCommandName is supported if it can be converted to a Midas command
5789   return true;
5790 }
5791 
QueryCommandValue(const nsAString & aHTMLCommandName,nsAString & aValue,ErrorResult & aRv)5792 void Document::QueryCommandValue(const nsAString& aHTMLCommandName,
5793                                  nsAString& aValue, ErrorResult& aRv) {
5794   aValue.Truncate();
5795 
5796   // Only allow on HTML documents.
5797   if (!IsHTMLOrXHTML()) {
5798     aRv.ThrowInvalidStateError(
5799         "queryCommandValue is only supported on HTML documents");
5800     return;
5801   }
5802   // Otherwise, don't throw exception for compatibility with Chrome.
5803 
5804   InternalCommandData commandData = ConvertToInternalCommand(aHTMLCommandName);
5805   switch (commandData.mCommand) {
5806     case Command::DoNothing:
5807       // Return empty string
5808       return;
5809     case Command::GetHTML:
5810       SetUseCounter(eUseCounter_custom_DocumentQueryCommandStateOrValueGetHTML);
5811       break;
5812     case Command::FormatBlock:
5813       if (aHTMLCommandName.LowerCaseEqualsLiteral("heading")) {
5814         SetUseCounter(
5815             eUseCounter_custom_DocumentQueryCommandStateOrValueHeading);
5816       }
5817       break;
5818     case Command::SetDocumentReadOnly:
5819       SetUseCounter(
5820           aHTMLCommandName.LowerCaseEqualsLiteral("contentreadonly")
5821               ? eUseCounter_custom_DocumentQueryCommandStateOrValueContentReadOnly
5822               : eUseCounter_custom_DocumentQueryCommandStateOrValueReadOnly);
5823       break;
5824     case Command::SetDocumentInsertBROnEnterKeyPress:
5825       SetUseCounter(
5826           eUseCounter_custom_DocumentQueryCommandStateOrValueInsertBrOnReturn);
5827       break;
5828     default:
5829       break;
5830   }
5831 
5832   AutoEditorCommandTarget editCommandTarget(*this, commandData);
5833   if (commandData.IsAvailableOnlyWhenEditable() &&
5834       !editCommandTarget.IsEditable(this)) {
5835     return;
5836   }
5837   RefPtr<nsCommandParams> params = new nsCommandParams();
5838   // FYI: Only GetHTML command is not implemented by editor.  Use window's
5839   //      command table instead.
5840   if (editCommandTarget.IsEditor()) {
5841     MOZ_ASSERT(commandData.mCommand != Command::GetHTML);
5842     if (NS_FAILED(params->SetCString("state_attribute", ""_ns))) {
5843       return;
5844     }
5845 
5846     if (NS_FAILED(editCommandTarget.GetCommandStateParams(*params))) {
5847       return;
5848     }
5849   } else {
5850     // get command manager and dispatch command to our window if it's acceptable
5851     RefPtr<nsCommandManager> commandManager = GetMidasCommandManager();
5852     if (!commandManager) {
5853       return;
5854     }
5855 
5856     nsCOMPtr<nsPIDOMWindowOuter> window = GetWindow();
5857     if (!window) {
5858       return;
5859     }
5860 
5861     // this is a special command since we are calling DoCommand rather than
5862     // GetCommandState like the other commands
5863     if (commandData.mCommand == Command::GetHTML) {
5864       if (NS_FAILED(params->SetBool("selection_only", true))) {
5865         return;
5866       }
5867       if (NS_FAILED(params->SetCString("format", "text/html"_ns))) {
5868         return;
5869       }
5870       if (NS_FAILED(commandManager->DoCommand(commandData.mXULCommandName,
5871                                               params, window))) {
5872         return;
5873       }
5874       params->GetString("result", aValue);
5875       return;
5876     }
5877 
5878     if (NS_FAILED(params->SetCString("state_attribute", ""_ns))) {
5879       return;
5880     }
5881 
5882     if (NS_FAILED(commandManager->GetCommandState(commandData.mXULCommandName,
5883                                                   window, params))) {
5884       return;
5885     }
5886   }
5887 
5888   // If command does not have a state_attribute value, this call fails, and
5889   // aValue will wind up being the empty string.  This is fine -- we want to
5890   // return "" in that case anyway (bug 738385), so we just return NS_OK
5891   // regardless.
5892   nsAutoCString result;
5893   params->GetCString("state_attribute", result);
5894   CopyUTF8toUTF16(result, aValue);
5895 }
5896 
MaybeEditingStateChanged()5897 void Document::MaybeEditingStateChanged() {
5898   if (!mPendingMaybeEditingStateChanged && mMayStartLayout &&
5899       mUpdateNestLevel == 0 && (mContentEditableCount > 0) != IsEditingOn()) {
5900     if (nsContentUtils::IsSafeToRunScript()) {
5901       EditingStateChanged();
5902     } else if (!mInDestructor) {
5903       nsContentUtils::AddScriptRunner(
5904           NewRunnableMethod("Document::MaybeEditingStateChanged", this,
5905                             &Document::MaybeEditingStateChanged));
5906     }
5907   }
5908 }
5909 
NotifyFetchOrXHRSuccess()5910 void Document::NotifyFetchOrXHRSuccess() {
5911   if (mShouldNotifyFetchSuccess) {
5912     nsContentUtils::DispatchEventOnlyToChrome(
5913         this, ToSupports(this), u"DOMDocFetchSuccess"_ns, CanBubble::eNo,
5914         Cancelable::eNo, /* DefaultAction */ nullptr);
5915   }
5916 }
5917 
SetNotifyFetchSuccess(bool aShouldNotify)5918 void Document::SetNotifyFetchSuccess(bool aShouldNotify) {
5919   mShouldNotifyFetchSuccess = aShouldNotify;
5920 }
5921 
SetNotifyFormOrPasswordRemoved(bool aShouldNotify)5922 void Document::SetNotifyFormOrPasswordRemoved(bool aShouldNotify) {
5923   mShouldNotifyFormOrPasswordRemoved = aShouldNotify;
5924 }
5925 
TearingDownEditor()5926 void Document::TearingDownEditor() {
5927   if (IsEditingOn()) {
5928     mEditingState = EditingState::eTearingDown;
5929     if (IsHTMLOrXHTML()) {
5930       RemoveContentEditableStyleSheets();
5931     }
5932   }
5933 }
5934 
TurnEditingOff()5935 nsresult Document::TurnEditingOff() {
5936   NS_ASSERTION(mEditingState != EditingState::eOff, "Editing is already off.");
5937 
5938   nsPIDOMWindowOuter* window = GetWindow();
5939   if (!window) {
5940     return NS_ERROR_FAILURE;
5941   }
5942 
5943   nsIDocShell* docshell = window->GetDocShell();
5944   if (!docshell) {
5945     return NS_ERROR_FAILURE;
5946   }
5947 
5948   bool isBeingDestroyed = false;
5949   docshell->IsBeingDestroyed(&isBeingDestroyed);
5950   if (isBeingDestroyed) {
5951     return NS_ERROR_FAILURE;
5952   }
5953 
5954   nsCOMPtr<nsIEditingSession> editSession;
5955   nsresult rv = docshell->GetEditingSession(getter_AddRefs(editSession));
5956   NS_ENSURE_SUCCESS(rv, rv);
5957 
5958   // turn editing off
5959   rv = editSession->TearDownEditorOnWindow(window);
5960   NS_ENSURE_SUCCESS(rv, rv);
5961 
5962   mEditingState = EditingState::eOff;
5963 
5964   // Editor resets selection since it is being destroyed.  But if focus is
5965   // still into editable control, we have to initialize selection again.
5966   if (nsFocusManager* fm = nsFocusManager::GetFocusManager()) {
5967     if (RefPtr<TextControlElement> textControlElement =
5968             TextControlElement::FromNodeOrNull(fm->GetFocusedElement())) {
5969       if (RefPtr<TextEditor> textEditor = textControlElement->GetTextEditor()) {
5970         textEditor->ReinitializeSelection(*textControlElement);
5971       }
5972     }
5973   }
5974 
5975   return NS_OK;
5976 }
5977 
HasPresShell(nsPIDOMWindowOuter * aWindow)5978 static bool HasPresShell(nsPIDOMWindowOuter* aWindow) {
5979   nsIDocShell* docShell = aWindow->GetDocShell();
5980   if (!docShell) {
5981     return false;
5982   }
5983   return docShell->GetPresShell() != nullptr;
5984 }
5985 
EditingStateChanged()5986 nsresult Document::EditingStateChanged() {
5987   if (mRemovedFromDocShell) {
5988     return NS_OK;
5989   }
5990 
5991   if (mEditingState == EditingState::eSettingUp ||
5992       mEditingState == EditingState::eTearingDown) {
5993     // XXX We shouldn't recurse
5994     return NS_OK;
5995   }
5996 
5997   const bool designMode = IsInDesignMode();
5998   EditingState newState =
5999       designMode ? EditingState::eDesignMode
6000                  : (mContentEditableCount > 0 ? EditingState::eContentEditable
6001                                               : EditingState::eOff);
6002   if (mEditingState == newState) {
6003     // No changes in editing mode.
6004     return NS_OK;
6005   }
6006 
6007   if (newState == EditingState::eOff) {
6008     // Editing is being turned off.
6009     nsAutoScriptBlocker scriptBlocker;
6010     NotifyEditableStateChange(*this);
6011     return TurnEditingOff();
6012   }
6013 
6014   // Flush out style changes on our _parent_ document, if any, so that
6015   // our check for a presshell won't get stale information.
6016   if (mParentDocument) {
6017     mParentDocument->FlushPendingNotifications(FlushType::Style);
6018   }
6019 
6020   // get editing session, make sure this is a strong reference so the
6021   // window can't get deleted during the rest of this call.
6022   nsCOMPtr<nsPIDOMWindowOuter> window = GetWindow();
6023   if (!window) {
6024     return NS_ERROR_FAILURE;
6025   }
6026 
6027   nsIDocShell* docshell = window->GetDocShell();
6028   if (!docshell) {
6029     return NS_ERROR_FAILURE;
6030   }
6031 
6032   // FlushPendingNotifications might destroy our docshell.
6033   bool isBeingDestroyed = false;
6034   docshell->IsBeingDestroyed(&isBeingDestroyed);
6035   if (isBeingDestroyed) {
6036     return NS_ERROR_FAILURE;
6037   }
6038 
6039   nsCOMPtr<nsIEditingSession> editSession;
6040   nsresult rv = docshell->GetEditingSession(getter_AddRefs(editSession));
6041   NS_ENSURE_SUCCESS(rv, rv);
6042 
6043   RefPtr<HTMLEditor> htmlEditor = editSession->GetHTMLEditorForWindow(window);
6044   if (htmlEditor) {
6045     // We might already have an editor if it was set up for mail, let's see
6046     // if this is actually the case.
6047     uint32_t flags = 0;
6048     htmlEditor->GetFlags(&flags);
6049     if (flags & nsIEditor::eEditorMailMask) {
6050       // We already have a mail editor, then we should not attempt to create
6051       // another one.
6052       return NS_OK;
6053     }
6054   }
6055 
6056   if (!HasPresShell(window)) {
6057     // We should not make the window editable or setup its editor.
6058     // It's probably style=display:none.
6059     return NS_OK;
6060   }
6061 
6062   bool makeWindowEditable = mEditingState == EditingState::eOff;
6063   bool spellRecheckAll = false;
6064   bool putOffToRemoveScriptBlockerUntilModifyingEditingState = false;
6065   htmlEditor = nullptr;
6066 
6067   {
6068     EditingState oldState = mEditingState;
6069     nsAutoEditingState push(this, EditingState::eSettingUp);
6070 
6071     RefPtr<PresShell> presShell = GetPresShell();
6072     NS_ENSURE_TRUE(presShell, NS_ERROR_FAILURE);
6073 
6074     // If we're entering the design mode from non-editable state, put the
6075     // selection at the beginning of the document for compatibility reasons.
6076     bool collapseSelectionAtBeginningOfDocument =
6077         designMode && oldState == EditingState::eOff;
6078     // However, mEditingState may be eOff even if there is some
6079     // `contenteditable` area and selection has been initialized for it because
6080     // mEditingState for `contenteditable` may have been scheduled to modify
6081     // when safe.  In such case, we should not reinitialize selection.
6082     if (collapseSelectionAtBeginningOfDocument && mContentEditableCount) {
6083       Selection* selection =
6084           presShell->GetSelection(nsISelectionController::SELECTION_NORMAL);
6085       NS_WARNING_ASSERTION(selection, "Why don't we have Selection?");
6086       if (selection && selection->RangeCount()) {
6087         // Perhaps, we don't need to check whether the selection is in
6088         // an editing host or not because all contents will be editable
6089         // in designMode. (And we don't want to make this code so complicated
6090         // because of legacy API.)
6091         collapseSelectionAtBeginningOfDocument = false;
6092       }
6093     }
6094 
6095     MOZ_ASSERT(mStyleSetFilled);
6096 
6097     // Before making this window editable, we need to modify UA style sheet
6098     // because new style may change whether focused element will be focusable
6099     // or not.
6100     if (IsHTMLOrXHTML()) {
6101       AddContentEditableStyleSheetsToStyleSet(designMode);
6102     }
6103 
6104     if (designMode) {
6105       // designMode is being turned on (overrides contentEditable).
6106       spellRecheckAll = oldState == EditingState::eContentEditable;
6107     }
6108 
6109     // Adjust focused element with new style but blur event shouldn't be fired
6110     // until mEditingState is modified with newState.
6111     nsAutoScriptBlocker scriptBlocker;
6112     if (designMode) {
6113       nsCOMPtr<nsPIDOMWindowOuter> focusedWindow;
6114       nsIContent* focusedContent = nsFocusManager::GetFocusedDescendant(
6115           window, nsFocusManager::eOnlyCurrentWindow,
6116           getter_AddRefs(focusedWindow));
6117       if (focusedContent) {
6118         nsIFrame* focusedFrame = focusedContent->GetPrimaryFrame();
6119         bool clearFocus = focusedFrame ? !focusedFrame->IsFocusable()
6120                                        : !focusedContent->IsFocusable();
6121         if (clearFocus) {
6122           nsFocusManager* fm = nsFocusManager::GetFocusManager();
6123           if (fm) {
6124             fm->ClearFocus(window);
6125             // If we need to dispatch blur event, we should put off after
6126             // modifying mEditingState since blur event handler may change
6127             // designMode state again.
6128             putOffToRemoveScriptBlockerUntilModifyingEditingState = true;
6129           }
6130         }
6131       }
6132     }
6133 
6134     if (makeWindowEditable) {
6135       // Editing is being turned on (through designMode or contentEditable)
6136       // Turn on editor.
6137       // XXX This can cause flushing which can change the editing state, so make
6138       //     sure to avoid recursing.
6139       rv = editSession->MakeWindowEditable(window, "html", false, false, true);
6140       NS_ENSURE_SUCCESS(rv, rv);
6141     }
6142 
6143     // XXX Need to call TearDownEditorOnWindow for all failures.
6144     htmlEditor = docshell->GetHTMLEditor();
6145     if (!htmlEditor) {
6146       // Return NS_OK even though we've failed to create an editor here.  This
6147       // is so that the setter of designMode on non-HTML documents does not
6148       // fail.
6149       // This is OK to do because in nsEditingSession::SetupEditorOnWindow() we
6150       // would detect that we can't support the mimetype if appropriate and
6151       // would fall onto the eEditorErrorCantEditMimeType path.
6152       return NS_OK;
6153     }
6154 
6155     if (collapseSelectionAtBeginningOfDocument) {
6156       htmlEditor->BeginningOfDocument();
6157     }
6158 
6159     if (putOffToRemoveScriptBlockerUntilModifyingEditingState) {
6160       nsContentUtils::AddScriptBlocker();
6161     }
6162   }
6163 
6164   mEditingState = newState;
6165   if (putOffToRemoveScriptBlockerUntilModifyingEditingState) {
6166     nsContentUtils::RemoveScriptBlocker();
6167     // If mEditingState is overwritten by another call and already disabled
6168     // the editing, we shouldn't keep making window editable.
6169     if (mEditingState == EditingState::eOff) {
6170       return NS_OK;
6171     }
6172   }
6173 
6174   if (makeWindowEditable) {
6175     // TODO: We should do this earlier in this method.
6176     //       Previously, we called `ExecCommand` with `insertBrOnReturn` command
6177     //       whose argument is false here.  Then, if it returns error, we
6178     //       stopped making it editable.  However, after bug 1697078 fixed,
6179     //       `ExecCommand` returns error only when the document is not XHTML's
6180     //       nor HTML's.  Therefore, we use same error handling for now.
6181     if (MOZ_UNLIKELY(NS_WARN_IF(!IsHTMLOrXHTML()))) {
6182       // Editor setup failed. Editing is not on after all.
6183       // XXX Should we reset the editable flag on nodes?
6184       editSession->TearDownEditorOnWindow(window);
6185       mEditingState = EditingState::eOff;
6186       return NS_ERROR_DOM_INVALID_STATE_ERR;
6187     }
6188     // Set the editor to not insert <br> elements on return when in <p> elements
6189     // by default.
6190     htmlEditor->SetReturnInParagraphCreatesNewParagraph(true);
6191   }
6192 
6193   // Resync the editor's spellcheck state.
6194   if (spellRecheckAll) {
6195     nsCOMPtr<nsISelectionController> selectionController =
6196         htmlEditor->GetSelectionController();
6197     if (NS_WARN_IF(!selectionController)) {
6198       return NS_ERROR_FAILURE;
6199     }
6200 
6201     RefPtr<Selection> spellCheckSelection = selectionController->GetSelection(
6202         nsISelectionController::SELECTION_SPELLCHECK);
6203     if (spellCheckSelection) {
6204       spellCheckSelection->RemoveAllRanges(IgnoreErrors());
6205     }
6206   }
6207   htmlEditor->SyncRealTimeSpell();
6208 
6209   MaybeDispatchCheckKeyPressEventModelEvent();
6210 
6211   return NS_OK;
6212 }
6213 
6214 // Helper class, used below in ChangeContentEditableCount().
6215 class DeferredContentEditableCountChangeEvent : public Runnable {
6216  public:
DeferredContentEditableCountChangeEvent(Document * aDoc,Element * aElement)6217   DeferredContentEditableCountChangeEvent(Document* aDoc, Element* aElement)
6218       : mozilla::Runnable("DeferredContentEditableCountChangeEvent"),
6219         mDoc(aDoc),
6220         mElement(aElement) {}
6221 
Run()6222   NS_IMETHOD Run() override {
6223     if (mElement && mElement->OwnerDoc() == mDoc) {
6224       mDoc->DeferredContentEditableCountChange(mElement);
6225     }
6226     return NS_OK;
6227   }
6228 
6229  private:
6230   RefPtr<Document> mDoc;
6231   RefPtr<Element> mElement;
6232 };
6233 
ChangeContentEditableCount(Element * aElement,int32_t aChange)6234 void Document::ChangeContentEditableCount(Element* aElement, int32_t aChange) {
6235   NS_ASSERTION(int32_t(mContentEditableCount) + aChange >= 0,
6236                "Trying to decrement too much.");
6237 
6238   mContentEditableCount += aChange;
6239 
6240   nsContentUtils::AddScriptRunner(
6241       new DeferredContentEditableCountChangeEvent(this, aElement));
6242 }
6243 
DeferredContentEditableCountChange(Element * aElement)6244 void Document::DeferredContentEditableCountChange(Element* aElement) {
6245   if (mParser ||
6246       (mUpdateNestLevel > 0 && (mContentEditableCount > 0) != IsEditingOn())) {
6247     return;
6248   }
6249 
6250   EditingState oldState = mEditingState;
6251 
6252   nsresult rv = EditingStateChanged();
6253   NS_ENSURE_SUCCESS_VOID(rv);
6254 
6255   if (oldState == mEditingState &&
6256       mEditingState == EditingState::eContentEditable) {
6257     // We just changed the contentEditable state of a node, we need to reset
6258     // the spellchecking state of that node.
6259     if (aElement) {
6260       nsPIDOMWindowOuter* window = GetWindow();
6261       if (!window) {
6262         return;
6263       }
6264 
6265       nsIDocShell* docshell = window->GetDocShell();
6266       if (!docshell) {
6267         return;
6268       }
6269 
6270       RefPtr<HTMLEditor> htmlEditor = docshell->GetHTMLEditor();
6271       if (htmlEditor) {
6272         RefPtr<nsRange> range = nsRange::Create(aElement);
6273         IgnoredErrorResult res;
6274         range->SelectNode(*aElement, res);
6275         if (res.Failed()) {
6276           // The node might be detached from the document at this point,
6277           // which would cause this call to fail.  In this case, we can
6278           // safely ignore the contenteditable count change.
6279           return;
6280         }
6281 
6282         nsCOMPtr<nsIInlineSpellChecker> spellChecker;
6283         rv = htmlEditor->GetInlineSpellChecker(false,
6284                                                getter_AddRefs(spellChecker));
6285         NS_ENSURE_SUCCESS_VOID(rv);
6286 
6287         if (spellChecker) {
6288           rv = spellChecker->SpellCheckRange(range);
6289           NS_ENSURE_SUCCESS_VOID(rv);
6290         }
6291       }
6292     }
6293   }
6294 }
6295 
MaybeDispatchCheckKeyPressEventModelEvent()6296 void Document::MaybeDispatchCheckKeyPressEventModelEvent() {
6297   // Currently, we need to check only when we're becoming editable for
6298   // contenteditable.
6299   if (mEditingState != EditingState::eContentEditable) {
6300     return;
6301   }
6302 
6303   if (mHasBeenEditable) {
6304     return;
6305   }
6306   mHasBeenEditable = true;
6307 
6308   // Dispatch "CheckKeyPressEventModel" event.  That is handled only by
6309   // KeyPressEventModelCheckerChild.  Then, it calls SetKeyPressEventModel()
6310   // with proper keypress event for the active web app.
6311   WidgetEvent checkEvent(true, eUnidentifiedEvent);
6312   checkEvent.mSpecifiedEventType = nsGkAtoms::onCheckKeyPressEventModel;
6313   checkEvent.mFlags.mCancelable = false;
6314   checkEvent.mFlags.mBubbles = false;
6315   checkEvent.mFlags.mOnlySystemGroupDispatch = true;
6316   // Post the event rather than dispatching it synchronously because we need
6317   // a call of SetKeyPressEventModel() before first key input.  Therefore, we
6318   // can avoid paying unnecessary runtime cost for most web apps.
6319   (new AsyncEventDispatcher(this, checkEvent))->PostDOMEvent();
6320 }
6321 
SetKeyPressEventModel(uint16_t aKeyPressEventModel)6322 void Document::SetKeyPressEventModel(uint16_t aKeyPressEventModel) {
6323   PresShell* presShell = GetPresShell();
6324   if (!presShell) {
6325     return;
6326   }
6327   presShell->SetKeyPressEventModel(aKeyPressEventModel);
6328 }
6329 
LastFocusTime() const6330 TimeStamp Document::LastFocusTime() const { return mLastFocusTime; }
6331 
SetLastFocusTime(const TimeStamp & aFocusTime)6332 void Document::SetLastFocusTime(const TimeStamp& aFocusTime) {
6333   MOZ_DIAGNOSTIC_ASSERT(!aFocusTime.IsNull());
6334   MOZ_DIAGNOSTIC_ASSERT(mLastFocusTime.IsNull() ||
6335                         aFocusTime >= mLastFocusTime);
6336   mLastFocusTime = aFocusTime;
6337 }
6338 
GetReferrer(nsAString & aReferrer) const6339 void Document::GetReferrer(nsAString& aReferrer) const {
6340   aReferrer.Truncate();
6341   if (!mReferrerInfo) {
6342     return;
6343   }
6344 
6345   nsCOMPtr<nsIURI> referrer = mReferrerInfo->GetComputedReferrer();
6346   if (!referrer) {
6347     return;
6348   }
6349 
6350   nsAutoCString uri;
6351   nsresult rv = URLDecorationStripper::StripTrackingIdentifiers(referrer, uri);
6352   if (NS_WARN_IF(NS_FAILED(rv))) {
6353     return;
6354   }
6355 
6356   CopyUTF8toUTF16(uri, aReferrer);
6357 }
6358 
GetCookie(nsAString & aCookie,ErrorResult & rv)6359 void Document::GetCookie(nsAString& aCookie, ErrorResult& rv) {
6360   aCookie.Truncate();  // clear current cookie in case service fails;
6361                        // no cookie isn't an error condition.
6362 
6363   if (mDisableCookieAccess) {
6364     return;
6365   }
6366 
6367   // If the document's sandboxed origin flag is set, access to read cookies
6368   // is prohibited.
6369   if (mSandboxFlags & SANDBOXED_ORIGIN) {
6370     rv.Throw(NS_ERROR_DOM_SECURITY_ERR);
6371     return;
6372   }
6373 
6374   StorageAccess storageAccess = StorageAllowedForDocument(this);
6375   if (storageAccess == StorageAccess::eDeny) {
6376     return;
6377   }
6378 
6379   if (ShouldPartitionStorage(storageAccess) &&
6380       !StoragePartitioningEnabled(storageAccess, CookieJarSettings())) {
6381     return;
6382   }
6383 
6384   // If the document is a cookie-averse Document... return the empty string.
6385   if (IsCookieAverse()) {
6386     return;
6387   }
6388 
6389   // not having a cookie service isn't an error
6390   nsCOMPtr<nsICookieService> service =
6391       do_GetService(NS_COOKIESERVICE_CONTRACTID);
6392   if (service) {
6393     nsAutoCString cookie;
6394     service->GetCookieStringFromDocument(this, cookie);
6395     // CopyUTF8toUTF16 doesn't handle error
6396     // because it assumes that the input is valid.
6397     UTF_8_ENCODING->DecodeWithoutBOMHandling(cookie, aCookie);
6398   }
6399 }
6400 
SetCookie(const nsAString & aCookie,ErrorResult & aRv)6401 void Document::SetCookie(const nsAString& aCookie, ErrorResult& aRv) {
6402   if (mDisableCookieAccess) {
6403     return;
6404   }
6405 
6406   // If the document's sandboxed origin flag is set, access to write cookies
6407   // is prohibited.
6408   if (mSandboxFlags & SANDBOXED_ORIGIN) {
6409     aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
6410     return;
6411   }
6412 
6413   StorageAccess storageAccess = StorageAllowedForDocument(this);
6414   if (storageAccess == StorageAccess::eDeny) {
6415     return;
6416   }
6417 
6418   if (ShouldPartitionStorage(storageAccess) &&
6419       !StoragePartitioningEnabled(storageAccess, CookieJarSettings())) {
6420     return;
6421   }
6422 
6423   // If the document is a cookie-averse Document... do nothing.
6424   if (IsCookieAverse()) {
6425     return;
6426   }
6427 
6428   if (!mDocumentURI) {
6429     return;
6430   }
6431 
6432   // not having a cookie service isn't an error
6433   nsCOMPtr<nsICookieService> service =
6434       do_GetService(NS_COOKIESERVICE_CONTRACTID);
6435   if (!service) {
6436     return;
6437   }
6438 
6439   NS_ConvertUTF16toUTF8 cookie(aCookie);
6440   nsresult rv = service->SetCookieStringFromDocument(this, cookie);
6441 
6442   // No warning messages here.
6443   if (NS_FAILED(rv)) {
6444     return;
6445   }
6446 
6447   nsCOMPtr<nsIObserverService> observerService =
6448       mozilla::services::GetObserverService();
6449   if (observerService) {
6450     observerService->NotifyObservers(ToSupports(this), "document-set-cookie",
6451                                      nsString(aCookie).get());
6452   }
6453 }
6454 
GetReferrerPolicy() const6455 ReferrerPolicy Document::GetReferrerPolicy() const {
6456   return mReferrerInfo ? mReferrerInfo->ReferrerPolicy()
6457                        : ReferrerPolicy::_empty;
6458 }
6459 
GetAlinkColor(nsAString & aAlinkColor)6460 void Document::GetAlinkColor(nsAString& aAlinkColor) {
6461   aAlinkColor.Truncate();
6462 
6463   HTMLBodyElement* body = GetBodyElement();
6464   if (body) {
6465     body->GetALink(aAlinkColor);
6466   }
6467 }
6468 
SetAlinkColor(const nsAString & aAlinkColor)6469 void Document::SetAlinkColor(const nsAString& aAlinkColor) {
6470   HTMLBodyElement* body = GetBodyElement();
6471   if (body) {
6472     body->SetALink(aAlinkColor);
6473   }
6474 }
6475 
GetLinkColor(nsAString & aLinkColor)6476 void Document::GetLinkColor(nsAString& aLinkColor) {
6477   aLinkColor.Truncate();
6478 
6479   HTMLBodyElement* body = GetBodyElement();
6480   if (body) {
6481     body->GetLink(aLinkColor);
6482   }
6483 }
6484 
SetLinkColor(const nsAString & aLinkColor)6485 void Document::SetLinkColor(const nsAString& aLinkColor) {
6486   HTMLBodyElement* body = GetBodyElement();
6487   if (body) {
6488     body->SetLink(aLinkColor);
6489   }
6490 }
6491 
GetVlinkColor(nsAString & aVlinkColor)6492 void Document::GetVlinkColor(nsAString& aVlinkColor) {
6493   aVlinkColor.Truncate();
6494 
6495   HTMLBodyElement* body = GetBodyElement();
6496   if (body) {
6497     body->GetVLink(aVlinkColor);
6498   }
6499 }
6500 
SetVlinkColor(const nsAString & aVlinkColor)6501 void Document::SetVlinkColor(const nsAString& aVlinkColor) {
6502   HTMLBodyElement* body = GetBodyElement();
6503   if (body) {
6504     body->SetVLink(aVlinkColor);
6505   }
6506 }
6507 
GetBgColor(nsAString & aBgColor)6508 void Document::GetBgColor(nsAString& aBgColor) {
6509   aBgColor.Truncate();
6510 
6511   HTMLBodyElement* body = GetBodyElement();
6512   if (body) {
6513     body->GetBgColor(aBgColor);
6514   }
6515 }
6516 
SetBgColor(const nsAString & aBgColor)6517 void Document::SetBgColor(const nsAString& aBgColor) {
6518   HTMLBodyElement* body = GetBodyElement();
6519   if (body) {
6520     body->SetBgColor(aBgColor);
6521   }
6522 }
6523 
GetFgColor(nsAString & aFgColor)6524 void Document::GetFgColor(nsAString& aFgColor) {
6525   aFgColor.Truncate();
6526 
6527   HTMLBodyElement* body = GetBodyElement();
6528   if (body) {
6529     body->GetText(aFgColor);
6530   }
6531 }
6532 
SetFgColor(const nsAString & aFgColor)6533 void Document::SetFgColor(const nsAString& aFgColor) {
6534   HTMLBodyElement* body = GetBodyElement();
6535   if (body) {
6536     body->SetText(aFgColor);
6537   }
6538 }
6539 
CaptureEvents()6540 void Document::CaptureEvents() {
6541   WarnOnceAbout(DeprecatedOperations::eUseOfCaptureEvents);
6542 }
6543 
ReleaseEvents()6544 void Document::ReleaseEvents() {
6545   WarnOnceAbout(DeprecatedOperations::eUseOfReleaseEvents);
6546 }
6547 
All()6548 HTMLAllCollection* Document::All() {
6549   if (!mAll) {
6550     mAll = new HTMLAllCollection(this);
6551   }
6552   return mAll;
6553 }
6554 
GetSrcdocData(nsAString & aSrcdocData)6555 nsresult Document::GetSrcdocData(nsAString& aSrcdocData) {
6556   if (mIsSrcdocDocument) {
6557     nsCOMPtr<nsIInputStreamChannel> inStrmChan = do_QueryInterface(mChannel);
6558     if (inStrmChan) {
6559       return inStrmChan->GetSrcdocData(aSrcdocData);
6560     }
6561   }
6562   aSrcdocData = VoidString();
6563   return NS_OK;
6564 }
6565 
GetDefaultView() const6566 Nullable<WindowProxyHolder> Document::GetDefaultView() const {
6567   nsPIDOMWindowOuter* win = GetWindow();
6568   if (!win) {
6569     return nullptr;
6570   }
6571   return WindowProxyHolder(win->GetBrowsingContext());
6572 }
6573 
GetUnretargetedFocusedContent() const6574 nsIContent* Document::GetUnretargetedFocusedContent() const {
6575   nsCOMPtr<nsPIDOMWindowOuter> window = GetWindow();
6576   if (!window) {
6577     return nullptr;
6578   }
6579   nsCOMPtr<nsPIDOMWindowOuter> focusedWindow;
6580   nsIContent* focusedContent = nsFocusManager::GetFocusedDescendant(
6581       window, nsFocusManager::eOnlyCurrentWindow,
6582       getter_AddRefs(focusedWindow));
6583   if (!focusedContent) {
6584     return nullptr;
6585   }
6586   // be safe and make sure the element is from this document
6587   if (focusedContent->OwnerDoc() != this) {
6588     return nullptr;
6589   }
6590 
6591   if (focusedContent->ChromeOnlyAccess()) {
6592     return focusedContent->FindFirstNonChromeOnlyAccessContent();
6593   }
6594   return focusedContent;
6595 }
6596 
GetActiveElement()6597 Element* Document::GetActiveElement() {
6598   // Get the focused element.
6599   Element* focusedElement = GetRetargetedFocusedElement();
6600   if (focusedElement) {
6601     return focusedElement;
6602   }
6603 
6604   // No focused element anywhere in this document.  Try to get the BODY.
6605   if (IsHTMLOrXHTML()) {
6606     Element* bodyElement = AsHTMLDocument()->GetBody();
6607     if (bodyElement) {
6608       return bodyElement;
6609     }
6610     // Special case to handle the transition to XHTML from XUL documents
6611     // where there currently isn't a body element, but we need to match the
6612     // XUL behavior. This should be removed when bug 1540278 is resolved.
6613     if (nsContentUtils::IsChromeDoc(this)) {
6614       Element* docElement = GetDocumentElement();
6615       if (docElement && docElement->IsXULElement()) {
6616         return docElement;
6617       }
6618     }
6619     // Because of IE compatibility, return null when html document doesn't have
6620     // a body.
6621     return nullptr;
6622   }
6623 
6624   // If we couldn't get a BODY, return the root element.
6625   return GetDocumentElement();
6626 }
6627 
GetCurrentScript()6628 Element* Document::GetCurrentScript() {
6629   nsCOMPtr<Element> el(do_QueryInterface(ScriptLoader()->GetCurrentScript()));
6630   return el;
6631 }
6632 
ReleaseCapture() const6633 void Document::ReleaseCapture() const {
6634   // only release the capture if the caller can access it. This prevents a
6635   // page from stopping a scrollbar grab for example.
6636   nsCOMPtr<nsINode> node = PresShell::GetCapturingContent();
6637   if (node && nsContentUtils::CanCallerAccess(node)) {
6638     PresShell::ReleaseCapturingContent();
6639   }
6640 }
6641 
GetBaseURI(bool aTryUseXHRDocBaseURI) const6642 nsIURI* Document::GetBaseURI(bool aTryUseXHRDocBaseURI) const {
6643   if (aTryUseXHRDocBaseURI && mChromeXHRDocBaseURI) {
6644     return mChromeXHRDocBaseURI;
6645   }
6646 
6647   return GetDocBaseURI();
6648 }
6649 
SetBaseURI(nsIURI * aURI)6650 void Document::SetBaseURI(nsIURI* aURI) {
6651   if (!aURI && !mDocumentBaseURI) {
6652     return;
6653   }
6654 
6655   // Don't do anything if the URI wasn't actually changed.
6656   if (aURI && mDocumentBaseURI) {
6657     bool equalBases = false;
6658     mDocumentBaseURI->Equals(aURI, &equalBases);
6659     if (equalBases) {
6660       return;
6661     }
6662   }
6663 
6664   mDocumentBaseURI = aURI;
6665   RefreshLinkHrefs();
6666 }
6667 
ResolveWithBaseURI(const nsAString & aURI)6668 Result<OwningNonNull<nsIURI>, nsresult> Document::ResolveWithBaseURI(
6669     const nsAString& aURI) {
6670   RefPtr<nsIURI> resolvedURI;
6671   MOZ_TRY(
6672       NS_NewURI(getter_AddRefs(resolvedURI), aURI, nullptr, GetDocBaseURI()));
6673   return OwningNonNull<nsIURI>(std::move(resolvedURI));
6674 }
6675 
DefaultStyleAttrURLData()6676 URLExtraData* Document::DefaultStyleAttrURLData() {
6677   MOZ_ASSERT(NS_IsMainThread());
6678   nsIURI* baseURI = GetDocBaseURI();
6679   nsIPrincipal* principal = NodePrincipal();
6680   bool equals;
6681   if (!mCachedURLData || mCachedURLData->BaseURI() != baseURI ||
6682       mCachedURLData->Principal() != principal || !mCachedReferrerInfo ||
6683       NS_FAILED(mCachedURLData->ReferrerInfo()->Equals(mCachedReferrerInfo,
6684                                                        &equals)) ||
6685       !equals) {
6686     mCachedReferrerInfo = ReferrerInfo::CreateForInternalCSSResources(this);
6687     mCachedURLData = new URLExtraData(baseURI, mCachedReferrerInfo, principal);
6688   }
6689   return mCachedURLData;
6690 }
6691 
SetDocumentCharacterSet(NotNull<const Encoding * > aEncoding)6692 void Document::SetDocumentCharacterSet(NotNull<const Encoding*> aEncoding) {
6693   if (mCharacterSet != aEncoding) {
6694     mCharacterSet = aEncoding;
6695     mEncodingMenuDisabled = aEncoding == UTF_8_ENCODING;
6696     RecomputeLanguageFromCharset();
6697 
6698     if (nsPresContext* context = GetPresContext()) {
6699       context->DocumentCharSetChanged(aEncoding);
6700     }
6701   }
6702 }
6703 
GetSandboxFlagsAsString(nsAString & aFlags)6704 void Document::GetSandboxFlagsAsString(nsAString& aFlags) {
6705   nsContentUtils::SandboxFlagsToString(mSandboxFlags, aFlags);
6706 }
6707 
GetHeaderData(nsAtom * aHeaderField,nsAString & aData) const6708 void Document::GetHeaderData(nsAtom* aHeaderField, nsAString& aData) const {
6709   aData.Truncate();
6710   const HeaderData* data = mHeaderData.get();
6711   while (data) {
6712     if (data->mField == aHeaderField) {
6713       aData = data->mData;
6714       break;
6715     }
6716     data = data->mNext.get();
6717   }
6718 }
6719 
SetHeaderData(nsAtom * aHeaderField,const nsAString & aData)6720 void Document::SetHeaderData(nsAtom* aHeaderField, const nsAString& aData) {
6721   if (!aHeaderField) {
6722     NS_ERROR("null headerField");
6723     return;
6724   }
6725 
6726   if (!mHeaderData) {
6727     if (!aData.IsEmpty()) {  // don't bother storing empty string
6728       mHeaderData = MakeUnique<HeaderData>(aHeaderField, aData);
6729     }
6730   } else {
6731     HeaderData* data = mHeaderData.get();
6732     UniquePtr<HeaderData>* lastPtr = &mHeaderData;
6733     bool found = false;
6734     do {  // look for existing and replace
6735       if (data->mField == aHeaderField) {
6736         if (!aData.IsEmpty()) {
6737           data->mData.Assign(aData);
6738         } else {  // don't store empty string
6739           // Note that data->mNext is moved to a temporary before the old value
6740           // of *lastPtr is deleted.
6741           *lastPtr = std::move(data->mNext);
6742         }
6743         found = true;
6744 
6745         break;
6746       }
6747       lastPtr = &data->mNext;
6748       data = lastPtr->get();
6749     } while (data);
6750 
6751     if (!aData.IsEmpty() && !found) {
6752       // didn't find, append
6753       *lastPtr = MakeUnique<HeaderData>(aHeaderField, aData);
6754     }
6755   }
6756 
6757   if (aHeaderField == nsGkAtoms::headerContentLanguage) {
6758     CopyUTF16toUTF8(aData, mContentLanguage);
6759     mMayNeedFontPrefsUpdate = true;
6760     if (auto* presContext = GetPresContext()) {
6761       presContext->ContentLanguageChanged();
6762     }
6763   }
6764 
6765   if (aHeaderField == nsGkAtoms::origin_trial) {
6766     mTrials.UpdateFromToken(aData, NodePrincipal());
6767   }
6768 
6769   if (aHeaderField == nsGkAtoms::headerDefaultStyle) {
6770     SetPreferredStyleSheetSet(aData);
6771   }
6772 
6773   if (aHeaderField == nsGkAtoms::refresh && !IsStaticDocument()) {
6774     // We get into this code before we have a script global yet, so get to our
6775     // container via mDocumentContainer.
6776     if (mDocumentContainer) {
6777       // Note: using mDocumentURI instead of mBaseURI here, for consistency
6778       // (used to just use the current URI of our webnavigation, but that
6779       // should really be the same thing).  Note that this code can run
6780       // before the current URI of the webnavigation has been updated, so we
6781       // can't assert equality here.
6782       mDocumentContainer->SetupRefreshURIFromHeader(this, aData);
6783     }
6784   }
6785 
6786   if (aHeaderField == nsGkAtoms::headerDNSPrefetchControl &&
6787       mAllowDNSPrefetch) {
6788     // Chromium treats any value other than 'on' (case insensitive) as 'off'.
6789     mAllowDNSPrefetch = aData.IsEmpty() || aData.LowerCaseEqualsLiteral("on");
6790   }
6791 
6792   if (aHeaderField == nsGkAtoms::handheldFriendly) {
6793     mViewportType = Unknown;
6794   }
6795 }
6796 
TryChannelCharset(nsIChannel * aChannel,int32_t & aCharsetSource,NotNull<const Encoding * > & aEncoding,nsHtml5TreeOpExecutor * aExecutor)6797 void Document::TryChannelCharset(nsIChannel* aChannel, int32_t& aCharsetSource,
6798                                  NotNull<const Encoding*>& aEncoding,
6799                                  nsHtml5TreeOpExecutor* aExecutor) {
6800   if (aChannel) {
6801     nsAutoCString charsetVal;
6802     nsresult rv = aChannel->GetContentCharset(charsetVal);
6803     if (NS_SUCCEEDED(rv)) {
6804       const Encoding* preferred = Encoding::ForLabel(charsetVal);
6805       if (preferred) {
6806         if (aExecutor && preferred == REPLACEMENT_ENCODING) {
6807           aExecutor->ComplainAboutBogusProtocolCharset(this, false);
6808         }
6809         aEncoding = WrapNotNull(preferred);
6810         aCharsetSource = kCharsetFromChannel;
6811         return;
6812       } else if (aExecutor && !charsetVal.IsEmpty()) {
6813         aExecutor->ComplainAboutBogusProtocolCharset(this, true);
6814       }
6815     }
6816   }
6817 }
6818 
AssertNoStaleServoDataIn(nsINode & aSubtreeRoot)6819 static inline void AssertNoStaleServoDataIn(nsINode& aSubtreeRoot) {
6820 #ifdef DEBUG
6821   for (nsINode* node : ShadowIncludingTreeIterator(aSubtreeRoot)) {
6822     const Element* element = Element::FromNode(node);
6823     if (!element) {
6824       continue;
6825     }
6826     MOZ_ASSERT(!element->HasServoData());
6827   }
6828 #endif
6829 }
6830 
CreatePresShell(nsPresContext * aContext,nsViewManager * aViewManager)6831 already_AddRefed<PresShell> Document::CreatePresShell(
6832     nsPresContext* aContext, nsViewManager* aViewManager) {
6833   MOZ_DIAGNOSTIC_ASSERT(!mPresShell, "We have a presshell already!");
6834 
6835   NS_ENSURE_FALSE(GetBFCacheEntry(), nullptr);
6836 
6837   AssertNoStaleServoDataIn(*this);
6838 
6839   RefPtr<PresShell> presShell = new PresShell(this);
6840   // Note: we don't hold a ref to the shell (it holds a ref to us)
6841   mPresShell = presShell;
6842 
6843   if (!mStyleSetFilled) {
6844     FillStyleSet();
6845   }
6846 
6847   presShell->Init(aContext, aViewManager);
6848 
6849   // Gaining a shell causes changes in how media queries are evaluated, so
6850   // invalidate that.
6851   aContext->MediaFeatureValuesChanged(
6852       {MediaFeatureChange::kAllChanges},
6853       MediaFeatureChangePropagation::JustThisDocument);
6854 
6855   // Make sure to never paint if we belong to an invisible DocShell.
6856   nsCOMPtr<nsIDocShell> docShell(mDocumentContainer);
6857   if (docShell && docShell->IsInvisible()) {
6858     presShell->SetNeverPainting(true);
6859   }
6860 
6861   MOZ_LOG(gDocumentLeakPRLog, LogLevel::Debug,
6862           ("DOCUMENT %p with PressShell %p and DocShell %p", this,
6863            presShell.get(), docShell.get()));
6864 
6865   mExternalResourceMap.ShowViewers();
6866 
6867   UpdateFrameRequestCallbackSchedulingState();
6868 
6869   if (mDocumentL10n) {
6870     // In case we already accumulated mutations,
6871     // we'll trigger the refresh driver now.
6872     mDocumentL10n->OnCreatePresShell();
6873   }
6874 
6875   // Now that we have a shell, we might have @font-face rules (the presence of a
6876   // shell may change which rules apply to us). We don't need to do anything
6877   // like EnsureStyleFlush or such, there's nothing to update yet and when stuff
6878   // is ready to update we'll flush the font set.
6879   MarkUserFontSetDirty();
6880 
6881   // Take the author style disabled state from the top browsing cvontext.
6882   // (PageStyleChild.jsm ensures this is up to date.)
6883   if (BrowsingContext* bc = GetBrowsingContext()) {
6884     presShell->SetAuthorStyleDisabled(bc->Top()->AuthorStyleDisabledDefault());
6885   }
6886 
6887   return presShell.forget();
6888 }
6889 
UpdateFrameRequestCallbackSchedulingState(PresShell * aOldPresShell)6890 void Document::UpdateFrameRequestCallbackSchedulingState(
6891     PresShell* aOldPresShell) {
6892   // If this condition changes to depend on some other variable, make sure to
6893   // call UpdateFrameRequestCallbackSchedulingState() calls to the places where
6894   // that variable can change. Also consider if you should change
6895   // WouldScheduleFrameRequestCallbacks() instead of adding more stuff to this
6896   // condition.
6897   bool shouldBeScheduled =
6898       WouldScheduleFrameRequestCallbacks() && !mFrameRequestManager.IsEmpty();
6899   if (shouldBeScheduled == mFrameRequestCallbacksScheduled) {
6900     // nothing to do
6901     return;
6902   }
6903 
6904   PresShell* presShell = aOldPresShell ? aOldPresShell : mPresShell;
6905   MOZ_RELEASE_ASSERT(presShell);
6906 
6907   nsRefreshDriver* rd = presShell->GetPresContext()->RefreshDriver();
6908   if (shouldBeScheduled) {
6909     rd->ScheduleFrameRequestCallbacks(this);
6910   } else {
6911     rd->RevokeFrameRequestCallbacks(this);
6912   }
6913 
6914   mFrameRequestCallbacksScheduled = shouldBeScheduled;
6915 }
6916 
TakeFrameRequestCallbacks(nsTArray<FrameRequest> & aCallbacks)6917 void Document::TakeFrameRequestCallbacks(nsTArray<FrameRequest>& aCallbacks) {
6918   MOZ_ASSERT(aCallbacks.IsEmpty());
6919   mFrameRequestManager.Take(aCallbacks);
6920   // No need to manually remove ourselves from the refresh driver; it will
6921   // handle that part.  But we do have to update our state.
6922   mFrameRequestCallbacksScheduled = false;
6923 }
6924 
ShouldThrottleFrameRequests() const6925 bool Document::ShouldThrottleFrameRequests() const {
6926   if (mStaticCloneCount > 0) {
6927     // Even if we're not visible, a static clone may be, so run at full speed.
6928     return false;
6929   }
6930 
6931   if (Hidden()) {
6932     // We're not visible (probably in a background tab or the bf cache).
6933     return true;
6934   }
6935 
6936   if (!mPresShell) {
6937     return false;  // Can't do anything smarter.
6938   }
6939 
6940   if (!mPresShell->IsActive()) {
6941     // The pres shell is not active (we're an invisible OOP iframe or such), so
6942     // throttle.
6943     return true;
6944   }
6945 
6946   nsIFrame* frame = mPresShell->GetRootFrame();
6947   if (!frame) {
6948     return false;  // Can't do anything smarter.
6949   }
6950 
6951   nsIFrame* displayRootFrame = nsLayoutUtils::GetDisplayRootFrame(frame);
6952   if (!displayRootFrame) {
6953     return false;  // Can't do anything smarter.
6954   }
6955 
6956   if (!displayRootFrame->DidPaintPresShell(mPresShell)) {
6957     // We didn't get painted during the last paint, so we're not visible.
6958     // Throttle. Note that because we have to paint this document at least
6959     // once to unthrottle it, we will drop one requestAnimationFrame frame
6960     // when a document that previously wasn't visible scrolls into view. This
6961     // is acceptable since it would happen outside the viewport on APZ
6962     // platforms and is unlikely to be human-perceivable on non-APZ platforms.
6963     return true;
6964   }
6965 
6966   // We got painted during the last paint, so run at full speed.
6967   return false;
6968 }
6969 
DeletePresShell()6970 void Document::DeletePresShell() {
6971   mExternalResourceMap.HideViewers();
6972   if (nsPresContext* presContext = mPresShell->GetPresContext()) {
6973     presContext->RefreshDriver()->CancelPendingFullscreenEvents(this);
6974   }
6975 
6976   // When our shell goes away, request that all our images be immediately
6977   // discarded, so we don't carry around decoded image data for a document we
6978   // no longer intend to paint.
6979   ImageTracker()->RequestDiscardAll();
6980 
6981   // Now that we no longer have a shell, we need to forget about any FontFace
6982   // objects for @font-face rules that came from the style set. There's no need
6983   // to call EnsureStyleFlush either, the shell is going away anyway, so there's
6984   // no point on it.
6985   MarkUserFontSetDirty();
6986 
6987   if (mResizeObserverController) {
6988     mResizeObserverController->ShellDetachedFromDocument();
6989   }
6990 
6991   if (IsEditingOn()) {
6992     TurnEditingOff();
6993   }
6994 
6995   PresShell* oldPresShell = mPresShell;
6996   mPresShell = nullptr;
6997   UpdateFrameRequestCallbackSchedulingState(oldPresShell);
6998 
6999   ClearStaleServoData();
7000   AssertNoStaleServoDataIn(*this);
7001 
7002   mStyleSet->ShellDetachedFromDocument();
7003   mStyleSetFilled = false;
7004   mQuirkSheetAdded = false;
7005   mContentEditableSheetAdded = false;
7006   mDesignModeSheetAdded = false;
7007 }
7008 
DisallowBFCaching(uint32_t aStatus)7009 void Document::DisallowBFCaching(uint32_t aStatus) {
7010   NS_ASSERTION(!mBFCacheEntry, "We're already in the bfcache!");
7011   if (!mBFCacheDisallowed) {
7012     WindowGlobalChild* wgc = GetWindowGlobalChild();
7013     if (wgc) {
7014       wgc->SendUpdateBFCacheStatus(aStatus, 0);
7015     }
7016   }
7017   mBFCacheDisallowed = true;
7018 }
7019 
SetBFCacheEntry(nsIBFCacheEntry * aEntry)7020 void Document::SetBFCacheEntry(nsIBFCacheEntry* aEntry) {
7021   MOZ_ASSERT(IsBFCachingAllowed() || !aEntry, "You should have checked!");
7022 
7023   if (mPresShell) {
7024     if (aEntry) {
7025       mPresShell->StopObservingRefreshDriver();
7026     } else if (mBFCacheEntry) {
7027       mPresShell->StartObservingRefreshDriver();
7028     }
7029   }
7030   mBFCacheEntry = aEntry;
7031 }
7032 
RemoveFromBFCacheSync()7033 bool Document::RemoveFromBFCacheSync() {
7034   bool removed = false;
7035   if (nsCOMPtr<nsIBFCacheEntry> entry = GetBFCacheEntry()) {
7036     entry->RemoveFromBFCacheSync();
7037     removed = true;
7038   } else if (!IsCurrentActiveDocument()) {
7039     // In the old bfcache implementation while the new page is loading, but
7040     // before nsIContentViewer.show() has been called, the previous page doesn't
7041     // yet have nsIBFCacheEntry. However, the previous page isn't the current
7042     // active document anymore.
7043     DisallowBFCaching();
7044     removed = true;
7045   }
7046 
7047   if (mozilla::SessionHistoryInParent() && XRE_IsContentProcess()) {
7048     if (BrowsingContext* bc = GetBrowsingContext()) {
7049       if (bc->IsInBFCache()) {
7050         ContentChild* cc = ContentChild::GetSingleton();
7051         // IPC is asynchronous but the caller is supposed to check the return
7052         // value. The reason for 'Sync' in the method name is that the old
7053         // implementation may run scripts. There is Async variant in
7054         // the old session history implementation for the cases where
7055         // synchronous operation isn't safe.
7056         cc->SendRemoveFromBFCache(bc->Top());
7057         removed = true;
7058       }
7059     }
7060   }
7061   return removed;
7062 }
7063 
SubDocClearEntry(PLDHashTable * table,PLDHashEntryHdr * entry)7064 static void SubDocClearEntry(PLDHashTable* table, PLDHashEntryHdr* entry) {
7065   SubDocMapEntry* e = static_cast<SubDocMapEntry*>(entry);
7066 
7067   NS_RELEASE(e->mKey);
7068   if (e->mSubDocument) {
7069     e->mSubDocument->SetParentDocument(nullptr);
7070     NS_RELEASE(e->mSubDocument);
7071   }
7072 }
7073 
SubDocInitEntry(PLDHashEntryHdr * entry,const void * key)7074 static void SubDocInitEntry(PLDHashEntryHdr* entry, const void* key) {
7075   SubDocMapEntry* e =
7076       const_cast<SubDocMapEntry*>(static_cast<const SubDocMapEntry*>(entry));
7077 
7078   e->mKey = const_cast<Element*>(static_cast<const Element*>(key));
7079   NS_ADDREF(e->mKey);
7080 
7081   e->mSubDocument = nullptr;
7082 }
7083 
SetSubDocumentFor(Element * aElement,Document * aSubDoc)7084 nsresult Document::SetSubDocumentFor(Element* aElement, Document* aSubDoc) {
7085   NS_ENSURE_TRUE(aElement, NS_ERROR_UNEXPECTED);
7086 
7087   if (!aSubDoc) {
7088     // aSubDoc is nullptr, remove the mapping
7089 
7090     if (mSubDocuments) {
7091       mSubDocuments->Remove(aElement);
7092     }
7093   } else {
7094     if (!mSubDocuments) {
7095       // Create a new hashtable
7096 
7097       static const PLDHashTableOps hash_table_ops = {
7098           PLDHashTable::HashVoidPtrKeyStub, PLDHashTable::MatchEntryStub,
7099           PLDHashTable::MoveEntryStub, SubDocClearEntry, SubDocInitEntry};
7100 
7101       mSubDocuments = new PLDHashTable(&hash_table_ops, sizeof(SubDocMapEntry));
7102     }
7103 
7104     // Add a mapping to the hash table
7105     auto entry =
7106         static_cast<SubDocMapEntry*>(mSubDocuments->Add(aElement, fallible));
7107 
7108     if (!entry) {
7109       return NS_ERROR_OUT_OF_MEMORY;
7110     }
7111 
7112     if (entry->mSubDocument) {
7113       entry->mSubDocument->SetParentDocument(nullptr);
7114 
7115       // Release the old sub document
7116       NS_RELEASE(entry->mSubDocument);
7117     }
7118 
7119     entry->mSubDocument = aSubDoc;
7120     NS_ADDREF(entry->mSubDocument);
7121 
7122     aSubDoc->SetParentDocument(this);
7123   }
7124 
7125   return NS_OK;
7126 }
7127 
GetSubDocumentFor(nsIContent * aContent) const7128 Document* Document::GetSubDocumentFor(nsIContent* aContent) const {
7129   if (mSubDocuments && aContent->IsElement()) {
7130     auto entry = static_cast<SubDocMapEntry*>(
7131         mSubDocuments->Search(aContent->AsElement()));
7132 
7133     if (entry) {
7134       return entry->mSubDocument;
7135     }
7136   }
7137 
7138   return nullptr;
7139 }
7140 
GetEmbedderElement() const7141 Element* Document::GetEmbedderElement() const {
7142   // We check if we're the active document in our BrowsingContext
7143   // by comparing against its document, rather than checking if the
7144   // WindowContext is cached, since mWindow may be null when we're
7145   // called (such as in nsPresContext::Init).
7146   if (BrowsingContext* bc = GetBrowsingContext()) {
7147     return bc->GetExtantDocument() == this ? bc->GetEmbedderElement() : nullptr;
7148   }
7149 
7150   return nullptr;
7151 }
7152 
IsNodeOfType(uint32_t aFlags) const7153 bool Document::IsNodeOfType(uint32_t aFlags) const { return false; }
7154 
GetRootElement() const7155 Element* Document::GetRootElement() const {
7156   return (mCachedRootElement && mCachedRootElement->GetParentNode() == this)
7157              ? mCachedRootElement
7158              : GetRootElementInternal();
7159 }
7160 
GetUnfocusedKeyEventTarget()7161 Element* Document::GetUnfocusedKeyEventTarget() { return GetRootElement(); }
7162 
GetRootElementInternal() const7163 Element* Document::GetRootElementInternal() const {
7164   // We invoke GetRootElement() immediately before the servo traversal, so we
7165   // should always have a cache hit from Servo.
7166   MOZ_ASSERT(NS_IsMainThread());
7167 
7168   // Loop backwards because any non-elements, such as doctypes and PIs
7169   // are likely to appear before the root element.
7170   for (nsIContent* child = GetLastChild(); child;
7171        child = child->GetPreviousSibling()) {
7172     if (Element* element = Element::FromNode(child)) {
7173       const_cast<Document*>(this)->mCachedRootElement = element;
7174       return element;
7175     }
7176   }
7177 
7178   const_cast<Document*>(this)->mCachedRootElement = nullptr;
7179   return nullptr;
7180 }
7181 
InsertChildBefore(nsIContent * aKid,nsIContent * aBeforeThis,bool aNotify,ErrorResult & aRv)7182 void Document::InsertChildBefore(nsIContent* aKid, nsIContent* aBeforeThis,
7183                                  bool aNotify, ErrorResult& aRv) {
7184   if (aKid->IsElement() && GetRootElement()) {
7185     NS_WARNING("Inserting root element when we already have one");
7186     aRv.ThrowHierarchyRequestError("There is already a root element.");
7187     return;
7188   }
7189 
7190   nsINode::InsertChildBefore(aKid, aBeforeThis, aNotify, aRv);
7191 }
7192 
RemoveChildNode(nsIContent * aKid,bool aNotify)7193 void Document::RemoveChildNode(nsIContent* aKid, bool aNotify) {
7194   Maybe<mozAutoDocUpdate> updateBatch;
7195   if (aKid->IsElement()) {
7196     updateBatch.emplace(this, aNotify);
7197     // Destroy the link map up front before we mess with the child list.
7198     DestroyElementMaps();
7199   }
7200 
7201   // Preemptively clear mCachedRootElement, since we may be about to remove it
7202   // from our child list, and we don't want to return this maybe-obsolete value
7203   // from any GetRootElement() calls that happen inside of RemoveChildNode().
7204   // (NOTE: for this to be useful, RemoveChildNode() must NOT trigger any
7205   // GetRootElement() calls until after it's removed the child from mChildren.
7206   // Any call before that point would restore this soon-to-be-obsolete cached
7207   // answer, and our clearing here would be fruitless.)
7208   mCachedRootElement = nullptr;
7209   nsINode::RemoveChildNode(aKid, aNotify);
7210   MOZ_ASSERT(mCachedRootElement != aKid,
7211              "Stale pointer in mCachedRootElement, after we tried to clear it "
7212              "(maybe somebody called GetRootElement() too early?)");
7213 }
7214 
AddStyleSheetToStyleSets(StyleSheet & aSheet)7215 void Document::AddStyleSheetToStyleSets(StyleSheet& aSheet) {
7216   if (mStyleSetFilled) {
7217     mStyleSet->AddDocStyleSheet(aSheet);
7218     ApplicableStylesChanged();
7219   }
7220 }
7221 
RecordShadowStyleChange(ShadowRoot & aShadowRoot)7222 void Document::RecordShadowStyleChange(ShadowRoot& aShadowRoot) {
7223   mStyleSet->RecordShadowStyleChange(aShadowRoot);
7224   ApplicableStylesChanged();
7225 }
7226 
ApplicableStylesChanged()7227 void Document::ApplicableStylesChanged() {
7228   // TODO(emilio): if we decide to resolve style in display: none iframes, then
7229   // we need to always track style changes and remove the mStyleSetFilled.
7230   if (!mStyleSetFilled) {
7231     return;
7232   }
7233 
7234   MarkUserFontSetDirty();
7235   PresShell* ps = GetPresShell();
7236   if (!ps) {
7237     return;
7238   }
7239 
7240   ps->EnsureStyleFlush();
7241   nsPresContext* pc = ps->GetPresContext();
7242   if (!pc) {
7243     return;
7244   }
7245 
7246   pc->MarkCounterStylesDirty();
7247   pc->MarkFontFeatureValuesDirty();
7248   pc->RestyleManager()->NextRestyleIsForCSSRuleChanges();
7249 }
7250 
RemoveStyleSheetFromStyleSets(StyleSheet & aSheet)7251 void Document::RemoveStyleSheetFromStyleSets(StyleSheet& aSheet) {
7252   if (mStyleSetFilled) {
7253     mStyleSet->RemoveStyleSheet(aSheet);
7254     ApplicableStylesChanged();
7255   }
7256 }
7257 
InsertSheetAt(size_t aIndex,StyleSheet & aSheet)7258 void Document::InsertSheetAt(size_t aIndex, StyleSheet& aSheet) {
7259   DocumentOrShadowRoot::InsertSheetAt(aIndex, aSheet);
7260 
7261   if (aSheet.IsApplicable()) {
7262     AddStyleSheetToStyleSets(aSheet);
7263   }
7264 }
7265 
StyleSheetApplicableStateChanged(StyleSheet & aSheet)7266 void Document::StyleSheetApplicableStateChanged(StyleSheet& aSheet) {
7267   const bool applicable = aSheet.IsApplicable();
7268   // If we're actually in the document style sheet list
7269   if (StyleOrderIndexOfSheet(aSheet) >= 0) {
7270     if (applicable) {
7271       AddStyleSheetToStyleSets(aSheet);
7272     } else {
7273       RemoveStyleSheetFromStyleSets(aSheet);
7274     }
7275   }
7276 
7277   PostStyleSheetApplicableStateChangeEvent(aSheet);
7278 
7279   if (!mSSApplicableStateNotificationPending) {
7280     MOZ_RELEASE_ASSERT(NS_IsMainThread());
7281     nsCOMPtr<nsIRunnable> notification = NewRunnableMethod(
7282         "Document::NotifyStyleSheetApplicableStateChanged", this,
7283         &Document::NotifyStyleSheetApplicableStateChanged);
7284     mSSApplicableStateNotificationPending =
7285         NS_SUCCEEDED(Dispatch(TaskCategory::Other, notification.forget()));
7286   }
7287 }
7288 
PostStyleSheetApplicableStateChangeEvent(StyleSheet & aSheet)7289 void Document::PostStyleSheetApplicableStateChangeEvent(StyleSheet& aSheet) {
7290   if (!StyleSheetChangeEventsEnabled()) {
7291     return;
7292   }
7293 
7294   StyleSheetApplicableStateChangeEventInit init;
7295   init.mBubbles = true;
7296   init.mCancelable = true;
7297   init.mStylesheet = &aSheet;
7298   init.mApplicable = aSheet.IsApplicable();
7299 
7300   RefPtr<StyleSheetApplicableStateChangeEvent> event =
7301       StyleSheetApplicableStateChangeEvent::Constructor(
7302           this, u"StyleSheetApplicableStateChanged"_ns, init);
7303   event->SetTrusted(true);
7304   event->SetTarget(this);
7305   RefPtr<AsyncEventDispatcher> asyncDispatcher =
7306       new AsyncEventDispatcher(this, event);
7307   asyncDispatcher->mOnlyChromeDispatch = ChromeOnlyDispatch::eYes;
7308   asyncDispatcher->PostDOMEvent();
7309 }
7310 
NotifyStyleSheetApplicableStateChanged()7311 void Document::NotifyStyleSheetApplicableStateChanged() {
7312   mSSApplicableStateNotificationPending = false;
7313   nsCOMPtr<nsIObserverService> observerService =
7314       mozilla::services::GetObserverService();
7315   if (observerService) {
7316     observerService->NotifyObservers(
7317         ToSupports(this), "style-sheet-applicable-state-changed", nullptr);
7318   }
7319 }
7320 
FindSheet(const nsTArray<RefPtr<StyleSheet>> & aSheets,nsIURI * aSheetURI)7321 static int32_t FindSheet(const nsTArray<RefPtr<StyleSheet>>& aSheets,
7322                          nsIURI* aSheetURI) {
7323   for (int32_t i = aSheets.Length() - 1; i >= 0; i--) {
7324     bool bEqual;
7325     nsIURI* uri = aSheets[i]->GetSheetURI();
7326 
7327     if (uri && NS_SUCCEEDED(uri->Equals(aSheetURI, &bEqual)) && bEqual)
7328       return i;
7329   }
7330 
7331   return -1;
7332 }
7333 
LoadAdditionalStyleSheet(additionalSheetType aType,nsIURI * aSheetURI)7334 nsresult Document::LoadAdditionalStyleSheet(additionalSheetType aType,
7335                                             nsIURI* aSheetURI) {
7336   MOZ_ASSERT(aSheetURI, "null arg");
7337 
7338   // Checking if we have loaded this one already.
7339   if (FindSheet(mAdditionalSheets[aType], aSheetURI) >= 0)
7340     return NS_ERROR_INVALID_ARG;
7341 
7342   // Loading the sheet sync.
7343   RefPtr<css::Loader> loader = new css::Loader(GetDocGroup());
7344 
7345   css::SheetParsingMode parsingMode;
7346   switch (aType) {
7347     case Document::eAgentSheet:
7348       parsingMode = css::eAgentSheetFeatures;
7349       break;
7350 
7351     case Document::eUserSheet:
7352       parsingMode = css::eUserSheetFeatures;
7353       break;
7354 
7355     case Document::eAuthorSheet:
7356       parsingMode = css::eAuthorSheetFeatures;
7357       break;
7358 
7359     default:
7360       MOZ_CRASH("impossible value for aType");
7361   }
7362 
7363   auto result = loader->LoadSheetSync(aSheetURI, parsingMode,
7364                                       css::Loader::UseSystemPrincipal::Yes);
7365   if (result.isErr()) {
7366     return result.unwrapErr();
7367   }
7368 
7369   RefPtr<StyleSheet> sheet = result.unwrap();
7370 
7371   sheet->SetAssociatedDocumentOrShadowRoot(this);
7372   MOZ_ASSERT(sheet->IsApplicable());
7373 
7374   return AddAdditionalStyleSheet(aType, sheet);
7375 }
7376 
AddAdditionalStyleSheet(additionalSheetType aType,StyleSheet * aSheet)7377 nsresult Document::AddAdditionalStyleSheet(additionalSheetType aType,
7378                                            StyleSheet* aSheet) {
7379   if (mAdditionalSheets[aType].Contains(aSheet)) {
7380     return NS_ERROR_INVALID_ARG;
7381   }
7382 
7383   if (!aSheet->IsApplicable()) {
7384     return NS_ERROR_INVALID_ARG;
7385   }
7386 
7387   mAdditionalSheets[aType].AppendElement(aSheet);
7388 
7389   if (mStyleSetFilled) {
7390     mStyleSet->AppendStyleSheet(*aSheet);
7391     ApplicableStylesChanged();
7392   }
7393   return NS_OK;
7394 }
7395 
RemoveAdditionalStyleSheet(additionalSheetType aType,nsIURI * aSheetURI)7396 void Document::RemoveAdditionalStyleSheet(additionalSheetType aType,
7397                                           nsIURI* aSheetURI) {
7398   MOZ_ASSERT(aSheetURI);
7399 
7400   nsTArray<RefPtr<StyleSheet>>& sheets = mAdditionalSheets[aType];
7401 
7402   int32_t i = FindSheet(mAdditionalSheets[aType], aSheetURI);
7403   if (i >= 0) {
7404     RefPtr<StyleSheet> sheetRef = std::move(sheets[i]);
7405     sheets.RemoveElementAt(i);
7406 
7407     if (!mIsGoingAway) {
7408       MOZ_ASSERT(sheetRef->IsApplicable());
7409       if (mStyleSetFilled) {
7410         mStyleSet->RemoveStyleSheet(*sheetRef);
7411         ApplicableStylesChanged();
7412       }
7413     }
7414     sheetRef->ClearAssociatedDocumentOrShadowRoot();
7415   }
7416 }
7417 
GetScopeObject() const7418 nsIGlobalObject* Document::GetScopeObject() const {
7419   nsCOMPtr<nsIGlobalObject> scope(do_QueryReferent(mScopeObject));
7420   return scope;
7421 }
7422 
CrossOriginIsolated() const7423 bool Document::CrossOriginIsolated() const {
7424   if (auto* bc = GetBrowsingContext()) {
7425     return bc->CrossOriginIsolated();
7426   }
7427 
7428   // For a data document without a browsing context we check the
7429   // cross-origin-isolated state from its creator's inner window.
7430   if (mLoadedAsData) {
7431     nsCOMPtr<nsPIDOMWindowInner> window = do_QueryInterface(GetScopeObject());
7432     return window && window->GetBrowsingContext() &&
7433            window->GetBrowsingContext()->CrossOriginIsolated();
7434   }
7435 
7436   return false;
7437 }
7438 
GetDocGroupOrCreate()7439 DocGroup* Document::GetDocGroupOrCreate() {
7440   if (!mDocGroup) {
7441     nsAutoCString docGroupKey;
7442     nsresult rv = mozilla::dom::DocGroup::GetKey(
7443         NodePrincipal(), CrossOriginIsolated(), docGroupKey);
7444     if (NS_SUCCEEDED(rv) && mDocumentContainer) {
7445       BrowsingContextGroup* group = GetBrowsingContext()->Group();
7446       if (group) {
7447         mDocGroup = group->AddDocument(docGroupKey, this);
7448       }
7449     }
7450   }
7451   return mDocGroup;
7452 }
7453 
SetScopeObject(nsIGlobalObject * aGlobal)7454 void Document::SetScopeObject(nsIGlobalObject* aGlobal) {
7455   mScopeObject = do_GetWeakReference(aGlobal);
7456   if (aGlobal) {
7457     mHasHadScriptHandlingObject = true;
7458 
7459     nsCOMPtr<nsPIDOMWindowInner> window = do_QueryInterface(aGlobal);
7460     if (!window) {
7461       return;
7462     }
7463     BrowsingContextGroup* browsingContextGroup =
7464         window->GetBrowsingContextGroup();
7465 
7466     // We should already have the principal, and now that we have been added
7467     // to a window, we should be able to join a DocGroup!
7468     nsAutoCString docGroupKey;
7469     nsresult rv = mozilla::dom::DocGroup::GetKey(
7470         NodePrincipal(), CrossOriginIsolated(), docGroupKey);
7471     if (mDocGroup) {
7472       if (NS_SUCCEEDED(rv)) {
7473         MOZ_RELEASE_ASSERT(mDocGroup->MatchesKey(docGroupKey));
7474       }
7475       MOZ_RELEASE_ASSERT(mDocGroup->GetBrowsingContextGroup() ==
7476                          browsingContextGroup);
7477     } else {
7478       mDocGroup = browsingContextGroup->AddDocument(docGroupKey, this);
7479 
7480       MOZ_ASSERT(mDocGroup);
7481     }
7482 
7483     MOZ_ASSERT_IF(
7484         mNodeInfoManager->GetArenaAllocator(),
7485         mNodeInfoManager->GetArenaAllocator() == mDocGroup->ArenaAllocator());
7486   }
7487 }
7488 
ContainsEMEContent()7489 bool Document::ContainsEMEContent() {
7490   nsPIDOMWindowInner* win = GetInnerWindow();
7491   // Note this case is different from checking just media elements in that
7492   // it covers when we've created MediaKeys but not associated them with a
7493   // media element.
7494   return win && win->HasActiveMediaKeysInstance();
7495 }
7496 
ContainsMSEContent()7497 bool Document::ContainsMSEContent() {
7498   bool containsMSE = false;
7499 
7500   auto check = [&containsMSE](nsISupports* aSupports) {
7501     nsCOMPtr<nsIContent> content(do_QueryInterface(aSupports));
7502     if (auto* mediaElem = HTMLMediaElement::FromNodeOrNull(content)) {
7503       RefPtr<MediaSource> ms = mediaElem->GetMozMediaSourceObject();
7504       if (ms) {
7505         containsMSE = true;
7506       }
7507     }
7508   };
7509 
7510   EnumerateActivityObservers(check);
7511   return containsMSE;
7512 }
7513 
NotifyActivityChangedCallback(nsISupports * aSupports)7514 static void NotifyActivityChangedCallback(nsISupports* aSupports) {
7515   nsCOMPtr<nsIContent> content(do_QueryInterface(aSupports));
7516   if (auto mediaElem = HTMLMediaElement::FromNodeOrNull(content)) {
7517     mediaElem->NotifyOwnerDocumentActivityChanged();
7518   }
7519   nsCOMPtr<nsIObjectLoadingContent> objectLoadingContent(
7520       do_QueryInterface(aSupports));
7521   if (objectLoadingContent) {
7522     nsObjectLoadingContent* olc =
7523         static_cast<nsObjectLoadingContent*>(objectLoadingContent.get());
7524     olc->NotifyOwnerDocumentActivityChanged();
7525   }
7526   nsCOMPtr<nsIDocumentActivity> objectDocumentActivity(
7527       do_QueryInterface(aSupports));
7528   if (objectDocumentActivity) {
7529     objectDocumentActivity->NotifyOwnerDocumentActivityChanged();
7530   } else {
7531     nsCOMPtr<nsIImageLoadingContent> imageLoadingContent(
7532         do_QueryInterface(aSupports));
7533     if (imageLoadingContent) {
7534       auto ilc = static_cast<nsImageLoadingContent*>(imageLoadingContent.get());
7535       ilc->NotifyOwnerDocumentActivityChanged();
7536     }
7537   }
7538 }
7539 
NotifyActivityChanged()7540 void Document::NotifyActivityChanged() {
7541   EnumerateActivityObservers(NotifyActivityChangedCallback);
7542 }
7543 
IsTopLevelWindowInactive() const7544 bool Document::IsTopLevelWindowInactive() const {
7545   if (BrowsingContext* bc = GetBrowsingContext()) {
7546     return !bc->GetIsActiveBrowserWindow();
7547   }
7548 
7549   return false;
7550 }
7551 
SetContainer(nsDocShell * aContainer)7552 void Document::SetContainer(nsDocShell* aContainer) {
7553   if (aContainer) {
7554     mDocumentContainer = aContainer;
7555   } else {
7556     mDocumentContainer = WeakPtr<nsDocShell>();
7557   }
7558 
7559   mInChromeDocShell =
7560       aContainer && aContainer->GetBrowsingContext()->IsChrome();
7561 
7562   NotifyActivityChanged();
7563 
7564   // IsTopLevelWindowInactive depends on the docshell, so
7565   // update the cached value now that it's available.
7566   UpdateDocumentStates(NS_DOCUMENT_STATE_WINDOW_INACTIVE, false);
7567   if (!aContainer) {
7568     return;
7569   }
7570 
7571   BrowsingContext* context = aContainer->GetBrowsingContext();
7572   if (context && context->IsContent()) {
7573     SetIsTopLevelContentDocument(context->IsTopContent());
7574     SetIsContentDocument(true);
7575   } else {
7576     SetIsTopLevelContentDocument(false);
7577     SetIsContentDocument(false);
7578   }
7579 }
7580 
GetContainer() const7581 nsISupports* Document::GetContainer() const {
7582   return static_cast<nsIDocShell*>(mDocumentContainer);
7583 }
7584 
SetScriptGlobalObject(nsIScriptGlobalObject * aScriptGlobalObject)7585 void Document::SetScriptGlobalObject(
7586     nsIScriptGlobalObject* aScriptGlobalObject) {
7587   MOZ_ASSERT(aScriptGlobalObject || !mAnimationController ||
7588                  mAnimationController->IsPausedByType(
7589                      SMILTimeContainer::PAUSE_PAGEHIDE |
7590                      SMILTimeContainer::PAUSE_BEGIN),
7591              "Clearing window pointer while animations are unpaused");
7592 
7593   if (mScriptGlobalObject && !aScriptGlobalObject) {
7594     // We're detaching from the window.  We need to grab a pointer to
7595     // our layout history state now.
7596     mLayoutHistoryState = GetLayoutHistoryState();
7597 
7598     // Also make sure to remove our onload blocker now if we haven't done it yet
7599     if (mOnloadBlockCount != 0) {
7600       nsCOMPtr<nsILoadGroup> loadGroup = GetDocumentLoadGroup();
7601       if (loadGroup) {
7602         loadGroup->RemoveRequest(mOnloadBlocker, nullptr, NS_OK);
7603       }
7604     }
7605 
7606     if (GetController().isSome()) {
7607       if (imgLoader* loader = nsContentUtils::GetImgLoaderForDocument(this)) {
7608         loader->ClearCacheForControlledDocument(this);
7609       }
7610 
7611       // We may become controlled again if this document comes back out
7612       // of bfcache.  Clear our state to allow that to happen.  Only
7613       // clear this flag if we are actually controlled, though, so pages
7614       // that were force reloaded don't become controlled when they
7615       // come out of bfcache.
7616       mMaybeServiceWorkerControlled = false;
7617     }
7618 
7619     if (GetWindowContext()) {
7620       // The document is about to lose its window, so this is a good time to
7621       // send our page use counters, while we still have access to our
7622       // WindowContext.
7623       //
7624       // (We also do this in nsGlobalWindowInner::FreeInnerObjects(), which
7625       // catches some cases of documents losing their window that don't
7626       // get in here.)
7627       SendPageUseCounters();
7628     }
7629   }
7630 
7631   // BlockOnload() might be called before mScriptGlobalObject is set.
7632   // We may need to add the blocker once mScriptGlobalObject is set.
7633   bool needOnloadBlocker = !mScriptGlobalObject && aScriptGlobalObject;
7634 
7635   mScriptGlobalObject = aScriptGlobalObject;
7636 
7637   if (needOnloadBlocker) {
7638     EnsureOnloadBlocker();
7639   }
7640 
7641   UpdateFrameRequestCallbackSchedulingState();
7642 
7643   if (aScriptGlobalObject) {
7644     // Go back to using the docshell for the layout history state
7645     mLayoutHistoryState = nullptr;
7646     SetScopeObject(aScriptGlobalObject);
7647     mHasHadDefaultView = true;
7648 
7649     if (mAllowDNSPrefetch) {
7650       nsCOMPtr<nsIDocShell> docShell(mDocumentContainer);
7651       if (docShell) {
7652 #ifdef DEBUG
7653         nsCOMPtr<nsIWebNavigation> webNav =
7654             do_GetInterface(aScriptGlobalObject);
7655         NS_ASSERTION(SameCOMIdentity(webNav, docShell),
7656                      "Unexpected container or script global?");
7657 #endif
7658         bool allowDNSPrefetch;
7659         docShell->GetAllowDNSPrefetch(&allowDNSPrefetch);
7660         mAllowDNSPrefetch = allowDNSPrefetch;
7661       }
7662     }
7663 
7664     // If we are set in a window that is already focused we should remember this
7665     // as the time the document gained focus.
7666     if (HasFocus(IgnoreErrors())) {
7667       SetLastFocusTime(TimeStamp::Now());
7668     }
7669   }
7670 
7671   // Remember the pointer to our window (or lack there of), to avoid
7672   // having to QI every time it's asked for.
7673   nsCOMPtr<nsPIDOMWindowInner> window = do_QueryInterface(mScriptGlobalObject);
7674   mWindow = window;
7675 
7676   // Now that we know what our window is, we can flush the CSP errors to the
7677   // Web Console. We are flushing all messages that occurred and were stored in
7678   // the queue prior to this point.
7679   if (mCSP) {
7680     static_cast<nsCSPContext*>(mCSP.get())->flushConsoleMessages();
7681   }
7682 
7683   nsCOMPtr<nsIHttpChannelInternal> internalChannel =
7684       do_QueryInterface(GetChannel());
7685   if (internalChannel) {
7686     nsCOMArray<nsISecurityConsoleMessage> messages;
7687     DebugOnly<nsresult> rv = internalChannel->TakeAllSecurityMessages(messages);
7688     MOZ_ASSERT(NS_SUCCEEDED(rv));
7689     SendToConsole(messages);
7690   }
7691 
7692   // Set our visibility state, but do not fire the event.  This is correct
7693   // because either we're coming out of bfcache (in which case IsVisible() will
7694   // still test false at this point and no state change will happen) or we're
7695   // doing the initial document load and don't want to fire the event for this
7696   // change.
7697   //
7698   // When the visibility is changed, notify it to observers.
7699   // Some observers need the notification, for example HTMLMediaElement uses
7700   // it to update internal media resource allocation.
7701   // When video is loaded via VideoDocument, HTMLMediaElement and MediaDecoder
7702   // creation are already done before Document::SetScriptGlobalObject() call.
7703   // MediaDecoder decides whether starting decoding is decided based on
7704   // document's visibility. When the MediaDecoder is created,
7705   // Document::SetScriptGlobalObject() is not yet called and document is
7706   // hidden state. Therefore the MediaDecoder decides that decoding is
7707   // not yet necessary. But soon after Document::SetScriptGlobalObject()
7708   // call, the document becomes not hidden. At the time, MediaDecoder needs
7709   // to know it and needs to start updating decoding.
7710   UpdateVisibilityState(DispatchVisibilityChange::No);
7711 
7712   // The global in the template contents owner document should be the same.
7713   if (mTemplateContentsOwner && mTemplateContentsOwner != this) {
7714     mTemplateContentsOwner->SetScriptGlobalObject(aScriptGlobalObject);
7715   }
7716 
7717   if (!mMaybeServiceWorkerControlled && mDocumentContainer &&
7718       mScriptGlobalObject && GetChannel()) {
7719     // If we are shift-reloaded, don't associate with a ServiceWorker.
7720     if (mDocumentContainer->IsForceReloading()) {
7721       NS_WARNING("Page was shift reloaded, skipping ServiceWorker control");
7722       return;
7723     }
7724 
7725     mMaybeServiceWorkerControlled = true;
7726   }
7727 }
7728 
GetScriptHandlingObjectInternal() const7729 nsIScriptGlobalObject* Document::GetScriptHandlingObjectInternal() const {
7730   MOZ_ASSERT(!mScriptGlobalObject,
7731              "Do not call this when mScriptGlobalObject is set!");
7732   if (mHasHadDefaultView) {
7733     return nullptr;
7734   }
7735 
7736   nsCOMPtr<nsIScriptGlobalObject> scriptHandlingObject =
7737       do_QueryReferent(mScopeObject);
7738   nsCOMPtr<nsPIDOMWindowInner> win = do_QueryInterface(scriptHandlingObject);
7739   if (win) {
7740     nsPIDOMWindowOuter* outer = win->GetOuterWindow();
7741     if (!outer || outer->GetCurrentInnerWindow() != win) {
7742       NS_WARNING("Wrong inner/outer window combination!");
7743       return nullptr;
7744     }
7745   }
7746   return scriptHandlingObject;
7747 }
SetScriptHandlingObject(nsIScriptGlobalObject * aScriptObject)7748 void Document::SetScriptHandlingObject(nsIScriptGlobalObject* aScriptObject) {
7749   NS_ASSERTION(!mScriptGlobalObject || mScriptGlobalObject == aScriptObject,
7750                "Wrong script object!");
7751   if (aScriptObject) {
7752     SetScopeObject(aScriptObject);
7753     mHasHadDefaultView = false;
7754   }
7755 }
7756 
GetWindowInternal() const7757 nsPIDOMWindowOuter* Document::GetWindowInternal() const {
7758   MOZ_ASSERT(!mWindow, "This should not be called when mWindow is not null!");
7759   // Let's use mScriptGlobalObject. Even if the document is already removed from
7760   // the docshell, the outer window might be still obtainable from the it.
7761   nsCOMPtr<nsPIDOMWindowOuter> win;
7762   if (mRemovedFromDocShell) {
7763     // The docshell returns the outer window we are done.
7764     nsCOMPtr<nsIDocShell> kungFuDeathGrip(mDocumentContainer);
7765     if (kungFuDeathGrip) {
7766       win = kungFuDeathGrip->GetWindow();
7767     }
7768   } else {
7769     if (nsCOMPtr<nsPIDOMWindowInner> inner =
7770             do_QueryInterface(mScriptGlobalObject)) {
7771       // mScriptGlobalObject is always the inner window, let's get the outer.
7772       win = inner->GetOuterWindow();
7773     }
7774   }
7775 
7776   return win;
7777 }
7778 
InternalAllowXULXBL()7779 bool Document::InternalAllowXULXBL() {
7780   if (nsContentUtils::AllowXULXBLForPrincipal(NodePrincipal())) {
7781     mAllowXULXBL = eTriTrue;
7782     return true;
7783   }
7784 
7785   mAllowXULXBL = eTriFalse;
7786   return false;
7787 }
7788 
7789 // Note: We don't hold a reference to the document observer; we assume
7790 // that it has a live reference to the document.
AddObserver(nsIDocumentObserver * aObserver)7791 void Document::AddObserver(nsIDocumentObserver* aObserver) {
7792   NS_ASSERTION(mObservers.IndexOf(aObserver) == nsTArray<int>::NoIndex,
7793                "Observer already in the list");
7794   mObservers.AppendElement(aObserver);
7795   AddMutationObserver(aObserver);
7796 }
7797 
RemoveObserver(nsIDocumentObserver * aObserver)7798 bool Document::RemoveObserver(nsIDocumentObserver* aObserver) {
7799   // If we're in the process of destroying the document (and we're
7800   // informing the observers of the destruction), don't remove the
7801   // observers from the list. This is not a big deal, since we
7802   // don't hold a live reference to the observers.
7803   if (!mInDestructor) {
7804     RemoveMutationObserver(aObserver);
7805     return mObservers.RemoveElement(aObserver);
7806   }
7807 
7808   return mObservers.Contains(aObserver);
7809 }
7810 
BeginUpdate()7811 void Document::BeginUpdate() {
7812   ++mUpdateNestLevel;
7813   nsContentUtils::AddScriptBlocker();
7814   NS_DOCUMENT_NOTIFY_OBSERVERS(BeginUpdate, (this));
7815 }
7816 
EndUpdate()7817 void Document::EndUpdate() {
7818   const bool reset = !mPendingMaybeEditingStateChanged;
7819   mPendingMaybeEditingStateChanged = true;
7820 
7821   NS_DOCUMENT_NOTIFY_OBSERVERS(EndUpdate, (this));
7822 
7823   --mUpdateNestLevel;
7824 
7825   nsContentUtils::RemoveScriptBlocker();
7826 
7827   if (mXULBroadcastManager) {
7828     mXULBroadcastManager->MaybeBroadcast();
7829   }
7830 
7831   if (reset) {
7832     mPendingMaybeEditingStateChanged = false;
7833   }
7834   MaybeEditingStateChanged();
7835 }
7836 
BeginLoad()7837 void Document::BeginLoad() {
7838   if (IsEditingOn()) {
7839     // Reset() blows away all event listeners in the document, and our
7840     // editor relies heavily on those. Midas is turned on, to make it
7841     // work, re-initialize it to give it a chance to add its event
7842     // listeners again.
7843 
7844     TurnEditingOff();
7845     EditingStateChanged();
7846   }
7847 
7848   MOZ_ASSERT(!mDidCallBeginLoad);
7849   mDidCallBeginLoad = true;
7850 
7851   // Block onload here to prevent having to deal with blocking and
7852   // unblocking it while we know the document is loading.
7853   BlockOnload();
7854   mDidFireDOMContentLoaded = false;
7855   BlockDOMContentLoaded();
7856 
7857   if (mScriptLoader) {
7858     mScriptLoader->BeginDeferringScripts();
7859   }
7860 
7861   NS_DOCUMENT_NOTIFY_OBSERVERS(BeginLoad, (this));
7862 }
7863 
MozSetImageElement(const nsAString & aImageElementId,Element * aElement)7864 void Document::MozSetImageElement(const nsAString& aImageElementId,
7865                                   Element* aElement) {
7866   if (aImageElementId.IsEmpty()) return;
7867 
7868   // Hold a script blocker while calling SetImageElement since that can call
7869   // out to id-observers
7870   nsAutoScriptBlocker scriptBlocker;
7871 
7872   IdentifierMapEntry* entry = mIdentifierMap.PutEntry(aImageElementId);
7873   if (entry) {
7874     entry->SetImageElement(aElement);
7875     if (entry->IsEmpty()) {
7876       mIdentifierMap.RemoveEntry(entry);
7877     }
7878   }
7879 }
7880 
DispatchContentLoadedEvents()7881 void Document::DispatchContentLoadedEvents() {
7882   // If you add early returns from this method, make sure you're
7883   // calling UnblockOnload properly.
7884 
7885   // Unpin references to preloaded images
7886   mPreloadingImages.Clear();
7887 
7888   // DOM manipulation after content loaded should not care if the element
7889   // came from the preloader.
7890   mPreloadedPreconnects.Clear();
7891 
7892   if (mTiming) {
7893     mTiming->NotifyDOMContentLoadedStart(Document::GetDocumentURI());
7894   }
7895 
7896   // Dispatch observer notification to notify observers document is interactive.
7897   nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
7898   if (os) {
7899     nsIPrincipal* principal = NodePrincipal();
7900     os->NotifyObservers(ToSupports(this),
7901                         principal->IsSystemPrincipal()
7902                             ? "chrome-document-interactive"
7903                             : "content-document-interactive",
7904                         nullptr);
7905   }
7906 
7907   // Fire a DOM event notifying listeners that this document has been
7908   // loaded (excluding images and other loads initiated by this
7909   // document).
7910   nsContentUtils::DispatchTrustedEvent(this, ToSupports(this),
7911                                        u"DOMContentLoaded"_ns, CanBubble::eYes,
7912                                        Cancelable::eNo);
7913 
7914   if (auto* const window = GetInnerWindow()) {
7915     const RefPtr<ServiceWorkerContainer> serviceWorker =
7916         window->Navigator()->ServiceWorker();
7917 
7918     // This could cause queued messages from a service worker to get
7919     // dispatched on serviceWorker.
7920     serviceWorker->StartMessages();
7921   }
7922 
7923   if (MayStartLayout()) {
7924     MaybeResolveReadyForIdle();
7925   }
7926 
7927   RefPtr<TimelineConsumers> timelines = TimelineConsumers::Get();
7928   nsIDocShell* docShell = this->GetDocShell();
7929 
7930   if (timelines && timelines->HasConsumer(docShell)) {
7931     timelines->AddMarkerForDocShell(
7932         docShell,
7933         MakeUnique<DocLoadingTimelineMarker>("document::DOMContentLoaded"));
7934   }
7935 
7936   if (mTiming) {
7937     mTiming->NotifyDOMContentLoadedEnd(Document::GetDocumentURI());
7938   }
7939 
7940   // If this document is a [i]frame, fire a DOMFrameContentLoaded
7941   // event on all parent documents notifying that the HTML (excluding
7942   // other external files such as images and stylesheets) in a frame
7943   // has finished loading.
7944 
7945   // target_frame is the [i]frame element that will be used as the
7946   // target for the event. It's the [i]frame whose content is done
7947   // loading.
7948   nsCOMPtr<Element> target_frame = GetEmbedderElement();
7949 
7950   if (target_frame && target_frame->IsInComposedDoc()) {
7951     nsCOMPtr<Document> parent = target_frame->OwnerDoc();
7952     while (parent) {
7953       RefPtr<Event> event;
7954       if (parent) {
7955         IgnoredErrorResult ignored;
7956         event = parent->CreateEvent(u"Events"_ns, CallerType::System, ignored);
7957       }
7958 
7959       if (event) {
7960         event->InitEvent(u"DOMFrameContentLoaded"_ns, true, true);
7961 
7962         event->SetTarget(target_frame);
7963         event->SetTrusted(true);
7964 
7965         // To dispatch this event we must manually call
7966         // EventDispatcher::Dispatch() on the ancestor document since the
7967         // target is not in the same document, so the event would never reach
7968         // the ancestor document if we used the normal event
7969         // dispatching code.
7970 
7971         WidgetEvent* innerEvent = event->WidgetEventPtr();
7972         if (innerEvent) {
7973           nsEventStatus status = nsEventStatus_eIgnore;
7974 
7975           if (RefPtr<nsPresContext> context = parent->GetPresContext()) {
7976             // TODO: Bug 1506441
7977             EventDispatcher::Dispatch(MOZ_KnownLive(ToSupports(parent)),
7978                                       context, innerEvent, event, &status);
7979           }
7980         }
7981       }
7982 
7983       parent = parent->GetInProcessParentDocument();
7984     }
7985   }
7986 
7987   // If the document has a manifest attribute, fire a MozApplicationManifest
7988   // event.
7989   Element* root = GetRootElement();
7990   if (root && root->HasAttr(kNameSpaceID_None, nsGkAtoms::manifest)) {
7991     nsContentUtils::DispatchChromeEvent(this, ToSupports(this),
7992                                         u"MozApplicationManifest"_ns,
7993                                         CanBubble::eYes, Cancelable::eYes);
7994   }
7995 
7996   nsPIDOMWindowInner* inner = GetInnerWindow();
7997   if (inner) {
7998     inner->NoteDOMContentLoaded();
7999   }
8000 
8001   // TODO
8002   if (mMaybeServiceWorkerControlled) {
8003     using mozilla::dom::ServiceWorkerManager;
8004     RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
8005     if (swm) {
8006       Maybe<ClientInfo> clientInfo = GetClientInfo();
8007       if (clientInfo.isSome()) {
8008         swm->MaybeCheckNavigationUpdate(clientInfo.ref());
8009       }
8010     }
8011   }
8012 
8013   if (mSetCompleteAfterDOMContentLoaded) {
8014     SetReadyStateInternal(ReadyState::READYSTATE_COMPLETE);
8015     mSetCompleteAfterDOMContentLoaded = false;
8016   }
8017 
8018   UnblockOnload(true);
8019 }
8020 
EndLoad()8021 void Document::EndLoad() {
8022   bool turnOnEditing =
8023       mParser && (IsInDesignMode() || mContentEditableCount > 0);
8024 
8025 #if defined(DEBUG)
8026   // only assert if nothing stopped the load on purpose
8027   if (!mParserAborted) {
8028     nsContentSecurityUtils::AssertAboutPageHasCSP(this);
8029   }
8030 #endif
8031 
8032   // EndLoad may have been called without a matching call to BeginLoad, in the
8033   // case of a failed parse (for example, due to timeout). In such a case, we
8034   // still want to execute part of this code to do appropriate cleanup, but we
8035   // gate part of it because it is intended to match 1-for-1 with calls to
8036   // BeginLoad. We have an explicit flag bit for this purpose, since it's
8037   // complicated and error prone to derive this condition from other related
8038   // flags that can be manipulated outside of a BeginLoad/EndLoad pair.
8039 
8040   // Part 1: Code that always executes to cleanup end of parsing, whether
8041   // that parsing was successful or not.
8042 
8043   // Drop the ref to our parser, if any, but keep hold of the sink so that we
8044   // can flush it from FlushPendingNotifications as needed.  We might have to
8045   // do that to get a StartLayout() to happen.
8046   if (mParser) {
8047     mWeakSink = do_GetWeakReference(mParser->GetContentSink());
8048     mParser = nullptr;
8049   }
8050 
8051   // Update the attributes on the PerformanceNavigationTiming before notifying
8052   // the onload observers.
8053   if (nsPIDOMWindowInner* window = GetInnerWindow()) {
8054     if (RefPtr<Performance> performance = window->GetPerformance()) {
8055       performance->UpdateNavigationTimingEntry();
8056     }
8057   }
8058 
8059   NS_DOCUMENT_NOTIFY_OBSERVERS(EndLoad, (this));
8060 
8061   // Part 2: Code that only executes when this EndLoad matches a BeginLoad.
8062 
8063   if (!mDidCallBeginLoad) {
8064     return;
8065   }
8066   mDidCallBeginLoad = false;
8067 
8068   UnblockDOMContentLoaded();
8069 
8070   if (turnOnEditing) {
8071     EditingStateChanged();
8072   }
8073 
8074   if (!GetWindow()) {
8075     // This is a document that's not in a window.  For example, this could be an
8076     // XMLHttpRequest responseXML document, or a document created via DOMParser
8077     // or DOMImplementation.  We don't reach this code normally for such
8078     // documents (which is not obviously correct), but can reach it via
8079     // document.open()/document.close().
8080     //
8081     // Such documents don't fire load events, but per spec should set their
8082     // readyState to "complete" when parsing and all loading of subresources is
8083     // done.  Parsing is done now, and documents not in a window don't load
8084     // subresources, so just go ahead and mark ourselves as complete.
8085     SetReadyStateInternal(Document::READYSTATE_COMPLETE,
8086                           /* updateTimingInformation = */ false);
8087 
8088     // Reset mSkipLoadEventAfterClose just in case.
8089     mSkipLoadEventAfterClose = false;
8090   }
8091 }
8092 
UnblockDOMContentLoaded()8093 void Document::UnblockDOMContentLoaded() {
8094   MOZ_ASSERT(mBlockDOMContentLoaded);
8095   if (--mBlockDOMContentLoaded != 0 || mDidFireDOMContentLoaded) {
8096     return;
8097   }
8098 
8099   MOZ_LOG(gDocumentLeakPRLog, LogLevel::Debug,
8100           ("DOCUMENT %p UnblockDOMContentLoaded", this));
8101 
8102   mDidFireDOMContentLoaded = true;
8103   if (PresShell* presShell = GetPresShell()) {
8104     presShell->GetRefreshDriver()->NotifyDOMContentLoaded();
8105   }
8106 
8107   MOZ_ASSERT(mReadyState == READYSTATE_INTERACTIVE);
8108   if (!mSynchronousDOMContentLoaded) {
8109     MOZ_RELEASE_ASSERT(NS_IsMainThread());
8110     nsCOMPtr<nsIRunnable> ev =
8111         NewRunnableMethod("Document::DispatchContentLoadedEvents", this,
8112                           &Document::DispatchContentLoadedEvents);
8113     Dispatch(TaskCategory::Other, ev.forget());
8114   } else {
8115     DispatchContentLoadedEvents();
8116   }
8117 }
8118 
ContentStateChanged(nsIContent * aContent,EventStates aStateMask)8119 void Document::ContentStateChanged(nsIContent* aContent,
8120                                    EventStates aStateMask) {
8121   MOZ_ASSERT(!nsContentUtils::IsSafeToRunScript(),
8122              "Someone forgot a scriptblocker");
8123   NS_DOCUMENT_NOTIFY_OBSERVERS(ContentStateChanged,
8124                                (this, aContent, aStateMask));
8125 }
8126 
RuleChanged(StyleSheet & aSheet,css::Rule *,StyleRuleChangeKind)8127 void Document::RuleChanged(StyleSheet& aSheet, css::Rule*,
8128                            StyleRuleChangeKind) {
8129   if (aSheet.IsApplicable()) {
8130     ApplicableStylesChanged();
8131   }
8132 }
8133 
RuleAdded(StyleSheet & aSheet,css::Rule & aRule)8134 void Document::RuleAdded(StyleSheet& aSheet, css::Rule& aRule) {
8135   if (aRule.IsIncompleteImportRule()) {
8136     return;
8137   }
8138 
8139   if (aSheet.IsApplicable()) {
8140     ApplicableStylesChanged();
8141   }
8142 }
8143 
ImportRuleLoaded(dom::CSSImportRule & aRule,StyleSheet & aSheet)8144 void Document::ImportRuleLoaded(dom::CSSImportRule& aRule, StyleSheet& aSheet) {
8145   if (aSheet.IsApplicable()) {
8146     ApplicableStylesChanged();
8147   }
8148 }
8149 
RuleRemoved(StyleSheet & aSheet,css::Rule & aRule)8150 void Document::RuleRemoved(StyleSheet& aSheet, css::Rule& aRule) {
8151   if (aSheet.IsApplicable()) {
8152     ApplicableStylesChanged();
8153   }
8154 }
8155 
GetCustomContentContainer(PresShell * aPresShell)8156 static Element* GetCustomContentContainer(PresShell* aPresShell) {
8157   if (!aPresShell || !aPresShell->GetCanvasFrame()) {
8158     return nullptr;
8159   }
8160 
8161   return aPresShell->GetCanvasFrame()->GetCustomContentContainer();
8162 }
8163 
InsertAnonymousContent(Element & aElement,bool aForce,ErrorResult & aRv)8164 already_AddRefed<AnonymousContent> Document::InsertAnonymousContent(
8165     Element& aElement, bool aForce, ErrorResult& aRv) {
8166   // Clone the node to avoid returning a direct reference.
8167   nsCOMPtr<nsINode> clone = aElement.CloneNode(true, aRv);
8168   if (aRv.Failed()) {
8169     return nullptr;
8170   }
8171 
8172   PresShell* shell = GetPresShell();
8173   if (aForce && !GetCustomContentContainer(shell)) {
8174     FlushPendingNotifications(FlushType::Layout);
8175     shell = GetPresShell();
8176   }
8177 
8178   nsAutoScriptBlocker scriptBlocker;
8179 
8180   auto anonContent =
8181       MakeRefPtr<AnonymousContent>(clone.forget().downcast<Element>());
8182 
8183   mAnonymousContents.AppendElement(anonContent);
8184 
8185   if (Element* container = GetCustomContentContainer(shell)) {
8186     container->AppendChildTo(&anonContent->ContentNode(), true, IgnoreErrors());
8187     shell->GetCanvasFrame()->ShowCustomContentContainer();
8188   }
8189 
8190   return anonContent.forget();
8191 }
8192 
RemoveAnonContentFromCanvas(AnonymousContent & aAnonContent,PresShell * aPresShell)8193 static void RemoveAnonContentFromCanvas(AnonymousContent& aAnonContent,
8194                                         PresShell* aPresShell) {
8195   RefPtr<Element> container = GetCustomContentContainer(aPresShell);
8196   if (!container) {
8197     return;
8198   }
8199   container->RemoveChild(aAnonContent.ContentNode(), IgnoreErrors());
8200 }
8201 
RemoveAnonymousContent(AnonymousContent & aContent,ErrorResult & aRv)8202 void Document::RemoveAnonymousContent(AnonymousContent& aContent,
8203                                       ErrorResult& aRv) {
8204   nsAutoScriptBlocker scriptBlocker;
8205 
8206   auto index = mAnonymousContents.IndexOf(&aContent);
8207   if (index == mAnonymousContents.NoIndex) {
8208     return;
8209   }
8210 
8211   mAnonymousContents.RemoveElementAt(index);
8212   RemoveAnonContentFromCanvas(aContent, GetPresShell());
8213 
8214   if (mAnonymousContents.IsEmpty() &&
8215       GetCustomContentContainer(GetPresShell())) {
8216     GetPresShell()->GetCanvasFrame()->HideCustomContentContainer();
8217   }
8218 }
8219 
GetAnonRootIfInAnonymousContentContainer(nsINode * aNode) const8220 Element* Document::GetAnonRootIfInAnonymousContentContainer(
8221     nsINode* aNode) const {
8222   if (!aNode->IsInNativeAnonymousSubtree()) {
8223     return nullptr;
8224   }
8225 
8226   PresShell* presShell = GetPresShell();
8227   if (!presShell || !presShell->GetCanvasFrame()) {
8228     return nullptr;
8229   }
8230 
8231   nsAutoScriptBlocker scriptBlocker;
8232   nsCOMPtr<Element> customContainer =
8233       presShell->GetCanvasFrame()->GetCustomContentContainer();
8234   if (!customContainer) {
8235     return nullptr;
8236   }
8237 
8238   // An arbitrary number of elements can be inserted as children of the custom
8239   // container frame.  We want the one that was added that contains aNode, so
8240   // we need to keep track of the last child separately using |child| here.
8241   nsINode* child = aNode;
8242   nsINode* parent = aNode->GetParentNode();
8243   while (parent && parent->IsInNativeAnonymousSubtree()) {
8244     if (parent == customContainer) {
8245       return Element::FromNode(child);
8246     }
8247     child = parent;
8248     parent = child->GetParentNode();
8249   }
8250   return nullptr;
8251 }
8252 
GetClientInfo() const8253 Maybe<ClientInfo> Document::GetClientInfo() const {
8254   if (const Document* orig = GetOriginalDocument()) {
8255     if (Maybe<ClientInfo> info = orig->GetClientInfo()) {
8256       return info;
8257     }
8258   }
8259 
8260   if (nsPIDOMWindowInner* inner = GetInnerWindow()) {
8261     return inner->GetClientInfo();
8262   }
8263 
8264   return Maybe<ClientInfo>();
8265 }
8266 
GetClientState() const8267 Maybe<ClientState> Document::GetClientState() const {
8268   if (const Document* orig = GetOriginalDocument()) {
8269     if (Maybe<ClientState> state = orig->GetClientState()) {
8270       return state;
8271     }
8272   }
8273 
8274   if (nsPIDOMWindowInner* inner = GetInnerWindow()) {
8275     return inner->GetClientState();
8276   }
8277 
8278   return Maybe<ClientState>();
8279 }
8280 
GetController() const8281 Maybe<ServiceWorkerDescriptor> Document::GetController() const {
8282   if (const Document* orig = GetOriginalDocument()) {
8283     if (Maybe<ServiceWorkerDescriptor> controller = orig->GetController()) {
8284       return controller;
8285     }
8286   }
8287 
8288   if (nsPIDOMWindowInner* inner = GetInnerWindow()) {
8289     return inner->GetController();
8290   }
8291 
8292   return Maybe<ServiceWorkerDescriptor>();
8293 }
8294 
8295 //
8296 // Document interface
8297 //
GetDoctype() const8298 DocumentType* Document::GetDoctype() const {
8299   for (nsIContent* child = GetFirstChild(); child;
8300        child = child->GetNextSibling()) {
8301     if (child->NodeType() == DOCUMENT_TYPE_NODE) {
8302       return static_cast<DocumentType*>(child);
8303     }
8304   }
8305   return nullptr;
8306 }
8307 
GetImplementation(ErrorResult & rv)8308 DOMImplementation* Document::GetImplementation(ErrorResult& rv) {
8309   if (!mDOMImplementation) {
8310     nsCOMPtr<nsIURI> uri;
8311     NS_NewURI(getter_AddRefs(uri), "about:blank");
8312     if (!uri) {
8313       rv.Throw(NS_ERROR_OUT_OF_MEMORY);
8314       return nullptr;
8315     }
8316     bool hasHadScriptObject = true;
8317     nsIScriptGlobalObject* scriptObject =
8318         GetScriptHandlingObject(hasHadScriptObject);
8319     if (!scriptObject && hasHadScriptObject) {
8320       rv.Throw(NS_ERROR_UNEXPECTED);
8321       return nullptr;
8322     }
8323     mDOMImplementation = new DOMImplementation(
8324         this, scriptObject ? scriptObject : GetScopeObject(), uri, uri);
8325   }
8326 
8327   return mDOMImplementation;
8328 }
8329 
IsLowercaseASCII(const nsAString & aValue)8330 bool IsLowercaseASCII(const nsAString& aValue) {
8331   int32_t len = aValue.Length();
8332   for (int32_t i = 0; i < len; ++i) {
8333     char16_t c = aValue[i];
8334     if (!(0x0061 <= (c) && ((c) <= 0x007a))) {
8335       return false;
8336     }
8337   }
8338   return true;
8339 }
8340 
CreateElement(const nsAString & aTagName,const ElementCreationOptionsOrString & aOptions,ErrorResult & rv)8341 already_AddRefed<Element> Document::CreateElement(
8342     const nsAString& aTagName, const ElementCreationOptionsOrString& aOptions,
8343     ErrorResult& rv) {
8344   rv = nsContentUtils::CheckQName(aTagName, false);
8345   if (rv.Failed()) {
8346     return nullptr;
8347   }
8348 
8349   bool needsLowercase = IsHTMLDocument() && !IsLowercaseASCII(aTagName);
8350   nsAutoString lcTagName;
8351   if (needsLowercase) {
8352     nsContentUtils::ASCIIToLower(aTagName, lcTagName);
8353   }
8354 
8355   const nsString* is = nullptr;
8356   PseudoStyleType pseudoType = PseudoStyleType::NotPseudo;
8357   if (aOptions.IsElementCreationOptions()) {
8358     const ElementCreationOptions& options =
8359         aOptions.GetAsElementCreationOptions();
8360 
8361     if (options.mIs.WasPassed()) {
8362       is = &options.mIs.Value();
8363     }
8364 
8365     // Check 'pseudo' and throw an exception if it's not one allowed
8366     // with CSS_PSEUDO_ELEMENT_IS_JS_CREATED_NAC.
8367     if (options.mPseudo.WasPassed()) {
8368       Maybe<PseudoStyleType> type =
8369           nsCSSPseudoElements::GetPseudoType(options.mPseudo.Value());
8370       if (!type || *type == PseudoStyleType::NotPseudo ||
8371           !nsCSSPseudoElements::PseudoElementIsJSCreatedNAC(*type)) {
8372         rv.ThrowNotSupportedError("Invalid pseudo-element");
8373         return nullptr;
8374       }
8375       pseudoType = *type;
8376     }
8377   }
8378 
8379   RefPtr<Element> elem = CreateElem(needsLowercase ? lcTagName : aTagName,
8380                                     nullptr, mDefaultElementType, is);
8381 
8382   if (pseudoType != PseudoStyleType::NotPseudo) {
8383     elem->SetPseudoElementType(pseudoType);
8384   }
8385 
8386   return elem.forget();
8387 }
8388 
CreateElementNS(const nsAString & aNamespaceURI,const nsAString & aQualifiedName,const ElementCreationOptionsOrString & aOptions,ErrorResult & rv)8389 already_AddRefed<Element> Document::CreateElementNS(
8390     const nsAString& aNamespaceURI, const nsAString& aQualifiedName,
8391     const ElementCreationOptionsOrString& aOptions, ErrorResult& rv) {
8392   RefPtr<mozilla::dom::NodeInfo> nodeInfo;
8393   rv = nsContentUtils::GetNodeInfoFromQName(aNamespaceURI, aQualifiedName,
8394                                             mNodeInfoManager, ELEMENT_NODE,
8395                                             getter_AddRefs(nodeInfo));
8396   if (rv.Failed()) {
8397     return nullptr;
8398   }
8399 
8400   const nsString* is = nullptr;
8401   if (aOptions.IsElementCreationOptions()) {
8402     const ElementCreationOptions& options =
8403         aOptions.GetAsElementCreationOptions();
8404     if (options.mIs.WasPassed()) {
8405       is = &options.mIs.Value();
8406     }
8407   }
8408 
8409   nsCOMPtr<Element> element;
8410   rv = NS_NewElement(getter_AddRefs(element), nodeInfo.forget(),
8411                      NOT_FROM_PARSER, is);
8412   if (rv.Failed()) {
8413     return nullptr;
8414   }
8415 
8416   return element.forget();
8417 }
8418 
CreateXULElement(const nsAString & aTagName,const ElementCreationOptionsOrString & aOptions,ErrorResult & aRv)8419 already_AddRefed<Element> Document::CreateXULElement(
8420     const nsAString& aTagName, const ElementCreationOptionsOrString& aOptions,
8421     ErrorResult& aRv) {
8422   aRv = nsContentUtils::CheckQName(aTagName, false);
8423   if (aRv.Failed()) {
8424     return nullptr;
8425   }
8426 
8427   const nsString* is = nullptr;
8428   if (aOptions.IsElementCreationOptions()) {
8429     const ElementCreationOptions& options =
8430         aOptions.GetAsElementCreationOptions();
8431     if (options.mIs.WasPassed()) {
8432       is = &options.mIs.Value();
8433     }
8434   }
8435 
8436   RefPtr<Element> elem = CreateElem(aTagName, nullptr, kNameSpaceID_XUL, is);
8437   if (!elem) {
8438     aRv.Throw(NS_ERROR_NOT_AVAILABLE);
8439     return nullptr;
8440   }
8441   return elem.forget();
8442 }
8443 
CreateEmptyTextNode() const8444 already_AddRefed<nsTextNode> Document::CreateEmptyTextNode() const {
8445   RefPtr<nsTextNode> text = new (mNodeInfoManager) nsTextNode(mNodeInfoManager);
8446   return text.forget();
8447 }
8448 
CreateTextNode(const nsAString & aData) const8449 already_AddRefed<nsTextNode> Document::CreateTextNode(
8450     const nsAString& aData) const {
8451   RefPtr<nsTextNode> text = new (mNodeInfoManager) nsTextNode(mNodeInfoManager);
8452   // Don't notify; this node is still being created.
8453   text->SetText(aData, false);
8454   return text.forget();
8455 }
8456 
CreateDocumentFragment() const8457 already_AddRefed<DocumentFragment> Document::CreateDocumentFragment() const {
8458   RefPtr<DocumentFragment> frag =
8459       new (mNodeInfoManager) DocumentFragment(mNodeInfoManager);
8460   return frag.forget();
8461 }
8462 
8463 // Unfortunately, bareword "Comment" is ambiguous with some Mac system headers.
CreateComment(const nsAString & aData) const8464 already_AddRefed<dom::Comment> Document::CreateComment(
8465     const nsAString& aData) const {
8466   RefPtr<dom::Comment> comment =
8467       new (mNodeInfoManager) dom::Comment(mNodeInfoManager);
8468 
8469   // Don't notify; this node is still being created.
8470   comment->SetText(aData, false);
8471   return comment.forget();
8472 }
8473 
CreateCDATASection(const nsAString & aData,ErrorResult & rv)8474 already_AddRefed<CDATASection> Document::CreateCDATASection(
8475     const nsAString& aData, ErrorResult& rv) {
8476   if (IsHTMLDocument()) {
8477     rv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
8478     return nullptr;
8479   }
8480 
8481   if (FindInReadable(u"]]>"_ns, aData)) {
8482     rv.Throw(NS_ERROR_DOM_INVALID_CHARACTER_ERR);
8483     return nullptr;
8484   }
8485 
8486   RefPtr<CDATASection> cdata =
8487       new (mNodeInfoManager) CDATASection(mNodeInfoManager);
8488 
8489   // Don't notify; this node is still being created.
8490   cdata->SetText(aData, false);
8491 
8492   return cdata.forget();
8493 }
8494 
CreateProcessingInstruction(const nsAString & aTarget,const nsAString & aData,ErrorResult & rv) const8495 already_AddRefed<ProcessingInstruction> Document::CreateProcessingInstruction(
8496     const nsAString& aTarget, const nsAString& aData, ErrorResult& rv) const {
8497   nsresult res = nsContentUtils::CheckQName(aTarget, false);
8498   if (NS_FAILED(res)) {
8499     rv.Throw(res);
8500     return nullptr;
8501   }
8502 
8503   if (FindInReadable(u"?>"_ns, aData)) {
8504     rv.Throw(NS_ERROR_DOM_INVALID_CHARACTER_ERR);
8505     return nullptr;
8506   }
8507 
8508   RefPtr<ProcessingInstruction> pi =
8509       NS_NewXMLProcessingInstruction(mNodeInfoManager, aTarget, aData);
8510 
8511   return pi.forget();
8512 }
8513 
CreateAttribute(const nsAString & aName,ErrorResult & rv)8514 already_AddRefed<Attr> Document::CreateAttribute(const nsAString& aName,
8515                                                  ErrorResult& rv) {
8516   if (!mNodeInfoManager) {
8517     rv.Throw(NS_ERROR_NOT_INITIALIZED);
8518     return nullptr;
8519   }
8520 
8521   nsresult res = nsContentUtils::CheckQName(aName, false);
8522   if (NS_FAILED(res)) {
8523     rv.Throw(res);
8524     return nullptr;
8525   }
8526 
8527   nsAutoString name;
8528   if (IsHTMLDocument()) {
8529     nsContentUtils::ASCIIToLower(aName, name);
8530   } else {
8531     name = aName;
8532   }
8533 
8534   RefPtr<mozilla::dom::NodeInfo> nodeInfo;
8535   res = mNodeInfoManager->GetNodeInfo(name, nullptr, kNameSpaceID_None,
8536                                       ATTRIBUTE_NODE, getter_AddRefs(nodeInfo));
8537   if (NS_FAILED(res)) {
8538     rv.Throw(res);
8539     return nullptr;
8540   }
8541 
8542   RefPtr<Attr> attribute =
8543       new (mNodeInfoManager) Attr(nullptr, nodeInfo.forget(), u""_ns);
8544   return attribute.forget();
8545 }
8546 
CreateAttributeNS(const nsAString & aNamespaceURI,const nsAString & aQualifiedName,ErrorResult & rv)8547 already_AddRefed<Attr> Document::CreateAttributeNS(
8548     const nsAString& aNamespaceURI, const nsAString& aQualifiedName,
8549     ErrorResult& rv) {
8550   RefPtr<mozilla::dom::NodeInfo> nodeInfo;
8551   rv = nsContentUtils::GetNodeInfoFromQName(aNamespaceURI, aQualifiedName,
8552                                             mNodeInfoManager, ATTRIBUTE_NODE,
8553                                             getter_AddRefs(nodeInfo));
8554   if (rv.Failed()) {
8555     return nullptr;
8556   }
8557 
8558   RefPtr<Attr> attribute =
8559       new (mNodeInfoManager) Attr(nullptr, nodeInfo.forget(), u""_ns);
8560   return attribute.forget();
8561 }
8562 
ResolveScheduledSVGPresAttrs()8563 void Document::ResolveScheduledSVGPresAttrs() {
8564   for (SVGElement* svg : mLazySVGPresElements) {
8565     svg->UpdateContentDeclarationBlock();
8566   }
8567   mLazySVGPresElements.Clear();
8568 }
8569 
BlockedNodesByClassifier() const8570 already_AddRefed<nsSimpleContentList> Document::BlockedNodesByClassifier()
8571     const {
8572   RefPtr<nsSimpleContentList> list = new nsSimpleContentList(nullptr);
8573 
8574   const nsTArray<nsWeakPtr> blockedNodes = mBlockedNodesByClassifier.Clone();
8575 
8576   for (unsigned long i = 0; i < blockedNodes.Length(); i++) {
8577     nsWeakPtr weakNode = blockedNodes[i];
8578     nsCOMPtr<nsIContent> node = do_QueryReferent(weakNode);
8579     // Consider only nodes to which we have managed to get strong references.
8580     // Coping with nullptrs since it's expected for nodes to disappear when
8581     // nobody else is referring to them.
8582     if (node) {
8583       list->AppendElement(node);
8584     }
8585   }
8586 
8587   return list.forget();
8588 }
8589 
GetSelectedStyleSheetSet(nsAString & aSheetSet)8590 void Document::GetSelectedStyleSheetSet(nsAString& aSheetSet) {
8591   aSheetSet.Truncate();
8592 
8593   // Look through our sheets, find the selected set title
8594   size_t count = SheetCount();
8595   nsAutoString title;
8596   for (size_t index = 0; index < count; index++) {
8597     StyleSheet* sheet = SheetAt(index);
8598     NS_ASSERTION(sheet, "Null sheet in sheet list!");
8599 
8600     if (sheet->Disabled()) {
8601       // Disabled sheets don't affect the currently selected set
8602       continue;
8603     }
8604 
8605     sheet->GetTitle(title);
8606 
8607     if (aSheetSet.IsEmpty()) {
8608       aSheetSet = title;
8609     } else if (!title.IsEmpty() && !aSheetSet.Equals(title)) {
8610       // Sheets from multiple sets enabled; return null string, per spec.
8611       SetDOMStringToNull(aSheetSet);
8612       return;
8613     }
8614   }
8615 }
8616 
SetSelectedStyleSheetSet(const nsAString & aSheetSet)8617 void Document::SetSelectedStyleSheetSet(const nsAString& aSheetSet) {
8618   if (DOMStringIsNull(aSheetSet)) {
8619     return;
8620   }
8621 
8622   // Must update mLastStyleSheetSet before doing anything else with stylesheets
8623   // or CSSLoaders.
8624   mLastStyleSheetSet = aSheetSet;
8625   EnableStyleSheetsForSetInternal(aSheetSet, true);
8626 }
8627 
SetPreferredStyleSheetSet(const nsAString & aSheetSet)8628 void Document::SetPreferredStyleSheetSet(const nsAString& aSheetSet) {
8629   mPreferredStyleSheetSet = aSheetSet;
8630   // Only mess with our stylesheets if we don't have a lastStyleSheetSet, per
8631   // spec.
8632   if (DOMStringIsNull(mLastStyleSheetSet)) {
8633     // Calling EnableStyleSheetsForSetInternal, not SetSelectedStyleSheetSet,
8634     // per spec.  The idea here is that we're changing our preferred set and
8635     // that shouldn't change the value of lastStyleSheetSet.  Also, we're
8636     // using the Internal version so we can update the CSSLoader and not have
8637     // to worry about null strings.
8638     EnableStyleSheetsForSetInternal(aSheetSet, true);
8639   }
8640 }
8641 
StyleSheetSets()8642 DOMStringList* Document::StyleSheetSets() {
8643   if (!mStyleSheetSetList) {
8644     mStyleSheetSetList = new DOMStyleSheetSetList(this);
8645   }
8646   return mStyleSheetSetList;
8647 }
8648 
EnableStyleSheetsForSet(const nsAString & aSheetSet)8649 void Document::EnableStyleSheetsForSet(const nsAString& aSheetSet) {
8650   // Per spec, passing in null is a no-op.
8651   if (!DOMStringIsNull(aSheetSet)) {
8652     // Note: must make sure to not change the CSSLoader's preferred sheet --
8653     // that value should be equal to either our lastStyleSheetSet (if that's
8654     // non-null) or to our preferredStyleSheetSet.  And this method doesn't
8655     // change either of those.
8656     EnableStyleSheetsForSetInternal(aSheetSet, false);
8657   }
8658 }
8659 
EnableStyleSheetsForSetInternal(const nsAString & aSheetSet,bool aUpdateCSSLoader)8660 void Document::EnableStyleSheetsForSetInternal(const nsAString& aSheetSet,
8661                                                bool aUpdateCSSLoader) {
8662   size_t count = SheetCount();
8663   nsAutoString title;
8664   for (size_t index = 0; index < count; index++) {
8665     StyleSheet* sheet = SheetAt(index);
8666     NS_ASSERTION(sheet, "Null sheet in sheet list!");
8667 
8668     sheet->GetTitle(title);
8669     if (!title.IsEmpty()) {
8670       sheet->SetEnabled(title.Equals(aSheetSet));
8671     }
8672   }
8673   if (aUpdateCSSLoader) {
8674     CSSLoader()->DocumentStyleSheetSetChanged();
8675   }
8676   if (mStyleSet->StyleSheetsHaveChanged()) {
8677     ApplicableStylesChanged();
8678   }
8679 }
8680 
GetCharacterSet(nsAString & aCharacterSet) const8681 void Document::GetCharacterSet(nsAString& aCharacterSet) const {
8682   nsAutoCString charset;
8683   GetDocumentCharacterSet()->Name(charset);
8684   CopyASCIItoUTF16(charset, aCharacterSet);
8685 }
8686 
ImportNode(nsINode & aNode,bool aDeep,ErrorResult & rv) const8687 already_AddRefed<nsINode> Document::ImportNode(nsINode& aNode, bool aDeep,
8688                                                ErrorResult& rv) const {
8689   nsINode* imported = &aNode;
8690 
8691   switch (imported->NodeType()) {
8692     case DOCUMENT_NODE: {
8693       break;
8694     }
8695     case DOCUMENT_FRAGMENT_NODE:
8696     case ATTRIBUTE_NODE:
8697     case ELEMENT_NODE:
8698     case PROCESSING_INSTRUCTION_NODE:
8699     case TEXT_NODE:
8700     case CDATA_SECTION_NODE:
8701     case COMMENT_NODE:
8702     case DOCUMENT_TYPE_NODE: {
8703       return imported->Clone(aDeep, mNodeInfoManager, rv);
8704     }
8705     default: {
8706       NS_WARNING("Don't know how to clone this nodetype for importNode.");
8707     }
8708   }
8709 
8710   rv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
8711   return nullptr;
8712 }
8713 
CreateRange(ErrorResult & rv)8714 already_AddRefed<nsRange> Document::CreateRange(ErrorResult& rv) {
8715   return nsRange::Create(this, 0, this, 0, rv);
8716 }
8717 
CreateNodeIterator(nsINode & aRoot,uint32_t aWhatToShow,NodeFilter * aFilter,ErrorResult & rv) const8718 already_AddRefed<NodeIterator> Document::CreateNodeIterator(
8719     nsINode& aRoot, uint32_t aWhatToShow, NodeFilter* aFilter,
8720     ErrorResult& rv) const {
8721   RefPtr<NodeIterator> iterator =
8722       new NodeIterator(&aRoot, aWhatToShow, aFilter);
8723   return iterator.forget();
8724 }
8725 
CreateTreeWalker(nsINode & aRoot,uint32_t aWhatToShow,NodeFilter * aFilter,ErrorResult & rv) const8726 already_AddRefed<TreeWalker> Document::CreateTreeWalker(nsINode& aRoot,
8727                                                         uint32_t aWhatToShow,
8728                                                         NodeFilter* aFilter,
8729                                                         ErrorResult& rv) const {
8730   RefPtr<TreeWalker> walker = new TreeWalker(&aRoot, aWhatToShow, aFilter);
8731   return walker.forget();
8732 }
8733 
GetLocation() const8734 already_AddRefed<Location> Document::GetLocation() const {
8735   nsCOMPtr<nsPIDOMWindowInner> w = do_QueryInterface(mScriptGlobalObject);
8736 
8737   if (!w) {
8738     return nullptr;
8739   }
8740 
8741   return do_AddRef(w->Location());
8742 }
8743 
GetDomainURI()8744 already_AddRefed<nsIURI> Document::GetDomainURI() {
8745   nsIPrincipal* principal = NodePrincipal();
8746 
8747   nsCOMPtr<nsIURI> uri;
8748   principal->GetDomain(getter_AddRefs(uri));
8749   if (uri) {
8750     return uri.forget();
8751   }
8752   auto* basePrin = BasePrincipal::Cast(principal);
8753   basePrin->GetURI(getter_AddRefs(uri));
8754   return uri.forget();
8755 }
8756 
GetDomain(nsAString & aDomain)8757 void Document::GetDomain(nsAString& aDomain) {
8758   nsCOMPtr<nsIURI> uri = GetDomainURI();
8759 
8760   if (!uri) {
8761     aDomain.Truncate();
8762     return;
8763   }
8764 
8765   nsAutoCString hostName;
8766   nsresult rv = nsContentUtils::GetHostOrIPv6WithBrackets(uri, hostName);
8767   if (NS_SUCCEEDED(rv)) {
8768     CopyUTF8toUTF16(hostName, aDomain);
8769   } else {
8770     // If we can't get the host from the URI (e.g. about:, javascript:,
8771     // etc), just return an empty string.
8772     aDomain.Truncate();
8773   }
8774 }
8775 
SetDomain(const nsAString & aDomain,ErrorResult & rv)8776 void Document::SetDomain(const nsAString& aDomain, ErrorResult& rv) {
8777   if (!GetBrowsingContext()) {
8778     // If our browsing context is null; disallow setting domain
8779     rv.Throw(NS_ERROR_DOM_SECURITY_ERR);
8780     return;
8781   }
8782 
8783   if (mSandboxFlags & SANDBOXED_DOMAIN) {
8784     // We're sandboxed; disallow setting domain
8785     rv.Throw(NS_ERROR_DOM_SECURITY_ERR);
8786     return;
8787   }
8788 
8789   if (!FeaturePolicyUtils::IsFeatureAllowed(this, u"document-domain"_ns)) {
8790     rv.Throw(NS_ERROR_DOM_SECURITY_ERR);
8791     return;
8792   }
8793 
8794   if (aDomain.IsEmpty()) {
8795     rv.Throw(NS_ERROR_DOM_SECURITY_ERR);
8796     return;
8797   }
8798 
8799   nsCOMPtr<nsIURI> uri = GetDomainURI();
8800   if (!uri) {
8801     rv.Throw(NS_ERROR_FAILURE);
8802     return;
8803   }
8804 
8805   // Check new domain - must be a superdomain of the current host
8806   // For example, a page from foo.bar.com may set domain to bar.com,
8807   // but not to ar.com, baz.com, or fi.foo.bar.com.
8808 
8809   nsCOMPtr<nsIURI> newURI = RegistrableDomainSuffixOfInternal(aDomain, uri);
8810   if (!newURI) {
8811     // Error: illegal domain
8812     rv.Throw(NS_ERROR_DOM_SECURITY_ERR);
8813     return;
8814   }
8815 
8816   if (CrossOriginIsolated()) {
8817     WarnOnceAbout(Document::eDocumentSetDomainNotAllowed);
8818     return;
8819   }
8820 
8821   MOZ_ALWAYS_SUCCEEDS(NodePrincipal()->SetDomain(newURI));
8822   MOZ_ALWAYS_SUCCEEDS(PartitionedPrincipal()->SetDomain(newURI));
8823   WindowGlobalChild* wgc = GetWindowGlobalChild();
8824   if (wgc) {
8825     wgc->SendSetDocumentDomain(newURI);
8826   }
8827 }
8828 
CreateInheritingURIForHost(const nsACString & aHostString)8829 already_AddRefed<nsIURI> Document::CreateInheritingURIForHost(
8830     const nsACString& aHostString) {
8831   if (aHostString.IsEmpty()) {
8832     return nullptr;
8833   }
8834 
8835   // Create new URI
8836   nsCOMPtr<nsIURI> uri = GetDomainURI();
8837   if (!uri) {
8838     return nullptr;
8839   }
8840 
8841   nsresult rv;
8842   rv = NS_MutateURI(uri)
8843            .SetUserPass(""_ns)
8844            .SetPort(-1)  // we want to reset the port number if needed.
8845            .SetHostPort(aHostString)
8846            .Finalize(uri);
8847   if (NS_FAILED(rv)) {
8848     return nullptr;
8849   }
8850 
8851   return uri.forget();
8852 }
8853 
RegistrableDomainSuffixOfInternal(const nsAString & aNewDomain,nsIURI * aOrigHost)8854 already_AddRefed<nsIURI> Document::RegistrableDomainSuffixOfInternal(
8855     const nsAString& aNewDomain, nsIURI* aOrigHost) {
8856   if (NS_WARN_IF(!aOrigHost)) {
8857     return nullptr;
8858   }
8859 
8860   nsCOMPtr<nsIURI> newURI =
8861       CreateInheritingURIForHost(NS_ConvertUTF16toUTF8(aNewDomain));
8862   if (!newURI) {
8863     // Error: failed to parse input domain
8864     return nullptr;
8865   }
8866 
8867   if (!IsValidDomain(aOrigHost, newURI)) {
8868     // Error: illegal domain
8869     return nullptr;
8870   }
8871 
8872   nsAutoCString domain;
8873   if (NS_FAILED(newURI->GetAsciiHost(domain))) {
8874     return nullptr;
8875   }
8876 
8877   return CreateInheritingURIForHost(domain);
8878 }
8879 
8880 /* static */
IsValidDomain(nsIURI * aOrigHost,nsIURI * aNewURI)8881 bool Document::IsValidDomain(nsIURI* aOrigHost, nsIURI* aNewURI) {
8882   // Check new domain - must be a superdomain of the current host
8883   // For example, a page from foo.bar.com may set domain to bar.com,
8884   // but not to ar.com, baz.com, or fi.foo.bar.com.
8885   nsAutoCString current;
8886   nsAutoCString domain;
8887   if (NS_FAILED(aOrigHost->GetAsciiHost(current))) {
8888     current.Truncate();
8889   }
8890   if (NS_FAILED(aNewURI->GetAsciiHost(domain))) {
8891     domain.Truncate();
8892   }
8893 
8894   bool ok = current.Equals(domain);
8895   if (current.Length() > domain.Length() && StringEndsWith(current, domain) &&
8896       current.CharAt(current.Length() - domain.Length() - 1) == '.') {
8897     // We're golden if the new domain is the current page's base domain or a
8898     // subdomain of it.
8899     nsCOMPtr<nsIEffectiveTLDService> tldService =
8900         do_GetService(NS_EFFECTIVETLDSERVICE_CONTRACTID);
8901     if (!tldService) {
8902       return false;
8903     }
8904 
8905     nsAutoCString currentBaseDomain;
8906     ok = NS_SUCCEEDED(
8907         tldService->GetBaseDomain(aOrigHost, 0, currentBaseDomain));
8908     NS_ASSERTION(StringEndsWith(domain, currentBaseDomain) ==
8909                      (domain.Length() >= currentBaseDomain.Length()),
8910                  "uh-oh!  slight optimization wasn't valid somehow!");
8911     ok = ok && domain.Length() >= currentBaseDomain.Length();
8912   }
8913 
8914   return ok;
8915 }
8916 
GetHtmlElement() const8917 Element* Document::GetHtmlElement() const {
8918   Element* rootElement = GetRootElement();
8919   if (rootElement && rootElement->IsHTMLElement(nsGkAtoms::html))
8920     return rootElement;
8921   return nullptr;
8922 }
8923 
GetHtmlChildElement(nsAtom * aTag)8924 Element* Document::GetHtmlChildElement(nsAtom* aTag) {
8925   Element* html = GetHtmlElement();
8926   if (!html) return nullptr;
8927 
8928   // Look for the element with aTag inside html. This needs to run
8929   // forwards to find the first such element.
8930   for (nsIContent* child = html->GetFirstChild(); child;
8931        child = child->GetNextSibling()) {
8932     if (child->IsHTMLElement(aTag)) return child->AsElement();
8933   }
8934   return nullptr;
8935 }
8936 
GetBody()8937 nsGenericHTMLElement* Document::GetBody() {
8938   Element* html = GetHtmlElement();
8939   if (!html) {
8940     return nullptr;
8941   }
8942 
8943   for (nsIContent* child = html->GetFirstChild(); child;
8944        child = child->GetNextSibling()) {
8945     if (child->IsHTMLElement(nsGkAtoms::body) ||
8946         child->IsHTMLElement(nsGkAtoms::frameset)) {
8947       return static_cast<nsGenericHTMLElement*>(child);
8948     }
8949   }
8950 
8951   return nullptr;
8952 }
8953 
SetBody(nsGenericHTMLElement * newBody,ErrorResult & rv)8954 void Document::SetBody(nsGenericHTMLElement* newBody, ErrorResult& rv) {
8955   nsCOMPtr<Element> root = GetRootElement();
8956 
8957   // The body element must be either a body tag or a frameset tag. And we must
8958   // have a root element to be able to add kids to it.
8959   if (!newBody ||
8960       !newBody->IsAnyOfHTMLElements(nsGkAtoms::body, nsGkAtoms::frameset)) {
8961     rv.ThrowHierarchyRequestError(
8962         "The new body must be either a body tag or frameset tag.");
8963     return;
8964   }
8965 
8966   if (!root) {
8967     rv.ThrowHierarchyRequestError("No root element.");
8968     return;
8969   }
8970 
8971   // Use DOM methods so that we pass through the appropriate security checks.
8972   nsCOMPtr<Element> currentBody = GetBody();
8973   if (currentBody) {
8974     root->ReplaceChild(*newBody, *currentBody, rv);
8975   } else {
8976     root->AppendChild(*newBody, rv);
8977   }
8978 }
8979 
GetHead()8980 HTMLSharedElement* Document::GetHead() {
8981   return static_cast<HTMLSharedElement*>(GetHeadElement());
8982 }
8983 
GetTitleElement()8984 Element* Document::GetTitleElement() {
8985   // mMayHaveTitleElement will have been set to true if any HTML or SVG
8986   // <title> element has been bound to this document. So if it's false,
8987   // we know there is nothing to do here. This avoids us having to search
8988   // the whole DOM if someone calls document.title on a large document
8989   // without a title.
8990   if (!mMayHaveTitleElement) return nullptr;
8991 
8992   Element* root = GetRootElement();
8993   if (root && root->IsSVGElement(nsGkAtoms::svg)) {
8994     // In SVG, the document's title must be a child
8995     for (nsIContent* child = root->GetFirstChild(); child;
8996          child = child->GetNextSibling()) {
8997       if (child->IsSVGElement(nsGkAtoms::title)) {
8998         return child->AsElement();
8999       }
9000     }
9001     return nullptr;
9002   }
9003 
9004   // We check the HTML namespace even for non-HTML documents, except SVG.  This
9005   // matches the spec and the behavior of all tested browsers.
9006   // We avoid creating a live nsContentList since we don't need to watch for DOM
9007   // tree mutations.
9008   RefPtr<nsContentList> list = new nsContentList(
9009       this, kNameSpaceID_XHTML, nsGkAtoms::title, nsGkAtoms::title,
9010       /* aDeep = */ true,
9011       /* aLiveList = */ false);
9012 
9013   nsIContent* first = list->Item(0, false);
9014 
9015   return first ? first->AsElement() : nullptr;
9016 }
9017 
GetTitle(nsAString & aTitle)9018 void Document::GetTitle(nsAString& aTitle) {
9019   aTitle.Truncate();
9020 
9021   Element* rootElement = GetRootElement();
9022   if (!rootElement) {
9023     return;
9024   }
9025 
9026   nsAutoString tmp;
9027 
9028   if (rootElement->IsXULElement()) {
9029     rootElement->GetAttr(kNameSpaceID_None, nsGkAtoms::title, tmp);
9030   } else {
9031     Element* title = GetTitleElement();
9032     if (!title) {
9033       return;
9034     }
9035     nsContentUtils::GetNodeTextContent(title, false, tmp);
9036   }
9037 
9038   tmp.CompressWhitespace();
9039   aTitle = tmp;
9040 }
9041 
SetTitle(const nsAString & aTitle,ErrorResult & aRv)9042 void Document::SetTitle(const nsAString& aTitle, ErrorResult& aRv) {
9043   Element* rootElement = GetRootElement();
9044   if (!rootElement) {
9045     return;
9046   }
9047 
9048   if (rootElement->IsXULElement()) {
9049     aRv =
9050         rootElement->SetAttr(kNameSpaceID_None, nsGkAtoms::title, aTitle, true);
9051     return;
9052   }
9053 
9054   Maybe<mozAutoDocUpdate> updateBatch;
9055   nsCOMPtr<Element> title = GetTitleElement();
9056   if (rootElement->IsSVGElement(nsGkAtoms::svg)) {
9057     if (!title) {
9058       // Batch updates so that mutation events don't change "the title
9059       // element" under us
9060       updateBatch.emplace(this, true);
9061       RefPtr<mozilla::dom::NodeInfo> titleInfo = mNodeInfoManager->GetNodeInfo(
9062           nsGkAtoms::title, nullptr, kNameSpaceID_SVG, ELEMENT_NODE);
9063       NS_NewSVGElement(getter_AddRefs(title), titleInfo.forget(),
9064                        NOT_FROM_PARSER);
9065       if (!title) {
9066         return;
9067       }
9068       rootElement->InsertChildBefore(title, rootElement->GetFirstChild(), true,
9069                                      IgnoreErrors());
9070     }
9071   } else if (rootElement->IsHTMLElement()) {
9072     if (!title) {
9073       // Batch updates so that mutation events don't change "the title
9074       // element" under us
9075       updateBatch.emplace(this, true);
9076       Element* head = GetHeadElement();
9077       if (!head) {
9078         return;
9079       }
9080 
9081       RefPtr<mozilla::dom::NodeInfo> titleInfo;
9082       titleInfo = mNodeInfoManager->GetNodeInfo(
9083           nsGkAtoms::title, nullptr, kNameSpaceID_XHTML, ELEMENT_NODE);
9084       title = NS_NewHTMLTitleElement(titleInfo.forget());
9085       if (!title) {
9086         return;
9087       }
9088 
9089       head->AppendChildTo(title, true, IgnoreErrors());
9090     }
9091   } else {
9092     return;
9093   }
9094 
9095   aRv = nsContentUtils::SetNodeTextContent(title, aTitle, false);
9096 }
9097 
NotifyPossibleTitleChange(bool aBoundTitleElement)9098 void Document::NotifyPossibleTitleChange(bool aBoundTitleElement) {
9099   NS_ASSERTION(!mInUnlinkOrDeletion || !aBoundTitleElement,
9100                "Setting a title while unlinking or destroying the element?");
9101   if (mInUnlinkOrDeletion) {
9102     return;
9103   }
9104 
9105   if (aBoundTitleElement) {
9106     mMayHaveTitleElement = true;
9107   }
9108   if (mPendingTitleChangeEvent.IsPending()) return;
9109 
9110   MOZ_RELEASE_ASSERT(NS_IsMainThread());
9111   RefPtr<nsRunnableMethod<Document, void, false>> event =
9112       NewNonOwningRunnableMethod("Document::DoNotifyPossibleTitleChange", this,
9113                                  &Document::DoNotifyPossibleTitleChange);
9114   nsresult rv = Dispatch(TaskCategory::Other, do_AddRef(event));
9115   if (NS_SUCCEEDED(rv)) {
9116     mPendingTitleChangeEvent = std::move(event);
9117   }
9118 }
9119 
DoNotifyPossibleTitleChange()9120 void Document::DoNotifyPossibleTitleChange() {
9121   if (!mPendingTitleChangeEvent.IsPending()) {
9122     return;
9123   }
9124   // Make sure the pending runnable method is cleared.
9125   mPendingTitleChangeEvent.Revoke();
9126   mHaveFiredTitleChange = true;
9127 
9128   nsAutoString title;
9129   GetTitle(title);
9130 
9131   RefPtr<PresShell> presShell = GetPresShell();
9132   if (presShell) {
9133     nsCOMPtr<nsISupports> container =
9134         presShell->GetPresContext()->GetContainerWeak();
9135     if (container) {
9136       nsCOMPtr<nsIBaseWindow> docShellWin = do_QueryInterface(container);
9137       if (docShellWin) {
9138         docShellWin->SetTitle(title);
9139       }
9140     }
9141   }
9142 
9143   if (nsPIDOMWindowInner* inner = GetInnerWindow()) {
9144     if (WindowGlobalChild* child = inner->GetWindowGlobalChild()) {
9145       child->SendUpdateDocumentTitle(title);
9146     }
9147   }
9148 
9149   // Fire a DOM event for the title change.
9150   nsContentUtils::DispatchChromeEvent(this, ToSupports(this),
9151                                       u"DOMTitleChanged"_ns, CanBubble::eYes,
9152                                       Cancelable::eYes);
9153 
9154   nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
9155   if (obs) {
9156     obs->NotifyObservers(ToSupports(this), "document-title-changed", nullptr);
9157   }
9158 }
9159 
MatchMedia(const nsACString & aMediaQueryList,CallerType aCallerType)9160 already_AddRefed<MediaQueryList> Document::MatchMedia(
9161     const nsACString& aMediaQueryList, CallerType aCallerType) {
9162   RefPtr<MediaQueryList> result =
9163       new MediaQueryList(this, aMediaQueryList, aCallerType);
9164 
9165   mDOMMediaQueryLists.insertBack(result);
9166 
9167   return result.forget();
9168 }
9169 
SetMayStartLayout(bool aMayStartLayout)9170 void Document::SetMayStartLayout(bool aMayStartLayout) {
9171   mMayStartLayout = aMayStartLayout;
9172   if (MayStartLayout()) {
9173     // Before starting layout, check whether we're a toplevel chrome
9174     // window.  If we are, setup some state so that we don't have to restyle
9175     // the whole tree after StartLayout.
9176     if (nsCOMPtr<nsIAppWindow> win = GetAppWindowIfToplevelChrome()) {
9177       // We're the chrome document!
9178       win->BeforeStartLayout();
9179     }
9180     ReadyState state = GetReadyStateEnum();
9181     if (state >= READYSTATE_INTERACTIVE) {
9182       // DOMContentLoaded has fired already.
9183       MaybeResolveReadyForIdle();
9184     }
9185   }
9186 
9187   MaybeEditingStateChanged();
9188 }
9189 
InitializeFrameLoader(nsFrameLoader * aLoader)9190 nsresult Document::InitializeFrameLoader(nsFrameLoader* aLoader) {
9191   mInitializableFrameLoaders.RemoveElement(aLoader);
9192   // Don't even try to initialize.
9193   if (mInDestructor) {
9194     NS_WARNING(
9195         "Trying to initialize a frame loader while"
9196         "document is being deleted");
9197     return NS_ERROR_FAILURE;
9198   }
9199 
9200   mInitializableFrameLoaders.AppendElement(aLoader);
9201   if (!mFrameLoaderRunner) {
9202     mFrameLoaderRunner =
9203         NewRunnableMethod("Document::MaybeInitializeFinalizeFrameLoaders", this,
9204                           &Document::MaybeInitializeFinalizeFrameLoaders);
9205     NS_ENSURE_TRUE(mFrameLoaderRunner, NS_ERROR_OUT_OF_MEMORY);
9206     nsContentUtils::AddScriptRunner(mFrameLoaderRunner);
9207   }
9208   return NS_OK;
9209 }
9210 
FinalizeFrameLoader(nsFrameLoader * aLoader,nsIRunnable * aFinalizer)9211 nsresult Document::FinalizeFrameLoader(nsFrameLoader* aLoader,
9212                                        nsIRunnable* aFinalizer) {
9213   mInitializableFrameLoaders.RemoveElement(aLoader);
9214   if (mInDestructor) {
9215     return NS_ERROR_FAILURE;
9216   }
9217 
9218   LogRunnable::LogDispatch(aFinalizer);
9219   mFrameLoaderFinalizers.AppendElement(aFinalizer);
9220   if (!mFrameLoaderRunner) {
9221     mFrameLoaderRunner =
9222         NewRunnableMethod("Document::MaybeInitializeFinalizeFrameLoaders", this,
9223                           &Document::MaybeInitializeFinalizeFrameLoaders);
9224     NS_ENSURE_TRUE(mFrameLoaderRunner, NS_ERROR_OUT_OF_MEMORY);
9225     nsContentUtils::AddScriptRunner(mFrameLoaderRunner);
9226   }
9227   return NS_OK;
9228 }
9229 
MaybeInitializeFinalizeFrameLoaders()9230 void Document::MaybeInitializeFinalizeFrameLoaders() {
9231   if (mDelayFrameLoaderInitialization) {
9232     // This method will be recalled when !mDelayFrameLoaderInitialization.
9233     mFrameLoaderRunner = nullptr;
9234     return;
9235   }
9236 
9237   // We're not in an update, but it is not safe to run scripts, so
9238   // postpone frameloader initialization and finalization.
9239   if (!nsContentUtils::IsSafeToRunScript()) {
9240     if (!mInDestructor && !mFrameLoaderRunner &&
9241         (mInitializableFrameLoaders.Length() ||
9242          mFrameLoaderFinalizers.Length())) {
9243       mFrameLoaderRunner = NewRunnableMethod(
9244           "Document::MaybeInitializeFinalizeFrameLoaders", this,
9245           &Document::MaybeInitializeFinalizeFrameLoaders);
9246       nsContentUtils::AddScriptRunner(mFrameLoaderRunner);
9247     }
9248     return;
9249   }
9250   mFrameLoaderRunner = nullptr;
9251 
9252   // Don't use a temporary array for mInitializableFrameLoaders, because
9253   // loading a frame may cause some other frameloader to be removed from the
9254   // array. But be careful to keep the loader alive when starting the load!
9255   while (mInitializableFrameLoaders.Length()) {
9256     RefPtr<nsFrameLoader> loader = mInitializableFrameLoaders[0];
9257     mInitializableFrameLoaders.RemoveElementAt(0);
9258     NS_ASSERTION(loader, "null frameloader in the array?");
9259     loader->ReallyStartLoading();
9260   }
9261 
9262   uint32_t length = mFrameLoaderFinalizers.Length();
9263   if (length > 0) {
9264     nsTArray<nsCOMPtr<nsIRunnable>> finalizers =
9265         std::move(mFrameLoaderFinalizers);
9266     for (uint32_t i = 0; i < length; ++i) {
9267       LogRunnable::Run run(finalizers[i]);
9268       finalizers[i]->Run();
9269     }
9270   }
9271 }
9272 
TryCancelFrameLoaderInitialization(nsIDocShell * aShell)9273 void Document::TryCancelFrameLoaderInitialization(nsIDocShell* aShell) {
9274   uint32_t length = mInitializableFrameLoaders.Length();
9275   for (uint32_t i = 0; i < length; ++i) {
9276     if (mInitializableFrameLoaders[i]->GetExistingDocShell() == aShell) {
9277       mInitializableFrameLoaders.RemoveElementAt(i);
9278       return;
9279     }
9280   }
9281 }
9282 
SetPrototypeDocument(nsXULPrototypeDocument * aPrototype)9283 void Document::SetPrototypeDocument(nsXULPrototypeDocument* aPrototype) {
9284   mPrototypeDocument = aPrototype;
9285   mSynchronousDOMContentLoaded = true;
9286 }
9287 
PermDelegateHandler()9288 nsIPermissionDelegateHandler* Document::PermDelegateHandler() {
9289   return GetPermissionDelegateHandler();
9290 }
9291 
RequestExternalResource(nsIURI * aURI,nsIReferrerInfo * aReferrerInfo,nsINode * aRequestingNode,ExternalResourceLoad ** aPendingLoad)9292 Document* Document::RequestExternalResource(
9293     nsIURI* aURI, nsIReferrerInfo* aReferrerInfo, nsINode* aRequestingNode,
9294     ExternalResourceLoad** aPendingLoad) {
9295   MOZ_ASSERT(aURI, "Must have a URI");
9296   MOZ_ASSERT(aRequestingNode, "Must have a node");
9297   MOZ_ASSERT(aReferrerInfo, "Must have a referrerInfo");
9298   if (mDisplayDocument) {
9299     return mDisplayDocument->RequestExternalResource(
9300         aURI, aReferrerInfo, aRequestingNode, aPendingLoad);
9301   }
9302 
9303   return mExternalResourceMap.RequestResource(
9304       aURI, aReferrerInfo, aRequestingNode, this, aPendingLoad);
9305 }
9306 
EnumerateExternalResources(SubDocEnumFunc aCallback)9307 void Document::EnumerateExternalResources(SubDocEnumFunc aCallback) {
9308   mExternalResourceMap.EnumerateResources(aCallback);
9309 }
9310 
GetAnimationController()9311 SMILAnimationController* Document::GetAnimationController() {
9312   // We create the animation controller lazily because most documents won't want
9313   // one and only SVG documents and the like will call this
9314   if (mAnimationController) return mAnimationController;
9315   // Refuse to create an Animation Controller for data documents.
9316   if (mLoadedAsData) return nullptr;
9317 
9318   mAnimationController = new SMILAnimationController(this);
9319 
9320   // If there's a presContext then check the animation mode and pause if
9321   // necessary.
9322   nsPresContext* context = GetPresContext();
9323   if (mAnimationController && context &&
9324       context->ImageAnimationMode() == imgIContainer::kDontAnimMode) {
9325     mAnimationController->Pause(SMILTimeContainer::PAUSE_USERPREF);
9326   }
9327 
9328   // If we're hidden (or being hidden), notify the newly-created animation
9329   // controller. (Skip this check for SVG-as-an-image documents, though,
9330   // because they don't get OnPageShow / OnPageHide calls).
9331   if (!mIsShowing && !mIsBeingUsedAsImage) {
9332     mAnimationController->OnPageHide();
9333   }
9334 
9335   return mAnimationController;
9336 }
9337 
GetOrCreatePendingAnimationTracker()9338 PendingAnimationTracker* Document::GetOrCreatePendingAnimationTracker() {
9339   if (!mPendingAnimationTracker) {
9340     mPendingAnimationTracker = new PendingAnimationTracker(this);
9341   }
9342 
9343   return mPendingAnimationTracker;
9344 }
9345 
9346 /**
9347  * Retrieve the "direction" property of the document.
9348  *
9349  * @lina 01/09/2001
9350  */
GetDir(nsAString & aDirection) const9351 void Document::GetDir(nsAString& aDirection) const {
9352   aDirection.Truncate();
9353   Element* rootElement = GetHtmlElement();
9354   if (rootElement) {
9355     static_cast<nsGenericHTMLElement*>(rootElement)->GetDir(aDirection);
9356   }
9357 }
9358 
9359 /**
9360  * Set the "direction" property of the document.
9361  *
9362  * @lina 01/09/2001
9363  */
SetDir(const nsAString & aDirection)9364 void Document::SetDir(const nsAString& aDirection) {
9365   Element* rootElement = GetHtmlElement();
9366   if (rootElement) {
9367     rootElement->SetAttr(kNameSpaceID_None, nsGkAtoms::dir, aDirection, true);
9368   }
9369 }
9370 
Images()9371 nsIHTMLCollection* Document::Images() {
9372   if (!mImages) {
9373     mImages = new nsContentList(this, kNameSpaceID_XHTML, nsGkAtoms::img,
9374                                 nsGkAtoms::img);
9375   }
9376   return mImages;
9377 }
9378 
Embeds()9379 nsIHTMLCollection* Document::Embeds() {
9380   if (!mEmbeds) {
9381     mEmbeds = new nsContentList(this, kNameSpaceID_XHTML, nsGkAtoms::embed,
9382                                 nsGkAtoms::embed);
9383   }
9384   return mEmbeds;
9385 }
9386 
MatchLinks(Element * aElement,int32_t aNamespaceID,nsAtom * aAtom,void * aData)9387 static bool MatchLinks(Element* aElement, int32_t aNamespaceID, nsAtom* aAtom,
9388                        void* aData) {
9389   return aElement->IsAnyOfHTMLElements(nsGkAtoms::a, nsGkAtoms::area) &&
9390          aElement->HasAttr(kNameSpaceID_None, nsGkAtoms::href);
9391 }
9392 
Links()9393 nsIHTMLCollection* Document::Links() {
9394   if (!mLinks) {
9395     mLinks = new nsContentList(this, MatchLinks, nullptr, nullptr);
9396   }
9397   return mLinks;
9398 }
9399 
Forms()9400 nsIHTMLCollection* Document::Forms() {
9401   if (!mForms) {
9402     // Please keep this in sync with nsHTMLDocument::GetFormsAndFormControls.
9403     mForms = new nsContentList(this, kNameSpaceID_XHTML, nsGkAtoms::form,
9404                                nsGkAtoms::form);
9405   }
9406 
9407   return mForms;
9408 }
9409 
Scripts()9410 nsIHTMLCollection* Document::Scripts() {
9411   if (!mScripts) {
9412     mScripts = new nsContentList(this, kNameSpaceID_XHTML, nsGkAtoms::script,
9413                                  nsGkAtoms::script);
9414   }
9415   return mScripts;
9416 }
9417 
Applets()9418 nsIHTMLCollection* Document::Applets() {
9419   if (!mApplets) {
9420     mApplets = new nsEmptyContentList(this);
9421   }
9422   return mApplets;
9423 }
9424 
MatchAnchors(Element * aElement,int32_t aNamespaceID,nsAtom * aAtom,void * aData)9425 static bool MatchAnchors(Element* aElement, int32_t aNamespaceID, nsAtom* aAtom,
9426                          void* aData) {
9427   return aElement->IsHTMLElement(nsGkAtoms::a) &&
9428          aElement->HasAttr(kNameSpaceID_None, nsGkAtoms::name);
9429 }
9430 
Anchors()9431 nsIHTMLCollection* Document::Anchors() {
9432   if (!mAnchors) {
9433     mAnchors = new nsContentList(this, MatchAnchors, nullptr, nullptr);
9434   }
9435   return mAnchors;
9436 }
9437 
Open(const nsAString & aURL,const nsAString & aName,const nsAString & aFeatures,ErrorResult & rv)9438 mozilla::dom::Nullable<mozilla::dom::WindowProxyHolder> Document::Open(
9439     const nsAString& aURL, const nsAString& aName, const nsAString& aFeatures,
9440     ErrorResult& rv) {
9441   MOZ_ASSERT(nsContentUtils::CanCallerAccess(this),
9442              "XOW should have caught this!");
9443 
9444   nsCOMPtr<nsPIDOMWindowInner> window = GetInnerWindow();
9445   if (!window) {
9446     rv.Throw(NS_ERROR_DOM_INVALID_ACCESS_ERR);
9447     return nullptr;
9448   }
9449   nsCOMPtr<nsPIDOMWindowOuter> outer =
9450       nsPIDOMWindowOuter::GetFromCurrentInner(window);
9451   if (!outer) {
9452     rv.Throw(NS_ERROR_NOT_INITIALIZED);
9453     return nullptr;
9454   }
9455   RefPtr<nsGlobalWindowOuter> win = nsGlobalWindowOuter::Cast(outer);
9456   RefPtr<BrowsingContext> newBC;
9457   rv = win->OpenJS(aURL, aName, aFeatures, getter_AddRefs(newBC));
9458   if (!newBC) {
9459     return nullptr;
9460   }
9461   return WindowProxyHolder(std::move(newBC));
9462 }
9463 
Open(const Optional<nsAString> &,const Optional<nsAString> &,ErrorResult & aError)9464 Document* Document::Open(const Optional<nsAString>& /* unused */,
9465                          const Optional<nsAString>& /* unused */,
9466                          ErrorResult& aError) {
9467   // Implements
9468   // <https://html.spec.whatwg.org/multipage/dynamic-markup-insertion.html#document-open-steps>
9469 
9470   MOZ_ASSERT(nsContentUtils::CanCallerAccess(this),
9471              "XOW should have caught this!");
9472 
9473   // Step 1 -- throw if we're an XML document.
9474   if (!IsHTMLDocument() || mDisableDocWrite) {
9475     aError.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
9476     return nullptr;
9477   }
9478 
9479   // Step 2 -- throw if dynamic markup insertion should throw.
9480   if (ShouldThrowOnDynamicMarkupInsertion()) {
9481     aError.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
9482     return nullptr;
9483   }
9484 
9485   // Step 3 -- get the entry document, so we can use it for security checks.
9486   nsCOMPtr<Document> callerDoc = GetEntryDocument();
9487   if (!callerDoc) {
9488     // If we're called from C++ or in some other way without an originating
9489     // document we can't do a document.open w/o changing the principal of the
9490     // document to something like about:blank (as that's the only sane thing to
9491     // do when we don't know the origin of this call), and since we can't
9492     // change the principals of a document for security reasons we'll have to
9493     // refuse to go ahead with this call.
9494 
9495     aError.Throw(NS_ERROR_DOM_SECURITY_ERR);
9496     return nullptr;
9497   }
9498 
9499   // Step 4 -- make sure we're same-origin (not just same origin-domain) with
9500   // the entry document.
9501   if (!callerDoc->NodePrincipal()->Equals(NodePrincipal())) {
9502     aError.Throw(NS_ERROR_DOM_SECURITY_ERR);
9503     return nullptr;
9504   }
9505 
9506   // Step 5 -- if we have an active parser with a nonzero script nesting level,
9507   // just no-op.
9508   if ((mParser && mParser->HasNonzeroScriptNestingLevel()) || mParserAborted) {
9509     return this;
9510   }
9511 
9512   // Step 6 -- check for open() during unload.  Per spec, this is just a check
9513   // of the ignore-opens-during-unload counter, but our unload event code
9514   // doesn't affect that counter yet (unlike pagehide and beforeunload, which
9515   // do), so we check for unload directly.
9516   if (ShouldIgnoreOpens()) {
9517     return this;
9518   }
9519 
9520   RefPtr<nsDocShell> shell(mDocumentContainer);
9521   if (shell) {
9522     bool inUnload;
9523     shell->GetIsInUnload(&inUnload);
9524     if (inUnload) {
9525       return this;
9526     }
9527   }
9528 
9529   // document.open() inherits the CSP from the opening document.
9530   // Please create an actual copy of the CSP (do not share the same
9531   // reference) otherwise appending a new policy within the opened
9532   // document will be incorrectly propagated to the opening doc.
9533   nsCOMPtr<nsIContentSecurityPolicy> csp = callerDoc->GetCsp();
9534   if (csp) {
9535     RefPtr<nsCSPContext> cspToInherit = new nsCSPContext();
9536     cspToInherit->InitFromOther(static_cast<nsCSPContext*>(csp.get()));
9537     mCSP = cspToInherit;
9538   }
9539 
9540   // At this point we know this is a valid-enough document.open() call
9541   // and not a no-op.  Increment our use counter.
9542   SetUseCounter(eUseCounter_custom_DocumentOpen);
9543 
9544   // Step 7 -- stop existing navigation of our browsing context (and all other
9545   // loads it's doing) if we're the active document of our browsing context.
9546   // Note that we do not want to stop anything if there is no existing
9547   // navigation.
9548   if (shell && IsCurrentActiveDocument() &&
9549       shell->GetIsAttemptingToNavigate()) {
9550     shell->Stop(nsIWebNavigation::STOP_NETWORK);
9551 
9552     // The Stop call may have cancelled the onload blocker request or
9553     // prevented it from getting added, so we need to make sure it gets added
9554     // to the document again otherwise the document could have a non-zero
9555     // onload block count without the onload blocker request being in the
9556     // loadgroup.
9557     EnsureOnloadBlocker();
9558   }
9559 
9560   // Step 8 -- clear event listeners out of our DOM tree
9561   for (nsINode* node : ShadowIncludingTreeIterator(*this)) {
9562     if (EventListenerManager* elm = node->GetExistingListenerManager()) {
9563       elm->RemoveAllListeners();
9564     }
9565   }
9566 
9567   // Step 9 -- clear event listeners from our window, if we have one.
9568   //
9569   // Note that we explicitly want the inner window, and only if we're its
9570   // document.  We want to do this (per spec) even when we're not the "active
9571   // document", so we can't go through GetWindow(), because it might forward to
9572   // the wrong inner.
9573   if (nsPIDOMWindowInner* win = GetInnerWindow()) {
9574     if (win->GetExtantDoc() == this) {
9575       if (EventListenerManager* elm =
9576               nsGlobalWindowInner::Cast(win)->GetExistingListenerManager()) {
9577         elm->RemoveAllListeners();
9578       }
9579     }
9580   }
9581 
9582   // If we have a parser that has a zero script nesting level, we need to
9583   // properly terminate it.  We do that after we've removed all the event
9584   // listeners (so termination won't trigger event listeners if it does
9585   // something to the DOM), but before we remove all elements from the document
9586   // (so if termination does modify the DOM in some way we will just blow it
9587   // away immediately.  See the similar code in WriteCommon that handles the
9588   // !IsInsertionPointDefined() case and should stay in sync with this code.
9589   if (mParser) {
9590     MOZ_ASSERT(!mParser->HasNonzeroScriptNestingLevel(),
9591                "Why didn't we take the early return?");
9592     // Make sure we don't re-enter.
9593     IgnoreOpensDuringUnload ignoreOpenGuard(this);
9594     mParser->Terminate();
9595     MOZ_RELEASE_ASSERT(!mParser, "mParser should have been null'd out");
9596   }
9597 
9598   // Step 10 -- remove all our DOM kids without firing any mutation events.
9599   {
9600     // We want to ignore any recursive calls to Open() that happen while
9601     // disconnecting the node tree.  The spec doesn't say to do this, but the
9602     // spec also doesn't envision unload events on subframes firing while we do
9603     // this, while all browsers fire them in practice.  See
9604     // <https://github.com/whatwg/html/issues/4611>.
9605     IgnoreOpensDuringUnload ignoreOpenGuard(this);
9606     DisconnectNodeTree();
9607   }
9608 
9609   // Step 11 -- if we're the current document in our docshell, do the
9610   // equivalent of pushState() with the new URL we should have.
9611   if (shell && IsCurrentActiveDocument()) {
9612     nsCOMPtr<nsIURI> newURI = callerDoc->GetDocumentURI();
9613     if (callerDoc != this) {
9614       nsCOMPtr<nsIURI> noFragmentURI;
9615       nsresult rv = NS_GetURIWithoutRef(newURI, getter_AddRefs(noFragmentURI));
9616       if (NS_WARN_IF(NS_FAILED(rv))) {
9617         aError.Throw(rv);
9618         return nullptr;
9619       }
9620       newURI = std::move(noFragmentURI);
9621     }
9622 
9623     // UpdateURLAndHistory might do various member-setting, so make sure we're
9624     // holding strong refs to all the refcounted args on the stack.  We can
9625     // assume that our caller is holding on to "this" already.
9626     nsCOMPtr<nsIURI> currentURI = GetDocumentURI();
9627     bool equalURIs;
9628     nsresult rv = currentURI->Equals(newURI, &equalURIs);
9629     if (NS_WARN_IF(NS_FAILED(rv))) {
9630       aError.Throw(rv);
9631       return nullptr;
9632     }
9633     nsCOMPtr<nsIStructuredCloneContainer> stateContainer(mStateObjectContainer);
9634     rv = shell->UpdateURLAndHistory(this, newURI, stateContainer, u""_ns,
9635                                     /* aReplace = */ true, currentURI,
9636                                     equalURIs);
9637     if (NS_WARN_IF(NS_FAILED(rv))) {
9638       aError.Throw(rv);
9639       return nullptr;
9640     }
9641 
9642     // And use the security info of the caller document as well, since
9643     // it's the thing providing our data.
9644     mSecurityInfo = callerDoc->GetSecurityInfo();
9645 
9646     // This is not mentioned in the spec, but I think that's a spec bug.  See
9647     // <https://github.com/whatwg/html/issues/4299>.  In any case, since our
9648     // URL may be changing away from about:blank here, we really want to unset
9649     // this flag no matter what, since only about:blank can be an initial
9650     // document.
9651     SetIsInitialDocument(false);
9652 
9653     // And let our docloader know that it will need to track our load event.
9654     nsDocShell::Cast(shell)->SetDocumentOpenedButNotLoaded();
9655   }
9656 
9657   // Per spec nothing happens with our URI in other cases, though note
9658   // <https://github.com/whatwg/html/issues/4286>.
9659 
9660   // Note that we don't need to do anything here with base URIs per spec.
9661   // That said, this might be assuming that we implement
9662   // https://html.spec.whatwg.org/multipage/urls-and-fetching.html#fallback-base-url
9663   // correctly, which we don't right now for the about:blank case.
9664 
9665   // Step 12, but note <https://github.com/whatwg/html/issues/4292>.
9666   mSkipLoadEventAfterClose = mLoadEventFiring;
9667 
9668   // Preliminary to steps 13-16.  Set our ready state to uninitialized before
9669   // we do anything else, so we can then proceed to later ready state levels.
9670   SetReadyStateInternal(READYSTATE_UNINITIALIZED,
9671                         /* updateTimingInformation = */ false);
9672   // Reset a flag that affects readyState behavior.
9673   mSetCompleteAfterDOMContentLoaded = false;
9674 
9675   // Step 13 -- set our compat mode to standards.
9676   SetCompatibilityMode(eCompatibility_FullStandards);
9677 
9678   // Step 14 -- create a new parser associated with document.  This also does
9679   // step 16 implicitly.
9680   mParserAborted = false;
9681   RefPtr<nsHtml5Parser> parser = nsHtml5Module::NewHtml5Parser();
9682   mParser = parser;
9683   parser->Initialize(this, GetDocumentURI(), ToSupports(shell), nullptr);
9684   nsresult rv = parser->StartExecutor();
9685   if (NS_WARN_IF(NS_FAILED(rv))) {
9686     aError.Throw(rv);
9687     return nullptr;
9688   }
9689 
9690   // Clear out our form control state, because the state of controls
9691   // in the pre-open() document should not affect the state of
9692   // controls that are now going to be written.
9693   mLayoutHistoryState = nullptr;
9694 
9695   if (shell) {
9696     // Prepare the docshell and the document viewer for the impending
9697     // out-of-band document.write()
9698     shell->PrepareForNewContentModel();
9699 
9700     nsCOMPtr<nsIContentViewer> cv;
9701     shell->GetContentViewer(getter_AddRefs(cv));
9702     if (cv) {
9703       cv->LoadStart(this);
9704     }
9705   }
9706 
9707   // Step 15.
9708   SetReadyStateInternal(Document::READYSTATE_LOADING,
9709                         /* updateTimingInformation = */ false);
9710 
9711   // Step 16 happened with step 14 above.
9712 
9713   // Step 17.
9714   return this;
9715 }
9716 
Close(ErrorResult & rv)9717 void Document::Close(ErrorResult& rv) {
9718   if (!IsHTMLDocument()) {
9719     // No calling document.close() on XHTML!
9720 
9721     rv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
9722     return;
9723   }
9724 
9725   if (ShouldThrowOnDynamicMarkupInsertion()) {
9726     rv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
9727     return;
9728   }
9729 
9730   if (!mParser || !mParser->IsScriptCreated()) {
9731     return;
9732   }
9733 
9734   ++mWriteLevel;
9735   rv = (static_cast<nsHtml5Parser*>(mParser.get()))
9736            ->Parse(u""_ns, nullptr, true);
9737   --mWriteLevel;
9738 }
9739 
WriteCommon(const Sequence<nsString> & aText,bool aNewlineTerminate,mozilla::ErrorResult & rv)9740 void Document::WriteCommon(const Sequence<nsString>& aText,
9741                            bool aNewlineTerminate, mozilla::ErrorResult& rv) {
9742   // Fast path the common case
9743   if (aText.Length() == 1) {
9744     WriteCommon(aText[0], aNewlineTerminate, rv);
9745   } else {
9746     // XXXbz it would be nice if we could pass all the strings to the parser
9747     // without having to do all this copying and then ask it to start
9748     // parsing....
9749     nsString text;
9750     for (size_t i = 0; i < aText.Length(); ++i) {
9751       text.Append(aText[i]);
9752     }
9753     WriteCommon(text, aNewlineTerminate, rv);
9754   }
9755 }
9756 
WriteCommon(const nsAString & aText,bool aNewlineTerminate,ErrorResult & aRv)9757 void Document::WriteCommon(const nsAString& aText, bool aNewlineTerminate,
9758                            ErrorResult& aRv) {
9759 #ifdef DEBUG
9760   {
9761     // Assert that we do not use or accidentally introduce doc.write()
9762     // in system privileged context or in any of our about: pages.
9763     nsCOMPtr<nsIPrincipal> principal = NodePrincipal();
9764     bool isAboutOrPrivContext = principal->IsSystemPrincipal();
9765     if (!isAboutOrPrivContext) {
9766       if (principal->SchemeIs("about")) {
9767         // about:blank inherits the security contetext and this assertion
9768         // is only meant for actual about: pages.
9769         nsAutoCString host;
9770         principal->GetHost(host);
9771         isAboutOrPrivContext = !host.EqualsLiteral("blank");
9772       }
9773     }
9774     // Some automated tests use an empty string to kick off some parsing
9775     // mechansims, but they do not do any harm since they use an empty string.
9776     MOZ_ASSERT(!isAboutOrPrivContext || aText.IsEmpty(),
9777                "do not use doc.write in privileged context!");
9778   }
9779 #endif
9780 
9781   mTooDeepWriteRecursion =
9782       (mWriteLevel > NS_MAX_DOCUMENT_WRITE_DEPTH || mTooDeepWriteRecursion);
9783   if (NS_WARN_IF(mTooDeepWriteRecursion)) {
9784     aRv.Throw(NS_ERROR_UNEXPECTED);
9785     return;
9786   }
9787 
9788   if (!IsHTMLDocument() || mDisableDocWrite) {
9789     // No calling document.write*() on XHTML!
9790 
9791     aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
9792     return;
9793   }
9794 
9795   if (ShouldThrowOnDynamicMarkupInsertion()) {
9796     aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
9797     return;
9798   }
9799 
9800   if (mParserAborted) {
9801     // Hixie says aborting the parser doesn't undefine the insertion point.
9802     // However, since we null out mParser in that case, we track the
9803     // theoretically defined insertion point using mParserAborted.
9804     return;
9805   }
9806 
9807   // Implement Step 4.1 of:
9808   // https://html.spec.whatwg.org/multipage/dynamic-markup-insertion.html#document-write-steps
9809   if (ShouldIgnoreOpens()) {
9810     return;
9811   }
9812 
9813   void* key = GenerateParserKey();
9814   if (mParser && !mParser->IsInsertionPointDefined()) {
9815     if (mIgnoreDestructiveWritesCounter) {
9816       // Instead of implying a call to document.open(), ignore the call.
9817       nsContentUtils::ReportToConsole(
9818           nsIScriptError::warningFlag, "DOM Events"_ns, this,
9819           nsContentUtils::eDOM_PROPERTIES, "DocumentWriteIgnored");
9820       return;
9821     }
9822     // The spec doesn't tell us to ignore opens from here, but we need to
9823     // ensure opens are ignored here.  See similar code in Open() that handles
9824     // the case of an existing parser which is not currently running script and
9825     // should stay in sync with this code.
9826     IgnoreOpensDuringUnload ignoreOpenGuard(this);
9827     mParser->Terminate();
9828     MOZ_RELEASE_ASSERT(!mParser, "mParser should have been null'd out");
9829   }
9830 
9831   if (!mParser) {
9832     if (mIgnoreDestructiveWritesCounter) {
9833       // Instead of implying a call to document.open(), ignore the call.
9834       nsContentUtils::ReportToConsole(
9835           nsIScriptError::warningFlag, "DOM Events"_ns, this,
9836           nsContentUtils::eDOM_PROPERTIES, "DocumentWriteIgnored");
9837       return;
9838     }
9839 
9840     Open({}, {}, aRv);
9841 
9842     // If Open() fails, or if it didn't create a parser (as it won't
9843     // if the user chose to not discard the current document through
9844     // onbeforeunload), don't write anything.
9845     if (aRv.Failed() || !mParser) {
9846       return;
9847     }
9848   }
9849 
9850   static constexpr auto new_line = u"\n"_ns;
9851 
9852   ++mWriteLevel;
9853 
9854   // This could be done with less code, but for performance reasons it
9855   // makes sense to have the code for two separate Parse() calls here
9856   // since the concatenation of strings costs more than we like. And
9857   // why pay that price when we don't need to?
9858   if (aNewlineTerminate) {
9859     aRv = (static_cast<nsHtml5Parser*>(mParser.get()))
9860               ->Parse(aText + new_line, key, false);
9861   } else {
9862     aRv =
9863         (static_cast<nsHtml5Parser*>(mParser.get()))->Parse(aText, key, false);
9864   }
9865 
9866   --mWriteLevel;
9867 
9868   mTooDeepWriteRecursion = (mWriteLevel != 0 && mTooDeepWriteRecursion);
9869 }
9870 
Write(const Sequence<nsString> & aText,ErrorResult & rv)9871 void Document::Write(const Sequence<nsString>& aText, ErrorResult& rv) {
9872   WriteCommon(aText, false, rv);
9873 }
9874 
Writeln(const Sequence<nsString> & aText,ErrorResult & rv)9875 void Document::Writeln(const Sequence<nsString>& aText, ErrorResult& rv) {
9876   WriteCommon(aText, true, rv);
9877 }
9878 
GenerateParserKey(void)9879 void* Document::GenerateParserKey(void) {
9880   if (!mScriptLoader) {
9881     // If we don't have a script loader, then the parser probably isn't parsing
9882     // anything anyway, so just return null.
9883     return nullptr;
9884   }
9885 
9886   // The script loader provides us with the currently executing script element,
9887   // which is guaranteed to be unique per script.
9888   nsIScriptElement* script = mScriptLoader->GetCurrentParserInsertedScript();
9889   if (script && mParser && mParser->IsScriptCreated()) {
9890     nsCOMPtr<nsIParser> creatorParser = script->GetCreatorParser();
9891     if (creatorParser != mParser) {
9892       // Make scripts that aren't inserted by the active parser of this document
9893       // participate in the context of the script that document.open()ed
9894       // this document.
9895       return nullptr;
9896     }
9897   }
9898   return script;
9899 }
9900 
9901 /* static */
MatchNameAttribute(Element * aElement,int32_t aNamespaceID,nsAtom * aAtom,void * aData)9902 bool Document::MatchNameAttribute(Element* aElement, int32_t aNamespaceID,
9903                                   nsAtom* aAtom, void* aData) {
9904   MOZ_ASSERT(aElement, "Must have element to work with!");
9905 
9906   if (!aElement->HasName()) {
9907     return false;
9908   }
9909 
9910   nsString* elementName = static_cast<nsString*>(aData);
9911   return aElement->GetNameSpaceID() == kNameSpaceID_XHTML &&
9912          aElement->AttrValueIs(kNameSpaceID_None, nsGkAtoms::name, *elementName,
9913                                eCaseMatters);
9914 }
9915 
9916 /* static */
UseExistingNameString(nsINode * aRootNode,const nsString * aName)9917 void* Document::UseExistingNameString(nsINode* aRootNode,
9918                                       const nsString* aName) {
9919   return const_cast<nsString*>(aName);
9920 }
9921 
GetDocumentURI(nsString & aDocumentURI) const9922 nsresult Document::GetDocumentURI(nsString& aDocumentURI) const {
9923   if (mDocumentURI) {
9924     nsAutoCString uri;
9925     nsresult rv = mDocumentURI->GetSpec(uri);
9926     NS_ENSURE_SUCCESS(rv, rv);
9927 
9928     CopyUTF8toUTF16(uri, aDocumentURI);
9929   } else {
9930     aDocumentURI.Truncate();
9931   }
9932 
9933   return NS_OK;
9934 }
9935 
9936 // Alias of above
GetURL(nsString & aURL) const9937 nsresult Document::GetURL(nsString& aURL) const { return GetDocumentURI(aURL); }
9938 
GetDocumentURIFromJS(nsString & aDocumentURI,CallerType aCallerType,ErrorResult & aRv) const9939 void Document::GetDocumentURIFromJS(nsString& aDocumentURI,
9940                                     CallerType aCallerType,
9941                                     ErrorResult& aRv) const {
9942   if (!mChromeXHRDocURI || aCallerType != CallerType::System) {
9943     aRv = GetDocumentURI(aDocumentURI);
9944     return;
9945   }
9946 
9947   nsAutoCString uri;
9948   nsresult res = mChromeXHRDocURI->GetSpec(uri);
9949   if (NS_FAILED(res)) {
9950     aRv.Throw(res);
9951     return;
9952   }
9953   CopyUTF8toUTF16(uri, aDocumentURI);
9954 }
9955 
GetDocumentURIObject() const9956 nsIURI* Document::GetDocumentURIObject() const {
9957   if (!mChromeXHRDocURI) {
9958     return GetDocumentURI();
9959   }
9960 
9961   return mChromeXHRDocURI;
9962 }
9963 
GetCompatMode(nsString & aCompatMode) const9964 void Document::GetCompatMode(nsString& aCompatMode) const {
9965   NS_ASSERTION(mCompatMode == eCompatibility_NavQuirks ||
9966                    mCompatMode == eCompatibility_AlmostStandards ||
9967                    mCompatMode == eCompatibility_FullStandards,
9968                "mCompatMode is neither quirks nor strict for this document");
9969 
9970   if (mCompatMode == eCompatibility_NavQuirks) {
9971     aCompatMode.AssignLiteral("BackCompat");
9972   } else {
9973     aCompatMode.AssignLiteral("CSS1Compat");
9974   }
9975 }
9976 
9977 }  // namespace dom
9978 }  // namespace mozilla
9979 
BlastSubtreeToPieces(nsINode * aNode)9980 void nsDOMAttributeMap::BlastSubtreeToPieces(nsINode* aNode) {
9981   if (Element* element = Element::FromNode(aNode)) {
9982     if (const nsDOMAttributeMap* map = element->GetAttributeMap()) {
9983       while (true) {
9984         RefPtr<Attr> attr;
9985         {
9986           // Use an iterator to get an arbitrary attribute from the
9987           // cache. The iterator must be destroyed before any other
9988           // operations on mAttributeCache, to avoid hash table
9989           // assertions.
9990           auto iter = map->mAttributeCache.ConstIter();
9991           if (iter.Done()) {
9992             break;
9993           }
9994           attr = iter.UserData();
9995         }
9996 
9997         BlastSubtreeToPieces(attr);
9998 
9999         mozilla::DebugOnly<nsresult> rv =
10000             element->UnsetAttr(attr->NodeInfo()->NamespaceID(),
10001                                attr->NodeInfo()->NameAtom(), false);
10002 
10003         // XXX Should we abort here?
10004         NS_ASSERTION(NS_SUCCEEDED(rv), "Uh-oh, UnsetAttr shouldn't fail!");
10005       }
10006     }
10007 
10008     if (mozilla::dom::ShadowRoot* shadow = element->GetShadowRoot()) {
10009       BlastSubtreeToPieces(shadow);
10010       element->UnattachShadow();
10011     }
10012   }
10013 
10014   while (aNode->HasChildren()) {
10015     nsIContent* node = aNode->GetFirstChild();
10016     BlastSubtreeToPieces(node);
10017     aNode->RemoveChildNode(node, false);
10018   }
10019 }
10020 
10021 namespace mozilla::dom {
10022 
AdoptNode(nsINode & aAdoptedNode,ErrorResult & rv)10023 nsINode* Document::AdoptNode(nsINode& aAdoptedNode, ErrorResult& rv) {
10024   OwningNonNull<nsINode> adoptedNode = aAdoptedNode;
10025 
10026   // Scope firing mutation events so that we don't carry any state that
10027   // might be stale
10028   {
10029     if (nsCOMPtr<nsINode> parent = adoptedNode->GetParentNode()) {
10030       nsContentUtils::MaybeFireNodeRemoved(adoptedNode, parent);
10031     }
10032   }
10033 
10034   nsAutoScriptBlocker scriptBlocker;
10035 
10036   switch (adoptedNode->NodeType()) {
10037     case ATTRIBUTE_NODE: {
10038       // Remove from ownerElement.
10039       OwningNonNull<Attr> adoptedAttr = static_cast<Attr&>(*adoptedNode);
10040 
10041       nsCOMPtr<Element> ownerElement = adoptedAttr->GetOwnerElement(rv);
10042       if (rv.Failed()) {
10043         return nullptr;
10044       }
10045 
10046       if (ownerElement) {
10047         OwningNonNull<Attr> newAttr =
10048             ownerElement->RemoveAttributeNode(*adoptedAttr, rv);
10049         if (rv.Failed()) {
10050           return nullptr;
10051         }
10052       }
10053 
10054       break;
10055     }
10056     case DOCUMENT_FRAGMENT_NODE: {
10057       if (adoptedNode->IsShadowRoot()) {
10058         rv.ThrowHierarchyRequestError("The adopted node is a shadow root.");
10059         return nullptr;
10060       }
10061       [[fallthrough]];
10062     }
10063     case ELEMENT_NODE:
10064     case PROCESSING_INSTRUCTION_NODE:
10065     case TEXT_NODE:
10066     case CDATA_SECTION_NODE:
10067     case COMMENT_NODE:
10068     case DOCUMENT_TYPE_NODE: {
10069       // Don't allow adopting a node's anonymous subtree out from under it.
10070       if (adoptedNode->IsRootOfNativeAnonymousSubtree()) {
10071         rv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
10072         return nullptr;
10073       }
10074 
10075       // We don't want to adopt an element into its own contentDocument or into
10076       // a descendant contentDocument, so we check if the frameElement of this
10077       // document or any of its parents is the adopted node or one of its
10078       // descendants.
10079       RefPtr<BrowsingContext> bc = GetBrowsingContext();
10080       while (bc) {
10081         nsCOMPtr<nsINode> node = bc->GetEmbedderElement();
10082         if (node && node->IsInclusiveDescendantOf(adoptedNode)) {
10083           rv.ThrowHierarchyRequestError(
10084               "Trying to adopt a node into its own contentDocument or a "
10085               "descendant contentDocument.");
10086           return nullptr;
10087         }
10088 
10089         if (XRE_IsParentProcess()) {
10090           bc = bc->Canonical()->GetParentCrossChromeBoundary();
10091         } else {
10092           bc = bc->GetParent();
10093         }
10094       }
10095 
10096       // Remove from parent.
10097       nsCOMPtr<nsINode> parent = adoptedNode->GetParentNode();
10098       if (parent) {
10099         parent->RemoveChildNode(adoptedNode->AsContent(), true);
10100       } else {
10101         MOZ_ASSERT(!adoptedNode->IsInUncomposedDoc());
10102       }
10103 
10104       break;
10105     }
10106     case DOCUMENT_NODE: {
10107       rv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
10108       return nullptr;
10109     }
10110     default: {
10111       NS_WARNING("Don't know how to adopt this nodetype for adoptNode.");
10112 
10113       rv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
10114       return nullptr;
10115     }
10116   }
10117 
10118   nsCOMPtr<Document> oldDocument = adoptedNode->OwnerDoc();
10119   bool sameDocument = oldDocument == this;
10120 
10121   AutoJSContext cx;
10122   JS::Rooted<JSObject*> newScope(cx, nullptr);
10123   if (!sameDocument) {
10124     newScope = GetWrapper();
10125     if (!newScope && GetScopeObject() && GetScopeObject()->HasJSGlobal()) {
10126       // Make sure cx is in a semi-sane compartment before we call WrapNative.
10127       // It's kind of irrelevant, given that we're passing aAllowWrapping =
10128       // false, and documents should always insist on being wrapped in an
10129       // canonical scope. But we try to pass something sane anyway.
10130       JSObject* globalObject = GetScopeObject()->GetGlobalJSObject();
10131       JSAutoRealm ar(cx, globalObject);
10132       JS::Rooted<JS::Value> v(cx);
10133       rv = nsContentUtils::WrapNative(cx, ToSupports(this), this, &v,
10134                                       /* aAllowWrapping = */ false);
10135       if (rv.Failed()) return nullptr;
10136       newScope = &v.toObject();
10137     }
10138   }
10139 
10140   adoptedNode->Adopt(sameDocument ? nullptr : mNodeInfoManager, newScope, rv);
10141   if (rv.Failed()) {
10142     // Disconnect all nodes from their parents, since some have the old document
10143     // as their ownerDocument and some have this as their ownerDocument.
10144     nsDOMAttributeMap::BlastSubtreeToPieces(adoptedNode);
10145     return nullptr;
10146   }
10147 
10148   MOZ_ASSERT(adoptedNode->OwnerDoc() == this,
10149              "Should still be in the document we just got adopted into");
10150 
10151   return adoptedNode;
10152 }
10153 
UseWidthDeviceWidthFallbackViewport() const10154 bool Document::UseWidthDeviceWidthFallbackViewport() const { return false; }
10155 
ParseScaleString(const nsString & aScaleString)10156 static Maybe<LayoutDeviceToScreenScale> ParseScaleString(
10157     const nsString& aScaleString) {
10158   // https://drafts.csswg.org/css-device-adapt/#min-scale-max-scale
10159   if (aScaleString.EqualsLiteral("device-width") ||
10160       aScaleString.EqualsLiteral("device-height")) {
10161     return Some(LayoutDeviceToScreenScale(10.0f));
10162   } else if (aScaleString.EqualsLiteral("yes")) {
10163     return Some(LayoutDeviceToScreenScale(1.0f));
10164   } else if (aScaleString.EqualsLiteral("no")) {
10165     return Some(LayoutDeviceToScreenScale(kViewportMinScale));
10166   } else if (aScaleString.IsEmpty()) {
10167     return Nothing();
10168   }
10169 
10170   nsresult scaleErrorCode;
10171   float scale = aScaleString.ToFloatAllowTrailingChars(&scaleErrorCode);
10172   if (NS_FAILED(scaleErrorCode)) {
10173     return Some(LayoutDeviceToScreenScale(kViewportMinScale));
10174   }
10175 
10176   if (scale < 0) {
10177     return Nothing();
10178   }
10179   return Some(clamped(LayoutDeviceToScreenScale(scale), kViewportMinScale,
10180                       kViewportMaxScale));
10181 }
10182 
ParseScalesInViewportMetaData(const ViewportMetaData & aViewportMetaData)10183 void Document::ParseScalesInViewportMetaData(
10184     const ViewportMetaData& aViewportMetaData) {
10185   Maybe<LayoutDeviceToScreenScale> scale;
10186 
10187   scale = ParseScaleString(aViewportMetaData.mInitialScale);
10188   mScaleFloat = scale.valueOr(LayoutDeviceToScreenScale(0.0f));
10189   mValidScaleFloat = scale.isSome();
10190 
10191   scale = ParseScaleString(aViewportMetaData.mMaximumScale);
10192   // Chrome uses '5' for the fallback value of maximum-scale, we might
10193   // consider matching it in future.
10194   // https://cs.chromium.org/chromium/src/third_party/blink/renderer/core/html/html_meta_element.cc?l=452&rcl=65ca4278b42d269ca738fc93ef7ae04a032afeb0
10195   mScaleMaxFloat = scale.valueOr(kViewportMaxScale);
10196   mValidMaxScale = scale.isSome();
10197 
10198   scale = ParseScaleString(aViewportMetaData.mMinimumScale);
10199   mScaleMinFloat = scale.valueOr(kViewportMinScale);
10200   mValidMinScale = scale.isSome();
10201 
10202   // Resolve min-zoom and max-zoom values.
10203   // https://drafts.csswg.org/css-device-adapt/#constraining-min-max-zoom
10204   if (mValidMaxScale && mValidMinScale) {
10205     mScaleMaxFloat = std::max(mScaleMinFloat, mScaleMaxFloat);
10206   }
10207 }
10208 
ParseWidthAndHeightInMetaViewport(const nsAString & aWidthString,const nsAString & aHeightString,bool aHasValidScale)10209 void Document::ParseWidthAndHeightInMetaViewport(const nsAString& aWidthString,
10210                                                  const nsAString& aHeightString,
10211                                                  bool aHasValidScale) {
10212   // The width and height properties
10213   // https://drafts.csswg.org/css-device-adapt/#width-and-height-properties
10214   //
10215   // The width and height viewport <META> properties are translated into width
10216   // and height descriptors, setting the min-width/min-height value to
10217   // extend-to-zoom and the max-width/max-height value to the length from the
10218   // viewport <META> property as follows:
10219   //
10220   // 1. Non-negative number values are translated to pixel lengths, clamped to
10221   //    the range: [1px, 10000px]
10222   // 2. Negative number values are dropped
10223   // 3. device-width and device-height translate to 100vw and 100vh respectively
10224   // 4. Other keywords and unknown values are also dropped
10225   mMinWidth = nsViewportInfo::Auto;
10226   mMaxWidth = nsViewportInfo::Auto;
10227   if (!aWidthString.IsEmpty()) {
10228     mMinWidth = nsViewportInfo::ExtendToZoom;
10229     if (aWidthString.EqualsLiteral("device-width")) {
10230       mMaxWidth = nsViewportInfo::DeviceSize;
10231     } else {
10232       nsresult widthErrorCode;
10233       mMaxWidth = aWidthString.ToInteger(&widthErrorCode);
10234       if (NS_FAILED(widthErrorCode)) {
10235         mMaxWidth = nsViewportInfo::Auto;
10236       } else if (mMaxWidth >= 0.0f) {
10237         mMaxWidth = clamped(mMaxWidth, CSSCoord(1.0f), CSSCoord(10000.0f));
10238       } else {
10239         mMaxWidth = nsViewportInfo::Auto;
10240       }
10241     }
10242   } else if (aHasValidScale) {
10243     if (aHeightString.IsEmpty()) {
10244       mMinWidth = nsViewportInfo::ExtendToZoom;
10245       mMaxWidth = nsViewportInfo::ExtendToZoom;
10246     }
10247   } else if (aHeightString.IsEmpty() && UseWidthDeviceWidthFallbackViewport()) {
10248     mMinWidth = nsViewportInfo::ExtendToZoom;
10249     mMaxWidth = nsViewportInfo::DeviceSize;
10250   }
10251 
10252   mMinHeight = nsViewportInfo::Auto;
10253   mMaxHeight = nsViewportInfo::Auto;
10254   if (!aHeightString.IsEmpty()) {
10255     mMinHeight = nsViewportInfo::ExtendToZoom;
10256     if (aHeightString.EqualsLiteral("device-height")) {
10257       mMaxHeight = nsViewportInfo::DeviceSize;
10258     } else {
10259       nsresult heightErrorCode;
10260       mMaxHeight = aHeightString.ToInteger(&heightErrorCode);
10261       if (NS_FAILED(heightErrorCode)) {
10262         mMaxHeight = nsViewportInfo::Auto;
10263       } else if (mMaxHeight >= 0.0f) {
10264         mMaxHeight = clamped(mMaxHeight, CSSCoord(1.0f), CSSCoord(10000.0f));
10265       } else {
10266         mMaxHeight = nsViewportInfo::Auto;
10267       }
10268     }
10269   }
10270 }
10271 
GetViewportInfo(const ScreenIntSize & aDisplaySize)10272 nsViewportInfo Document::GetViewportInfo(const ScreenIntSize& aDisplaySize) {
10273   MOZ_ASSERT(mPresShell);
10274 
10275   // Compute the CSS-to-LayoutDevice pixel scale as the product of the
10276   // widget scale and the full zoom.
10277   nsPresContext* context = mPresShell->GetPresContext();
10278   // When querying the full zoom, get it from the device context rather than
10279   // directly from the pres context, because the device context's value can
10280   // include an adjustment necessay to keep the number of app units per device
10281   // pixel an integer, and we want the adjusted value.
10282   float fullZoom = context ? context->DeviceContext()->GetFullZoom() : 1.0;
10283   fullZoom = (fullZoom == 0.0) ? 1.0 : fullZoom;
10284   CSSToLayoutDeviceScale layoutDeviceScale =
10285       context ? context->CSSToDevPixelScale() : CSSToLayoutDeviceScale(1);
10286 
10287   CSSToScreenScale defaultScale =
10288       layoutDeviceScale * LayoutDeviceToScreenScale(1.0);
10289 
10290   // Special behaviour for desktop mode, provided we are not on an about: page,
10291   // or fullscreen.
10292   const bool fullscreen = Fullscreen();
10293   nsPIDOMWindowOuter* win = GetWindow();
10294   if (win && win->IsDesktopModeViewport() && !IsAboutPage() && !fullscreen) {
10295     CSSCoord viewportWidth =
10296         StaticPrefs::browser_viewport_desktopWidth() / fullZoom;
10297     CSSToScreenScale scaleToFit(aDisplaySize.width / viewportWidth);
10298     float aspectRatio = (float)aDisplaySize.height / aDisplaySize.width;
10299     CSSSize viewportSize(viewportWidth, viewportWidth * aspectRatio);
10300     ScreenIntSize fakeDesktopSize = RoundedToInt(viewportSize * scaleToFit);
10301     return nsViewportInfo(fakeDesktopSize, scaleToFit,
10302                           nsViewportInfo::ZoomFlag::AllowZoom,
10303                           nsViewportInfo::ZoomBehaviour::Mobile);
10304   }
10305 
10306   // We ignore viewport meta tage etc when in fullscreen, see bug 1696717.
10307   if (fullscreen || !nsLayoutUtils::ShouldHandleMetaViewport(this)) {
10308     return nsViewportInfo(aDisplaySize, defaultScale,
10309                           nsLayoutUtils::AllowZoomingForDocument(this)
10310                               ? nsViewportInfo::ZoomFlag::AllowZoom
10311                               : nsViewportInfo::ZoomFlag::DisallowZoom,
10312                           StaticPrefs::apz_allow_zooming_out()
10313                               ? nsViewportInfo::ZoomBehaviour::Mobile
10314                               : nsViewportInfo::ZoomBehaviour::Desktop);
10315   }
10316 
10317   // In cases where the width of the CSS viewport is less than or equal to the
10318   // width of the display (i.e. width <= device-width) then we disable
10319   // double-tap-to-zoom behaviour. See bug 941995 for details.
10320 
10321   switch (mViewportType) {
10322     case DisplayWidthHeight:
10323       return nsViewportInfo(aDisplaySize, defaultScale,
10324                             nsViewportInfo::ZoomFlag::AllowZoom,
10325                             nsViewportInfo::ZoomBehaviour::Mobile);
10326     case Unknown: {
10327       // We might early exit if the viewport is empty. Even if we don't,
10328       // at the end of this case we'll note that it was empty. Later, when
10329       // we're using the cached values, this will trigger alternate code paths.
10330       if (!mLastModifiedViewportMetaData) {
10331         // If the docType specifies that we are on a site optimized for mobile,
10332         // then we want to return specially crafted defaults for the viewport
10333         // info.
10334         if (RefPtr<DocumentType> docType = GetDoctype()) {
10335           nsAutoString docId;
10336           docType->GetPublicId(docId);
10337           if ((docId.Find("WAP") != -1) || (docId.Find("Mobile") != -1) ||
10338               (docId.Find("WML") != -1)) {
10339             // We're making an assumption that the docType can't change here
10340             mViewportType = DisplayWidthHeight;
10341             return nsViewportInfo(aDisplaySize, defaultScale,
10342                                   nsViewportInfo::ZoomFlag::AllowZoom,
10343                                   nsViewportInfo::ZoomBehaviour::Mobile);
10344           }
10345         }
10346 
10347         nsAutoString handheldFriendly;
10348         GetHeaderData(nsGkAtoms::handheldFriendly, handheldFriendly);
10349         if (handheldFriendly.EqualsLiteral("true")) {
10350           mViewportType = DisplayWidthHeight;
10351           return nsViewportInfo(aDisplaySize, defaultScale,
10352                                 nsViewportInfo::ZoomFlag::AllowZoom,
10353                                 nsViewportInfo::ZoomBehaviour::Mobile);
10354         }
10355       }
10356 
10357       ViewportMetaData metaData = GetViewportMetaData();
10358 
10359       // Parse initial-scale, minimum-scale and maximum-scale.
10360       ParseScalesInViewportMetaData(metaData);
10361 
10362       // Parse width and height properties
10363       // This function sets m{Min,Max}{Width,Height}.
10364       ParseWidthAndHeightInMetaViewport(metaData.mWidth, metaData.mHeight,
10365                                         mValidScaleFloat);
10366 
10367       mAllowZoom = true;
10368       if ((metaData.mUserScalable.EqualsLiteral("0")) ||
10369           (metaData.mUserScalable.EqualsLiteral("no")) ||
10370           (metaData.mUserScalable.EqualsLiteral("false"))) {
10371         mAllowZoom = false;
10372       }
10373 
10374       // Resolve viewport-fit value.
10375       // https://drafts.csswg.org/css-round-display/#viewport-fit-descriptor
10376       mViewportFit = ViewportFitType::Auto;
10377       if (!metaData.mViewportFit.IsEmpty()) {
10378         if (metaData.mViewportFit.EqualsLiteral("contain")) {
10379           mViewportFit = ViewportFitType::Contain;
10380         } else if (metaData.mViewportFit.EqualsLiteral("cover")) {
10381           mViewportFit = ViewportFitType::Cover;
10382         }
10383       }
10384 
10385       mWidthStrEmpty = metaData.mWidth.IsEmpty();
10386 
10387       mViewportType = Specified;
10388       [[fallthrough]];
10389     }
10390     case Specified:
10391     default:
10392       LayoutDeviceToScreenScale effectiveMinScale = mScaleMinFloat;
10393       LayoutDeviceToScreenScale effectiveMaxScale = mScaleMaxFloat;
10394       bool effectiveValidMaxScale = mValidMaxScale;
10395 
10396       nsViewportInfo::ZoomFlag effectiveZoomFlag =
10397           mAllowZoom ? nsViewportInfo::ZoomFlag::AllowZoom
10398                      : nsViewportInfo::ZoomFlag::DisallowZoom;
10399       if (StaticPrefs::browser_ui_zoom_force_user_scalable()) {
10400         // If the pref to force user-scalable is enabled, we ignore the values
10401         // from the meta-viewport tag for these properties and just assume they
10402         // allow the page to be scalable. Note in particular that this code is
10403         // in the "Specified" branch of the enclosing switch statement, so that
10404         // calls to GetViewportInfo always use the latest value of the
10405         // browser_ui_zoom_force_user_scalable pref. Other codepaths that
10406         // return nsViewportInfo instances are all consistent with
10407         // browser_ui_zoom_force_user_scalable() already.
10408         effectiveMinScale = kViewportMinScale;
10409         effectiveMaxScale = kViewportMaxScale;
10410         effectiveValidMaxScale = true;
10411         effectiveZoomFlag = nsViewportInfo::ZoomFlag::AllowZoom;
10412       }
10413 
10414       // Returns extend-zoom value which is MIN(mScaleFloat, mScaleMaxFloat).
10415       auto ComputeExtendZoom = [&]() -> float {
10416         if (mValidScaleFloat && effectiveValidMaxScale) {
10417           return std::min(mScaleFloat.scale, effectiveMaxScale.scale);
10418         }
10419         if (mValidScaleFloat) {
10420           return mScaleFloat.scale;
10421         }
10422         if (effectiveValidMaxScale) {
10423           return effectiveMaxScale.scale;
10424         }
10425         return nsViewportInfo::Auto;
10426       };
10427 
10428       // Resolving 'extend-to-zoom'
10429       // https://drafts.csswg.org/css-device-adapt/#resolve-extend-to-zoom
10430       float extendZoom = ComputeExtendZoom();
10431 
10432       CSSCoord minWidth = mMinWidth;
10433       CSSCoord maxWidth = mMaxWidth;
10434       CSSCoord minHeight = mMinHeight;
10435       CSSCoord maxHeight = mMaxHeight;
10436 
10437       // aDisplaySize is in screen pixels; convert them to CSS pixels for the
10438       // viewport size. We need to use this scaled size for any clamping of
10439       // width or height.
10440       CSSSize displaySize = ScreenSize(aDisplaySize) / defaultScale;
10441 
10442       // Our min and max width and height values are mostly as specified by
10443       // the viewport declaration, but we make an exception for max width.
10444       // Max width, if auto, and if there's no initial-scale, will be set
10445       // to a default size. This is to support legacy site design with no
10446       // viewport declaration, and to do that using the same scheme as
10447       // Chrome does, in order to maintain web compatibility. Since the
10448       // default size has a complicated calculation, we fixup the maxWidth
10449       // value after setting it, above.
10450       if (maxWidth == nsViewportInfo::Auto && !mValidScaleFloat) {
10451         BrowsingContext* bc = GetBrowsingContext();
10452         if (bc && bc->TouchEventsOverride() == TouchEventsOverride::Enabled &&
10453             bc->InRDMPane()) {
10454           // If RDM and touch simulation are active, then use the simulated
10455           // screen width to accomodate for cases where the screen width is
10456           // larger than the desktop viewport default.
10457           maxWidth = nsViewportInfo::Max(
10458               displaySize.width, StaticPrefs::browser_viewport_desktopWidth());
10459         } else {
10460           maxWidth = StaticPrefs::browser_viewport_desktopWidth();
10461         }
10462         // Divide by fullZoom to stretch CSS pixel size of viewport in order
10463         // to keep device pixel size unchanged after full zoom applied.
10464         // See bug 974242.
10465         maxWidth /= fullZoom;
10466 
10467         // We set minWidth to ExtendToZoom, which will cause our later width
10468         // calculation to expand to maxWidth, if scale restrictions allow it.
10469         minWidth = nsViewportInfo::ExtendToZoom;
10470       }
10471 
10472       // Resolve device-width and device-height first.
10473       if (maxWidth == nsViewportInfo::DeviceSize) {
10474         maxWidth = displaySize.width;
10475       }
10476       if (maxHeight == nsViewportInfo::DeviceSize) {
10477         maxHeight = displaySize.height;
10478       }
10479       if (extendZoom == nsViewportInfo::Auto) {
10480         if (maxWidth == nsViewportInfo::ExtendToZoom) {
10481           maxWidth = nsViewportInfo::Auto;
10482         }
10483         if (maxHeight == nsViewportInfo::ExtendToZoom) {
10484           maxHeight = nsViewportInfo::Auto;
10485         }
10486         if (minWidth == nsViewportInfo::ExtendToZoom) {
10487           minWidth = maxWidth;
10488         }
10489         if (minHeight == nsViewportInfo::ExtendToZoom) {
10490           minHeight = maxHeight;
10491         }
10492       } else {
10493         CSSSize extendSize = displaySize / extendZoom;
10494         if (maxWidth == nsViewportInfo::ExtendToZoom) {
10495           maxWidth = extendSize.width;
10496         }
10497         if (maxHeight == nsViewportInfo::ExtendToZoom) {
10498           maxHeight = extendSize.height;
10499         }
10500         if (minWidth == nsViewportInfo::ExtendToZoom) {
10501           minWidth = nsViewportInfo::Max(extendSize.width, maxWidth);
10502         }
10503         if (minHeight == nsViewportInfo::ExtendToZoom) {
10504           minHeight = nsViewportInfo::Max(extendSize.height, maxHeight);
10505         }
10506       }
10507 
10508       // Resolve initial width and height from min/max descriptors
10509       // https://drafts.csswg.org/css-device-adapt/#resolve-initial-width-height
10510       CSSCoord width = nsViewportInfo::Auto;
10511       if (minWidth != nsViewportInfo::Auto ||
10512           maxWidth != nsViewportInfo::Auto) {
10513         width = nsViewportInfo::Max(
10514             minWidth, nsViewportInfo::Min(maxWidth, displaySize.width));
10515       }
10516       CSSCoord height = nsViewportInfo::Auto;
10517       if (minHeight != nsViewportInfo::Auto ||
10518           maxHeight != nsViewportInfo::Auto) {
10519         height = nsViewportInfo::Max(
10520             minHeight, nsViewportInfo::Min(maxHeight, displaySize.height));
10521       }
10522 
10523       // Resolve width value
10524       // https://drafts.csswg.org/css-device-adapt/#resolve-width
10525       if (width == nsViewportInfo::Auto) {
10526         if (height == nsViewportInfo::Auto || aDisplaySize.height == 0) {
10527           width = displaySize.width;
10528         } else {
10529           width = height * aDisplaySize.width / aDisplaySize.height;
10530         }
10531       }
10532 
10533       // Resolve height value
10534       // https://drafts.csswg.org/css-device-adapt/#resolve-height
10535       if (height == nsViewportInfo::Auto) {
10536         if (aDisplaySize.width == 0) {
10537           height = displaySize.height;
10538         } else {
10539           height = width * aDisplaySize.height / aDisplaySize.width;
10540         }
10541       }
10542       MOZ_ASSERT(width != nsViewportInfo::Auto &&
10543                  height != nsViewportInfo::Auto);
10544 
10545       CSSSize size(width, height);
10546 
10547       CSSToScreenScale scaleFloat = mScaleFloat * layoutDeviceScale;
10548       CSSToScreenScale scaleMinFloat = effectiveMinScale * layoutDeviceScale;
10549       CSSToScreenScale scaleMaxFloat = effectiveMaxScale * layoutDeviceScale;
10550 
10551       nsViewportInfo::AutoSizeFlag sizeFlag =
10552           nsViewportInfo::AutoSizeFlag::FixedSize;
10553       if (mMaxWidth == nsViewportInfo::DeviceSize ||
10554           (mWidthStrEmpty && (mMaxHeight == nsViewportInfo::DeviceSize ||
10555                               mScaleFloat.scale == 1.0f)) ||
10556           (!mWidthStrEmpty && mMaxWidth == nsViewportInfo::Auto &&
10557            mMaxHeight < 0)) {
10558         sizeFlag = nsViewportInfo::AutoSizeFlag::AutoSize;
10559       }
10560 
10561       // FIXME: Resolving width and height should be done above 'Resolve width
10562       // value' and 'Resolve height value'.
10563       if (sizeFlag == nsViewportInfo::AutoSizeFlag::AutoSize) {
10564         size = displaySize;
10565       }
10566 
10567       // The purpose of clamping the viewport width to a minimum size is to
10568       // prevent page authors from setting it to a ridiculously small value.
10569       // If the page is actually being rendered in a very small area (as might
10570       // happen in e.g. Android 8's picture-in-picture mode), we don't want to
10571       // prevent the viewport from taking on that size.
10572       CSSSize effectiveMinSize = Min(CSSSize(kViewportMinSize), displaySize);
10573 
10574       size.width = clamped(size.width, effectiveMinSize.width,
10575                            float(kViewportMaxSize.width));
10576 
10577       // Also recalculate the default zoom, if it wasn't specified in the
10578       // metadata, and the width is specified.
10579       if (!mValidScaleFloat && !mWidthStrEmpty) {
10580         CSSToScreenScale bestFitScale(float(aDisplaySize.width) / size.width);
10581         scaleFloat = (scaleFloat > bestFitScale) ? scaleFloat : bestFitScale;
10582       }
10583 
10584       size.height = clamped(size.height, effectiveMinSize.height,
10585                             float(kViewportMaxSize.height));
10586 
10587       // In cases of user-scalable=no, if we have a positive scale, clamp it to
10588       // min and max, and then use the clamped value for the scale, the min, and
10589       // the max. If we don't have a positive scale, assert that we are setting
10590       // the auto scale flag.
10591       if (effectiveZoomFlag == nsViewportInfo::ZoomFlag::DisallowZoom &&
10592           scaleFloat > CSSToScreenScale(0.0f)) {
10593         scaleFloat = scaleMinFloat = scaleMaxFloat =
10594             clamped(scaleFloat, scaleMinFloat, scaleMaxFloat);
10595       }
10596       MOZ_ASSERT(
10597           scaleFloat > CSSToScreenScale(0.0f) || !mValidScaleFloat,
10598           "If we don't have a positive scale, we should be using auto scale.");
10599 
10600       // We need to perform a conversion, but only if the initial or maximum
10601       // scale were set explicitly by the user.
10602       if (mValidScaleFloat && scaleFloat >= scaleMinFloat &&
10603           scaleFloat <= scaleMaxFloat) {
10604         CSSSize displaySize = ScreenSize(aDisplaySize) / scaleFloat;
10605         size.width = std::max(size.width, displaySize.width);
10606         size.height = std::max(size.height, displaySize.height);
10607       } else if (effectiveValidMaxScale) {
10608         CSSSize displaySize = ScreenSize(aDisplaySize) / scaleMaxFloat;
10609         size.width = std::max(size.width, displaySize.width);
10610         size.height = std::max(size.height, displaySize.height);
10611       }
10612 
10613       return nsViewportInfo(
10614           scaleFloat, scaleMinFloat, scaleMaxFloat, size, sizeFlag,
10615           mValidScaleFloat ? nsViewportInfo::AutoScaleFlag::FixedScale
10616                            : nsViewportInfo::AutoScaleFlag::AutoScale,
10617           effectiveZoomFlag, mViewportFit);
10618   }
10619 }
10620 
GetViewportMetaData() const10621 ViewportMetaData Document::GetViewportMetaData() const {
10622   return mLastModifiedViewportMetaData ? *mLastModifiedViewportMetaData
10623                                        : ViewportMetaData();
10624 }
10625 
SetMetaViewportData(UniquePtr<ViewportMetaData> aData)10626 void Document::SetMetaViewportData(UniquePtr<ViewportMetaData> aData) {
10627   mLastModifiedViewportMetaData = std::move(aData);
10628   // Trigger recomputation of the nsViewportInfo the next time it's queried.
10629   mViewportType = Unknown;
10630 
10631   RefPtr<AsyncEventDispatcher> asyncDispatcher =
10632       new AsyncEventDispatcher(this, u"DOMMetaViewportFitChanged"_ns,
10633                                CanBubble::eYes, ChromeOnlyDispatch::eYes);
10634   asyncDispatcher->RunDOMEventWhenSafe();
10635 }
10636 
GetOrCreateListenerManager()10637 EventListenerManager* Document::GetOrCreateListenerManager() {
10638   if (!mListenerManager) {
10639     mListenerManager =
10640         new EventListenerManager(static_cast<EventTarget*>(this));
10641     SetFlags(NODE_HAS_LISTENERMANAGER);
10642   }
10643 
10644   return mListenerManager;
10645 }
10646 
GetExistingListenerManager() const10647 EventListenerManager* Document::GetExistingListenerManager() const {
10648   return mListenerManager;
10649 }
10650 
GetEventTargetParent(EventChainPreVisitor & aVisitor)10651 void Document::GetEventTargetParent(EventChainPreVisitor& aVisitor) {
10652   aVisitor.mCanHandle = true;
10653   // FIXME! This is a hack to make middle mouse paste working also in Editor.
10654   // Bug 329119
10655   aVisitor.mForceContentDispatch = true;
10656 
10657   // Load events must not propagate to |window| object, see bug 335251.
10658   if (aVisitor.mEvent->mMessage != eLoad) {
10659     nsGlobalWindowOuter* window = nsGlobalWindowOuter::Cast(GetWindow());
10660     aVisitor.SetParentTarget(
10661         window ? window->GetTargetForEventTargetChain() : nullptr, false);
10662   }
10663 }
10664 
CreateEvent(const nsAString & aEventType,CallerType aCallerType,ErrorResult & rv) const10665 already_AddRefed<Event> Document::CreateEvent(const nsAString& aEventType,
10666                                               CallerType aCallerType,
10667                                               ErrorResult& rv) const {
10668   nsPresContext* presContext = GetPresContext();
10669 
10670   // Create event even without presContext.
10671   RefPtr<Event> ev =
10672       EventDispatcher::CreateEvent(const_cast<Document*>(this), presContext,
10673                                    nullptr, aEventType, aCallerType);
10674   if (!ev) {
10675     rv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
10676     return nullptr;
10677   }
10678   WidgetEvent* e = ev->WidgetEventPtr();
10679   e->mFlags.mBubbles = false;
10680   e->mFlags.mCancelable = false;
10681   return ev.forget();
10682 }
10683 
FlushPendingNotifications(FlushType aType)10684 void Document::FlushPendingNotifications(FlushType aType) {
10685   mozilla::ChangesToFlush flush(aType, aType >= FlushType::Style);
10686   FlushPendingNotifications(flush);
10687 }
10688 
10689 class nsDocumentOnStack {
10690  public:
nsDocumentOnStack(Document * aDoc)10691   explicit nsDocumentOnStack(Document* aDoc) : mDoc(aDoc) {
10692     mDoc->IncreaseStackRefCnt();
10693   }
~nsDocumentOnStack()10694   ~nsDocumentOnStack() { mDoc->DecreaseStackRefCnt(); }
10695 
10696  private:
10697   Document* mDoc;
10698 };
10699 
FlushPendingNotifications(mozilla::ChangesToFlush aFlush)10700 void Document::FlushPendingNotifications(mozilla::ChangesToFlush aFlush) {
10701   FlushType flushType = aFlush.mFlushType;
10702 
10703   nsDocumentOnStack dos(this);
10704 
10705   // We need to flush the sink for non-HTML documents (because the XML
10706   // parser still does insertion with deferred notifications).  We
10707   // also need to flush the sink if this is a layout-related flush, to
10708   // make sure that layout is started as needed.  But we can skip that
10709   // part if we have no presshell or if it's already done an initial
10710   // reflow.
10711   if ((!IsHTMLDocument() || (flushType > FlushType::ContentAndNotify &&
10712                              mPresShell && !mPresShell->DidInitialize())) &&
10713       (mParser || mWeakSink)) {
10714     nsCOMPtr<nsIContentSink> sink;
10715     if (mParser) {
10716       sink = mParser->GetContentSink();
10717     } else {
10718       sink = do_QueryReferent(mWeakSink);
10719       if (!sink) {
10720         mWeakSink = nullptr;
10721       }
10722     }
10723     // Determine if it is safe to flush the sink notifications
10724     // by determining if it safe to flush all the presshells.
10725     if (sink && (flushType == FlushType::Content || IsSafeToFlush())) {
10726       sink->FlushPendingNotifications(flushType);
10727     }
10728   }
10729 
10730   // Should we be flushing pending binding constructors in here?
10731 
10732   if (flushType <= FlushType::ContentAndNotify) {
10733     // Nothing to do here
10734     return;
10735   }
10736 
10737   // If we have a parent we must flush the parent too to ensure that our
10738   // container is reflowed if its size was changed.
10739   //
10740   // We do it only if the subdocument and the parent can observe each other
10741   // synchronously (that is, if we're not cross-origin), to avoid work that is
10742   // not observable, and if the parent document has finished loading all its
10743   // render-blocking stylesheets and may start laying out the document, to avoid
10744   // unnecessary flashes of unstyled content on the parent document. Note that
10745   // this last bit means that size-dependent media queries in this document may
10746   // produce incorrect results temporarily.
10747   //
10748   // But if it's not safe to flush ourselves, then don't flush the parent, since
10749   // that can cause things like resizes of our frame's widget, which we can't
10750   // handle while flushing is unsafe.
10751   if (StyleOrLayoutObservablyDependsOnParentDocumentLayout() &&
10752       mParentDocument->MayStartLayout() && IsSafeToFlush()) {
10753     ChangesToFlush parentFlush = aFlush;
10754     if (flushType >= FlushType::Style) {
10755       // Since media queries mean that a size change of our container can affect
10756       // style, we need to promote a style flush on ourself to a layout flush on
10757       // our parent, since we need our container to be the correct size to
10758       // determine the correct style.
10759       parentFlush.mFlushType = std::max(FlushType::Layout, flushType);
10760     }
10761     mParentDocument->FlushPendingNotifications(parentFlush);
10762   }
10763 
10764   if (RefPtr<PresShell> presShell = GetPresShell()) {
10765     presShell->FlushPendingNotifications(aFlush);
10766   }
10767 }
10768 
FlushExternalResources(FlushType aType)10769 void Document::FlushExternalResources(FlushType aType) {
10770   NS_ASSERTION(
10771       aType >= FlushType::Style,
10772       "should only need to flush for style or higher in external resources");
10773   if (GetDisplayDocument()) {
10774     return;
10775   }
10776 
10777   auto flush = [aType](Document& aDoc) {
10778     aDoc.FlushPendingNotifications(aType);
10779     return CallState::Continue;
10780   };
10781 
10782   EnumerateExternalResources(flush);
10783 }
10784 
SetXMLDeclaration(const char16_t * aVersion,const char16_t * aEncoding,const int32_t aStandalone)10785 void Document::SetXMLDeclaration(const char16_t* aVersion,
10786                                  const char16_t* aEncoding,
10787                                  const int32_t aStandalone) {
10788   if (!aVersion || *aVersion == '\0') {
10789     mXMLDeclarationBits = 0;
10790     return;
10791   }
10792 
10793   mXMLDeclarationBits = XML_DECLARATION_BITS_DECLARATION_EXISTS;
10794 
10795   if (aEncoding && *aEncoding != '\0') {
10796     mXMLDeclarationBits |= XML_DECLARATION_BITS_ENCODING_EXISTS;
10797   }
10798 
10799   if (aStandalone == 1) {
10800     mXMLDeclarationBits |= XML_DECLARATION_BITS_STANDALONE_EXISTS |
10801                            XML_DECLARATION_BITS_STANDALONE_YES;
10802   } else if (aStandalone == 0) {
10803     mXMLDeclarationBits |= XML_DECLARATION_BITS_STANDALONE_EXISTS;
10804   }
10805 }
10806 
GetXMLDeclaration(nsAString & aVersion,nsAString & aEncoding,nsAString & aStandalone)10807 void Document::GetXMLDeclaration(nsAString& aVersion, nsAString& aEncoding,
10808                                  nsAString& aStandalone) {
10809   aVersion.Truncate();
10810   aEncoding.Truncate();
10811   aStandalone.Truncate();
10812 
10813   if (!(mXMLDeclarationBits & XML_DECLARATION_BITS_DECLARATION_EXISTS)) {
10814     return;
10815   }
10816 
10817   // always until we start supporting 1.1 etc.
10818   aVersion.AssignLiteral("1.0");
10819 
10820   if (mXMLDeclarationBits & XML_DECLARATION_BITS_ENCODING_EXISTS) {
10821     // This is what we have stored, not necessarily what was written
10822     // in the original
10823     GetCharacterSet(aEncoding);
10824   }
10825 
10826   if (mXMLDeclarationBits & XML_DECLARATION_BITS_STANDALONE_EXISTS) {
10827     if (mXMLDeclarationBits & XML_DECLARATION_BITS_STANDALONE_YES) {
10828       aStandalone.AssignLiteral("yes");
10829     } else {
10830       aStandalone.AssignLiteral("no");
10831     }
10832   }
10833 }
10834 
AddColorSchemeMeta(HTMLMetaElement & aMeta)10835 void Document::AddColorSchemeMeta(HTMLMetaElement& aMeta) {
10836   mColorSchemeMetaTags.Insert(aMeta);
10837   RecomputeColorScheme();
10838 }
10839 
RemoveColorSchemeMeta(HTMLMetaElement & aMeta)10840 void Document::RemoveColorSchemeMeta(HTMLMetaElement& aMeta) {
10841   mColorSchemeMetaTags.RemoveElement(aMeta);
10842   RecomputeColorScheme();
10843 }
10844 
RecomputeColorScheme()10845 void Document::RecomputeColorScheme() {
10846   if (!StaticPrefs::layout_css_color_scheme_enabled()) {
10847     return;
10848   }
10849   auto oldColorScheme = mColorSchemeBits;
10850   mColorSchemeBits = 0;
10851   const nsTArray<HTMLMetaElement*>& elements = mColorSchemeMetaTags;
10852   for (const HTMLMetaElement* el : elements) {
10853     nsAutoString content;
10854     if (!el->GetAttr(nsGkAtoms::content, content)) {
10855       continue;
10856     }
10857 
10858     NS_ConvertUTF16toUTF8 contentU8(content);
10859     if (Servo_ColorScheme_Parse(&contentU8, &mColorSchemeBits)) {
10860       break;
10861     }
10862   }
10863 
10864   if (mColorSchemeBits == oldColorScheme) {
10865     return;
10866   }
10867 
10868   if (nsPresContext* pc = GetPresContext()) {
10869     // This affects system colors, which are inherited, so we need to recascade.
10870     pc->RebuildAllStyleData(nsChangeHint(0), RestyleHint::RecascadeSubtree());
10871   }
10872 }
10873 
IsScriptEnabled()10874 bool Document::IsScriptEnabled() {
10875   // If this document is sandboxed without 'allow-scripts'
10876   // script is not enabled
10877   if (HasScriptsBlockedBySandbox()) {
10878     return false;
10879   }
10880 
10881   nsCOMPtr<nsIScriptGlobalObject> globalObject =
10882       do_QueryInterface(GetInnerWindow());
10883   if (!globalObject || !globalObject->HasJSGlobal()) {
10884     return false;
10885   }
10886 
10887   return xpc::Scriptability::Get(globalObject->GetGlobalJSObjectPreserveColor())
10888       .Allowed();
10889 }
10890 
RetrieveRelevantHeaders(nsIChannel * aChannel)10891 void Document::RetrieveRelevantHeaders(nsIChannel* aChannel) {
10892   PRTime modDate = 0;
10893   nsresult rv;
10894 
10895   nsCOMPtr<nsIHttpChannel> httpChannel;
10896   rv = GetHttpChannelHelper(aChannel, getter_AddRefs(httpChannel));
10897   if (NS_WARN_IF(NS_FAILED(rv))) {
10898     return;
10899   }
10900 
10901   if (httpChannel) {
10902     nsAutoCString tmp;
10903     rv = httpChannel->GetResponseHeader("last-modified"_ns, tmp);
10904 
10905     if (NS_SUCCEEDED(rv)) {
10906       PRTime time;
10907       PRStatus st = PR_ParseTimeString(tmp.get(), true, &time);
10908       if (st == PR_SUCCESS) {
10909         modDate = time;
10910       }
10911     }
10912 
10913     static const char* const headers[] = {
10914         "default-style", "content-style-type", "content-language",
10915         "content-disposition", "refresh", "x-dns-prefetch-control",
10916         "x-frame-options", "origin-trial",
10917         // add more http headers if you need
10918         // XXXbz don't add content-location support without reading bug
10919         // 238654 and its dependencies/dups first.
10920         0};
10921 
10922     nsAutoCString headerVal;
10923     const char* const* name = headers;
10924     while (*name) {
10925       rv = httpChannel->GetResponseHeader(nsDependentCString(*name), headerVal);
10926       if (NS_SUCCEEDED(rv) && !headerVal.IsEmpty()) {
10927         RefPtr<nsAtom> key = NS_Atomize(*name);
10928         SetHeaderData(key, NS_ConvertASCIItoUTF16(headerVal));
10929       }
10930       ++name;
10931     }
10932   } else {
10933     nsCOMPtr<nsIFileChannel> fileChannel = do_QueryInterface(aChannel);
10934     if (fileChannel) {
10935       nsCOMPtr<nsIFile> file;
10936       fileChannel->GetFile(getter_AddRefs(file));
10937       if (file) {
10938         PRTime msecs;
10939         rv = file->GetLastModifiedTime(&msecs);
10940 
10941         if (NS_SUCCEEDED(rv)) {
10942           modDate = msecs * int64_t(PR_USEC_PER_MSEC);
10943         }
10944       }
10945     } else {
10946       nsAutoCString contentDisp;
10947       rv = aChannel->GetContentDispositionHeader(contentDisp);
10948       if (NS_SUCCEEDED(rv)) {
10949         SetHeaderData(nsGkAtoms::headerContentDisposition,
10950                       NS_ConvertASCIItoUTF16(contentDisp));
10951       }
10952     }
10953   }
10954 
10955   mLastModified.Truncate();
10956   if (modDate != 0) {
10957     GetFormattedTimeString(modDate, mLastModified);
10958   }
10959 }
10960 
ProcessMETATag(HTMLMetaElement * aMetaElement)10961 void Document::ProcessMETATag(HTMLMetaElement* aMetaElement) {
10962   // set any HTTP-EQUIV data into document's header data as well as url
10963   nsAutoString header;
10964   aMetaElement->GetAttr(nsGkAtoms::httpEquiv, header);
10965   if (!header.IsEmpty()) {
10966     // Ignore META REFRESH when document is sandboxed from automatic features.
10967     nsContentUtils::ASCIIToLower(header);
10968     if (nsGkAtoms::refresh->Equals(header) &&
10969         (GetSandboxFlags() & SANDBOXED_AUTOMATIC_FEATURES)) {
10970       return;
10971     }
10972 
10973     nsAutoString result;
10974     aMetaElement->GetAttr(nsGkAtoms::content, result);
10975     if (!result.IsEmpty()) {
10976       RefPtr<nsAtom> fieldAtom(NS_Atomize(header));
10977       SetHeaderData(fieldAtom, result);
10978     }
10979   }
10980 
10981   if (aMetaElement->AttrValueIs(kNameSpaceID_None, nsGkAtoms::name,
10982                                 nsGkAtoms::handheldFriendly, eIgnoreCase)) {
10983     nsAutoString result;
10984     aMetaElement->GetAttr(kNameSpaceID_None, nsGkAtoms::content, result);
10985     if (!result.IsEmpty()) {
10986       nsContentUtils::ASCIIToLower(result);
10987       SetHeaderData(nsGkAtoms::handheldFriendly, result);
10988     }
10989   }
10990 }
10991 
CreateElem(const nsAString & aName,nsAtom * aPrefix,int32_t aNamespaceID,const nsAString * aIs)10992 already_AddRefed<Element> Document::CreateElem(const nsAString& aName,
10993                                                nsAtom* aPrefix,
10994                                                int32_t aNamespaceID,
10995                                                const nsAString* aIs) {
10996 #ifdef DEBUG
10997   nsAutoString qName;
10998   if (aPrefix) {
10999     aPrefix->ToString(qName);
11000     qName.Append(':');
11001   }
11002   qName.Append(aName);
11003 
11004   // Note: "a:b:c" is a valid name in non-namespaces XML, and
11005   // Document::CreateElement can call us with such a name and no prefix,
11006   // which would cause an error if we just used true here.
11007   bool nsAware = aPrefix != nullptr || aNamespaceID != GetDefaultNamespaceID();
11008   NS_ASSERTION(NS_SUCCEEDED(nsContentUtils::CheckQName(qName, nsAware)),
11009                "Don't pass invalid prefixes to Document::CreateElem, "
11010                "check caller.");
11011 #endif
11012 
11013   RefPtr<mozilla::dom::NodeInfo> nodeInfo;
11014   mNodeInfoManager->GetNodeInfo(aName, aPrefix, aNamespaceID, ELEMENT_NODE,
11015                                 getter_AddRefs(nodeInfo));
11016   NS_ENSURE_TRUE(nodeInfo, nullptr);
11017 
11018   nsCOMPtr<Element> element;
11019   nsresult rv = NS_NewElement(getter_AddRefs(element), nodeInfo.forget(),
11020                               NOT_FROM_PARSER, aIs);
11021   return NS_SUCCEEDED(rv) ? element.forget() : nullptr;
11022 }
11023 
IsSafeToFlush() const11024 bool Document::IsSafeToFlush() const {
11025   PresShell* presShell = GetPresShell();
11026   if (!presShell) {
11027     return true;
11028   }
11029   return presShell->IsSafeToFlush();
11030 }
11031 
Sanitize()11032 void Document::Sanitize() {
11033   // Sanitize the document by resetting all (current and former) password fields
11034   // and any form fields with autocomplete=off to their default values.  We do
11035   // this now, instead of when the presentation is restored, to offer some
11036   // protection in case there is ever an exploit that allows a cached document
11037   // to be accessed from a different document.
11038 
11039   // First locate all input elements, regardless of whether they are
11040   // in a form, and reset the password and autocomplete=off elements.
11041 
11042   RefPtr<nsContentList> nodes = GetElementsByTagName(u"input"_ns);
11043 
11044   nsAutoString value;
11045 
11046   uint32_t length = nodes->Length(true);
11047   for (uint32_t i = 0; i < length; ++i) {
11048     NS_ASSERTION(nodes->Item(i), "null item in node list!");
11049 
11050     RefPtr<HTMLInputElement> input =
11051         HTMLInputElement::FromNodeOrNull(nodes->Item(i));
11052     if (!input) continue;
11053 
11054     input->GetAttr(nsGkAtoms::autocomplete, value);
11055     if (value.LowerCaseEqualsLiteral("off") || input->HasBeenTypePassword()) {
11056       input->Reset();
11057     }
11058   }
11059 
11060   // Now locate all _form_ elements that have autocomplete=off and reset them
11061   nodes = GetElementsByTagName(u"form"_ns);
11062 
11063   length = nodes->Length(true);
11064   for (uint32_t i = 0; i < length; ++i) {
11065     // Reset() may change the list dynamically.
11066     RefPtr<HTMLFormElement> form =
11067         HTMLFormElement::FromNodeOrNull(nodes->Item(i));
11068     if (!form) continue;
11069 
11070     form->GetAttr(kNameSpaceID_None, nsGkAtoms::autocomplete, value);
11071     if (value.LowerCaseEqualsLiteral("off")) form->Reset();
11072   }
11073 }
11074 
EnumerateSubDocuments(SubDocEnumFunc aCallback)11075 void Document::EnumerateSubDocuments(SubDocEnumFunc aCallback) {
11076   if (!mSubDocuments) {
11077     return;
11078   }
11079 
11080   // PLDHashTable::Iterator can't handle modifications while iterating so we
11081   // copy all entries to an array first before calling any callbacks.
11082   AutoTArray<RefPtr<Document>, 8> subdocs;
11083   for (auto iter = mSubDocuments->Iter(); !iter.Done(); iter.Next()) {
11084     auto entry = static_cast<SubDocMapEntry*>(iter.Get());
11085     if (Document* subdoc = entry->mSubDocument) {
11086       subdocs.AppendElement(subdoc);
11087     }
11088   }
11089   for (auto& subdoc : subdocs) {
11090     if (aCallback(*subdoc) == CallState::Stop) {
11091       break;
11092     }
11093   }
11094 }
11095 
CollectDescendantDocuments(nsTArray<RefPtr<Document>> & aDescendants,nsDocTestFunc aCallback) const11096 void Document::CollectDescendantDocuments(
11097     nsTArray<RefPtr<Document>>& aDescendants, nsDocTestFunc aCallback) const {
11098   if (!mSubDocuments) {
11099     return;
11100   }
11101 
11102   for (auto iter = mSubDocuments->Iter(); !iter.Done(); iter.Next()) {
11103     auto entry = static_cast<SubDocMapEntry*>(iter.Get());
11104     const Document* subdoc = entry->mSubDocument;
11105     if (subdoc) {
11106       if (aCallback(subdoc)) {
11107         aDescendants.AppendElement(entry->mSubDocument);
11108       }
11109       subdoc->CollectDescendantDocuments(aDescendants, aCallback);
11110     }
11111   }
11112 }
11113 
CanSavePresentation(nsIRequest * aNewRequest,uint32_t & aBFCacheCombo,bool aIncludeSubdocuments,bool aAllowUnloadListeners)11114 bool Document::CanSavePresentation(nsIRequest* aNewRequest,
11115                                    uint32_t& aBFCacheCombo,
11116                                    bool aIncludeSubdocuments,
11117                                    bool aAllowUnloadListeners) {
11118   bool ret = true;
11119 
11120   if (!IsBFCachingAllowed()) {
11121     aBFCacheCombo |= BFCacheStatus::NOT_ALLOWED;
11122     ret = false;
11123   }
11124 
11125   nsAutoCString uri;
11126   if (MOZ_UNLIKELY(MOZ_LOG_TEST(gPageCacheLog, LogLevel::Verbose))) {
11127     if (mDocumentURI) {
11128       mDocumentURI->GetSpec(uri);
11129     }
11130   }
11131 
11132   if (EventHandlingSuppressed()) {
11133     MOZ_LOG(gPageCacheLog, mozilla::LogLevel::Verbose,
11134             ("Save of %s blocked on event handling suppression", uri.get()));
11135     aBFCacheCombo |= BFCacheStatus::EVENT_HANDLING_SUPPRESSED;
11136     ret = false;
11137   }
11138 
11139   // Do not allow suspended windows to be placed in the
11140   // bfcache.  This method is also used to verify a document
11141   // coming out of the bfcache is ok to restore, though.  So
11142   // we only want to block suspend windows that aren't also
11143   // frozen.
11144   nsPIDOMWindowInner* win = GetInnerWindow();
11145   if (win && win->IsSuspended() && !win->IsFrozen()) {
11146     MOZ_LOG(gPageCacheLog, mozilla::LogLevel::Verbose,
11147             ("Save of %s blocked on suspended Window", uri.get()));
11148     aBFCacheCombo |= BFCacheStatus::SUSPENDED;
11149     ret = false;
11150   }
11151 
11152   nsCOMPtr<nsIChannel> channel = do_QueryInterface(aNewRequest);
11153   bool thirdParty = false;
11154   // Currently some other mobile browsers seem to bfcache only cross-domain
11155   // pages, but bfcache those also when there are unload event listeners, so
11156   // this is trying to match that behavior as much as possible.
11157   bool allowUnloadListeners =
11158       aAllowUnloadListeners &&
11159       StaticPrefs::docshell_shistory_bfcache_allow_unload_listeners() &&
11160       (!channel || (NS_SUCCEEDED(NodePrincipal()->IsThirdPartyChannel(
11161                         channel, &thirdParty)) &&
11162                     thirdParty));
11163 
11164   // Check our event listener manager for unload/beforeunload listeners.
11165   nsCOMPtr<EventTarget> piTarget = do_QueryInterface(mScriptGlobalObject);
11166   if (!allowUnloadListeners && piTarget) {
11167     EventListenerManager* manager = piTarget->GetExistingListenerManager();
11168     if (manager) {
11169       if (manager->HasUnloadListeners()) {
11170         MOZ_LOG(gPageCacheLog, mozilla::LogLevel::Verbose,
11171                 ("Save of %s blocked due to unload handlers", uri.get()));
11172         aBFCacheCombo |= BFCacheStatus::UNLOAD_LISTENER;
11173         ret = false;
11174       }
11175       if (manager->HasBeforeUnloadListeners()) {
11176         if (!mozilla::SessionHistoryInParent() ||
11177             !StaticPrefs::
11178                 docshell_shistory_bfcache_ship_allow_beforeunload_listeners()) {
11179           MOZ_LOG(
11180               gPageCacheLog, mozilla::LogLevel::Verbose,
11181               ("Save of %s blocked due to beforeUnload handlers", uri.get()));
11182           aBFCacheCombo |= BFCacheStatus::BEFOREUNLOAD_LISTENER;
11183           ret = false;
11184         }
11185       }
11186     }
11187   }
11188 
11189   // Check if we have pending network requests
11190   nsCOMPtr<nsILoadGroup> loadGroup = GetDocumentLoadGroup();
11191   if (loadGroup) {
11192     nsCOMPtr<nsISimpleEnumerator> requests;
11193     loadGroup->GetRequests(getter_AddRefs(requests));
11194 
11195     bool hasMore = false;
11196 
11197     // We want to bail out if we have any requests other than aNewRequest (or
11198     // in the case when aNewRequest is a part of a multipart response the base
11199     // channel the multipart response is coming in on).
11200     nsCOMPtr<nsIChannel> baseChannel;
11201     nsCOMPtr<nsIMultiPartChannel> part(do_QueryInterface(aNewRequest));
11202     if (part) {
11203       part->GetBaseChannel(getter_AddRefs(baseChannel));
11204     }
11205 
11206     while (NS_SUCCEEDED(requests->HasMoreElements(&hasMore)) && hasMore) {
11207       nsCOMPtr<nsISupports> elem;
11208       requests->GetNext(getter_AddRefs(elem));
11209 
11210       nsCOMPtr<nsIRequest> request = do_QueryInterface(elem);
11211       if (request && request != aNewRequest && request != baseChannel) {
11212         // Favicon loads don't need to block caching.
11213         nsCOMPtr<nsIChannel> channel = do_QueryInterface(request);
11214         if (channel) {
11215           nsCOMPtr<nsILoadInfo> li = channel->LoadInfo();
11216           if (li->InternalContentPolicyType() ==
11217               nsIContentPolicy::TYPE_INTERNAL_IMAGE_FAVICON) {
11218             continue;
11219           }
11220         }
11221 
11222         if (MOZ_UNLIKELY(MOZ_LOG_TEST(gPageCacheLog, LogLevel::Verbose))) {
11223           nsAutoCString requestName;
11224           request->GetName(requestName);
11225           MOZ_LOG(gPageCacheLog, LogLevel::Verbose,
11226                   ("Save of %s blocked because document has request %s",
11227                    uri.get(), requestName.get()));
11228         }
11229         aBFCacheCombo |= BFCacheStatus::REQUEST;
11230         ret = false;
11231       }
11232     }
11233   }
11234 
11235   // Check if we have active GetUserMedia use
11236   if (MediaManager::Exists() && win &&
11237       MediaManager::Get()->IsWindowStillActive(win->WindowID())) {
11238     MOZ_LOG(gPageCacheLog, mozilla::LogLevel::Verbose,
11239             ("Save of %s blocked due to GetUserMedia", uri.get()));
11240     aBFCacheCombo |= BFCacheStatus::ACTIVE_GET_USER_MEDIA;
11241     ret = false;
11242   }
11243 
11244 #ifdef MOZ_WEBRTC
11245   // Check if we have active PeerConnections
11246   if (win && win->HasActivePeerConnections()) {
11247     MOZ_LOG(gPageCacheLog, mozilla::LogLevel::Verbose,
11248             ("Save of %s blocked due to PeerConnection", uri.get()));
11249     aBFCacheCombo |= BFCacheStatus::ACTIVE_PEER_CONNECTION;
11250     ret = false;
11251   }
11252 #endif  // MOZ_WEBRTC
11253 
11254   // Don't save presentations for documents containing EME content, so that
11255   // CDMs reliably shutdown upon user navigation.
11256   if (ContainsEMEContent()) {
11257     aBFCacheCombo |= BFCacheStatus::CONTAINS_EME_CONTENT;
11258     ret = false;
11259   }
11260 
11261   // Don't save presentations for documents containing MSE content, to
11262   // reduce memory usage.
11263   if (ContainsMSEContent()) {
11264     MOZ_LOG(gPageCacheLog, mozilla::LogLevel::Verbose,
11265             ("Save of %s blocked due to MSE use", uri.get()));
11266     aBFCacheCombo |= BFCacheStatus::CONTAINS_MSE_CONTENT;
11267     ret = false;
11268   }
11269 
11270   if (aIncludeSubdocuments && mSubDocuments) {
11271     for (auto iter = mSubDocuments->Iter(); !iter.Done(); iter.Next()) {
11272       auto entry = static_cast<SubDocMapEntry*>(iter.Get());
11273       Document* subdoc = entry->mSubDocument;
11274 
11275       uint32_t subDocBFCacheCombo = 0;
11276       // The aIgnoreRequest we were passed is only for us, so don't pass it on.
11277       bool canCache =
11278           subdoc ? subdoc->CanSavePresentation(nullptr, subDocBFCacheCombo,
11279                                                true, allowUnloadListeners)
11280                  : false;
11281       if (!canCache) {
11282         MOZ_LOG(gPageCacheLog, mozilla::LogLevel::Verbose,
11283                 ("Save of %s blocked due to subdocument blocked", uri.get()));
11284         aBFCacheCombo |= subDocBFCacheCombo;
11285         ret = false;
11286       }
11287     }
11288   }
11289 
11290   if (!mozilla::BFCacheInParent()) {
11291     // BFCache is currently not compatible with remote subframes (bug 1609324)
11292     if (RefPtr<BrowsingContext> browsingContext = GetBrowsingContext()) {
11293       for (auto& child : browsingContext->Children()) {
11294         if (!child->IsInProcess()) {
11295           aBFCacheCombo |= BFCacheStatus::CONTAINS_REMOTE_SUBFRAMES;
11296           ret = false;
11297           break;
11298         }
11299       }
11300     }
11301   }
11302 
11303   if (win) {
11304     auto* globalWindow = nsGlobalWindowInner::Cast(win);
11305 #ifdef MOZ_WEBSPEECH
11306     if (globalWindow->HasActiveSpeechSynthesis()) {
11307       MOZ_LOG(gPageCacheLog, mozilla::LogLevel::Verbose,
11308               ("Save of %s blocked due to Speech use", uri.get()));
11309       aBFCacheCombo |= BFCacheStatus::HAS_ACTIVE_SPEECH_SYNTHESIS;
11310       ret = false;
11311     }
11312 #endif
11313     if (globalWindow->HasUsedVR()) {
11314       MOZ_LOG(gPageCacheLog, mozilla::LogLevel::Verbose,
11315               ("Save of %s blocked due to having used VR", uri.get()));
11316       aBFCacheCombo |= BFCacheStatus::HAS_USED_VR;
11317       ret = false;
11318     }
11319 
11320     if (win->HasActiveLocks()) {
11321       MOZ_LOG(
11322           gPageCacheLog, mozilla::LogLevel::Verbose,
11323           ("Save of %s blocked due to having active lock requests", uri.get()));
11324       aBFCacheCombo |= BFCacheStatus::ACTIVE_LOCK;
11325       ret = false;
11326     }
11327   }
11328 
11329   return ret;
11330 }
11331 
Destroy()11332 void Document::Destroy() {
11333   // The ContentViewer wants to release the document now.  So, tell our content
11334   // to drop any references to the document so that it can be destroyed.
11335   if (mIsGoingAway) {
11336     return;
11337   }
11338 
11339   ReportDocumentUseCounters();
11340   SetDevToolsWatchingDOMMutations(false);
11341 
11342   mIsGoingAway = true;
11343 
11344   ScriptLoader()->Destroy();
11345   SetScriptGlobalObject(nullptr);
11346   RemovedFromDocShell();
11347 
11348   bool oldVal = mInUnlinkOrDeletion;
11349   mInUnlinkOrDeletion = true;
11350 
11351 #ifdef DEBUG
11352   uint32_t oldChildCount = GetChildCount();
11353 #endif
11354 
11355   for (nsIContent* child = GetFirstChild(); child;
11356        child = child->GetNextSibling()) {
11357     child->DestroyContent();
11358     MOZ_ASSERT(child->GetParentNode() == this);
11359   }
11360   MOZ_ASSERT(oldChildCount == GetChildCount());
11361   MOZ_ASSERT(!mSubDocuments || mSubDocuments->EntryCount() == 0);
11362 
11363   mInUnlinkOrDeletion = oldVal;
11364 
11365   mLayoutHistoryState = nullptr;
11366 
11367   if (mOriginalDocument) {
11368     mOriginalDocument->mLatestStaticClone = nullptr;
11369   }
11370 
11371   if (IsStaticDocument()) {
11372     RemoveProperty(nsGkAtoms::printselectionranges);
11373   }
11374 
11375   // Shut down our external resource map.  We might not need this for
11376   // leak-fixing if we fix nsDocumentViewer to do cycle-collection, but
11377   // tearing down all those frame trees right now is the right thing to do.
11378   mExternalResourceMap.Shutdown();
11379 
11380   // Manually break cycles via promise's global object pointer.
11381   mReadyForIdle = nullptr;
11382   mOrientationPendingPromise = nullptr;
11383 
11384   // To break cycles.
11385   mPreloadService.ClearAllPreloads();
11386 
11387   if (mDocumentL10n) {
11388     mDocumentL10n->Destroy();
11389   }
11390 }
11391 
RemovedFromDocShell()11392 void Document::RemovedFromDocShell() {
11393   mEditingState = EditingState::eOff;
11394 
11395   if (mRemovedFromDocShell) return;
11396 
11397   mRemovedFromDocShell = true;
11398   NotifyActivityChanged();
11399 
11400   for (nsIContent* child = GetFirstChild(); child;
11401        child = child->GetNextSibling()) {
11402     child->SaveSubtreeState();
11403   }
11404 
11405   nsIDocShell* docShell = GetDocShell();
11406   if (docShell) {
11407     docShell->SynchronizeLayoutHistoryState();
11408   }
11409 }
11410 
GetLayoutHistoryState() const11411 already_AddRefed<nsILayoutHistoryState> Document::GetLayoutHistoryState()
11412     const {
11413   nsCOMPtr<nsILayoutHistoryState> state;
11414   if (!mScriptGlobalObject) {
11415     state = mLayoutHistoryState;
11416   } else {
11417     nsCOMPtr<nsIDocShell> docShell(mDocumentContainer);
11418     if (docShell) {
11419       docShell->GetLayoutHistoryState(getter_AddRefs(state));
11420     }
11421   }
11422 
11423   return state.forget();
11424 }
11425 
EnsureOnloadBlocker()11426 void Document::EnsureOnloadBlocker() {
11427   // If mScriptGlobalObject is null, we shouldn't be messing with the loadgroup
11428   // -- it's not ours.
11429   if (mOnloadBlockCount != 0 && mScriptGlobalObject) {
11430     nsCOMPtr<nsILoadGroup> loadGroup = GetDocumentLoadGroup();
11431     if (loadGroup) {
11432       // Check first to see if mOnloadBlocker is in the loadgroup.
11433       nsCOMPtr<nsISimpleEnumerator> requests;
11434       loadGroup->GetRequests(getter_AddRefs(requests));
11435 
11436       bool hasMore = false;
11437       while (NS_SUCCEEDED(requests->HasMoreElements(&hasMore)) && hasMore) {
11438         nsCOMPtr<nsISupports> elem;
11439         requests->GetNext(getter_AddRefs(elem));
11440         nsCOMPtr<nsIRequest> request = do_QueryInterface(elem);
11441         if (request && request == mOnloadBlocker) {
11442           return;
11443         }
11444       }
11445 
11446       // Not in the loadgroup, so add it.
11447       loadGroup->AddRequest(mOnloadBlocker, nullptr);
11448     }
11449   }
11450 }
11451 
BlockOnload()11452 void Document::BlockOnload() {
11453   if (mDisplayDocument) {
11454     mDisplayDocument->BlockOnload();
11455     return;
11456   }
11457 
11458   // If mScriptGlobalObject is null, we shouldn't be messing with the loadgroup
11459   // -- it's not ours.
11460   if (mOnloadBlockCount == 0 && mScriptGlobalObject) {
11461     if (nsCOMPtr<nsILoadGroup> loadGroup = GetDocumentLoadGroup()) {
11462       loadGroup->AddRequest(mOnloadBlocker, nullptr);
11463     }
11464   }
11465   ++mOnloadBlockCount;
11466 }
11467 
UnblockOnload(bool aFireSync)11468 void Document::UnblockOnload(bool aFireSync) {
11469   if (mDisplayDocument) {
11470     mDisplayDocument->UnblockOnload(aFireSync);
11471     return;
11472   }
11473 
11474   --mOnloadBlockCount;
11475 
11476   if (mOnloadBlockCount == 0) {
11477     if (mScriptGlobalObject) {
11478       // Only manipulate the loadgroup in this case, because if
11479       // mScriptGlobalObject is null, it's not ours.
11480       if (aFireSync) {
11481         // Increment mOnloadBlockCount, since DoUnblockOnload will decrement it
11482         ++mOnloadBlockCount;
11483         DoUnblockOnload();
11484       } else {
11485         PostUnblockOnloadEvent();
11486       }
11487     } else if (mIsBeingUsedAsImage) {
11488       // To correctly unblock onload for a document that contains an SVG
11489       // image, we need to know when all of the SVG document's resources are
11490       // done loading, in a way comparable to |window.onload|. We fire this
11491       // event to indicate that the SVG should be considered fully loaded.
11492       // Because scripting is disabled on SVG-as-image documents, this event
11493       // is not accessible to content authors. (See bug 837315.)
11494       RefPtr<AsyncEventDispatcher> asyncDispatcher =
11495           new AsyncEventDispatcher(this, u"MozSVGAsImageDocumentLoad"_ns,
11496                                    CanBubble::eNo, ChromeOnlyDispatch::eNo);
11497       asyncDispatcher->PostDOMEvent();
11498     }
11499   }
11500 }
11501 
11502 class nsUnblockOnloadEvent : public Runnable {
11503  public:
nsUnblockOnloadEvent(Document * aDoc)11504   explicit nsUnblockOnloadEvent(Document* aDoc)
11505       : mozilla::Runnable("nsUnblockOnloadEvent"), mDoc(aDoc) {}
Run()11506   NS_IMETHOD Run() override {
11507     mDoc->DoUnblockOnload();
11508     return NS_OK;
11509   }
11510 
11511  private:
11512   RefPtr<Document> mDoc;
11513 };
11514 
PostUnblockOnloadEvent()11515 void Document::PostUnblockOnloadEvent() {
11516   MOZ_RELEASE_ASSERT(NS_IsMainThread());
11517   nsCOMPtr<nsIRunnable> evt = new nsUnblockOnloadEvent(this);
11518   nsresult rv = Dispatch(TaskCategory::Other, evt.forget());
11519   if (NS_SUCCEEDED(rv)) {
11520     // Stabilize block count so we don't post more events while this one is up
11521     ++mOnloadBlockCount;
11522   } else {
11523     NS_WARNING("failed to dispatch nsUnblockOnloadEvent");
11524   }
11525 }
11526 
DoUnblockOnload()11527 void Document::DoUnblockOnload() {
11528   MOZ_ASSERT(!mDisplayDocument, "Shouldn't get here for resource document");
11529   MOZ_ASSERT(mOnloadBlockCount != 0,
11530              "Shouldn't have a count of zero here, since we stabilized in "
11531              "PostUnblockOnloadEvent");
11532 
11533   --mOnloadBlockCount;
11534 
11535   if (mOnloadBlockCount != 0) {
11536     // We blocked again after the last unblock.  Nothing to do here.  We'll
11537     // post a new event when we unblock again.
11538     return;
11539   }
11540 
11541   // If mScriptGlobalObject is null, we shouldn't be messing with the loadgroup
11542   // -- it's not ours.
11543   if (mScriptGlobalObject) {
11544     if (nsCOMPtr<nsILoadGroup> loadGroup = GetDocumentLoadGroup()) {
11545       loadGroup->RemoveRequest(mOnloadBlocker, nullptr, NS_OK);
11546     }
11547   }
11548 }
11549 
GetContentInThisDocument(nsIFrame * aFrame) const11550 nsIContent* Document::GetContentInThisDocument(nsIFrame* aFrame) const {
11551   for (nsIFrame* f = aFrame; f;
11552        f = nsLayoutUtils::GetParentOrPlaceholderForCrossDoc(f)) {
11553     nsIContent* content = f->GetContent();
11554     if (!content || content->IsInNativeAnonymousSubtree()) continue;
11555 
11556     if (content->OwnerDoc() == this) {
11557       return content;
11558     }
11559     // We must be in a subdocument so jump directly to the root frame.
11560     // GetParentOrPlaceholderForCrossDoc gets called immediately to jump up to
11561     // the containing document.
11562     f = f->PresContext()->GetPresShell()->GetRootFrame();
11563   }
11564 
11565   return nullptr;
11566 }
11567 
DispatchPageTransition(EventTarget * aDispatchTarget,const nsAString & aType,bool aInFrameSwap,bool aPersisted,bool aOnlySystemGroup)11568 void Document::DispatchPageTransition(EventTarget* aDispatchTarget,
11569                                       const nsAString& aType, bool aInFrameSwap,
11570                                       bool aPersisted, bool aOnlySystemGroup) {
11571   if (!aDispatchTarget) {
11572     return;
11573   }
11574 
11575   PageTransitionEventInit init;
11576   init.mBubbles = true;
11577   init.mCancelable = true;
11578   init.mPersisted = aPersisted;
11579   init.mInFrameSwap = aInFrameSwap;
11580 
11581   RefPtr<PageTransitionEvent> event =
11582       PageTransitionEvent::Constructor(this, aType, init);
11583 
11584   event->SetTrusted(true);
11585   event->SetTarget(this);
11586   if (aOnlySystemGroup) {
11587     event->WidgetEventPtr()->mFlags.mOnlySystemGroupDispatchInContent = true;
11588   }
11589   EventDispatcher::DispatchDOMEvent(aDispatchTarget, nullptr, event, nullptr,
11590                                     nullptr);
11591 }
11592 
OnPageShow(bool aPersisted,EventTarget * aDispatchStartTarget,bool aOnlySystemGroup)11593 void Document::OnPageShow(bool aPersisted, EventTarget* aDispatchStartTarget,
11594                           bool aOnlySystemGroup) {
11595   if (MOZ_LOG_TEST(gSHIPBFCacheLog, LogLevel::Debug)) {
11596     nsCString uri;
11597     if (GetDocumentURI()) {
11598       uri = GetDocumentURI()->GetSpecOrDefault();
11599     }
11600     MOZ_LOG(gSHIPBFCacheLog, LogLevel::Debug,
11601             ("Document::OnPageShow [%s] persisted=%i", uri.get(), aPersisted));
11602   }
11603 
11604   const bool inFrameLoaderSwap = !!aDispatchStartTarget;
11605   MOZ_DIAGNOSTIC_ASSERT(
11606       inFrameLoaderSwap ==
11607       (mDocumentContainer && mDocumentContainer->InFrameSwap()));
11608 
11609   Element* root = GetRootElement();
11610   if (aPersisted && root) {
11611     // Send out notifications that our <link> elements are attached.
11612     RefPtr<nsContentList> links =
11613         NS_GetContentList(root, kNameSpaceID_XHTML, u"link"_ns);
11614 
11615     uint32_t linkCount = links->Length(true);
11616     for (uint32_t i = 0; i < linkCount; ++i) {
11617       static_cast<HTMLLinkElement*>(links->Item(i, false))->LinkAdded();
11618     }
11619   }
11620 
11621   // See Document
11622   if (!inFrameLoaderSwap) {
11623     if (aPersisted) {
11624       ImageTracker()->SetAnimatingState(true);
11625     }
11626 
11627     // Set mIsShowing before firing events, in case those event handlers
11628     // move us around.
11629     mIsShowing = true;
11630     mVisible = true;
11631 
11632     UpdateVisibilityState();
11633   }
11634 
11635   NotifyActivityChanged();
11636 
11637   auto notifyExternal = [aPersisted](Document& aExternalResource) {
11638     aExternalResource.OnPageShow(aPersisted, nullptr);
11639     return CallState::Continue;
11640   };
11641   EnumerateExternalResources(notifyExternal);
11642 
11643   if (mAnimationController) {
11644     mAnimationController->OnPageShow();
11645   }
11646 
11647   if (!mIsBeingUsedAsImage) {
11648     // Dispatch observer notification to notify observers page is shown.
11649     nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
11650     if (os) {
11651       nsIPrincipal* principal = NodePrincipal();
11652       os->NotifyObservers(ToSupports(this),
11653                           principal->IsSystemPrincipal() ? "chrome-page-shown"
11654                                                          : "content-page-shown",
11655                           nullptr);
11656     }
11657 
11658     nsCOMPtr<EventTarget> target = aDispatchStartTarget;
11659     if (!target) {
11660       target = do_QueryInterface(GetWindow());
11661     }
11662     DispatchPageTransition(target, u"pageshow"_ns, inFrameLoaderSwap,
11663                            aPersisted, aOnlySystemGroup);
11664   }
11665 }
11666 
DispatchFullscreenChange(Document & aDocument,nsINode * aTarget)11667 static void DispatchFullscreenChange(Document& aDocument, nsINode* aTarget) {
11668   if (nsPresContext* presContext = aDocument.GetPresContext()) {
11669     auto pendingEvent = MakeUnique<PendingFullscreenEvent>(
11670         FullscreenEventType::Change, &aDocument, aTarget);
11671     presContext->RefreshDriver()->ScheduleFullscreenEvent(
11672         std::move(pendingEvent));
11673   }
11674 }
11675 
11676 static void ClearPendingFullscreenRequests(Document* aDoc);
11677 
OnPageHide(bool aPersisted,EventTarget * aDispatchStartTarget,bool aOnlySystemGroup)11678 void Document::OnPageHide(bool aPersisted, EventTarget* aDispatchStartTarget,
11679                           bool aOnlySystemGroup) {
11680   if (MOZ_LOG_TEST(gSHIPBFCacheLog, LogLevel::Debug)) {
11681     nsCString uri;
11682     if (GetDocumentURI()) {
11683       uri = GetDocumentURI()->GetSpecOrDefault();
11684     }
11685     MOZ_LOG(gSHIPBFCacheLog, LogLevel::Debug,
11686             ("Document::OnPageHide %s persisted=%i", uri.get(), aPersisted));
11687   }
11688 
11689   const bool inFrameLoaderSwap = !!aDispatchStartTarget;
11690   MOZ_DIAGNOSTIC_ASSERT(
11691       inFrameLoaderSwap ==
11692       (mDocumentContainer && mDocumentContainer->InFrameSwap()));
11693 
11694   // Send out notifications that our <link> elements are detached,
11695   // but only if this is not a full unload.
11696   Element* root = GetRootElement();
11697   if (aPersisted && root) {
11698     RefPtr<nsContentList> links =
11699         NS_GetContentList(root, kNameSpaceID_XHTML, u"link"_ns);
11700 
11701     uint32_t linkCount = links->Length(true);
11702     for (uint32_t i = 0; i < linkCount; ++i) {
11703       static_cast<HTMLLinkElement*>(links->Item(i, false))->LinkRemoved();
11704     }
11705   }
11706 
11707   if (mAnimationController) {
11708     mAnimationController->OnPageHide();
11709   }
11710 
11711   if (!inFrameLoaderSwap) {
11712     if (aPersisted) {
11713       // We do not stop the animations (bug 1024343) when the page is refreshing
11714       // while being dragged out.
11715       ImageTracker()->SetAnimatingState(false);
11716     }
11717 
11718     // Set mIsShowing before firing events, in case those event handlers
11719     // move us around.
11720     mIsShowing = false;
11721     mVisible = false;
11722   }
11723 
11724   ExitPointerLock();
11725 
11726   if (!mIsBeingUsedAsImage) {
11727     // Dispatch observer notification to notify observers page is hidden.
11728     nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
11729     if (os) {
11730       nsIPrincipal* principal = NodePrincipal();
11731       os->NotifyObservers(ToSupports(this),
11732                           principal->IsSystemPrincipal()
11733                               ? "chrome-page-hidden"
11734                               : "content-page-hidden",
11735                           nullptr);
11736     }
11737 
11738     // Now send out a PageHide event.
11739     nsCOMPtr<EventTarget> target = aDispatchStartTarget;
11740     if (!target) {
11741       target = do_QueryInterface(GetWindow());
11742     }
11743     {
11744       PageUnloadingEventTimeStamp timeStamp(this);
11745       DispatchPageTransition(target, u"pagehide"_ns, inFrameLoaderSwap,
11746                              aPersisted, aOnlySystemGroup);
11747     }
11748   }
11749 
11750   if (!inFrameLoaderSwap) {
11751     UpdateVisibilityState();
11752   }
11753 
11754   auto notifyExternal = [aPersisted](Document& aExternalResource) {
11755     aExternalResource.OnPageHide(aPersisted, nullptr);
11756     return CallState::Continue;
11757   };
11758   EnumerateExternalResources(notifyExternal);
11759   NotifyActivityChanged();
11760 
11761   ClearPendingFullscreenRequests(this);
11762   if (GetUnretargetedFullScreenElement()) {
11763     // If this document was fullscreen, we should exit fullscreen in this
11764     // doctree branch. This ensures that if the user navigates while in
11765     // fullscreen mode we don't leave its still visible ancestor documents
11766     // in fullscreen mode. So exit fullscreen in the document's fullscreen
11767     // root document, as this will exit fullscreen in all the root's
11768     // descendant documents. Note that documents are removed from the
11769     // doctree by the time OnPageHide() is called, so we must store a
11770     // reference to the root (in Document::mFullscreenRoot) since we can't
11771     // just traverse the doctree to get the root.
11772     Document::ExitFullscreenInDocTree(this);
11773 
11774     // Since the document is removed from the doctree before OnPageHide() is
11775     // called, ExitFullscreen() can't traverse from the root down to *this*
11776     // document, so we must manually call CleanupFullscreenState() below too.
11777     // Note that CleanupFullscreenState() clears Document::mFullscreenRoot,
11778     // so we *must* call it after ExitFullscreen(), not before.
11779     // OnPageHide() is called in every hidden (i.e. descendant) document,
11780     // so calling CleanupFullscreenState() here will ensure all hidden
11781     // documents have their fullscreen state reset.
11782     CleanupFullscreenState();
11783 
11784     // The fullscreenchange event is to be queued in the refresh driver,
11785     // however a hidden page wouldn't trigger that again, so it makes no
11786     // sense to dispatch such event here.
11787   }
11788 }
11789 
WillDispatchMutationEvent(nsINode * aTarget)11790 void Document::WillDispatchMutationEvent(nsINode* aTarget) {
11791   NS_ASSERTION(
11792       mSubtreeModifiedDepth != 0 || mSubtreeModifiedTargets.Count() == 0,
11793       "mSubtreeModifiedTargets not cleared after dispatching?");
11794   ++mSubtreeModifiedDepth;
11795   if (aTarget) {
11796     // MayDispatchMutationEvent is often called just before this method,
11797     // so it has already appended the node to mSubtreeModifiedTargets.
11798     int32_t count = mSubtreeModifiedTargets.Count();
11799     if (!count || mSubtreeModifiedTargets[count - 1] != aTarget) {
11800       mSubtreeModifiedTargets.AppendObject(aTarget);
11801     }
11802   }
11803 }
11804 
MutationEventDispatched(nsINode * aTarget)11805 void Document::MutationEventDispatched(nsINode* aTarget) {
11806   --mSubtreeModifiedDepth;
11807   if (mSubtreeModifiedDepth == 0) {
11808     int32_t count = mSubtreeModifiedTargets.Count();
11809     if (!count) {
11810       return;
11811     }
11812 
11813     nsPIDOMWindowInner* window = GetInnerWindow();
11814     if (window &&
11815         !window->HasMutationListeners(NS_EVENT_BITS_MUTATION_SUBTREEMODIFIED)) {
11816       mSubtreeModifiedTargets.Clear();
11817       return;
11818     }
11819 
11820     nsCOMArray<nsINode> realTargets;
11821     for (int32_t i = 0; i < count; ++i) {
11822       nsINode* possibleTarget = mSubtreeModifiedTargets[i];
11823       if (possibleTarget && possibleTarget->ChromeOnlyAccess()) {
11824         continue;
11825       }
11826 
11827       nsINode* commonAncestor = nullptr;
11828       int32_t realTargetCount = realTargets.Count();
11829       for (int32_t j = 0; j < realTargetCount; ++j) {
11830         commonAncestor = nsContentUtils::GetClosestCommonInclusiveAncestor(
11831             possibleTarget, realTargets[j]);
11832         if (commonAncestor) {
11833           realTargets.ReplaceObjectAt(commonAncestor, j);
11834           break;
11835         }
11836       }
11837       if (!commonAncestor) {
11838         realTargets.AppendObject(possibleTarget);
11839       }
11840     }
11841 
11842     mSubtreeModifiedTargets.Clear();
11843 
11844     int32_t realTargetCount = realTargets.Count();
11845     for (int32_t k = 0; k < realTargetCount; ++k) {
11846       InternalMutationEvent mutation(true, eLegacySubtreeModified);
11847       (new AsyncEventDispatcher(realTargets[k], mutation))
11848           ->RunDOMEventWhenSafe();
11849     }
11850   }
11851 }
11852 
DestroyElementMaps()11853 void Document::DestroyElementMaps() {
11854 #ifdef DEBUG
11855   mStyledLinksCleared = true;
11856 #endif
11857   mStyledLinks.Clear();
11858   // Notify ID change listeners before clearing the identifier map.
11859   for (auto iter = mIdentifierMap.Iter(); !iter.Done(); iter.Next()) {
11860     iter.Get()->ClearAndNotify();
11861   }
11862   mIdentifierMap.Clear();
11863   mComposedShadowRoots.Clear();
11864   mResponsiveContent.Clear();
11865   IncrementExpandoGeneration(*this);
11866 }
11867 
RefreshLinkHrefs()11868 void Document::RefreshLinkHrefs() {
11869   // Get a list of all links we know about.  We will reset them, which will
11870   // remove them from the document, so we need a copy of what is in the
11871   // hashtable.
11872   const LinkArray linksToNotify = ToArray(mStyledLinks);
11873 
11874   // Reset all of our styled links.
11875   nsAutoScriptBlocker scriptBlocker;
11876   for (LinkArray::size_type i = 0; i < linksToNotify.Length(); i++) {
11877     linksToNotify[i]->ResetLinkState(true, linksToNotify[i]->ElementHasHref());
11878   }
11879 }
11880 
CloneDocHelper(Document * clone) const11881 nsresult Document::CloneDocHelper(Document* clone) const {
11882   clone->mIsStaticDocument = mCreatingStaticClone;
11883 
11884   // Init document
11885   nsresult rv = clone->Init();
11886   NS_ENSURE_SUCCESS(rv, rv);
11887 
11888   if (mCreatingStaticClone) {
11889     if (mOriginalDocument) {
11890       clone->mOriginalDocument = mOriginalDocument;
11891     } else {
11892       clone->mOriginalDocument = const_cast<Document*>(this);
11893     }
11894     clone->mOriginalDocument->mLatestStaticClone = clone;
11895     clone->mOriginalDocument->mStaticCloneCount++;
11896 
11897     nsCOMPtr<nsILoadGroup> loadGroup;
11898 
11899     // |mDocumentContainer| is the container of the document that is being
11900     // created and not the original container. See CreateStaticClone function().
11901     nsCOMPtr<nsIDocumentLoader> docLoader(mDocumentContainer);
11902     if (docLoader) {
11903       docLoader->GetLoadGroup(getter_AddRefs(loadGroup));
11904     }
11905     nsCOMPtr<nsIChannel> channel = GetChannel();
11906     nsCOMPtr<nsIURI> uri;
11907     if (channel) {
11908       NS_GetFinalChannelURI(channel, getter_AddRefs(uri));
11909     } else {
11910       uri = Document::GetDocumentURI();
11911     }
11912     clone->mChannel = channel;
11913     if (uri) {
11914       clone->ResetToURI(uri, loadGroup, NodePrincipal(), mPartitionedPrincipal);
11915     }
11916 
11917     clone->mIsSrcdocDocument = mIsSrcdocDocument;
11918     clone->SetContainer(mDocumentContainer);
11919 
11920     // Setup the navigation time. This will be needed by any animations in the
11921     // document, even if they are only paused.
11922     MOZ_ASSERT(!clone->GetNavigationTiming(),
11923                "Navigation time was already set?");
11924     if (mTiming) {
11925       RefPtr<nsDOMNavigationTiming> timing =
11926           mTiming->CloneNavigationTime(nsDocShell::Cast(clone->GetDocShell()));
11927       clone->SetNavigationTiming(timing);
11928     }
11929     clone->SetCsp(mCSP);
11930   }
11931 
11932   // Now ensure that our clone has the same URI, base URI, and principal as us.
11933   // We do this after the mCreatingStaticClone block above, because that block
11934   // can set the base URI to an incorrect value in cases when base URI
11935   // information came from the channel.  So we override explicitly, and do it
11936   // for all these properties, in case ResetToURI messes with any of the rest of
11937   // them.
11938   clone->SetDocumentURI(Document::GetDocumentURI());
11939   clone->SetChromeXHRDocURI(mChromeXHRDocURI);
11940   clone->SetPrincipals(NodePrincipal(), mPartitionedPrincipal);
11941   clone->mActiveStoragePrincipal = mActiveStoragePrincipal;
11942   // NOTE(emilio): Intentionally setting this to the GetDocBaseURI rather than
11943   // just mDocumentBaseURI, so that srcdoc iframes get the right base URI even
11944   // when printed standalone via window.print() (where there won't be a parent
11945   // document to grab the URI from).
11946   clone->mDocumentBaseURI = GetDocBaseURI();
11947   clone->SetChromeXHRDocBaseURI(mChromeXHRDocBaseURI);
11948   clone->mReferrerInfo =
11949       static_cast<dom::ReferrerInfo*>(mReferrerInfo.get())->Clone();
11950   clone->mPreloadReferrerInfo = clone->mReferrerInfo;
11951 
11952   bool hasHadScriptObject = true;
11953   nsIScriptGlobalObject* scriptObject =
11954       GetScriptHandlingObject(hasHadScriptObject);
11955   NS_ENSURE_STATE(scriptObject || !hasHadScriptObject);
11956   if (mCreatingStaticClone) {
11957     // If we're doing a static clone (print, print preview), then we're going to
11958     // be setting a scope object after the clone. It's better to set it only
11959     // once, so we don't do that here. However, we do want to act as if there is
11960     // a script handling object. So we set mHasHadScriptHandlingObject.
11961     clone->mHasHadScriptHandlingObject = true;
11962   } else if (scriptObject) {
11963     clone->SetScriptHandlingObject(scriptObject);
11964   } else {
11965     clone->SetScopeObject(GetScopeObject());
11966   }
11967   // Make the clone a data document
11968   clone->SetLoadedAsData(
11969       true,
11970       /* aConsiderForMemoryReporting */ !mCreatingStaticClone);
11971 
11972   // Misc state
11973 
11974   // State from Document
11975   clone->mCharacterSet = mCharacterSet;
11976   clone->mCharacterSetSource = mCharacterSetSource;
11977   clone->SetCompatibilityMode(mCompatMode);
11978   clone->mBidiOptions = mBidiOptions;
11979   clone->mContentLanguage = mContentLanguage;
11980   clone->SetContentType(GetContentTypeInternal());
11981   clone->mSecurityInfo = mSecurityInfo;
11982 
11983   // State from Document
11984   clone->mType = mType;
11985   clone->mXMLDeclarationBits = mXMLDeclarationBits;
11986   clone->mBaseTarget = mBaseTarget;
11987 
11988   return NS_OK;
11989 }
11990 
NotifyLoading(bool aNewParentIsLoading,const ReadyState & aCurrentState,ReadyState aNewState)11991 void Document::NotifyLoading(bool aNewParentIsLoading,
11992                              const ReadyState& aCurrentState,
11993                              ReadyState aNewState) {
11994   // Mirror the top-level loading state down to all subdocuments
11995   bool was_loading = mAncestorIsLoading ||
11996                      aCurrentState == READYSTATE_LOADING ||
11997                      aCurrentState == READYSTATE_INTERACTIVE;
11998   bool is_loading = aNewParentIsLoading || aNewState == READYSTATE_LOADING ||
11999                     aNewState == READYSTATE_INTERACTIVE;  // new value for state
12000   bool set_load_state = was_loading != is_loading;
12001 
12002   MOZ_LOG(
12003       gTimeoutDeferralLog, mozilla::LogLevel::Debug,
12004       ("NotifyLoading for doc %p: currentAncestor: %d, newParent: %d, "
12005        "currentState %d newState: %d, was_loading: %d, is_loading: %d, "
12006        "set_load_state: %d",
12007        (void*)this, mAncestorIsLoading, aNewParentIsLoading, (int)aCurrentState,
12008        (int)aNewState, was_loading, is_loading, set_load_state));
12009 
12010   mAncestorIsLoading = aNewParentIsLoading;
12011   if (set_load_state && StaticPrefs::dom_timeout_defer_during_load()) {
12012     // Tell our innerwindow (and thus TimeoutManager)
12013     nsPIDOMWindowInner* inner = GetInnerWindow();
12014     if (inner) {
12015       inner->SetActiveLoadingState(is_loading);
12016     }
12017     BrowsingContext* context = GetBrowsingContext();
12018     if (context) {
12019       // Don't use PreOrderWalk to mirror this down; go down one level as a
12020       // time so we can set mAncestorIsLoading and take into account the
12021       // readystates of the subdocument.  In the child process it will call
12022       // NotifyLoading() to notify the innerwindow/TimeoutManager, and then
12023       // iterate it's children
12024       for (auto& child : context->Children()) {
12025         MOZ_LOG(gTimeoutDeferralLog, mozilla::LogLevel::Debug,
12026                 ("bc: %p SetAncestorLoading(%d)", (void*)child, is_loading));
12027         // Setting ancestor loading on a discarded browsing context has no
12028         // effect.
12029         Unused << child->SetAncestorLoading(is_loading);
12030       }
12031     }
12032   }
12033 }
12034 
SetReadyStateInternal(ReadyState aReadyState,bool aUpdateTimingInformation)12035 void Document::SetReadyStateInternal(ReadyState aReadyState,
12036                                      bool aUpdateTimingInformation) {
12037   if (aReadyState == READYSTATE_UNINITIALIZED) {
12038     // Transition back to uninitialized happens only to keep assertions happy
12039     // right before readyState transitions to something else. Make this
12040     // transition undetectable by Web content.
12041     mReadyState = aReadyState;
12042     return;
12043   }
12044 
12045   if (IsTopLevelContentDocument()) {
12046     if (aReadyState == READYSTATE_LOADING) {
12047       AddToplevelLoadingDocument(this);
12048     } else if (aReadyState == READYSTATE_COMPLETE) {
12049       RemoveToplevelLoadingDocument(this);
12050     }
12051   }
12052 
12053   if (aUpdateTimingInformation && READYSTATE_LOADING == aReadyState) {
12054     mLoadingTimeStamp = TimeStamp::Now();
12055   }
12056   NotifyLoading(mAncestorIsLoading, mReadyState, aReadyState);
12057   mReadyState = aReadyState;
12058   if (aUpdateTimingInformation && mTiming) {
12059     switch (aReadyState) {
12060       case READYSTATE_LOADING:
12061         mTiming->NotifyDOMLoading(GetDocumentURI());
12062         break;
12063       case READYSTATE_INTERACTIVE:
12064         mTiming->NotifyDOMInteractive(GetDocumentURI());
12065         break;
12066       case READYSTATE_COMPLETE:
12067         mTiming->NotifyDOMComplete(GetDocumentURI());
12068         break;
12069       default:
12070         MOZ_ASSERT_UNREACHABLE("Unexpected ReadyState value");
12071         break;
12072     }
12073   }
12074   // At the time of loading start, we don't have timing object, record time.
12075 
12076   if (READYSTATE_INTERACTIVE == aReadyState &&
12077       NodePrincipal()->IsSystemPrincipal()) {
12078     if (!mXULPersist) {
12079       mXULPersist = new XULPersist(this);
12080       mXULPersist->Init();
12081     }
12082     if (!mChromeObserver) {
12083       mChromeObserver = new ChromeObserver(this);
12084       mChromeObserver->Init();
12085     }
12086   }
12087 
12088   if (aUpdateTimingInformation) {
12089     RecordNavigationTiming(aReadyState);
12090   }
12091 
12092   RefPtr<AsyncEventDispatcher> asyncDispatcher = new AsyncEventDispatcher(
12093       this, u"readystatechange"_ns, CanBubble::eNo, ChromeOnlyDispatch::eNo);
12094   asyncDispatcher->RunDOMEventWhenSafe();
12095 }
12096 
GetReadyState(nsAString & aReadyState) const12097 void Document::GetReadyState(nsAString& aReadyState) const {
12098   switch (mReadyState) {
12099     case READYSTATE_LOADING:
12100       aReadyState.AssignLiteral(u"loading");
12101       break;
12102     case READYSTATE_INTERACTIVE:
12103       aReadyState.AssignLiteral(u"interactive");
12104       break;
12105     case READYSTATE_COMPLETE:
12106       aReadyState.AssignLiteral(u"complete");
12107       break;
12108     default:
12109       aReadyState.AssignLiteral(u"uninitialized");
12110   }
12111 }
12112 
SuppressEventHandling(uint32_t aIncrease)12113 void Document::SuppressEventHandling(uint32_t aIncrease) {
12114   mEventsSuppressed += aIncrease;
12115   if (mEventsSuppressed == aIncrease) {
12116     WindowGlobalChild* wgc = GetWindowGlobalChild();
12117     if (wgc) {
12118       wgc->BlockBFCacheFor(BFCacheStatus::EVENT_HANDLING_SUPPRESSED);
12119     }
12120   }
12121   UpdateFrameRequestCallbackSchedulingState();
12122   for (uint32_t i = 0; i < aIncrease; ++i) {
12123     ScriptLoader()->AddExecuteBlocker();
12124   }
12125 
12126   auto suppressInSubDoc = [aIncrease](Document& aSubDoc) {
12127     aSubDoc.SuppressEventHandling(aIncrease);
12128     return CallState::Continue;
12129   };
12130 
12131   EnumerateSubDocuments(suppressInSubDoc);
12132 }
12133 
NotifyAbortedLoad()12134 void Document::NotifyAbortedLoad() {
12135   // If we still have outstanding work blocking DOMContentLoaded,
12136   // then don't try to change the readystate now, but wait until
12137   // they finish and then do so.
12138   if (mBlockDOMContentLoaded > 0 && !mDidFireDOMContentLoaded) {
12139     mSetCompleteAfterDOMContentLoaded = true;
12140     return;
12141   }
12142 
12143   // Otherwise we're fully done at this point, so set the
12144   // readystate to complete.
12145   if (GetReadyStateEnum() == Document::READYSTATE_INTERACTIVE) {
12146     SetReadyStateInternal(Document::READYSTATE_COMPLETE);
12147   }
12148 }
12149 
FireOrClearDelayedEvents(nsTArray<nsCOMPtr<Document>> & aDocuments,bool aFireEvents)12150 static void FireOrClearDelayedEvents(nsTArray<nsCOMPtr<Document>>& aDocuments,
12151                                      bool aFireEvents) {
12152   nsFocusManager* fm = nsFocusManager::GetFocusManager();
12153   if (!fm) return;
12154 
12155   for (uint32_t i = 0; i < aDocuments.Length(); ++i) {
12156     // NB: Don't bother trying to fire delayed events on documents that were
12157     // closed before this event ran.
12158     if (!aDocuments[i]->EventHandlingSuppressed()) {
12159       fm->FireDelayedEvents(aDocuments[i]);
12160       RefPtr<PresShell> presShell = aDocuments[i]->GetPresShell();
12161       if (presShell) {
12162         // Only fire events for active documents.
12163         bool fire = aFireEvents && aDocuments[i]->GetInnerWindow() &&
12164                     aDocuments[i]->GetInnerWindow()->IsCurrentInnerWindow();
12165         presShell->FireOrClearDelayedEvents(fire);
12166       }
12167       aDocuments[i]->FireOrClearPostMessageEvents(aFireEvents);
12168     }
12169   }
12170 }
12171 
PreloadPictureClosed()12172 void Document::PreloadPictureClosed() {
12173   MOZ_ASSERT(mPreloadPictureDepth > 0);
12174   mPreloadPictureDepth--;
12175   if (mPreloadPictureDepth == 0) {
12176     mPreloadPictureFoundSource.SetIsVoid(true);
12177   }
12178 }
12179 
PreloadPictureImageSource(const nsAString & aSrcsetAttr,const nsAString & aSizesAttr,const nsAString & aTypeAttr,const nsAString & aMediaAttr)12180 void Document::PreloadPictureImageSource(const nsAString& aSrcsetAttr,
12181                                          const nsAString& aSizesAttr,
12182                                          const nsAString& aTypeAttr,
12183                                          const nsAString& aMediaAttr) {
12184   // Nested pictures are not valid syntax, so while we'll eventually load them,
12185   // it's not worth tracking sources mixed between nesting levels to preload
12186   // them effectively.
12187   if (mPreloadPictureDepth == 1 && mPreloadPictureFoundSource.IsVoid()) {
12188     // <picture> selects the first matching source, so if this returns a URI we
12189     // needn't consider new sources until a new <picture> is encountered.
12190     bool found = HTMLImageElement::SelectSourceForTagWithAttrs(
12191         this, true, VoidString(), aSrcsetAttr, aSizesAttr, aTypeAttr,
12192         aMediaAttr, mPreloadPictureFoundSource);
12193     if (found && mPreloadPictureFoundSource.IsVoid()) {
12194       // Found an empty source, which counts
12195       mPreloadPictureFoundSource.SetIsVoid(false);
12196     }
12197   }
12198 }
12199 
ResolvePreloadImage(nsIURI * aBaseURI,const nsAString & aSrcAttr,const nsAString & aSrcsetAttr,const nsAString & aSizesAttr,bool * aIsImgSet)12200 already_AddRefed<nsIURI> Document::ResolvePreloadImage(
12201     nsIURI* aBaseURI, const nsAString& aSrcAttr, const nsAString& aSrcsetAttr,
12202     const nsAString& aSizesAttr, bool* aIsImgSet) {
12203   nsString sourceURL;
12204   bool isImgSet;
12205   if (mPreloadPictureDepth == 1 && !mPreloadPictureFoundSource.IsVoid()) {
12206     // We're in a <picture> element and found a URI from a source previous to
12207     // this image, use it.
12208     sourceURL = mPreloadPictureFoundSource;
12209     isImgSet = true;
12210   } else {
12211     // Otherwise try to use this <img> as a source
12212     HTMLImageElement::SelectSourceForTagWithAttrs(
12213         this, false, aSrcAttr, aSrcsetAttr, aSizesAttr, VoidString(),
12214         VoidString(), sourceURL);
12215     isImgSet = !aSrcsetAttr.IsEmpty();
12216   }
12217 
12218   // Empty sources are not loaded by <img> (i.e. not resolved to the baseURI)
12219   if (sourceURL.IsEmpty()) {
12220     return nullptr;
12221   }
12222 
12223   // Construct into URI using passed baseURI (the parser may know of base URI
12224   // changes that have not reached us)
12225   nsresult rv;
12226   nsCOMPtr<nsIURI> uri;
12227   rv = nsContentUtils::NewURIWithDocumentCharset(getter_AddRefs(uri), sourceURL,
12228                                                  this, aBaseURI);
12229   if (NS_FAILED(rv)) {
12230     return nullptr;
12231   }
12232 
12233   *aIsImgSet = isImgSet;
12234 
12235   // We don't clear mPreloadPictureFoundSource because subsequent <img> tags in
12236   // this this <picture> share the same <sources> (though this is not valid per
12237   // spec)
12238   return uri.forget();
12239 }
12240 
PreLoadImage(nsIURI * aUri,const nsAString & aCrossOriginAttr,ReferrerPolicyEnum aReferrerPolicy,bool aIsImgSet,bool aLinkPreload)12241 void Document::PreLoadImage(nsIURI* aUri, const nsAString& aCrossOriginAttr,
12242                             ReferrerPolicyEnum aReferrerPolicy, bool aIsImgSet,
12243                             bool aLinkPreload) {
12244   nsLoadFlags loadFlags = nsIRequest::LOAD_NORMAL |
12245                           nsIRequest::LOAD_RECORD_START_REQUEST_DELAY |
12246                           nsContentUtils::CORSModeToLoadImageFlags(
12247                               Element::StringToCORSMode(aCrossOriginAttr));
12248 
12249   nsContentPolicyType policyType =
12250       aIsImgSet ? nsIContentPolicy::TYPE_IMAGESET
12251                 : nsIContentPolicy::TYPE_INTERNAL_IMAGE_PRELOAD;
12252 
12253   nsCOMPtr<nsIReferrerInfo> referrerInfo =
12254       ReferrerInfo::CreateFromDocumentAndPolicyOverride(this, aReferrerPolicy);
12255 
12256   RefPtr<imgRequestProxy> request;
12257   nsresult rv = nsContentUtils::LoadImage(
12258       aUri, static_cast<nsINode*>(this), this, NodePrincipal(), 0, referrerInfo,
12259       nullptr /* no observer */, loadFlags,
12260       aLinkPreload ? u"link"_ns : u"img"_ns, getter_AddRefs(request),
12261       policyType, false /* urgent */, aLinkPreload);
12262 
12263   // Pin image-reference to avoid evicting it from the img-cache before
12264   // the "real" load occurs. Unpinned in DispatchContentLoadedEvents and
12265   // unlink
12266   if (!aLinkPreload && NS_SUCCEEDED(rv)) {
12267     mPreloadingImages.InsertOrUpdate(aUri, std::move(request));
12268   }
12269 }
12270 
MaybePreLoadImage(nsIURI * aUri,const nsAString & aCrossOriginAttr,ReferrerPolicyEnum aReferrerPolicy,bool aIsImgSet,bool aLinkPreload,const TimeStamp & aInitTimestamp)12271 void Document::MaybePreLoadImage(nsIURI* aUri,
12272                                  const nsAString& aCrossOriginAttr,
12273                                  ReferrerPolicyEnum aReferrerPolicy,
12274                                  bool aIsImgSet, bool aLinkPreload,
12275                                  const TimeStamp& aInitTimestamp) {
12276   if (aLinkPreload) {
12277     // Check if the image was already preloaded in this document to avoid
12278     // duplicate preloading.
12279     PreloadHashKey key = PreloadHashKey::CreateAsImage(
12280         aUri, NodePrincipal(),
12281         dom::Element::StringToCORSMode(aCrossOriginAttr));
12282     if (!mPreloadService.PreloadExists(key)) {
12283       PreLoadImage(aUri, aCrossOriginAttr, aReferrerPolicy, aIsImgSet,
12284                    aLinkPreload);
12285     }
12286     return;
12287   }
12288 
12289   // Early exit if the img is already present in the img-cache
12290   // which indicates that the "real" load has already started and
12291   // that we shouldn't preload it.
12292   if (nsContentUtils::IsImageInCache(aUri, this)) {
12293     return;
12294   }
12295 
12296 #ifdef NIGHTLY_BUILD
12297   Telemetry::Accumulate(
12298       Telemetry::DOCUMENT_PRELOAD_IMAGE_ASYNCOPEN_DELAY,
12299       static_cast<uint32_t>(
12300           (TimeStamp::Now() - aInitTimestamp).ToMilliseconds()));
12301 #endif
12302 
12303   // Image not in cache - trigger preload
12304   PreLoadImage(aUri, aCrossOriginAttr, aReferrerPolicy, aIsImgSet,
12305                aLinkPreload);
12306 }
12307 
MaybePreconnect(nsIURI * aOrigURI,mozilla::CORSMode aCORSMode)12308 void Document::MaybePreconnect(nsIURI* aOrigURI, mozilla::CORSMode aCORSMode) {
12309   NS_MutateURI mutator(aOrigURI);
12310   if (NS_FAILED(mutator.GetStatus())) {
12311     return;
12312   }
12313 
12314   // The URI created here is used in 2 contexts. One is nsISpeculativeConnect
12315   // which ignores the path and uses only the origin. The other is for the
12316   // document mPreloadedPreconnects de-duplication hash. Anonymous vs
12317   // non-Anonymous preconnects create different connections on the wire and
12318   // therefore should not be considred duplicates of each other and we
12319   // normalize the path before putting it in the hash to accomplish that.
12320 
12321   if (aCORSMode == CORS_ANONYMOUS) {
12322     mutator.SetPathQueryRef("/anonymous"_ns);
12323   } else {
12324     mutator.SetPathQueryRef("/"_ns);
12325   }
12326 
12327   nsCOMPtr<nsIURI> uri;
12328   nsresult rv = mutator.Finalize(uri);
12329   if (NS_FAILED(rv)) {
12330     return;
12331   }
12332 
12333   const bool existingEntryFound =
12334       mPreloadedPreconnects.WithEntryHandle(uri, [](auto&& entry) {
12335         if (entry) {
12336           return true;
12337         }
12338         entry.Insert(true);
12339         return false;
12340       });
12341   if (existingEntryFound) {
12342     return;
12343   }
12344 
12345   nsCOMPtr<nsISpeculativeConnect> speculator(
12346       do_QueryInterface(nsContentUtils::GetIOService()));
12347   if (!speculator) {
12348     return;
12349   }
12350 
12351   if (aCORSMode == CORS_ANONYMOUS) {
12352     speculator->SpeculativeAnonymousConnect(uri, NodePrincipal(), nullptr);
12353   } else {
12354     speculator->SpeculativeConnect(uri, NodePrincipal(), nullptr);
12355   }
12356 }
12357 
ForgetImagePreload(nsIURI * aURI)12358 void Document::ForgetImagePreload(nsIURI* aURI) {
12359   // Checking count is faster than hashing the URI in the common
12360   // case of empty table.
12361   if (mPreloadingImages.Count() != 0) {
12362     nsCOMPtr<imgIRequest> req;
12363     mPreloadingImages.Remove(aURI, getter_AddRefs(req));
12364     if (req) {
12365       // Make sure to cancel the request so imagelib knows it's gone.
12366       req->CancelAndForgetObserver(NS_BINDING_ABORTED);
12367     }
12368   }
12369 }
12370 
UpdateDocumentStates(EventStates aMaybeChangedStates,bool aNotify)12371 void Document::UpdateDocumentStates(EventStates aMaybeChangedStates,
12372                                     bool aNotify) {
12373   const EventStates oldStates = mDocumentState;
12374   if (aMaybeChangedStates.HasAtLeastOneOfStates(
12375           NS_DOCUMENT_STATE_ALL_LOCALEDIR_BITS)) {
12376     mDocumentState &= ~NS_DOCUMENT_STATE_ALL_LOCALEDIR_BITS;
12377     if (IsDocumentRightToLeft()) {
12378       mDocumentState |= NS_DOCUMENT_STATE_RTL_LOCALE;
12379     } else {
12380       mDocumentState |= NS_DOCUMENT_STATE_LTR_LOCALE;
12381     }
12382   }
12383 
12384   if (aMaybeChangedStates.HasAtLeastOneOfStates(
12385           NS_DOCUMENT_STATE_ALL_LWTHEME_BITS)) {
12386     mDocumentState &= ~NS_DOCUMENT_STATE_ALL_LWTHEME_BITS;
12387     switch (GetDocumentLWTheme()) {
12388       case DocumentTheme::None:
12389         break;
12390       case DocumentTheme::Bright:
12391         mDocumentState |=
12392             NS_DOCUMENT_STATE_LWTHEME | NS_DOCUMENT_STATE_LWTHEME_BRIGHTTEXT;
12393         break;
12394       case DocumentTheme::Dark:
12395         mDocumentState |=
12396             NS_DOCUMENT_STATE_LWTHEME | NS_DOCUMENT_STATE_LWTHEME_DARKTEXT;
12397         break;
12398       case DocumentTheme::Neutral:
12399         mDocumentState |= NS_DOCUMENT_STATE_LWTHEME;
12400         break;
12401     }
12402   }
12403 
12404   if (aMaybeChangedStates.HasState(NS_DOCUMENT_STATE_WINDOW_INACTIVE)) {
12405     if (IsTopLevelWindowInactive()) {
12406       mDocumentState |= NS_DOCUMENT_STATE_WINDOW_INACTIVE;
12407     } else {
12408       mDocumentState &= ~NS_DOCUMENT_STATE_WINDOW_INACTIVE;
12409     }
12410   }
12411 
12412   EventStates changedStates = oldStates ^ mDocumentState;
12413   if (aNotify && !changedStates.IsEmpty()) {
12414     if (PresShell* ps = GetObservingPresShell()) {
12415       ps->DocumentStatesChanged(changedStates);
12416     }
12417   }
12418 }
12419 
12420 namespace {
12421 
12422 /**
12423  * Stub for LoadSheet(), since all we want is to get the sheet into
12424  * the CSSLoader's style cache
12425  */
12426 class StubCSSLoaderObserver final : public nsICSSLoaderObserver {
12427   ~StubCSSLoaderObserver() = default;
12428 
12429  public:
12430   NS_IMETHOD
StyleSheetLoaded(StyleSheet *,bool,nsresult)12431   StyleSheetLoaded(StyleSheet*, bool, nsresult) override { return NS_OK; }
12432   NS_DECL_ISUPPORTS
12433 };
12434 NS_IMPL_ISUPPORTS(StubCSSLoaderObserver, nsICSSLoaderObserver)
12435 
12436 }  // namespace
12437 
PreloadStyle(nsIURI * uri,const Encoding * aEncoding,const nsAString & aCrossOriginAttr,const enum ReferrerPolicy aReferrerPolicy,const nsAString & aIntegrity,css::StylePreloadKind aKind)12438 SheetPreloadStatus Document::PreloadStyle(
12439     nsIURI* uri, const Encoding* aEncoding, const nsAString& aCrossOriginAttr,
12440     const enum ReferrerPolicy aReferrerPolicy, const nsAString& aIntegrity,
12441     css::StylePreloadKind aKind) {
12442   MOZ_ASSERT(aKind != css::StylePreloadKind::None);
12443 
12444   // The CSSLoader will retain this object after we return.
12445   nsCOMPtr<nsICSSLoaderObserver> obs = new StubCSSLoaderObserver();
12446 
12447   nsCOMPtr<nsIReferrerInfo> referrerInfo =
12448       ReferrerInfo::CreateFromDocumentAndPolicyOverride(this, aReferrerPolicy);
12449 
12450   // Charset names are always ASCII.
12451   auto result = CSSLoader()->LoadSheet(
12452       uri, aKind, aEncoding, referrerInfo, obs,
12453       Element::StringToCORSMode(aCrossOriginAttr), aIntegrity);
12454   if (result.isErr()) {
12455     return SheetPreloadStatus::Errored;
12456   }
12457   RefPtr<StyleSheet> sheet = result.unwrap();
12458   if (sheet->IsComplete()) {
12459     return SheetPreloadStatus::AlreadyComplete;
12460   }
12461   return SheetPreloadStatus::InProgress;
12462 }
12463 
LoadChromeSheetSync(nsIURI * uri)12464 RefPtr<StyleSheet> Document::LoadChromeSheetSync(nsIURI* uri) {
12465   return CSSLoader()
12466       ->LoadSheetSync(uri, css::eAuthorSheetFeatures)
12467       .unwrapOr(nullptr);
12468 }
12469 
ResetDocumentDirection()12470 void Document::ResetDocumentDirection() {
12471   if (!nsContentUtils::IsChromeDoc(this)) {
12472     return;
12473   }
12474   UpdateDocumentStates(NS_DOCUMENT_STATE_ALL_LOCALEDIR_BITS, true);
12475 }
12476 
IsDocumentRightToLeft()12477 bool Document::IsDocumentRightToLeft() {
12478   if (!nsContentUtils::IsChromeDoc(this)) {
12479     return false;
12480   }
12481   // setting the localedir attribute on the root element forces a
12482   // specific direction for the document.
12483   Element* element = GetRootElement();
12484   if (element) {
12485     static Element::AttrValuesArray strings[] = {nsGkAtoms::ltr, nsGkAtoms::rtl,
12486                                                  nullptr};
12487     switch (element->FindAttrValueIn(kNameSpaceID_None, nsGkAtoms::localedir,
12488                                      strings, eCaseMatters)) {
12489       case 0:
12490         return false;
12491       case 1:
12492         return true;
12493       default:
12494         break;  // otherwise, not a valid value, so fall through
12495     }
12496   }
12497 
12498   if (!mDocumentURI->SchemeIs("chrome") && !mDocumentURI->SchemeIs("about") &&
12499       !mDocumentURI->SchemeIs("resource")) {
12500     return false;
12501   }
12502 
12503   return intl::LocaleService::GetInstance()->IsAppLocaleRTL();
12504 }
12505 
12506 class nsDelayedEventDispatcher : public Runnable {
12507  public:
nsDelayedEventDispatcher(nsTArray<nsCOMPtr<Document>> && aDocuments)12508   explicit nsDelayedEventDispatcher(nsTArray<nsCOMPtr<Document>>&& aDocuments)
12509       : mozilla::Runnable("nsDelayedEventDispatcher"),
12510         mDocuments(std::move(aDocuments)) {}
12511   virtual ~nsDelayedEventDispatcher() = default;
12512 
Run()12513   NS_IMETHOD Run() override {
12514     FireOrClearDelayedEvents(mDocuments, true);
12515     return NS_OK;
12516   }
12517 
12518  private:
12519   nsTArray<nsCOMPtr<Document>> mDocuments;
12520 };
12521 
GetAndUnsuppressSubDocuments(Document & aDocument,nsTArray<nsCOMPtr<Document>> & aDocuments)12522 static void GetAndUnsuppressSubDocuments(
12523     Document& aDocument, nsTArray<nsCOMPtr<Document>>& aDocuments) {
12524   if (aDocument.EventHandlingSuppressed() > 0) {
12525     aDocument.DecreaseEventSuppression();
12526     aDocument.ScriptLoader()->RemoveExecuteBlocker();
12527   }
12528   aDocuments.AppendElement(&aDocument);
12529   auto recurse = [&aDocuments](Document& aSubDoc) {
12530     GetAndUnsuppressSubDocuments(aSubDoc, aDocuments);
12531     return CallState::Continue;
12532   };
12533   aDocument.EnumerateSubDocuments(recurse);
12534 }
12535 
UnsuppressEventHandlingAndFireEvents(bool aFireEvents)12536 void Document::UnsuppressEventHandlingAndFireEvents(bool aFireEvents) {
12537   nsTArray<nsCOMPtr<Document>> documents;
12538   GetAndUnsuppressSubDocuments(*this, documents);
12539 
12540   for (nsCOMPtr<Document>& doc : documents) {
12541     if (!doc->EventHandlingSuppressed()) {
12542       WindowGlobalChild* wgc = doc->GetWindowGlobalChild();
12543       if (wgc) {
12544         wgc->UnblockBFCacheFor(BFCacheStatus::EVENT_HANDLING_SUPPRESSED);
12545       }
12546 
12547       MOZ_ASSERT(NS_IsMainThread());
12548       nsTArray<RefPtr<net::ChannelEventQueue>> queues =
12549           std::move(doc->mSuspendedQueues);
12550       for (net::ChannelEventQueue* queue : queues) {
12551         queue->Resume();
12552       }
12553 
12554       // If there have been any events driven by the refresh driver which were
12555       // delayed due to events being suppressed in this document, make sure
12556       // there is a refresh scheduled soon so the events will run.
12557       if (doc->mHasDelayedRefreshEvent) {
12558         doc->mHasDelayedRefreshEvent = false;
12559 
12560         if (doc->mPresShell) {
12561           nsRefreshDriver* rd =
12562               doc->mPresShell->GetPresContext()->RefreshDriver();
12563           rd->RunDelayedEventsSoon();
12564         }
12565       }
12566     }
12567   }
12568 
12569   if (aFireEvents) {
12570     MOZ_RELEASE_ASSERT(NS_IsMainThread());
12571     nsCOMPtr<nsIRunnable> ded =
12572         new nsDelayedEventDispatcher(std::move(documents));
12573     Dispatch(TaskCategory::Other, ded.forget());
12574   } else {
12575     FireOrClearDelayedEvents(documents, false);
12576   }
12577 }
12578 
AreClipboardCommandsUnconditionallyEnabled() const12579 bool Document::AreClipboardCommandsUnconditionallyEnabled() const {
12580   return IsHTMLOrXHTML() && !nsContentUtils::IsChromeDoc(this);
12581 }
12582 
AddSuspendedChannelEventQueue(net::ChannelEventQueue * aQueue)12583 void Document::AddSuspendedChannelEventQueue(net::ChannelEventQueue* aQueue) {
12584   MOZ_ASSERT(NS_IsMainThread());
12585   MOZ_ASSERT(EventHandlingSuppressed());
12586   mSuspendedQueues.AppendElement(aQueue);
12587 }
12588 
SuspendPostMessageEvent(PostMessageEvent * aEvent)12589 bool Document::SuspendPostMessageEvent(PostMessageEvent* aEvent) {
12590   MOZ_ASSERT(NS_IsMainThread());
12591 
12592   if (EventHandlingSuppressed() || !mSuspendedPostMessageEvents.IsEmpty()) {
12593     mSuspendedPostMessageEvents.AppendElement(aEvent);
12594     return true;
12595   }
12596   return false;
12597 }
12598 
FireOrClearPostMessageEvents(bool aFireEvents)12599 void Document::FireOrClearPostMessageEvents(bool aFireEvents) {
12600   nsTArray<RefPtr<PostMessageEvent>> events =
12601       std::move(mSuspendedPostMessageEvents);
12602 
12603   if (aFireEvents) {
12604     for (PostMessageEvent* event : events) {
12605       event->Run();
12606     }
12607   }
12608 }
12609 
SetSuppressedEventListener(EventListener * aListener)12610 void Document::SetSuppressedEventListener(EventListener* aListener) {
12611   mSuppressedEventListener = aListener;
12612   auto setOnSubDocs = [&](Document& aDocument) {
12613     aDocument.SetSuppressedEventListener(aListener);
12614     return CallState::Continue;
12615   };
12616   EnumerateSubDocuments(setOnSubDocs);
12617 }
12618 
IsActive() const12619 bool Document::IsActive() const {
12620   return mDocumentContainer && !mRemovedFromDocShell && GetBrowsingContext() &&
12621          !GetBrowsingContext()->IsInBFCache();
12622 }
12623 
GetCurrentContentSink()12624 nsISupports* Document::GetCurrentContentSink() {
12625   return mParser ? mParser->GetContentSink() : nullptr;
12626 }
12627 
GetTemplateContentsOwner()12628 Document* Document::GetTemplateContentsOwner() {
12629   if (!mTemplateContentsOwner) {
12630     bool hasHadScriptObject = true;
12631     nsIScriptGlobalObject* scriptObject =
12632         GetScriptHandlingObject(hasHadScriptObject);
12633 
12634     nsCOMPtr<Document> document;
12635     nsresult rv = NS_NewDOMDocument(
12636         getter_AddRefs(document),
12637         u""_ns,   // aNamespaceURI
12638         u""_ns,   // aQualifiedName
12639         nullptr,  // aDoctype
12640         Document::GetDocumentURI(), Document::GetDocBaseURI(), NodePrincipal(),
12641         true,          // aLoadedAsData
12642         scriptObject,  // aEventObject
12643         IsHTMLDocument() ? DocumentFlavorHTML : DocumentFlavorXML);
12644     NS_ENSURE_SUCCESS(rv, nullptr);
12645 
12646     mTemplateContentsOwner = document;
12647     NS_ENSURE_TRUE(mTemplateContentsOwner, nullptr);
12648 
12649     if (!scriptObject) {
12650       mTemplateContentsOwner->SetScopeObject(GetScopeObject());
12651     }
12652 
12653     mTemplateContentsOwner->mHasHadScriptHandlingObject = hasHadScriptObject;
12654 
12655     // Set |mTemplateContentsOwner| as the template contents owner of itself so
12656     // that it is the template contents owner of nested template elements.
12657     mTemplateContentsOwner->mTemplateContentsOwner = mTemplateContentsOwner;
12658   }
12659 
12660   return mTemplateContentsOwner;
12661 }
12662 
FindTopWindowForElement(Element * element)12663 static already_AddRefed<nsPIDOMWindowOuter> FindTopWindowForElement(
12664     Element* element) {
12665   Document* document = element->OwnerDoc();
12666   if (!document) {
12667     return nullptr;
12668   }
12669 
12670   nsCOMPtr<nsPIDOMWindowOuter> window = document->GetWindow();
12671   if (!window) {
12672     return nullptr;
12673   }
12674 
12675   // Trying to find the top window (equivalent to window.top).
12676   if (nsCOMPtr<nsPIDOMWindowOuter> top = window->GetInProcessTop()) {
12677     window = std::move(top);
12678   }
12679   return window.forget();
12680 }
12681 
12682 /**
12683  * nsAutoFocusEvent is used to dispatch a focus event for an
12684  * nsGenericHTMLFormElement with the autofocus attribute enabled.
12685  */
12686 class nsAutoFocusEvent : public Runnable {
12687  public:
nsAutoFocusEvent(nsCOMPtr<Element> && aElement,nsCOMPtr<nsPIDOMWindowOuter> && aTopWindow)12688   explicit nsAutoFocusEvent(nsCOMPtr<Element>&& aElement,
12689                             nsCOMPtr<nsPIDOMWindowOuter>&& aTopWindow)
12690       : mozilla::Runnable("nsAutoFocusEvent"),
12691         mElement(std::move(aElement)),
12692         mTopWindow(std::move(aTopWindow)) {}
12693 
Run()12694   NS_IMETHOD Run() override {
12695     nsCOMPtr<nsPIDOMWindowOuter> currentTopWindow =
12696         FindTopWindowForElement(mElement);
12697     if (currentTopWindow != mTopWindow) {
12698       // The element's top window changed from when the event was queued.
12699       // Don't take away focus from an unrelated window.
12700       return NS_OK;
12701     }
12702 
12703     if (Document* doc = mTopWindow->GetExtantDoc()) {
12704       if (doc->IsAutoFocusFired()) {
12705         return NS_OK;
12706       }
12707       doc->SetAutoFocusFired();
12708     }
12709 
12710     // Don't steal focus from the user.
12711     if (mTopWindow->GetFocusedElement()) {
12712       return NS_OK;
12713     }
12714 
12715     FocusOptions options;
12716     ErrorResult rv;
12717     mElement->Focus(options, CallerType::System, rv);
12718     return rv.StealNSResult();
12719   }
12720 
12721  private:
12722   nsCOMPtr<Element> mElement;
12723   nsCOMPtr<nsPIDOMWindowOuter> mTopWindow;
12724 };
12725 
SetAutoFocusElement(Element * aAutoFocusElement)12726 void Document::SetAutoFocusElement(Element* aAutoFocusElement) {
12727   if (mAutoFocusFired) {
12728     // Too late.
12729     return;
12730   }
12731 
12732   if (mAutoFocusElement) {
12733     // The spec disallows multiple autofocus elements, so we consider only the
12734     // first one to preserve the old behavior.
12735     return;
12736   }
12737 
12738   mAutoFocusElement = do_GetWeakReference(aAutoFocusElement);
12739   TriggerAutoFocus();
12740 }
12741 
SetAutoFocusFired()12742 void Document::SetAutoFocusFired() { mAutoFocusFired = true; }
12743 
IsAutoFocusFired()12744 bool Document::IsAutoFocusFired() { return mAutoFocusFired; }
12745 
TriggerAutoFocus()12746 void Document::TriggerAutoFocus() {
12747   if (mAutoFocusFired) {
12748     return;
12749   }
12750 
12751   if (!mPresShell || !mPresShell->DidInitialize()) {
12752     // Delay autofocus until frames are constructed so that we don't thrash
12753     // style and layout calculations.
12754     return;
12755   }
12756 
12757   nsCOMPtr<Element> autoFocusElement = do_QueryReferent(mAutoFocusElement);
12758   if (autoFocusElement && autoFocusElement->OwnerDoc() == this) {
12759     nsCOMPtr<nsPIDOMWindowOuter> topWindow =
12760         FindTopWindowForElement(autoFocusElement);
12761     if (!topWindow) {
12762       return;
12763     }
12764 
12765     // NOTE: This may be removed in the future since the spec technically
12766     // allows autofocus after load.
12767     nsCOMPtr<Document> topDoc = topWindow->GetExtantDoc();
12768     if (topDoc &&
12769         topDoc->GetReadyStateEnum() == Document::READYSTATE_COMPLETE) {
12770       return;
12771     }
12772 
12773     nsCOMPtr<nsIRunnable> event =
12774         new nsAutoFocusEvent(std::move(autoFocusElement), topWindow.forget());
12775     nsresult rv = NS_DispatchToCurrentThread(event.forget());
12776     NS_ENSURE_SUCCESS_VOID(rv);
12777   }
12778 }
12779 
SetScrollToRef(nsIURI * aDocumentURI)12780 void Document::SetScrollToRef(nsIURI* aDocumentURI) {
12781   if (!aDocumentURI) {
12782     return;
12783   }
12784 
12785   nsAutoCString ref;
12786 
12787   // Since all URI's that pass through here aren't URL's we can't
12788   // rely on the nsIURI implementation for providing a way for
12789   // finding the 'ref' part of the URI, we'll haveto revert to
12790   // string routines for finding the data past '#'
12791 
12792   nsresult rv = aDocumentURI->GetSpec(ref);
12793   if (NS_FAILED(rv)) {
12794     Unused << aDocumentURI->GetRef(mScrollToRef);
12795     return;
12796   }
12797 
12798   nsReadingIterator<char> start, end;
12799 
12800   ref.BeginReading(start);
12801   ref.EndReading(end);
12802 
12803   if (FindCharInReadable('#', start, end)) {
12804     ++start;  // Skip over the '#'
12805 
12806     mScrollToRef = Substring(start, end);
12807   }
12808 }
12809 
ScrollToRef()12810 void Document::ScrollToRef() {
12811   if (mScrolledToRefAlready) {
12812     RefPtr<PresShell> presShell = GetPresShell();
12813     if (presShell) {
12814       presShell->ScrollToAnchor();
12815     }
12816     return;
12817   }
12818 
12819   if (mScrollToRef.IsEmpty()) {
12820     return;
12821   }
12822 
12823   RefPtr<PresShell> presShell = GetPresShell();
12824   if (presShell) {
12825     nsresult rv = NS_ERROR_FAILURE;
12826     // We assume that the bytes are in UTF-8, as it says in the spec:
12827     // http://www.w3.org/TR/html4/appendix/notes.html#h-B.2.1
12828     NS_ConvertUTF8toUTF16 ref(mScrollToRef);
12829     // Check an empty string which might be caused by the UTF-8 conversion
12830     if (!ref.IsEmpty()) {
12831       // Note that GoToAnchor will handle flushing layout as needed.
12832       rv = presShell->GoToAnchor(ref, mChangeScrollPosWhenScrollingToRef);
12833     } else {
12834       rv = NS_ERROR_FAILURE;
12835     }
12836 
12837     if (NS_FAILED(rv)) {
12838       nsAutoCString buff;
12839       const bool unescaped =
12840           NS_UnescapeURL(mScrollToRef.BeginReading(), mScrollToRef.Length(),
12841                          /*aFlags =*/0, buff);
12842 
12843       // This attempt is only necessary if characters were unescaped.
12844       if (unescaped) {
12845         NS_ConvertUTF8toUTF16 utf16Str(buff);
12846         if (!utf16Str.IsEmpty()) {
12847           rv = presShell->GoToAnchor(utf16Str,
12848                                      mChangeScrollPosWhenScrollingToRef);
12849         }
12850       }
12851 
12852       // If UTF-8 URI failed then try to assume the string as a
12853       // document's charset.
12854       if (NS_FAILED(rv)) {
12855         const Encoding* encoding = GetDocumentCharacterSet();
12856         rv = encoding->DecodeWithoutBOMHandling(unescaped ? buff : mScrollToRef,
12857                                                 ref);
12858         if (NS_SUCCEEDED(rv) && !ref.IsEmpty()) {
12859           rv = presShell->GoToAnchor(ref, mChangeScrollPosWhenScrollingToRef);
12860         }
12861       }
12862     }
12863     if (NS_SUCCEEDED(rv)) {
12864       mScrolledToRefAlready = true;
12865     }
12866   }
12867 }
12868 
RegisterActivityObserver(nsISupports * aSupports)12869 void Document::RegisterActivityObserver(nsISupports* aSupports) {
12870   if (!mActivityObservers) {
12871     mActivityObservers = MakeUnique<nsTHashSet<nsISupports*>>();
12872   }
12873   mActivityObservers->Insert(aSupports);
12874 }
12875 
UnregisterActivityObserver(nsISupports * aSupports)12876 bool Document::UnregisterActivityObserver(nsISupports* aSupports) {
12877   if (!mActivityObservers) {
12878     return false;
12879   }
12880   return mActivityObservers->EnsureRemoved(aSupports);
12881 }
12882 
EnumerateActivityObservers(ActivityObserverEnumerator aEnumerator)12883 void Document::EnumerateActivityObservers(
12884     ActivityObserverEnumerator aEnumerator) {
12885   if (!mActivityObservers) {
12886     return;
12887   }
12888 
12889   const auto keyArray =
12890       ToTArray<nsTArray<nsCOMPtr<nsISupports>>>(*mActivityObservers);
12891   for (auto& observer : keyArray) {
12892     aEnumerator(observer.get());
12893   }
12894 }
12895 
RegisterPendingLinkUpdate(Link * aLink)12896 void Document::RegisterPendingLinkUpdate(Link* aLink) {
12897   if (aLink->HasPendingLinkUpdate()) {
12898     return;
12899   }
12900 
12901   aLink->SetHasPendingLinkUpdate();
12902 
12903   if (!mHasLinksToUpdateRunnable && !mFlushingPendingLinkUpdates) {
12904     nsCOMPtr<nsIRunnable> event =
12905         NewRunnableMethod("Document::FlushPendingLinkUpdates", this,
12906                           &Document::FlushPendingLinkUpdates);
12907     // Do this work in a second in the worst case.
12908     nsresult rv = NS_DispatchToCurrentThreadQueue(event.forget(), 1000,
12909                                                   EventQueuePriority::Idle);
12910     if (NS_FAILED(rv)) {
12911       // If during shutdown posting a runnable doesn't succeed, we probably
12912       // don't need to update link states.
12913       return;
12914     }
12915     mHasLinksToUpdateRunnable = true;
12916   }
12917 
12918   mLinksToUpdate.InfallibleAppend(aLink);
12919 }
12920 
FlushPendingLinkUpdates()12921 void Document::FlushPendingLinkUpdates() {
12922   MOZ_DIAGNOSTIC_ASSERT(!mFlushingPendingLinkUpdates);
12923   MOZ_ASSERT(mHasLinksToUpdateRunnable);
12924   mHasLinksToUpdateRunnable = false;
12925 
12926   auto restore = MakeScopeExit([&] { mFlushingPendingLinkUpdates = false; });
12927   mFlushingPendingLinkUpdates = true;
12928 
12929   while (!mLinksToUpdate.IsEmpty()) {
12930     LinksToUpdateList links(std::move(mLinksToUpdate));
12931     for (auto iter = links.Iter(); !iter.Done(); iter.Next()) {
12932       Link* link = iter.Get();
12933       Element* element = link->GetElement();
12934       if (element->OwnerDoc() == this) {
12935         link->ClearHasPendingLinkUpdate();
12936         if (element->IsInComposedDoc()) {
12937           element->UpdateLinkState(link->LinkState());
12938         }
12939       }
12940     }
12941   }
12942 }
12943 
12944 /**
12945  * Retrieves the node in a static-clone document that corresponds to aOrigNode,
12946  * which is a node in the original document from which aStaticClone was cloned.
12947  */
GetCorrespondingNodeInDocument(const nsINode * aOrigNode,Document & aStaticClone)12948 static nsINode* GetCorrespondingNodeInDocument(const nsINode* aOrigNode,
12949                                                Document& aStaticClone) {
12950   MOZ_ASSERT(aOrigNode);
12951 
12952   // Selections in anonymous subtrees aren't supported.
12953   if (NS_WARN_IF(aOrigNode->IsInNativeAnonymousSubtree())) {
12954     return nullptr;
12955   }
12956 
12957   // If the node is disconnected, this is a bug in the selection code, but it
12958   // can happen with shadow DOM so handle it.
12959   if (NS_WARN_IF(!aOrigNode->IsInComposedDoc())) {
12960     return nullptr;
12961   }
12962 
12963   AutoTArray<Maybe<uint32_t>, 32> indexArray;
12964   const nsINode* current = aOrigNode;
12965   while (const nsINode* parent = current->GetParentNode()) {
12966     Maybe<uint32_t> index = parent->ComputeIndexOf(current);
12967     NS_ENSURE_TRUE(index.isSome(), nullptr);
12968     indexArray.AppendElement(std::move(index));
12969     current = parent;
12970   }
12971   MOZ_ASSERT(current->IsDocument() || current->IsShadowRoot());
12972   nsINode* correspondingNode = [&]() -> nsINode* {
12973     if (current->IsDocument()) {
12974       return &aStaticClone;
12975     }
12976     const auto* shadow = ShadowRoot::FromNode(*current);
12977     if (!shadow) {
12978       return nullptr;
12979     }
12980     nsINode* correspondingHost =
12981         GetCorrespondingNodeInDocument(shadow->Host(), aStaticClone);
12982     if (NS_WARN_IF(!correspondingHost || !correspondingHost->IsElement())) {
12983       return nullptr;
12984     }
12985     return correspondingHost->AsElement()->GetShadowRoot();
12986   }();
12987 
12988   if (NS_WARN_IF(!correspondingNode)) {
12989     return nullptr;
12990   }
12991   for (const Maybe<uint32_t>& index : Reversed(indexArray)) {
12992     correspondingNode = correspondingNode->GetChildAt_Deprecated(*index);
12993     NS_ENSURE_TRUE(correspondingNode, nullptr);
12994   }
12995   return correspondingNode;
12996 }
12997 
12998 /**
12999  * Caches the selection ranges from the source document onto the static clone in
13000  * case the "Print Selection Only" functionality is invoked.
13001  *
13002  * Note that we cannot use the selection obtained from GetOriginalDocument()
13003  * since that selection may have mutated after the print was invoked.
13004  *
13005  * Note also that because nsRange objects point into a specific document's
13006  * nodes, we cannot reuse an array of nsRange objects across multiple static
13007  * clone documents. For that reason we cache a new array of ranges on each
13008  * static clone that we create.
13009  *
13010  * TODO(emilio): This can be simplified once we don't re-clone from static
13011  * documents.
13012  *
13013  * @param aSourceDoc the document from which we are caching selection ranges
13014  * @param aStaticClone the document that will hold the cache
13015  * @return true if a selection range was cached
13016  */
CachePrintSelectionRanges(const Document & aSourceDoc,Document & aStaticClone)13017 static void CachePrintSelectionRanges(const Document& aSourceDoc,
13018                                       Document& aStaticClone) {
13019   MOZ_ASSERT(aStaticClone.IsStaticDocument());
13020   MOZ_ASSERT(!aStaticClone.GetProperty(nsGkAtoms::printselectionranges));
13021 
13022   const Selection* origSelection = nullptr;
13023   const nsTArray<RefPtr<nsRange>>* origRanges = nullptr;
13024   bool sourceDocIsStatic = aSourceDoc.IsStaticDocument();
13025 
13026   if (sourceDocIsStatic) {
13027     origRanges = static_cast<nsTArray<RefPtr<nsRange>>*>(
13028         aSourceDoc.GetProperty(nsGkAtoms::printselectionranges));
13029   } else if (PresShell* shell = aSourceDoc.GetPresShell()) {
13030     origSelection = shell->GetCurrentSelection(SelectionType::eNormal);
13031   }
13032 
13033   if (!origSelection && !origRanges) {
13034     return;
13035   }
13036 
13037   const uint32_t rangeCount =
13038       sourceDocIsStatic ? origRanges->Length() : origSelection->RangeCount();
13039   auto printRanges = MakeUnique<nsTArray<RefPtr<nsRange>>>(rangeCount);
13040 
13041   for (const uint32_t i : IntegerRange(rangeCount)) {
13042     MOZ_ASSERT_IF(!sourceDocIsStatic,
13043                   origSelection->RangeCount() == rangeCount);
13044     const nsRange* range = sourceDocIsStatic ? origRanges->ElementAt(i).get()
13045                                              : origSelection->GetRangeAt(i);
13046     MOZ_ASSERT(range);
13047     nsINode* startContainer = range->GetStartContainer();
13048     nsINode* endContainer = range->GetEndContainer();
13049 
13050     if (!startContainer || !endContainer) {
13051       continue;
13052     }
13053 
13054     nsINode* startNode =
13055         GetCorrespondingNodeInDocument(startContainer, aStaticClone);
13056     nsINode* endNode =
13057         GetCorrespondingNodeInDocument(endContainer, aStaticClone);
13058 
13059     if (NS_WARN_IF(!startNode || !endNode)) {
13060       continue;
13061     }
13062 
13063     RefPtr<nsRange> clonedRange =
13064         nsRange::Create(startNode, range->StartOffset(), endNode,
13065                         range->EndOffset(), IgnoreErrors());
13066     if (clonedRange && !clonedRange->Collapsed()) {
13067       printRanges->AppendElement(std::move(clonedRange));
13068     }
13069   }
13070 
13071   if (printRanges->IsEmpty()) {
13072     return;
13073   }
13074 
13075   aStaticClone.SetProperty(nsGkAtoms::printselectionranges,
13076                            printRanges.release(),
13077                            nsINode::DeleteProperty<nsTArray<RefPtr<nsRange>>>);
13078 }
13079 
CreateStaticClone(nsIDocShell * aCloneContainer,nsIContentViewer * aViewer,nsIPrintSettings * aPrintSettings,bool * aOutHasInProcessPrintCallbacks)13080 already_AddRefed<Document> Document::CreateStaticClone(
13081     nsIDocShell* aCloneContainer, nsIContentViewer* aViewer,
13082     nsIPrintSettings* aPrintSettings, bool* aOutHasInProcessPrintCallbacks) {
13083   MOZ_ASSERT(!mCreatingStaticClone);
13084   MOZ_ASSERT(!GetProperty(nsGkAtoms::adoptedsheetclones));
13085   MOZ_DIAGNOSTIC_ASSERT(aViewer);
13086 
13087   mCreatingStaticClone = true;
13088   SetProperty(nsGkAtoms::adoptedsheetclones, new AdoptedStyleSheetCloneCache(),
13089               nsINode::DeleteProperty<AdoptedStyleSheetCloneCache>);
13090 
13091   auto raii = MakeScopeExit([&] {
13092     RemoveProperty(nsGkAtoms::adoptedsheetclones);
13093     mCreatingStaticClone = false;
13094   });
13095 
13096   // Make document use different container during cloning.
13097   //
13098   // FIXME(emilio): Why is this needed?
13099   RefPtr<nsDocShell> originalShell = mDocumentContainer.get();
13100   SetContainer(nsDocShell::Cast(aCloneContainer));
13101   IgnoredErrorResult rv;
13102   nsCOMPtr<nsINode> clonedNode = this->CloneNode(true, rv);
13103   SetContainer(originalShell);
13104   if (rv.Failed()) {
13105     return nullptr;
13106   }
13107 
13108   nsCOMPtr<Document> clonedDoc = do_QueryInterface(clonedNode);
13109   if (!clonedDoc) {
13110     return nullptr;
13111   }
13112 
13113   size_t sheetsCount = SheetCount();
13114   for (size_t i = 0; i < sheetsCount; ++i) {
13115     RefPtr<StyleSheet> sheet = SheetAt(i);
13116     if (sheet) {
13117       if (sheet->IsApplicable()) {
13118         RefPtr<StyleSheet> clonedSheet = sheet->Clone(nullptr, clonedDoc);
13119         NS_WARNING_ASSERTION(clonedSheet, "Cloning a stylesheet didn't work!");
13120         if (clonedSheet) {
13121           clonedDoc->AddStyleSheet(clonedSheet);
13122         }
13123       }
13124     }
13125   }
13126   clonedDoc->CloneAdoptedSheetsFrom(*this);
13127 
13128   for (int t = 0; t < AdditionalSheetTypeCount; ++t) {
13129     auto& sheets = mAdditionalSheets[additionalSheetType(t)];
13130     for (StyleSheet* sheet : sheets) {
13131       if (sheet->IsApplicable()) {
13132         RefPtr<StyleSheet> clonedSheet = sheet->Clone(nullptr, clonedDoc);
13133         NS_WARNING_ASSERTION(clonedSheet, "Cloning a stylesheet didn't work!");
13134         if (clonedSheet) {
13135           clonedDoc->AddAdditionalStyleSheet(additionalSheetType(t),
13136                                              clonedSheet);
13137         }
13138       }
13139     }
13140   }
13141 
13142   // Font faces created with the JS API will not be reflected in the
13143   // stylesheets and need to be copied over to the cloned document.
13144   if (const FontFaceSet* set = GetFonts()) {
13145     set->CopyNonRuleFacesTo(clonedDoc->Fonts());
13146   }
13147 
13148   clonedDoc->mReferrerInfo =
13149       static_cast<dom::ReferrerInfo*>(mReferrerInfo.get())->Clone();
13150   clonedDoc->mPreloadReferrerInfo = clonedDoc->mReferrerInfo;
13151   CachePrintSelectionRanges(*this, *clonedDoc);
13152 
13153   // We're done with the clone, embed ourselves into the document viewer and
13154   // clone our children. The order here is pretty important, because our
13155   // document our document needs to have an owner global before we can create
13156   // the frame loaders for subdocuments.
13157   aViewer->SetDocument(clonedDoc);
13158 
13159   *aOutHasInProcessPrintCallbacks |= clonedDoc->HasPrintCallbacks();
13160 
13161   auto pendingClones = std::move(clonedDoc->mPendingFrameStaticClones);
13162   for (const auto& clone : pendingClones) {
13163     RefPtr<Element> element = do_QueryObject(clone.mElement);
13164     RefPtr<nsFrameLoader> frameLoader =
13165         nsFrameLoader::Create(element, /* aNetworkCreated */ false);
13166 
13167     if (NS_WARN_IF(!frameLoader)) {
13168       continue;
13169     }
13170 
13171     clone.mElement->SetFrameLoader(frameLoader);
13172 
13173     nsresult rv = frameLoader->FinishStaticClone(
13174         clone.mStaticCloneOf, aPrintSettings, aOutHasInProcessPrintCallbacks);
13175     Unused << NS_WARN_IF(NS_FAILED(rv));
13176   }
13177 
13178   return clonedDoc.forget();
13179 }
13180 
UnlinkOriginalDocumentIfStatic()13181 void Document::UnlinkOriginalDocumentIfStatic() {
13182   if (IsStaticDocument() && mOriginalDocument) {
13183     MOZ_ASSERT(mOriginalDocument->mStaticCloneCount > 0);
13184     mOriginalDocument->mStaticCloneCount--;
13185     mOriginalDocument = nullptr;
13186   }
13187   MOZ_ASSERT(!mOriginalDocument);
13188 }
13189 
ScheduleFrameRequestCallback(FrameRequestCallback & aCallback,int32_t * aHandle)13190 nsresult Document::ScheduleFrameRequestCallback(FrameRequestCallback& aCallback,
13191                                                 int32_t* aHandle) {
13192   nsresult rv = mFrameRequestManager.Schedule(aCallback, aHandle);
13193   if (NS_FAILED(rv)) {
13194     return rv;
13195   }
13196 
13197   UpdateFrameRequestCallbackSchedulingState();
13198   return NS_OK;
13199 }
13200 
CancelFrameRequestCallback(int32_t aHandle)13201 void Document::CancelFrameRequestCallback(int32_t aHandle) {
13202   if (mFrameRequestManager.Cancel(aHandle)) {
13203     UpdateFrameRequestCallbackSchedulingState();
13204   }
13205 }
13206 
IsCanceledFrameRequestCallback(int32_t aHandle) const13207 bool Document::IsCanceledFrameRequestCallback(int32_t aHandle) const {
13208   return mFrameRequestManager.IsCanceled(aHandle);
13209 }
13210 
GetStateObject(nsIVariant ** aState)13211 nsresult Document::GetStateObject(nsIVariant** aState) {
13212   // Get the document's current state object. This is the object backing both
13213   // history.state and popStateEvent.state.
13214   //
13215   // mStateObjectContainer may be null; this just means that there's no
13216   // current state object.
13217 
13218   if (!mStateObjectCached && mStateObjectContainer) {
13219     AutoJSAPI jsapi;
13220     // Init with null is "OK" in the sense that it will just fail.
13221     if (!jsapi.Init(GetScopeObject())) {
13222       return NS_ERROR_UNEXPECTED;
13223     }
13224     mStateObjectContainer->DeserializeToVariant(
13225         jsapi.cx(), getter_AddRefs(mStateObjectCached));
13226   }
13227 
13228   NS_IF_ADDREF(*aState = mStateObjectCached);
13229   return NS_OK;
13230 }
13231 
SetNavigationTiming(nsDOMNavigationTiming * aTiming)13232 void Document::SetNavigationTiming(nsDOMNavigationTiming* aTiming) {
13233   mTiming = aTiming;
13234   if (!mLoadingTimeStamp.IsNull() && mTiming) {
13235     mTiming->SetDOMLoadingTimeStamp(GetDocumentURI(), mLoadingTimeStamp);
13236   }
13237 }
13238 
ImageMapList()13239 nsContentList* Document::ImageMapList() {
13240   if (!mImageMaps) {
13241     mImageMaps = new nsContentList(this, kNameSpaceID_XHTML, nsGkAtoms::map,
13242                                    nsGkAtoms::map);
13243   }
13244 
13245   return mImageMaps;
13246 }
13247 
13248 #define DEPRECATED_OPERATION(_op) #_op "Warning",
13249 static const char* kDeprecationWarnings[] = {
13250 #include "nsDeprecatedOperationList.h"
13251     nullptr};
13252 #undef DEPRECATED_OPERATION
13253 
13254 #define DOCUMENT_WARNING(_op) #_op "Warning",
13255 static const char* kDocumentWarnings[] = {
13256 #include "nsDocumentWarningList.h"
13257     nullptr};
13258 #undef DOCUMENT_WARNING
13259 
OperationToUseCounter(DeprecatedOperations aOperation)13260 static UseCounter OperationToUseCounter(DeprecatedOperations aOperation) {
13261   switch (aOperation) {
13262 #define DEPRECATED_OPERATION(_op)    \
13263   case DeprecatedOperations::e##_op: \
13264     return eUseCounter_##_op;
13265 #include "nsDeprecatedOperationList.h"
13266 #undef DEPRECATED_OPERATION
13267     default:
13268       MOZ_CRASH();
13269   }
13270 }
13271 
HasWarnedAbout(DeprecatedOperations aOperation) const13272 bool Document::HasWarnedAbout(DeprecatedOperations aOperation) const {
13273   return mDeprecationWarnedAbout[static_cast<size_t>(aOperation)];
13274 }
13275 
WarnOnceAbout(DeprecatedOperations aOperation,bool asError,const nsTArray<nsString> & aParams) const13276 void Document::WarnOnceAbout(
13277     DeprecatedOperations aOperation, bool asError /* = false */,
13278     const nsTArray<nsString>& aParams /* = empty array */) const {
13279   MOZ_ASSERT(NS_IsMainThread());
13280   if (HasWarnedAbout(aOperation)) {
13281     return;
13282   }
13283   mDeprecationWarnedAbout[static_cast<size_t>(aOperation)] = true;
13284   // Don't count deprecated operations for about pages since those pages
13285   // are almost in our control, and we always need to remove uses there
13286   // before we remove the operation itself anyway.
13287   if (!IsAboutPage()) {
13288     const_cast<Document*>(this)->SetUseCounter(
13289         OperationToUseCounter(aOperation));
13290   }
13291   uint32_t flags =
13292       asError ? nsIScriptError::errorFlag : nsIScriptError::warningFlag;
13293   nsContentUtils::ReportToConsole(
13294       flags, "DOM Core"_ns, this, nsContentUtils::eDOM_PROPERTIES,
13295       kDeprecationWarnings[static_cast<size_t>(aOperation)], aParams);
13296 }
13297 
HasWarnedAbout(DocumentWarnings aWarning) const13298 bool Document::HasWarnedAbout(DocumentWarnings aWarning) const {
13299   return mDocWarningWarnedAbout[aWarning];
13300 }
13301 
WarnOnceAbout(DocumentWarnings aWarning,bool asError,const nsTArray<nsString> & aParams) const13302 void Document::WarnOnceAbout(
13303     DocumentWarnings aWarning, bool asError /* = false */,
13304     const nsTArray<nsString>& aParams /* = empty array */) const {
13305   MOZ_ASSERT(NS_IsMainThread());
13306   if (HasWarnedAbout(aWarning)) {
13307     return;
13308   }
13309   mDocWarningWarnedAbout[aWarning] = true;
13310   uint32_t flags =
13311       asError ? nsIScriptError::errorFlag : nsIScriptError::warningFlag;
13312   nsContentUtils::ReportToConsole(flags, "DOM Core"_ns, this,
13313                                   nsContentUtils::eDOM_PROPERTIES,
13314                                   kDocumentWarnings[aWarning], aParams);
13315 }
13316 
ImageTracker()13317 mozilla::dom::ImageTracker* Document::ImageTracker() {
13318   if (!mImageTracker) {
13319     mImageTracker = new mozilla::dom::ImageTracker;
13320   }
13321   return mImageTracker;
13322 }
13323 
ScheduleSVGUseElementShadowTreeUpdate(SVGUseElement & aUseElement)13324 void Document::ScheduleSVGUseElementShadowTreeUpdate(
13325     SVGUseElement& aUseElement) {
13326   MOZ_ASSERT(aUseElement.IsInComposedDoc());
13327 
13328   if (MOZ_UNLIKELY(mIsStaticDocument)) {
13329     // Printing doesn't deal well with dynamic DOM mutations.
13330     return;
13331   }
13332 
13333   mSVGUseElementsNeedingShadowTreeUpdate.Insert(&aUseElement);
13334 
13335   if (PresShell* presShell = GetPresShell()) {
13336     presShell->EnsureStyleFlush();
13337   }
13338 }
13339 
DoUpdateSVGUseElementShadowTrees()13340 void Document::DoUpdateSVGUseElementShadowTrees() {
13341   MOZ_ASSERT(!mSVGUseElementsNeedingShadowTreeUpdate.IsEmpty());
13342 
13343   do {
13344     const auto useElementsToUpdate = ToTArray<nsTArray<RefPtr<SVGUseElement>>>(
13345         mSVGUseElementsNeedingShadowTreeUpdate);
13346     mSVGUseElementsNeedingShadowTreeUpdate.Clear();
13347 
13348     for (const auto& useElement : useElementsToUpdate) {
13349       if (MOZ_UNLIKELY(!useElement->IsInComposedDoc())) {
13350         // The element was in another <use> shadow tree which we processed
13351         // already and also needed an update, and is removed from the document
13352         // now, so nothing to do here.
13353         MOZ_ASSERT(useElementsToUpdate.Length() > 1);
13354         continue;
13355       }
13356       useElement->UpdateShadowTree();
13357     }
13358   } while (!mSVGUseElementsNeedingShadowTreeUpdate.IsEmpty());
13359 }
13360 
NotifyMediaFeatureValuesChanged()13361 void Document::NotifyMediaFeatureValuesChanged() {
13362   for (RefPtr<HTMLImageElement> imageElement : mResponsiveContent) {
13363     imageElement->MediaFeatureValuesChanged();
13364   }
13365 }
13366 
CreateTouch(nsGlobalWindowInner * aView,EventTarget * aTarget,int32_t aIdentifier,int32_t aPageX,int32_t aPageY,int32_t aScreenX,int32_t aScreenY,int32_t aClientX,int32_t aClientY,int32_t aRadiusX,int32_t aRadiusY,float aRotationAngle,float aForce)13367 already_AddRefed<Touch> Document::CreateTouch(
13368     nsGlobalWindowInner* aView, EventTarget* aTarget, int32_t aIdentifier,
13369     int32_t aPageX, int32_t aPageY, int32_t aScreenX, int32_t aScreenY,
13370     int32_t aClientX, int32_t aClientY, int32_t aRadiusX, int32_t aRadiusY,
13371     float aRotationAngle, float aForce) {
13372   RefPtr<Touch> touch =
13373       new Touch(aTarget, aIdentifier, aPageX, aPageY, aScreenX, aScreenY,
13374                 aClientX, aClientY, aRadiusX, aRadiusY, aRotationAngle, aForce);
13375   return touch.forget();
13376 }
13377 
CreateTouchList()13378 already_AddRefed<TouchList> Document::CreateTouchList() {
13379   RefPtr<TouchList> retval = new TouchList(ToSupports(this));
13380   return retval.forget();
13381 }
13382 
CreateTouchList(Touch & aTouch,const Sequence<OwningNonNull<Touch>> & aTouches)13383 already_AddRefed<TouchList> Document::CreateTouchList(
13384     Touch& aTouch, const Sequence<OwningNonNull<Touch>>& aTouches) {
13385   RefPtr<TouchList> retval = new TouchList(ToSupports(this));
13386   retval->Append(&aTouch);
13387   for (uint32_t i = 0; i < aTouches.Length(); ++i) {
13388     retval->Append(aTouches[i].get());
13389   }
13390   return retval.forget();
13391 }
13392 
CreateTouchList(const Sequence<OwningNonNull<Touch>> & aTouches)13393 already_AddRefed<TouchList> Document::CreateTouchList(
13394     const Sequence<OwningNonNull<Touch>>& aTouches) {
13395   RefPtr<TouchList> retval = new TouchList(ToSupports(this));
13396   for (uint32_t i = 0; i < aTouches.Length(); ++i) {
13397     retval->Append(aTouches[i].get());
13398   }
13399   return retval.forget();
13400 }
13401 
CaretPositionFromPoint(float aX,float aY)13402 already_AddRefed<nsDOMCaretPosition> Document::CaretPositionFromPoint(
13403     float aX, float aY) {
13404   using FrameForPointOption = nsLayoutUtils::FrameForPointOption;
13405 
13406   nscoord x = nsPresContext::CSSPixelsToAppUnits(aX);
13407   nscoord y = nsPresContext::CSSPixelsToAppUnits(aY);
13408   nsPoint pt(x, y);
13409 
13410   FlushPendingNotifications(FlushType::Layout);
13411 
13412   PresShell* presShell = GetPresShell();
13413   if (!presShell) {
13414     return nullptr;
13415   }
13416 
13417   nsIFrame* rootFrame = presShell->GetRootFrame();
13418 
13419   // XUL docs, unlike HTML, have no frame tree until everything's done loading
13420   if (!rootFrame) {
13421     return nullptr;
13422   }
13423 
13424   nsIFrame* ptFrame = nsLayoutUtils::GetFrameForPoint(
13425       RelativeTo{rootFrame}, pt,
13426       {{FrameForPointOption::IgnorePaintSuppression,
13427         FrameForPointOption::IgnoreCrossDoc}});
13428   if (!ptFrame) {
13429     return nullptr;
13430   }
13431 
13432   // We require frame-relative coordinates for GetContentOffsetsFromPoint.
13433   nsPoint aOffset;
13434   nsCOMPtr<nsIWidget> widget = nsContentUtils::GetWidget(presShell, &aOffset);
13435   LayoutDeviceIntPoint refPoint = nsContentUtils::ToWidgetPoint(
13436       CSSPoint(aX, aY), aOffset, GetPresContext());
13437   nsPoint adjustedPoint = nsLayoutUtils::GetEventCoordinatesRelativeTo(
13438       widget, refPoint, RelativeTo{ptFrame});
13439 
13440   nsIFrame::ContentOffsets offsets =
13441       ptFrame->GetContentOffsetsFromPoint(adjustedPoint);
13442 
13443   nsCOMPtr<nsIContent> node = offsets.content;
13444   uint32_t offset = offsets.offset;
13445   nsCOMPtr<nsIContent> anonNode = node;
13446   bool nodeIsAnonymous = node && node->IsInNativeAnonymousSubtree();
13447   if (nodeIsAnonymous) {
13448     node = ptFrame->GetContent();
13449     nsIContent* nonanon = node->FindFirstNonChromeOnlyAccessContent();
13450     HTMLTextAreaElement* textArea = HTMLTextAreaElement::FromNode(nonanon);
13451     nsITextControlFrame* textFrame = do_QueryFrame(nonanon->GetPrimaryFrame());
13452     if (textFrame) {
13453       // If the anonymous content node has a child, then we need to make sure
13454       // that we get the appropriate child, as otherwise the offset may not be
13455       // correct when we construct a range for it.
13456       nsCOMPtr<nsIContent> firstChild = anonNode->GetFirstChild();
13457       if (firstChild) {
13458         anonNode = firstChild;
13459       }
13460 
13461       if (textArea) {
13462         offset =
13463             nsContentUtils::GetAdjustedOffsetInTextControl(ptFrame, offset);
13464       }
13465 
13466       node = nonanon;
13467     } else {
13468       node = nullptr;
13469       offset = 0;
13470     }
13471   }
13472 
13473   RefPtr<nsDOMCaretPosition> aCaretPos = new nsDOMCaretPosition(node, offset);
13474   if (nodeIsAnonymous) {
13475     aCaretPos->SetAnonymousContentNode(anonNode);
13476   }
13477   return aCaretPos.forget();
13478 }
13479 
IsPotentiallyScrollable(HTMLBodyElement * aBody)13480 bool Document::IsPotentiallyScrollable(HTMLBodyElement* aBody) {
13481   // We rely on correct frame information here, so need to flush frames.
13482   FlushPendingNotifications(FlushType::Frames);
13483 
13484   // An element that is the HTML body element is potentially scrollable if all
13485   // of the following conditions are true:
13486 
13487   // The element has an associated CSS layout box.
13488   nsIFrame* bodyFrame = nsLayoutUtils::GetStyleFrame(aBody);
13489   if (!bodyFrame) {
13490     return false;
13491   }
13492 
13493   // The element's parent element's computed value of the overflow-x and
13494   // overflow-y properties are visible.
13495   MOZ_ASSERT(aBody->GetParent() == aBody->OwnerDoc()->GetRootElement());
13496   nsIFrame* parentFrame = nsLayoutUtils::GetStyleFrame(aBody->GetParent());
13497   if (parentFrame &&
13498       parentFrame->StyleDisplay()->OverflowIsVisibleInBothAxis()) {
13499     return false;
13500   }
13501 
13502   // The element's computed value of the overflow-x or overflow-y properties is
13503   // not visible.
13504   return !bodyFrame->StyleDisplay()->OverflowIsVisibleInBothAxis();
13505 }
13506 
GetScrollingElement()13507 Element* Document::GetScrollingElement() {
13508   // Keep this in sync with IsScrollingElement.
13509   if (GetCompatibilityMode() == eCompatibility_NavQuirks) {
13510     RefPtr<HTMLBodyElement> body = GetBodyElement();
13511     if (body && !IsPotentiallyScrollable(body)) {
13512       return body;
13513     }
13514 
13515     return nullptr;
13516   }
13517 
13518   return GetRootElement();
13519 }
13520 
IsScrollingElement(Element * aElement)13521 bool Document::IsScrollingElement(Element* aElement) {
13522   // Keep this in sync with GetScrollingElement.
13523   MOZ_ASSERT(aElement);
13524 
13525   if (GetCompatibilityMode() != eCompatibility_NavQuirks) {
13526     return aElement == GetRootElement();
13527   }
13528 
13529   // In the common case when aElement != body, avoid refcounting.
13530   HTMLBodyElement* body = GetBodyElement();
13531   if (aElement != body) {
13532     return false;
13533   }
13534 
13535   // Now we know body is non-null, since aElement is not null.  It's the
13536   // scrolling element for the document if it itself is not potentially
13537   // scrollable.
13538   RefPtr<HTMLBodyElement> strongBody(body);
13539   return !IsPotentiallyScrollable(strongBody);
13540 }
13541 
13542 class UnblockParsingPromiseHandler final : public PromiseNativeHandler {
13543  public:
13544   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
NS_DECL_CYCLE_COLLECTION_CLASS(UnblockParsingPromiseHandler)13545   NS_DECL_CYCLE_COLLECTION_CLASS(UnblockParsingPromiseHandler)
13546 
13547   explicit UnblockParsingPromiseHandler(Document* aDocument, Promise* aPromise,
13548                                         const BlockParsingOptions& aOptions)
13549       : mPromise(aPromise) {
13550     nsCOMPtr<nsIParser> parser = aDocument->CreatorParserOrNull();
13551     if (parser &&
13552         (aOptions.mBlockScriptCreated || !parser->IsScriptCreated())) {
13553       parser->BlockParser();
13554       mParser = do_GetWeakReference(parser);
13555       mDocument = aDocument;
13556       mDocument->BlockOnload();
13557       mDocument->BlockDOMContentLoaded();
13558     }
13559   }
13560 
ResolvedCallback(JSContext * aCx,JS::Handle<JS::Value> aValue,ErrorResult & aRv)13561   void ResolvedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue,
13562                         ErrorResult& aRv) override {
13563     MaybeUnblockParser();
13564 
13565     mPromise->MaybeResolve(aValue);
13566   }
13567 
RejectedCallback(JSContext * aCx,JS::Handle<JS::Value> aValue,ErrorResult & aRv)13568   void RejectedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue,
13569                         ErrorResult& aRv) override {
13570     MaybeUnblockParser();
13571 
13572     mPromise->MaybeReject(aValue);
13573   }
13574 
13575  protected:
~UnblockParsingPromiseHandler()13576   virtual ~UnblockParsingPromiseHandler() {
13577     // If we're being cleaned up by the cycle collector, our mDocument reference
13578     // may have been unlinked while our mParser weak reference is still alive.
13579     if (mDocument) {
13580       MaybeUnblockParser();
13581     }
13582   }
13583 
13584  private:
MaybeUnblockParser()13585   void MaybeUnblockParser() {
13586     nsCOMPtr<nsIParser> parser = do_QueryReferent(mParser);
13587     if (parser) {
13588       MOZ_DIAGNOSTIC_ASSERT(mDocument);
13589       nsCOMPtr<nsIParser> docParser = mDocument->CreatorParserOrNull();
13590       if (parser == docParser) {
13591         parser->UnblockParser();
13592         parser->ContinueInterruptedParsingAsync();
13593       }
13594     }
13595     if (mDocument) {
13596       // We blocked DOMContentLoaded and load events on this document.  Unblock
13597       // them.  Note that we want to do that no matter what's going on with the
13598       // parser state for this document.  Maybe someone caused it to stop being
13599       // parsed, so CreatorParserOrNull() is returning null, but we still want
13600       // to unblock these.
13601       mDocument->UnblockDOMContentLoaded();
13602       mDocument->UnblockOnload(false);
13603     }
13604     mParser = nullptr;
13605     mDocument = nullptr;
13606   }
13607 
13608   nsWeakPtr mParser;
13609   RefPtr<Promise> mPromise;
13610   RefPtr<Document> mDocument;
13611 };
13612 
NS_IMPL_CYCLE_COLLECTION(UnblockParsingPromiseHandler,mDocument,mPromise)13613 NS_IMPL_CYCLE_COLLECTION(UnblockParsingPromiseHandler, mDocument, mPromise)
13614 
13615 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(UnblockParsingPromiseHandler)
13616   NS_INTERFACE_MAP_ENTRY(nsISupports)
13617 NS_INTERFACE_MAP_END
13618 
13619 NS_IMPL_CYCLE_COLLECTING_ADDREF(UnblockParsingPromiseHandler)
13620 NS_IMPL_CYCLE_COLLECTING_RELEASE(UnblockParsingPromiseHandler)
13621 
13622 already_AddRefed<Promise> Document::BlockParsing(
13623     Promise& aPromise, const BlockParsingOptions& aOptions, ErrorResult& aRv) {
13624   RefPtr<Promise> resultPromise =
13625       Promise::Create(aPromise.GetParentObject(), aRv);
13626   if (aRv.Failed()) {
13627     return nullptr;
13628   }
13629 
13630   RefPtr<PromiseNativeHandler> promiseHandler =
13631       new UnblockParsingPromiseHandler(this, resultPromise, aOptions);
13632   aPromise.AppendNativeHandler(promiseHandler);
13633 
13634   return resultPromise.forget();
13635 }
13636 
GetMozDocumentURIIfNotForErrorPages()13637 already_AddRefed<nsIURI> Document::GetMozDocumentURIIfNotForErrorPages() {
13638   if (mFailedChannel) {
13639     nsCOMPtr<nsIURI> failedURI;
13640     if (NS_SUCCEEDED(mFailedChannel->GetURI(getter_AddRefs(failedURI)))) {
13641       return failedURI.forget();
13642     }
13643   }
13644 
13645   nsCOMPtr<nsIURI> uri = GetDocumentURIObject();
13646   if (!uri) {
13647     return nullptr;
13648   }
13649 
13650   return uri.forget();
13651 }
13652 
GetDocumentReadyForIdle(ErrorResult & aRv)13653 Promise* Document::GetDocumentReadyForIdle(ErrorResult& aRv) {
13654   if (mIsGoingAway) {
13655     aRv.Throw(NS_ERROR_NOT_AVAILABLE);
13656     return nullptr;
13657   }
13658 
13659   if (!mReadyForIdle) {
13660     nsIGlobalObject* global = GetScopeObject();
13661     if (!global) {
13662       aRv.Throw(NS_ERROR_NOT_AVAILABLE);
13663       return nullptr;
13664     }
13665 
13666     mReadyForIdle = Promise::Create(global, aRv);
13667     if (aRv.Failed()) {
13668       return nullptr;
13669     }
13670   }
13671 
13672   return mReadyForIdle;
13673 }
13674 
MaybeResolveReadyForIdle()13675 void Document::MaybeResolveReadyForIdle() {
13676   IgnoredErrorResult rv;
13677   Promise* readyPromise = GetDocumentReadyForIdle(rv);
13678   if (readyPromise) {
13679     readyPromise->MaybeResolveWithUndefined();
13680   }
13681 }
13682 
FeaturePolicy() const13683 mozilla::dom::FeaturePolicy* Document::FeaturePolicy() const {
13684   // The policy is created when the document is initialized. We _must_ have a
13685   // policy here even if the featurePolicy pref is off. If this assertion fails,
13686   // it means that ::FeaturePolicy() is called before ::StartDocumentLoad().
13687   MOZ_ASSERT(mFeaturePolicy);
13688   return mFeaturePolicy;
13689 }
13690 
GetCommandDispatcher()13691 nsIDOMXULCommandDispatcher* Document::GetCommandDispatcher() {
13692   // Only chrome documents are allowed to use command dispatcher.
13693   if (!nsContentUtils::IsChromeDoc(this)) {
13694     return nullptr;
13695   }
13696   if (!mCommandDispatcher) {
13697     // Create our command dispatcher and hook it up.
13698     mCommandDispatcher = new nsXULCommandDispatcher(this);
13699   }
13700   return mCommandDispatcher;
13701 }
13702 
InitializeXULBroadcastManager()13703 void Document::InitializeXULBroadcastManager() {
13704   if (mXULBroadcastManager) {
13705     return;
13706   }
13707   mXULBroadcastManager = new XULBroadcastManager(this);
13708 }
13709 
13710 namespace {
13711 
13712 class DevToolsMutationObserver final : public nsStubMutationObserver {
13713   NS_DECL_ISUPPORTS
13714   NS_DECL_NSIMUTATIONOBSERVER_ATTRIBUTECHANGED
13715   NS_DECL_NSIMUTATIONOBSERVER_CONTENTAPPENDED
13716   NS_DECL_NSIMUTATIONOBSERVER_CONTENTINSERTED
13717 
13718   // We handle this in nsContentUtils::MaybeFireNodeRemoved, since devtools
13719   // relies on the event firing _before_ the removal happens.
13720   // NS_DECL_NSIMUTATIONOBSERVER_CONTENTREMOVED
13721 
13722   // NOTE(emilio, bug 1694627): DevTools doesn't seem to deal with character
13723   // data changes right now (maybe intentionally?).
13724   // NS_DECL_NSIMUTATIONOBSERVER_CHARACTERDATACHANGED
13725 
13726   DevToolsMutationObserver() = default;
13727 
13728  private:
13729   void FireEvent(nsINode* aTarget, const nsAString& aType);
13730 
13731   ~DevToolsMutationObserver() = default;
13732 };
13733 
NS_IMPL_ISUPPORTS(DevToolsMutationObserver,nsIMutationObserver)13734 NS_IMPL_ISUPPORTS(DevToolsMutationObserver, nsIMutationObserver)
13735 
13736 void DevToolsMutationObserver::FireEvent(nsINode* aTarget,
13737                                          const nsAString& aType) {
13738   if (aTarget->ChromeOnlyAccess()) {
13739     return;
13740   }
13741   (new AsyncEventDispatcher(aTarget, aType, CanBubble::eNo,
13742                             ChromeOnlyDispatch::eYes, Composed::eYes))
13743       ->RunDOMEventWhenSafe();
13744 }
13745 
AttributeChanged(Element * aElement,int32_t aNamespaceID,nsAtom * aAttribute,int32_t aModType,const nsAttrValue * aOldValue)13746 void DevToolsMutationObserver::AttributeChanged(Element* aElement,
13747                                                 int32_t aNamespaceID,
13748                                                 nsAtom* aAttribute,
13749                                                 int32_t aModType,
13750                                                 const nsAttrValue* aOldValue) {
13751   FireEvent(aElement, u"devtoolsattrmodified"_ns);
13752 }
13753 
ContentAppended(nsIContent * aFirstNewContent)13754 void DevToolsMutationObserver::ContentAppended(nsIContent* aFirstNewContent) {
13755   for (nsIContent* c = aFirstNewContent; c; c = c->GetNextSibling()) {
13756     ContentInserted(c);
13757   }
13758 }
13759 
ContentInserted(nsIContent * aChild)13760 void DevToolsMutationObserver::ContentInserted(nsIContent* aChild) {
13761   FireEvent(aChild, u"devtoolschildinserted"_ns);
13762 }
13763 
13764 static StaticRefPtr<DevToolsMutationObserver> sDevToolsMutationObserver;
13765 
13766 }  // namespace
13767 
SetDevToolsWatchingDOMMutations(bool aValue)13768 void Document::SetDevToolsWatchingDOMMutations(bool aValue) {
13769   if (mDevToolsWatchingDOMMutations == aValue || mIsGoingAway) {
13770     return;
13771   }
13772   mDevToolsWatchingDOMMutations = aValue;
13773   if (aValue) {
13774     if (MOZ_UNLIKELY(!sDevToolsMutationObserver)) {
13775       sDevToolsMutationObserver = new DevToolsMutationObserver();
13776       ClearOnShutdown(&sDevToolsMutationObserver);
13777     }
13778     AddMutationObserver(sDevToolsMutationObserver);
13779   } else if (sDevToolsMutationObserver) {
13780     RemoveMutationObserver(sDevToolsMutationObserver);
13781   }
13782 }
13783 
MaybeWarnAboutZoom()13784 void Document::MaybeWarnAboutZoom() {
13785   if (mHasWarnedAboutZoom) {
13786     return;
13787   }
13788   const bool usedZoom = Servo_IsPropertyIdRecordedInUseCounter(
13789       mStyleUseCounters.get(), eCSSProperty_zoom);
13790   if (!usedZoom) {
13791     return;
13792   }
13793 
13794   mHasWarnedAboutZoom = true;
13795   nsContentUtils::ReportToConsole(nsIScriptError::warningFlag, "Layout"_ns,
13796                                   this, nsContentUtils::eLAYOUT_PROPERTIES,
13797                                   "ZoomPropertyWarning");
13798 }
13799 
Children()13800 nsIHTMLCollection* Document::Children() {
13801   if (!mChildrenCollection) {
13802     mChildrenCollection =
13803         new nsContentList(this, kNameSpaceID_Wildcard, nsGkAtoms::_asterisk,
13804                           nsGkAtoms::_asterisk, false);
13805   }
13806 
13807   return mChildrenCollection;
13808 }
13809 
ChildElementCount()13810 uint32_t Document::ChildElementCount() { return Children()->Length(); }
13811 
13812 // Singleton class to manage the list of fullscreen documents which are the
13813 // root of a branch which contains fullscreen documents. We maintain this list
13814 // so that we can easily exit all windows from fullscreen when the user
13815 // presses the escape key.
13816 class FullscreenRoots {
13817  public:
13818   // Adds the root of given document to the manager. Calling this method
13819   // with a document whose root is already contained has no effect.
13820   static void Add(Document* aDoc);
13821 
13822   // Iterates over every root in the root list, and calls aFunction, passing
13823   // each root once to aFunction. It is safe to call Add() and Remove() while
13824   // iterating over the list (i.e. in aFunction). Documents that are removed
13825   // from the manager during traversal are not traversed, and documents that
13826   // are added to the manager during traversal are also not traversed.
13827   static void ForEach(void (*aFunction)(Document* aDoc));
13828 
13829   // Removes the root of a specific document from the manager.
13830   static void Remove(Document* aDoc);
13831 
13832   // Returns true if all roots added to the list have been removed.
13833   static bool IsEmpty();
13834 
13835  private:
13836   MOZ_COUNTED_DEFAULT_CTOR(FullscreenRoots)
13837   MOZ_COUNTED_DTOR(FullscreenRoots)
13838 
13839   enum { NotFound = uint32_t(-1) };
13840   // Looks in mRoots for aRoot. Returns the index if found, otherwise NotFound.
13841   static uint32_t Find(Document* aRoot);
13842 
13843   // Returns true if aRoot is in the list of fullscreen roots.
13844   static bool Contains(Document* aRoot);
13845 
13846   // Singleton instance of the FullscreenRoots. This is instantiated when a
13847   // root is added, and it is deleted when the last root is removed.
13848   static FullscreenRoots* sInstance;
13849 
13850   // List of weak pointers to roots.
13851   nsTArray<nsWeakPtr> mRoots;
13852 };
13853 
13854 FullscreenRoots* FullscreenRoots::sInstance = nullptr;
13855 
13856 /* static */
ForEach(void (* aFunction)(Document * aDoc))13857 void FullscreenRoots::ForEach(void (*aFunction)(Document* aDoc)) {
13858   if (!sInstance) {
13859     return;
13860   }
13861   // Create a copy of the roots array, and iterate over the copy. This is so
13862   // that if an element is removed from mRoots we don't mess up our iteration.
13863   nsTArray<nsWeakPtr> roots(sInstance->mRoots.Clone());
13864   // Call aFunction on all entries.
13865   for (uint32_t i = 0; i < roots.Length(); i++) {
13866     nsCOMPtr<Document> root = do_QueryReferent(roots[i]);
13867     // Check that the root isn't in the manager. This is so that new additions
13868     // while we were running don't get traversed.
13869     if (root && FullscreenRoots::Contains(root)) {
13870       aFunction(root);
13871     }
13872   }
13873 }
13874 
13875 /* static */
Contains(Document * aRoot)13876 bool FullscreenRoots::Contains(Document* aRoot) {
13877   return FullscreenRoots::Find(aRoot) != NotFound;
13878 }
13879 
13880 /* static */
Add(Document * aDoc)13881 void FullscreenRoots::Add(Document* aDoc) {
13882   nsCOMPtr<Document> root =
13883       nsContentUtils::GetInProcessSubtreeRootDocument(aDoc);
13884   if (!FullscreenRoots::Contains(root)) {
13885     if (!sInstance) {
13886       sInstance = new FullscreenRoots();
13887     }
13888     sInstance->mRoots.AppendElement(do_GetWeakReference(root));
13889   }
13890 }
13891 
13892 /* static */
Find(Document * aRoot)13893 uint32_t FullscreenRoots::Find(Document* aRoot) {
13894   if (!sInstance) {
13895     return NotFound;
13896   }
13897   nsTArray<nsWeakPtr>& roots = sInstance->mRoots;
13898   for (uint32_t i = 0; i < roots.Length(); i++) {
13899     nsCOMPtr<Document> otherRoot(do_QueryReferent(roots[i]));
13900     if (otherRoot == aRoot) {
13901       return i;
13902     }
13903   }
13904   return NotFound;
13905 }
13906 
13907 /* static */
Remove(Document * aDoc)13908 void FullscreenRoots::Remove(Document* aDoc) {
13909   nsCOMPtr<Document> root =
13910       nsContentUtils::GetInProcessSubtreeRootDocument(aDoc);
13911   uint32_t index = Find(root);
13912   NS_ASSERTION(index != NotFound,
13913                "Should only try to remove roots which are still added!");
13914   if (index == NotFound || !sInstance) {
13915     return;
13916   }
13917   sInstance->mRoots.RemoveElementAt(index);
13918   if (sInstance->mRoots.IsEmpty()) {
13919     delete sInstance;
13920     sInstance = nullptr;
13921   }
13922 }
13923 
13924 /* static */
IsEmpty()13925 bool FullscreenRoots::IsEmpty() { return !sInstance; }
13926 
13927 // Any fullscreen change waiting for the widget to finish transition
13928 // is queued here. This is declared static instead of a member of
13929 // Document because in the majority of time, there would be at most
13930 // one document requesting or exiting fullscreen. We shouldn't waste
13931 // the space to hold for it in every document.
13932 class PendingFullscreenChangeList {
13933  public:
13934   PendingFullscreenChangeList() = delete;
13935 
13936   template <typename T>
Add(UniquePtr<T> aChange)13937   static void Add(UniquePtr<T> aChange) {
13938     sList.insertBack(aChange.release());
13939   }
13940 
GetLast()13941   static const FullscreenChange* GetLast() { return sList.getLast(); }
13942 
13943   enum IteratorOption {
13944     // When we are committing fullscreen changes or preparing for
13945     // that, we generally want to iterate all requests in the same
13946     // window with eDocumentsWithSameRoot option.
13947     eDocumentsWithSameRoot,
13948     // If we are removing a document from the tree, we would only
13949     // want to remove the requests from the given document and its
13950     // descendants. For that case, use eInclusiveDescendants.
13951     eInclusiveDescendants
13952   };
13953 
13954   template <typename T>
13955   class Iterator {
13956    public:
Iterator(Document * aDoc,IteratorOption aOption)13957     explicit Iterator(Document* aDoc, IteratorOption aOption)
13958         : mCurrent(PendingFullscreenChangeList::sList.getFirst()) {
13959       if (mCurrent) {
13960         if (aDoc->GetBrowsingContext()) {
13961           mRootBCForIteration = aDoc->GetBrowsingContext();
13962           if (aOption == eDocumentsWithSameRoot) {
13963             RefPtr<BrowsingContext> bc =
13964                 GetParentIgnoreChromeBoundary(mRootBCForIteration);
13965             while (bc) {
13966               mRootBCForIteration = bc;
13967               bc = GetParentIgnoreChromeBoundary(mRootBCForIteration);
13968             }
13969           }
13970         }
13971         SkipToNextMatch();
13972       }
13973     }
13974 
TakeAndNext()13975     UniquePtr<T> TakeAndNext() {
13976       auto thisChange = TakeAndNextInternal();
13977       SkipToNextMatch();
13978       return thisChange;
13979     }
AtEnd() const13980     bool AtEnd() const { return mCurrent == nullptr; }
13981 
13982    private:
GetParentIgnoreChromeBoundary(BrowsingContext * aBC)13983     already_AddRefed<BrowsingContext> GetParentIgnoreChromeBoundary(
13984         BrowsingContext* aBC) {
13985       // Chrome BrowsingContexts are only available in the parent process, so if
13986       // we're in a content process, we only worry about the context tree.
13987       if (XRE_IsParentProcess()) {
13988         return aBC->Canonical()->GetParentCrossChromeBoundary();
13989       }
13990       return do_AddRef(aBC->GetParent());
13991     }
13992 
TakeAndNextInternal()13993     UniquePtr<T> TakeAndNextInternal() {
13994       FullscreenChange* thisChange = mCurrent;
13995       MOZ_ASSERT(thisChange->Type() == T::kType);
13996       mCurrent = mCurrent->removeAndGetNext();
13997       return WrapUnique(static_cast<T*>(thisChange));
13998     }
SkipToNextMatch()13999     void SkipToNextMatch() {
14000       while (mCurrent) {
14001         if (mCurrent->Type() == T::kType) {
14002           RefPtr<BrowsingContext> bc =
14003               mCurrent->Document()->GetBrowsingContext();
14004           if (!bc) {
14005             // Always automatically drop fullscreen changes which are
14006             // from a document detached from the doc shell.
14007             UniquePtr<T> change = TakeAndNextInternal();
14008             change->MayRejectPromise("Document is not active");
14009             continue;
14010           }
14011           while (bc && bc != mRootBCForIteration) {
14012             bc = GetParentIgnoreChromeBoundary(bc);
14013           }
14014           if (bc) {
14015             break;
14016           }
14017         }
14018         // The current one either don't have matched type, or isn't
14019         // inside the given subtree, so skip this item.
14020         mCurrent = mCurrent->getNext();
14021       }
14022     }
14023 
14024     FullscreenChange* mCurrent;
14025     RefPtr<BrowsingContext> mRootBCForIteration;
14026   };
14027 
14028  private:
14029   static LinkedList<FullscreenChange> sList;
14030 };
14031 
14032 /* static */
14033 LinkedList<FullscreenChange> PendingFullscreenChangeList::sList;
14034 
GetFullscreenRoot()14035 Document* Document::GetFullscreenRoot() {
14036   nsCOMPtr<Document> root = do_QueryReferent(mFullscreenRoot);
14037   return root;
14038 }
14039 
CountFullscreenElements() const14040 size_t Document::CountFullscreenElements() const {
14041   size_t count = 0;
14042   for (const nsWeakPtr& ptr : mTopLayer) {
14043     if (nsCOMPtr<Element> elem = do_QueryReferent(ptr)) {
14044       if (elem->State().HasState(NS_EVENT_STATE_FULLSCREEN)) {
14045         count++;
14046       }
14047     }
14048   }
14049   return count;
14050 }
14051 
SetFullscreenRoot(Document * aRoot)14052 void Document::SetFullscreenRoot(Document* aRoot) {
14053   mFullscreenRoot = do_GetWeakReference(aRoot);
14054 }
14055 
TryCancelDialog()14056 void Document::TryCancelDialog() {
14057   // Check if the document is blocked by modal dialog
14058   for (const nsWeakPtr& weakPtr : Reversed(mTopLayer)) {
14059     nsCOMPtr<Element> element(do_QueryReferent(weakPtr));
14060     if (HTMLDialogElement* dialog =
14061             HTMLDialogElement::FromNodeOrNull(element)) {
14062       dialog->QueueCancelDialog();
14063       break;
14064     }
14065   }
14066 }
14067 
ExitFullscreen(ErrorResult & aRv)14068 already_AddRefed<Promise> Document::ExitFullscreen(ErrorResult& aRv) {
14069   UniquePtr<FullscreenExit> exit = FullscreenExit::Create(this, aRv);
14070   RefPtr<Promise> promise = exit->GetPromise();
14071   RestorePreviousFullscreenState(std::move(exit));
14072   return promise.forget();
14073 }
14074 
AskWindowToExitFullscreen(Document * aDoc)14075 static void AskWindowToExitFullscreen(Document* aDoc) {
14076   if (XRE_GetProcessType() == GeckoProcessType_Content) {
14077     nsContentUtils::DispatchEventOnlyToChrome(
14078         aDoc, ToSupports(aDoc), u"MozDOMFullscreen:Exit"_ns, CanBubble::eYes,
14079         Cancelable::eNo, /* DefaultAction */ nullptr);
14080   } else {
14081     if (nsPIDOMWindowOuter* win = aDoc->GetWindow()) {
14082       win->SetFullscreenInternal(FullscreenReason::ForFullscreenAPI, false);
14083     }
14084   }
14085 }
14086 
14087 class nsCallExitFullscreen : public Runnable {
14088  public:
nsCallExitFullscreen(Document * aDoc)14089   explicit nsCallExitFullscreen(Document* aDoc)
14090       : mozilla::Runnable("nsCallExitFullscreen"), mDoc(aDoc) {}
14091 
Run()14092   NS_IMETHOD Run() final {
14093     if (!mDoc) {
14094       FullscreenRoots::ForEach(&AskWindowToExitFullscreen);
14095     } else {
14096       AskWindowToExitFullscreen(mDoc);
14097     }
14098     return NS_OK;
14099   }
14100 
14101  private:
14102   nsCOMPtr<Document> mDoc;
14103 };
14104 
14105 /* static */
AsyncExitFullscreen(Document * aDoc)14106 void Document::AsyncExitFullscreen(Document* aDoc) {
14107   MOZ_RELEASE_ASSERT(NS_IsMainThread());
14108   nsCOMPtr<nsIRunnable> exit = new nsCallExitFullscreen(aDoc);
14109   if (aDoc) {
14110     aDoc->Dispatch(TaskCategory::Other, exit.forget());
14111   } else {
14112     NS_DispatchToCurrentThread(exit.forget());
14113   }
14114 }
14115 
CountFullscreenSubDocuments(Document & aDoc)14116 static uint32_t CountFullscreenSubDocuments(Document& aDoc) {
14117   uint32_t count = 0;
14118   // FIXME(emilio): Should this be recursive and dig into our nested subdocs?
14119   auto subDoc = [&count](Document& aSubDoc) {
14120     if (aSubDoc.GetUnretargetedFullScreenElement()) {
14121       count++;
14122     }
14123     return CallState::Continue;
14124   };
14125   aDoc.EnumerateSubDocuments(subDoc);
14126   return count;
14127 }
14128 
IsFullscreenLeaf()14129 bool Document::IsFullscreenLeaf() {
14130   // A fullscreen leaf document is fullscreen, and has no fullscreen
14131   // subdocuments.
14132   if (!GetUnretargetedFullScreenElement()) {
14133     return false;
14134   }
14135   return CountFullscreenSubDocuments(*this) == 0;
14136 }
14137 
GetFullscreenLeaf(Document & aDoc)14138 static Document* GetFullscreenLeaf(Document& aDoc) {
14139   if (aDoc.IsFullscreenLeaf()) {
14140     return &aDoc;
14141   }
14142   if (!aDoc.GetUnretargetedFullScreenElement()) {
14143     return nullptr;
14144   }
14145   Document* leaf = nullptr;
14146   auto recurse = [&leaf](Document& aSubDoc) {
14147     leaf = GetFullscreenLeaf(aSubDoc);
14148     return leaf ? CallState::Stop : CallState::Continue;
14149   };
14150   aDoc.EnumerateSubDocuments(recurse);
14151   return leaf;
14152 }
14153 
GetFullscreenLeaf(Document * aDoc)14154 static Document* GetFullscreenLeaf(Document* aDoc) {
14155   if (Document* leaf = GetFullscreenLeaf(*aDoc)) {
14156     return leaf;
14157   }
14158   // Otherwise we could be either in a non-fullscreen doc tree, or we're
14159   // below the fullscreen doc. Start the search from the root.
14160   Document* root = nsContentUtils::GetInProcessSubtreeRootDocument(aDoc);
14161   return GetFullscreenLeaf(*root);
14162 }
14163 
ResetFullscreen(Document & aDocument)14164 static CallState ResetFullscreen(Document& aDocument) {
14165   if (Element* fsElement = aDocument.GetUnretargetedFullScreenElement()) {
14166     NS_ASSERTION(CountFullscreenSubDocuments(aDocument) <= 1,
14167                  "Should have at most 1 fullscreen subdocument.");
14168     aDocument.CleanupFullscreenState();
14169     NS_ASSERTION(!aDocument.GetUnretargetedFullScreenElement(),
14170                  "Should reset fullscreen");
14171     DispatchFullscreenChange(aDocument, fsElement);
14172     aDocument.EnumerateSubDocuments(ResetFullscreen);
14173   }
14174   return CallState::Continue;
14175 }
14176 
14177 // Since Document::ExitFullscreenInDocTree() could be called from
14178 // Element::UnbindFromTree() where it is not safe to synchronously run
14179 // script. This runnable is the script part of that function.
14180 class ExitFullscreenScriptRunnable : public Runnable {
14181  public:
ExitFullscreenScriptRunnable(Document * aRoot,Document * aLeaf)14182   explicit ExitFullscreenScriptRunnable(Document* aRoot, Document* aLeaf)
14183       : mozilla::Runnable("ExitFullscreenScriptRunnable"),
14184         mRoot(aRoot),
14185         mLeaf(aLeaf) {}
14186 
Run()14187   NS_IMETHOD Run() override {
14188     // Dispatch MozDOMFullscreen:Exited to the original fullscreen leaf
14189     // document since we want this event to follow the same path that
14190     // MozDOMFullscreen:Entered was dispatched.
14191     nsContentUtils::DispatchEventOnlyToChrome(
14192         mLeaf, ToSupports(mLeaf), u"MozDOMFullscreen:Exited"_ns,
14193         CanBubble::eYes, Cancelable::eNo, /* DefaultAction */ nullptr);
14194     // Ensure the window exits fullscreen.
14195     if (nsPIDOMWindowOuter* win = mRoot->GetWindow()) {
14196       win->SetFullscreenInternal(FullscreenReason::ForForceExitFullscreen,
14197                                  false);
14198     }
14199     return NS_OK;
14200   }
14201 
14202  private:
14203   nsCOMPtr<Document> mRoot;
14204   nsCOMPtr<Document> mLeaf;
14205 };
14206 
14207 /* static */
ExitFullscreenInDocTree(Document * aMaybeNotARootDoc)14208 void Document::ExitFullscreenInDocTree(Document* aMaybeNotARootDoc) {
14209   MOZ_ASSERT(aMaybeNotARootDoc);
14210 
14211   // Unlock the pointer
14212   PointerLockManager::Unlock();
14213 
14214   // Resolve all promises which waiting for exit fullscreen.
14215   PendingFullscreenChangeList::Iterator<FullscreenExit> iter(
14216       aMaybeNotARootDoc, PendingFullscreenChangeList::eDocumentsWithSameRoot);
14217   while (!iter.AtEnd()) {
14218     UniquePtr<FullscreenExit> exit = iter.TakeAndNext();
14219     exit->MayResolvePromise();
14220   }
14221 
14222   nsCOMPtr<Document> root = aMaybeNotARootDoc->GetFullscreenRoot();
14223   if (!root || !root->GetUnretargetedFullScreenElement()) {
14224     // If a document was detached before exiting from fullscreen, it is
14225     // possible that the root had left fullscreen state. In this case,
14226     // we would not get anything from the ResetFullscreen() call. Root's
14227     // not being a fullscreen doc also means the widget should have
14228     // exited fullscreen state. It means even if we do not return here,
14229     // we would actually do nothing below except crashing ourselves via
14230     // dispatching the "MozDOMFullscreen:Exited" event to an nonexistent
14231     // document.
14232     return;
14233   }
14234 
14235   // Record the fullscreen leaf document for MozDOMFullscreen:Exited.
14236   // See ExitFullscreenScriptRunnable::Run for details. We have to
14237   // record it here because we don't have such information after we
14238   // reset the fullscreen state below.
14239   Document* fullscreenLeaf = GetFullscreenLeaf(root);
14240 
14241   // Walk the tree of fullscreen documents, and reset their fullscreen state.
14242   ResetFullscreen(*root);
14243 
14244   NS_ASSERTION(!root->GetUnretargetedFullScreenElement(),
14245                "Fullscreen root should no longer be a fullscreen doc...");
14246 
14247   // Move the top-level window out of fullscreen mode.
14248   FullscreenRoots::Remove(root);
14249 
14250   nsContentUtils::AddScriptRunner(
14251       new ExitFullscreenScriptRunnable(root, fullscreenLeaf));
14252 }
14253 
DispatchFullscreenNewOriginEvent(Document * aDoc)14254 static void DispatchFullscreenNewOriginEvent(Document* aDoc) {
14255   RefPtr<AsyncEventDispatcher> asyncDispatcher =
14256       new AsyncEventDispatcher(aDoc, u"MozDOMFullscreen:NewOrigin"_ns,
14257                                CanBubble::eYes, ChromeOnlyDispatch::eYes);
14258   asyncDispatcher->PostDOMEvent();
14259 }
14260 
RestorePreviousFullscreenState(UniquePtr<FullscreenExit> aExit)14261 void Document::RestorePreviousFullscreenState(UniquePtr<FullscreenExit> aExit) {
14262   NS_ASSERTION(
14263       !GetUnretargetedFullScreenElement() || !FullscreenRoots::IsEmpty(),
14264       "Should have at least 1 fullscreen root when fullscreen!");
14265 
14266   if (!GetWindow()) {
14267     aExit->MayRejectPromise("No active window");
14268     return;
14269   }
14270   if (!GetUnretargetedFullScreenElement() || FullscreenRoots::IsEmpty()) {
14271     aExit->MayRejectPromise("Not in fullscreen mode");
14272     return;
14273   }
14274 
14275   nsCOMPtr<Document> fullScreenDoc = GetFullscreenLeaf(this);
14276   AutoTArray<Element*, 8> exitElements;
14277 
14278   Document* doc = fullScreenDoc;
14279   // Collect all subdocuments.
14280   for (; doc != this; doc = doc->GetInProcessParentDocument()) {
14281     Element* fsElement = doc->GetUnretargetedFullScreenElement();
14282     MOZ_ASSERT(fsElement,
14283                "Parent document of "
14284                "a fullscreen document without fullscreen element?");
14285     exitElements.AppendElement(fsElement);
14286   }
14287   MOZ_ASSERT(doc == this, "Must have reached this doc");
14288   // Collect all ancestor documents which we are going to change.
14289   for (; doc; doc = doc->GetInProcessParentDocument()) {
14290     Element* fsElement = doc->GetUnretargetedFullScreenElement();
14291     MOZ_ASSERT(fsElement,
14292                "Ancestor of fullscreen document must also be in fullscreen");
14293     if (doc != this) {
14294       if (auto* iframe = HTMLIFrameElement::FromNode(fsElement)) {
14295         if (iframe->FullscreenFlag()) {
14296           // If this is an iframe, and it explicitly requested
14297           // fullscreen, don't rollback it automatically.
14298           break;
14299         }
14300       }
14301     }
14302     exitElements.AppendElement(fsElement);
14303     if (doc->CountFullscreenElements() > 1) {
14304       break;
14305     }
14306   }
14307 
14308   Document* lastDoc = exitElements.LastElement()->OwnerDoc();
14309   size_t fullscreenCount = lastDoc->CountFullscreenElements();
14310   if (!lastDoc->GetInProcessParentDocument() && fullscreenCount == 1) {
14311     // If we are fully exiting fullscreen, don't touch anything here,
14312     // just wait for the window to get out from fullscreen first.
14313     PendingFullscreenChangeList::Add(std::move(aExit));
14314     AskWindowToExitFullscreen(this);
14315     return;
14316   }
14317 
14318   // If fullscreen mode is updated the pointer should be unlocked
14319   PointerLockManager::Unlock();
14320   // All documents listed in the array except the last one are going to
14321   // completely exit from the fullscreen state.
14322   for (auto i : IntegerRange(exitElements.Length() - 1)) {
14323     exitElements[i]->OwnerDoc()->CleanupFullscreenState();
14324   }
14325   // The last document will either rollback one fullscreen element, or
14326   // completely exit from the fullscreen state as well.
14327   Document* newFullscreenDoc;
14328   if (fullscreenCount > 1) {
14329     lastDoc->UnsetFullscreenElement();
14330     newFullscreenDoc = lastDoc;
14331   } else {
14332     lastDoc->CleanupFullscreenState();
14333     newFullscreenDoc = lastDoc->GetInProcessParentDocument();
14334   }
14335   // Dispatch the fullscreenchange event to all document listed. Note
14336   // that the loop order is reversed so that events are dispatched in
14337   // the tree order as indicated in the spec.
14338   for (Element* e : Reversed(exitElements)) {
14339     DispatchFullscreenChange(*e->OwnerDoc(), e);
14340   }
14341   aExit->MayResolvePromise();
14342 
14343   MOZ_ASSERT(newFullscreenDoc,
14344              "If we were going to exit from fullscreen on "
14345              "all documents in this doctree, we should've asked the window to "
14346              "exit first instead of reaching here.");
14347   if (fullScreenDoc != newFullscreenDoc &&
14348       !nsContentUtils::HaveEqualPrincipals(fullScreenDoc, newFullscreenDoc)) {
14349     // We've popped so enough off the stack that we've rolled back to
14350     // a fullscreen element in a parent document. If this document is
14351     // cross origin, dispatch an event to chrome so it knows to show
14352     // the warning UI.
14353     DispatchFullscreenNewOriginEvent(newFullscreenDoc);
14354   }
14355 }
14356 
14357 class nsCallRequestFullscreen : public Runnable {
14358  public:
nsCallRequestFullscreen(UniquePtr<FullscreenRequest> aRequest)14359   explicit nsCallRequestFullscreen(UniquePtr<FullscreenRequest> aRequest)
14360       : mozilla::Runnable("nsCallRequestFullscreen"),
14361         mRequest(std::move(aRequest)) {}
14362 
Run()14363   NS_IMETHOD Run() override {
14364     Document* doc = mRequest->Document();
14365     doc->RequestFullscreen(std::move(mRequest));
14366     return NS_OK;
14367   }
14368 
14369   UniquePtr<FullscreenRequest> mRequest;
14370 };
14371 
AsyncRequestFullscreen(UniquePtr<FullscreenRequest> aRequest)14372 void Document::AsyncRequestFullscreen(UniquePtr<FullscreenRequest> aRequest) {
14373   // Request fullscreen asynchronously.
14374   MOZ_RELEASE_ASSERT(NS_IsMainThread());
14375   nsCOMPtr<nsIRunnable> event =
14376       new nsCallRequestFullscreen(std::move(aRequest));
14377   Dispatch(TaskCategory::Other, event.forget());
14378 }
14379 
UpdateViewportScrollbarOverrideForFullscreen(Document * aDoc)14380 static void UpdateViewportScrollbarOverrideForFullscreen(Document* aDoc) {
14381   if (nsPresContext* presContext = aDoc->GetPresContext()) {
14382     presContext->UpdateViewportScrollStylesOverride();
14383   }
14384 }
14385 
NotifyFullScreenChangedForMediaElement(Element * aElement,bool aIsInFullScreen)14386 static void NotifyFullScreenChangedForMediaElement(Element* aElement,
14387                                                    bool aIsInFullScreen) {
14388   // When a media element enters the fullscreen, we would like to notify that
14389   // to the media controller in order to update its status.
14390   if (!aElement->IsAnyOfHTMLElements(nsGkAtoms::audio, nsGkAtoms::video)) {
14391     return;
14392   }
14393   HTMLMediaElement* mediaElem = HTMLMediaElement::FromNodeOrNull(aElement);
14394   mediaElem->NotifyFullScreenChanged();
14395 }
14396 
ClearFullscreenStateOnElement(Element * aElement)14397 static void ClearFullscreenStateOnElement(Element* aElement) {
14398   // Remove styles from existing top element.
14399   EventStateManager::SetFullscreenState(aElement, false);
14400   NotifyFullScreenChangedForMediaElement(aElement, false);
14401   // Reset iframe fullscreen flag.
14402   if (aElement->IsHTMLElement(nsGkAtoms::iframe)) {
14403     static_cast<HTMLIFrameElement*>(aElement)->SetFullscreenFlag(false);
14404   }
14405 }
14406 
CleanupFullscreenState()14407 void Document::CleanupFullscreenState() {
14408   // Iterate the top layer and clear the fullscreen states.
14409   // Since we also need to clear the fullscreen-ancestor state, and
14410   // currently fullscreen elements can only be placed in hierarchy
14411   // order in the stack, reversely iterating the stack could be more
14412   // efficient. NOTE that fullscreen-ancestor state would be removed
14413   // in bug 1199529, and the elements may not in hierarchy order
14414   // after bug 1195213.
14415   mTopLayer.RemoveElementsBy([&](const nsWeakPtr& weakPtr) {
14416     nsCOMPtr<Element> element(do_QueryReferent(weakPtr));
14417     if (!element || !element->IsInComposedDoc() ||
14418         element->OwnerDoc() != this) {
14419       return true;
14420     }
14421 
14422     if (element->State().HasState(NS_EVENT_STATE_FULLSCREEN)) {
14423       ClearFullscreenStateOnElement(element);
14424       return true;
14425     }
14426     return false;
14427   });
14428 
14429   mFullscreenRoot = nullptr;
14430 
14431   // Restore the zoom level that was in place prior to entering fullscreen.
14432   if (PresShell* presShell = GetPresShell()) {
14433     if (presShell->GetMobileViewportManager()) {
14434       presShell->SetResolutionAndScaleTo(
14435           mSavedResolution, ResolutionChangeOrigin::MainThreadRestore);
14436     }
14437   }
14438 
14439   UpdateViewportScrollbarOverrideForFullscreen(this);
14440 }
14441 
UnsetFullscreenElement()14442 void Document::UnsetFullscreenElement() {
14443   Element* removedElement = TopLayerPop([](Element* element) -> bool {
14444     return element->State().HasState(NS_EVENT_STATE_FULLSCREEN);
14445   });
14446 
14447   MOZ_ASSERT(removedElement->State().HasState(NS_EVENT_STATE_FULLSCREEN));
14448   ClearFullscreenStateOnElement(removedElement);
14449   UpdateViewportScrollbarOverrideForFullscreen(this);
14450 }
14451 
SetFullscreenElement(Element * aElement)14452 void Document::SetFullscreenElement(Element* aElement) {
14453   TopLayerPush(aElement);
14454   EventStateManager::SetFullscreenState(aElement, true);
14455   NotifyFullScreenChangedForMediaElement(aElement, true);
14456   UpdateViewportScrollbarOverrideForFullscreen(this);
14457 }
14458 
TopLayerPush(Element * aElement)14459 void Document::TopLayerPush(Element* aElement) {
14460   NS_ASSERTION(aElement, "Must pass non-null to TopLayerPush()");
14461   auto predictFunc = [&aElement](Element* element) {
14462     return element == aElement;
14463   };
14464   TopLayerPop(predictFunc);
14465 
14466   mTopLayer.AppendElement(do_GetWeakReference(aElement));
14467   NS_ASSERTION(GetTopLayerTop() == aElement, "Should match");
14468 }
14469 
SetBlockedByModalDialog(HTMLDialogElement & aDialogElement)14470 void Document::SetBlockedByModalDialog(HTMLDialogElement& aDialogElement) {
14471   Element* root = GetRootElement();
14472   MOZ_RELEASE_ASSERT(root, "dialog in document without root?");
14473 
14474   // Add inert to the root element so that the inertness is
14475   // applied to the entire document. Since the modal dialog
14476   // also inherits the inertness, adding
14477   // NS_EVENT_STATE_TOPMOST_MODAL_DIALOG to remove the inertness
14478   // explicitly.
14479   root->AddStates(NS_EVENT_STATE_MOZINERT);
14480   aDialogElement.AddStates(NS_EVENT_STATE_TOPMOST_MODAL_DIALOG);
14481 
14482   // It's possible that there's another modal dialog has opened
14483   // previously which doesn't have the inertness (because we've
14484   // removed the inertness explicitly). Since a
14485   // new modal dialog is opened, we need to grant the inertness
14486   // to the previous one.
14487   for (const nsWeakPtr& weakPtr : Reversed(mTopLayer)) {
14488     nsCOMPtr<Element> element(do_QueryReferent(weakPtr));
14489     if (auto* dialog = HTMLDialogElement::FromNodeOrNull(element)) {
14490       if (dialog != &aDialogElement) {
14491         dialog->RemoveStates(NS_EVENT_STATE_TOPMOST_MODAL_DIALOG);
14492         // It's ok to exit the loop as only one modal dialog should
14493         // have the state
14494         break;
14495       }
14496     }
14497   }
14498 }
14499 
UnsetBlockedByModalDialog(HTMLDialogElement & aDialogElement)14500 void Document::UnsetBlockedByModalDialog(HTMLDialogElement& aDialogElement) {
14501   aDialogElement.RemoveStates(NS_EVENT_STATE_TOPMOST_MODAL_DIALOG);
14502 
14503   // The document could still be blocked by another modal dialog.
14504   // We need to remove the inertness from this modal dialog.
14505   for (const nsWeakPtr& weakPtr : Reversed(mTopLayer)) {
14506     nsCOMPtr<Element> element(do_QueryReferent(weakPtr));
14507     if (auto* dialog = HTMLDialogElement::FromNodeOrNull(element)) {
14508       if (dialog != &aDialogElement) {
14509         dialog->AddStates(NS_EVENT_STATE_TOPMOST_MODAL_DIALOG);
14510         // Return here because we want to keep the inertness for the
14511         // root element as the document is still blocked by a modal
14512         // dialog
14513         return;
14514       }
14515     }
14516   }
14517 
14518   Element* root = GetRootElement();
14519   if (root && !root->GetBoolAttr(nsGkAtoms::inert)) {
14520     root->RemoveStates(NS_EVENT_STATE_MOZINERT);
14521   }
14522 }
14523 
TopLayerPop(FunctionRef<bool (Element *)> aPredicateFunc)14524 Element* Document::TopLayerPop(FunctionRef<bool(Element*)> aPredicateFunc) {
14525   if (mTopLayer.IsEmpty()) {
14526     return nullptr;
14527   }
14528 
14529   // Remove the topmost element that qualifies aPredicate; This
14530   // is required is because the top layer contains not only
14531   // fullscreen elements, but also dialog elements.
14532   Element* removedElement = nullptr;
14533   for (auto i : Reversed(IntegerRange(mTopLayer.Length()))) {
14534     nsCOMPtr<Element> element(do_QueryReferent(mTopLayer[i]));
14535     if (element && aPredicateFunc(element)) {
14536       removedElement = element;
14537       mTopLayer.RemoveElementAt(i);
14538       break;
14539     }
14540   }
14541 
14542   // Pop from the stack null elements (references to elements which have
14543   // been GC'd since they were added to the stack) and elements which are
14544   // no longer in this document.
14545   //
14546   // FIXME(emilio): If this loop does something, it'd violate the assertions
14547   // from GetTopLayerTop()... What gives?
14548   while (!mTopLayer.IsEmpty()) {
14549     Element* element = GetTopLayerTop();
14550     if (!element || element->GetComposedDoc() != this) {
14551       mTopLayer.RemoveLastElement();
14552     } else {
14553       // The top element of the stack is now an in-doc element. Return here.
14554       break;
14555     }
14556   }
14557 
14558   return removedElement;
14559 }
14560 
GetWireframe(bool aIncludeNodes,Nullable<Wireframe> & aWireframe)14561 void Document::GetWireframe(bool aIncludeNodes,
14562                             Nullable<Wireframe>& aWireframe) {
14563   FlushPendingNotifications(FlushType::Layout);
14564   GetWireframeWithoutFlushing(aIncludeNodes, aWireframe);
14565 }
14566 
GetWireframeWithoutFlushing(bool aIncludeNodes,Nullable<Wireframe> & aWireframe)14567 void Document::GetWireframeWithoutFlushing(bool aIncludeNodes,
14568                                            Nullable<Wireframe>& aWireframe) {
14569   using FrameForPointOptions = nsLayoutUtils::FrameForPointOptions;
14570   using FrameForPointOption = nsLayoutUtils::FrameForPointOption;
14571 
14572   PresShell* shell = GetPresShell();
14573   if (!shell) {
14574     return;
14575   }
14576 
14577   nsPresContext* pc = shell->GetPresContext();
14578   if (!pc) {
14579     return;
14580   }
14581 
14582   nsIFrame* rootFrame = shell->GetRootFrame();
14583   if (!rootFrame) {
14584     return;
14585   }
14586 
14587   auto& wireframe = aWireframe.SetValue();
14588   nsStyleUtil::GetSerializedColorValue(shell->GetCanvasBackground(),
14589                                        wireframe.mCanvasBackground.Construct());
14590 
14591   FrameForPointOptions options;
14592   options.mBits += FrameForPointOption::IgnoreCrossDoc;
14593   options.mBits += FrameForPointOption::IgnorePaintSuppression;
14594   options.mBits += FrameForPointOption::OnlyVisible;
14595 
14596   AutoTArray<nsIFrame*, 32> frames;
14597   const RelativeTo relativeTo{rootFrame, mozilla::ViewportType::Layout};
14598   nsLayoutUtils::GetFramesForArea(relativeTo, pc->GetVisibleArea(), frames,
14599                                   options);
14600 
14601   // TODO(emilio): We could rewrite hit testing to return nsDisplayItem*s or
14602   // something perhaps, but seems hard / like it'd involve at least some extra
14603   // copying around, since they don't outlive GetFramesForArea.
14604   auto& rects = wireframe.mRects.Construct();
14605   if (!rects.SetCapacity(frames.Length(), fallible)) {
14606     return;
14607   }
14608   for (nsIFrame* frame : Reversed(frames)) {
14609     // Can't really fail because SetCapacity succeeded.
14610     auto& taggedRect = *rects.AppendElement(fallible);
14611     const auto r =
14612         CSSRect::FromAppUnits(nsLayoutUtils::TransformFrameRectToAncestor(
14613             frame, frame->GetRectRelativeToSelf(), relativeTo));
14614     if (aIncludeNodes) {
14615       if (nsIContent* c = frame->GetContent()) {
14616         taggedRect.mNode.Construct(c);
14617       }
14618     }
14619     taggedRect.mX = r.x;
14620     taggedRect.mY = r.y;
14621     taggedRect.mWidth = r.width;
14622     taggedRect.mHeight = r.height;
14623     taggedRect.mType.Construct() = [&] {
14624       if (frame->IsTextFrame()) {
14625         nsStyleUtil::GetSerializedColorValue(
14626             frame->StyleText()->mWebkitTextFillColor.CalcColor(frame),
14627             taggedRect.mColor.Construct());
14628         return WireframeRectType::Text;
14629       }
14630       if (frame->IsImageFrame() || frame->IsSVGOuterSVGFrame()) {
14631         return WireframeRectType::Image;
14632       }
14633       if (frame->IsThemed()) {
14634         return WireframeRectType::Background;
14635       }
14636       bool drawImage = false;
14637       bool drawColor = false;
14638       const nscolor color = nsCSSRendering::DetermineBackgroundColor(
14639           pc, frame->Style(), frame, drawImage, drawColor);
14640       if (drawImage &&
14641           !frame->StyleBackground()->mImage.BottomLayer().mImage.IsNone()) {
14642         return WireframeRectType::Image;
14643       }
14644       if (drawColor) {
14645         nsStyleUtil::GetSerializedColorValue(color,
14646                                              taggedRect.mColor.Construct());
14647         return WireframeRectType::Background;
14648       }
14649       return WireframeRectType::Unknown;
14650     }();
14651   }
14652 }
14653 
GetTopLayerTop()14654 Element* Document::GetTopLayerTop() {
14655   if (mTopLayer.IsEmpty()) {
14656     return nullptr;
14657   }
14658   uint32_t last = mTopLayer.Length() - 1;
14659   nsCOMPtr<Element> element(do_QueryReferent(mTopLayer[last]));
14660   NS_ASSERTION(element, "Should have a top layer element!");
14661   NS_ASSERTION(element->IsInComposedDoc(),
14662                "Top layer element should be in doc");
14663   NS_ASSERTION(element->OwnerDoc() == this,
14664                "Top layer element should be in this doc");
14665   return element;
14666 }
14667 
GetUnretargetedFullScreenElement() const14668 Element* Document::GetUnretargetedFullScreenElement() const {
14669   for (const nsWeakPtr& weakPtr : Reversed(mTopLayer)) {
14670     nsCOMPtr<Element> element(do_QueryReferent(weakPtr));
14671     // Per spec, the fullscreen element is the topmost element in the document’s
14672     // top layer whose fullscreen flag is set, if any, and null otherwise.
14673     if (element && element->State().HasState(NS_EVENT_STATE_FULLSCREEN)) {
14674       return element;
14675     }
14676   }
14677   return nullptr;
14678 }
14679 
GetTopLayer() const14680 nsTArray<Element*> Document::GetTopLayer() const {
14681   nsTArray<Element*> elements;
14682   for (const nsWeakPtr& ptr : mTopLayer) {
14683     if (nsCOMPtr<Element> elem = do_QueryReferent(ptr)) {
14684       elements.AppendElement(elem);
14685     }
14686   }
14687   return elements;
14688 }
14689 
14690 // Returns true if aDoc is in the focused tab in the active window.
IsInActiveTab(Document * aDoc)14691 bool IsInActiveTab(Document* aDoc) {
14692   BrowsingContext* bc = aDoc->GetBrowsingContext();
14693   if (!bc) {
14694     return false;
14695   }
14696 
14697   if (!bc->IsActive()) {
14698     return false;
14699   }
14700 
14701   nsFocusManager* fm = nsFocusManager::GetFocusManager();
14702   if (!fm) {
14703     return false;
14704   }
14705 
14706   if (XRE_IsParentProcess()) {
14707     // Keep dom/tests/mochitest/chrome/test_MozDomFullscreen_event.xhtml happy
14708     // by retaining the old code path for the parent process.
14709     nsIDocShell* docshell = aDoc->GetDocShell();
14710     if (!docshell) {
14711       return false;
14712     }
14713     nsCOMPtr<nsIDocShellTreeItem> rootItem;
14714     docshell->GetInProcessRootTreeItem(getter_AddRefs(rootItem));
14715     if (!rootItem) {
14716       return false;
14717     }
14718     nsCOMPtr<nsPIDOMWindowOuter> rootWin = rootItem->GetWindow();
14719     if (!rootWin) {
14720       return false;
14721     }
14722 
14723     nsCOMPtr<nsPIDOMWindowOuter> activeWindow;
14724     activeWindow = fm->GetActiveWindow();
14725     if (!activeWindow) {
14726       return false;
14727     }
14728 
14729     return activeWindow == rootWin;
14730   }
14731 
14732   return fm->GetActiveBrowsingContext() == bc->Top();
14733 }
14734 
RemoteFrameFullscreenChanged(Element * aFrameElement)14735 void Document::RemoteFrameFullscreenChanged(Element* aFrameElement) {
14736   // Ensure the frame element is the fullscreen element in this document.
14737   // If the frame element is already the fullscreen element in this document,
14738   // this has no effect.
14739   auto request = FullscreenRequest::CreateForRemote(aFrameElement);
14740   RequestFullscreen(std::move(request), XRE_IsContentProcess());
14741 }
14742 
RemoteFrameFullscreenReverted()14743 void Document::RemoteFrameFullscreenReverted() {
14744   UniquePtr<FullscreenExit> exit = FullscreenExit::CreateForRemote(this);
14745   RestorePreviousFullscreenState(std::move(exit));
14746 }
14747 
HasFullscreenSubDocument(Document & aDoc)14748 static bool HasFullscreenSubDocument(Document& aDoc) {
14749   uint32_t count = CountFullscreenSubDocuments(aDoc);
14750   NS_ASSERTION(count <= 1,
14751                "Fullscreen docs should have at most 1 fullscreen child!");
14752   return count >= 1;
14753 }
14754 
14755 // Returns nullptr if a request for Fullscreen API is currently enabled
14756 // in the given document. Returns a static string indicates the reason
14757 // why it is not enabled otherwise.
GetFullscreenError(CallerType aCallerType)14758 const char* Document::GetFullscreenError(CallerType aCallerType) {
14759   if (!StaticPrefs::full_screen_api_enabled()) {
14760     return "FullscreenDeniedDisabled";
14761   }
14762 
14763   if (aCallerType == CallerType::System) {
14764     // Chrome code can always use the fullscreen API, provided it's not
14765     // explicitly disabled.
14766     return nullptr;
14767   }
14768 
14769   if (!IsVisible()) {
14770     return "FullscreenDeniedHidden";
14771   }
14772 
14773   if (!FeaturePolicyUtils::IsFeatureAllowed(this, u"fullscreen"_ns)) {
14774     return "FullscreenDeniedFeaturePolicy";
14775   }
14776 
14777   // Ensure that all containing elements are <iframe> and have allowfullscreen
14778   // attribute set.
14779   BrowsingContext* bc = GetBrowsingContext();
14780   if (!bc || !bc->FullscreenAllowed()) {
14781     return "FullscreenDeniedContainerNotAllowed";
14782   }
14783 
14784   return nullptr;
14785 }
14786 
FullscreenElementReadyCheck(FullscreenRequest & aRequest)14787 bool Document::FullscreenElementReadyCheck(FullscreenRequest& aRequest) {
14788   Element* elem = aRequest.Element();
14789   // Strictly speaking, this isn't part of the fullscreen element ready
14790   // check in the spec, but per steps in the spec, when an element which
14791   // is already the fullscreen element requests fullscreen, nothing
14792   // should change and no event should be dispatched, but we still need
14793   // to resolve the returned promise.
14794   Element* fullscreenElement = GetUnretargetedFullScreenElement();
14795   if (elem == fullscreenElement) {
14796     aRequest.MayResolvePromise();
14797     return false;
14798   }
14799   if (!elem->IsInComposedDoc()) {
14800     aRequest.Reject("FullscreenDeniedNotInDocument");
14801     return false;
14802   }
14803   if (elem->OwnerDoc() != this) {
14804     aRequest.Reject("FullscreenDeniedMovedDocument");
14805     return false;
14806   }
14807   if (!GetWindow()) {
14808     aRequest.Reject("FullscreenDeniedLostWindow");
14809     return false;
14810   }
14811   if (const char* msg = GetFullscreenError(aRequest.mCallerType)) {
14812     aRequest.Reject(msg);
14813     return false;
14814   }
14815   if (HasFullscreenSubDocument(*this)) {
14816     aRequest.Reject("FullscreenDeniedSubDocFullScreen");
14817     return false;
14818   }
14819   if (elem->IsHTMLElement(nsGkAtoms::dialog)) {
14820     aRequest.Reject("FullscreenDeniedHTMLDialog");
14821     return false;
14822   }
14823   // XXXsmaug Note, we don't follow the latest fullscreen spec here.
14824   //         This whole check could be probably removed.
14825   if (fullscreenElement && !nsContentUtils::ContentIsHostIncludingDescendantOf(
14826                                elem, fullscreenElement)) {
14827     // If this document is fullscreen, only grant fullscreen requests from
14828     // a descendant of the current fullscreen element.
14829     aRequest.Reject("FullscreenDeniedNotDescendant");
14830     return false;
14831   }
14832   if (!nsContentUtils::IsChromeDoc(this) && !IsInActiveTab(this)) {
14833     aRequest.Reject("FullscreenDeniedNotFocusedTab");
14834     return false;
14835   }
14836   // Deny requests when a windowed plugin is focused.
14837   nsFocusManager* fm = nsFocusManager::GetFocusManager();
14838   if (!fm) {
14839     NS_WARNING("Failed to retrieve focus manager in fullscreen request.");
14840     aRequest.MayRejectPromise("An unexpected error occurred");
14841     return false;
14842   }
14843   if (nsContentUtils::HasPluginWithUncontrolledEventDispatch(
14844           fm->GetFocusedElement())) {
14845     aRequest.Reject("FullscreenDeniedFocusedPlugin");
14846     return false;
14847   }
14848   return true;
14849 }
14850 
GetRootWindow(Document * aDoc)14851 static nsCOMPtr<nsPIDOMWindowOuter> GetRootWindow(Document* aDoc) {
14852   MOZ_ASSERT(XRE_IsParentProcess());
14853   nsIDocShell* docShell = aDoc->GetDocShell();
14854   if (!docShell) {
14855     return nullptr;
14856   }
14857   nsCOMPtr<nsIDocShellTreeItem> rootItem;
14858   docShell->GetInProcessRootTreeItem(getter_AddRefs(rootItem));
14859   return rootItem ? rootItem->GetWindow() : nullptr;
14860 }
14861 
ShouldApplyFullscreenDirectly(Document * aDoc,nsPIDOMWindowOuter * aRootWin)14862 static bool ShouldApplyFullscreenDirectly(Document* aDoc,
14863                                           nsPIDOMWindowOuter* aRootWin) {
14864   MOZ_ASSERT(XRE_IsParentProcess());
14865   // If we are in the chrome process, and the window has not been in
14866   // fullscreen, we certainly need to make that fullscreen first.
14867   if (!aRootWin->GetFullScreen()) {
14868     return false;
14869   }
14870   // The iterator not being at end indicates there is still some
14871   // pending fullscreen request relates to this document. We have to
14872   // push the request to the pending queue so requests are handled
14873   // in the correct order.
14874   PendingFullscreenChangeList::Iterator<FullscreenRequest> iter(
14875       aDoc, PendingFullscreenChangeList::eDocumentsWithSameRoot);
14876   if (!iter.AtEnd()) {
14877     return false;
14878   }
14879   // We have to apply the fullscreen state directly in this case,
14880   // because nsGlobalWindow::SetFullscreenInternal() will do nothing
14881   // if it is already in fullscreen. If we do not apply the state but
14882   // instead add it to the queue and wait for the window as normal,
14883   // we would get stuck.
14884   return true;
14885 }
14886 
CheckFullscreenAllowedElementType(const Element * elem)14887 static bool CheckFullscreenAllowedElementType(const Element* elem) {
14888   // Per spec only HTML, <svg>, and <math> should be allowed, but
14889   // we also need to allow XUL elements right now.
14890   return elem->IsHTMLElement() || elem->IsXULElement() ||
14891          elem->IsSVGElement(nsGkAtoms::svg) ||
14892          elem->IsMathMLElement(nsGkAtoms::math);
14893 }
14894 
RequestFullscreen(UniquePtr<FullscreenRequest> aRequest,bool applyFullScreenDirectly)14895 void Document::RequestFullscreen(UniquePtr<FullscreenRequest> aRequest,
14896                                  bool applyFullScreenDirectly) {
14897   if (XRE_IsContentProcess()) {
14898     RequestFullscreenInContentProcess(std::move(aRequest),
14899                                       applyFullScreenDirectly);
14900   } else {
14901     RequestFullscreenInParentProcess(std::move(aRequest),
14902                                      applyFullScreenDirectly);
14903   }
14904 }
14905 
RequestFullscreenInContentProcess(UniquePtr<FullscreenRequest> aRequest,bool applyFullScreenDirectly)14906 void Document::RequestFullscreenInContentProcess(
14907     UniquePtr<FullscreenRequest> aRequest, bool applyFullScreenDirectly) {
14908   MOZ_ASSERT(XRE_IsContentProcess());
14909 
14910   // If we are in the content process, we can apply the fullscreen
14911   // state directly only if we have been in DOM fullscreen, because
14912   // otherwise we always need to notify the chrome.
14913   if (applyFullScreenDirectly ||
14914       !!nsContentUtils::GetInProcessSubtreeRootDocument(this)
14915             ->GetUnretargetedFullScreenElement()) {
14916     ApplyFullscreen(std::move(aRequest));
14917     return;
14918   }
14919 
14920   if (!CheckFullscreenAllowedElementType(aRequest->Element())) {
14921     aRequest->Reject("FullscreenDeniedNotHTMLSVGOrMathML");
14922     return;
14923   }
14924 
14925   // We don't need to check element ready before this point, because
14926   // if we called ApplyFullscreen, it would check that for us.
14927   if (!FullscreenElementReadyCheck(*aRequest)) {
14928     return;
14929   }
14930 
14931   PendingFullscreenChangeList::Add(std::move(aRequest));
14932   // If we are not the top level process, dispatch an event to make
14933   // our parent process go fullscreen first.
14934   nsContentUtils::DispatchEventOnlyToChrome(
14935       this, ToSupports(this), u"MozDOMFullscreen:Request"_ns, CanBubble::eYes,
14936       Cancelable::eNo, /* DefaultAction */ nullptr);
14937 }
14938 
RequestFullscreenInParentProcess(UniquePtr<FullscreenRequest> aRequest,bool applyFullScreenDirectly)14939 void Document::RequestFullscreenInParentProcess(
14940     UniquePtr<FullscreenRequest> aRequest, bool applyFullScreenDirectly) {
14941   MOZ_ASSERT(XRE_IsParentProcess());
14942   nsCOMPtr<nsPIDOMWindowOuter> rootWin = GetRootWindow(this);
14943   if (!rootWin) {
14944     aRequest->MayRejectPromise("No active window");
14945     return;
14946   }
14947 
14948   if (applyFullScreenDirectly || ShouldApplyFullscreenDirectly(this, rootWin)) {
14949     ApplyFullscreen(std::move(aRequest));
14950     return;
14951   }
14952 
14953   if (!CheckFullscreenAllowedElementType(aRequest->Element())) {
14954     aRequest->Reject("FullscreenDeniedNotHTMLSVGOrMathML");
14955     return;
14956   }
14957 
14958   // We don't need to check element ready before this point, because
14959   // if we called ApplyFullscreen, it would check that for us.
14960   if (!FullscreenElementReadyCheck(*aRequest)) {
14961     return;
14962   }
14963 
14964   PendingFullscreenChangeList::Add(std::move(aRequest));
14965   // Make the window fullscreen.
14966   rootWin->SetFullscreenInternal(FullscreenReason::ForFullscreenAPI, true);
14967 }
14968 
14969 /* static */
HandlePendingFullscreenRequests(Document * aDoc)14970 bool Document::HandlePendingFullscreenRequests(Document* aDoc) {
14971   bool handled = false;
14972   PendingFullscreenChangeList::Iterator<FullscreenRequest> iter(
14973       aDoc, PendingFullscreenChangeList::eDocumentsWithSameRoot);
14974   while (!iter.AtEnd()) {
14975     UniquePtr<FullscreenRequest> request = iter.TakeAndNext();
14976     Document* doc = request->Document();
14977     if (doc->ApplyFullscreen(std::move(request))) {
14978       handled = true;
14979     }
14980   }
14981   return handled;
14982 }
14983 
ClearPendingFullscreenRequests(Document * aDoc)14984 static void ClearPendingFullscreenRequests(Document* aDoc) {
14985   PendingFullscreenChangeList::Iterator<FullscreenRequest> iter(
14986       aDoc, PendingFullscreenChangeList::eInclusiveDescendants);
14987   while (!iter.AtEnd()) {
14988     UniquePtr<FullscreenRequest> request = iter.TakeAndNext();
14989     request->MayRejectPromise("Fullscreen request aborted");
14990   }
14991 }
14992 
ApplyFullscreen(UniquePtr<FullscreenRequest> aRequest)14993 bool Document::ApplyFullscreen(UniquePtr<FullscreenRequest> aRequest) {
14994   if (!FullscreenElementReadyCheck(*aRequest)) {
14995     return false;
14996   }
14997 
14998   // Stash a reference to any existing fullscreen doc, we'll use this later
14999   // to detect if the origin which is fullscreen has changed.
15000   nsCOMPtr<Document> previousFullscreenDoc = GetFullscreenLeaf(this);
15001 
15002   // Stores a list of documents which we must dispatch "fullscreenchange"
15003   // too. We're required by the spec to dispatch the events in root-to-leaf
15004   // order, but we traverse the doctree in a leaf-to-root order, so we save
15005   // references to the documents we must dispatch to so that we get the order
15006   // as specified.
15007   AutoTArray<Document*, 8> changed;
15008 
15009   // Remember the root document, so that if a fullscreen document is hidden
15010   // we can reset fullscreen state in the remaining visible fullscreen
15011   // documents.
15012   Document* fullScreenRootDoc =
15013       nsContentUtils::GetInProcessSubtreeRootDocument(this);
15014 
15015   // If a document is already in fullscreen, then unlock the mouse pointer
15016   // before setting a new document to fullscreen
15017   PointerLockManager::Unlock();
15018 
15019   // Set the fullscreen element. This sets the fullscreen style on the
15020   // element, and the fullscreen-ancestor styles on ancestors of the element
15021   // in this document.
15022   Element* elem = aRequest->Element();
15023   SetFullscreenElement(elem);
15024   // Set the iframe fullscreen flag.
15025   if (auto* iframe = HTMLIFrameElement::FromNode(elem)) {
15026     iframe->SetFullscreenFlag(true);
15027   }
15028   changed.AppendElement(this);
15029 
15030   // Propagate up the document hierarchy, setting the fullscreen element as
15031   // the element's container in ancestor documents. This also sets the
15032   // appropriate css styles as well. Note we don't propagate down the
15033   // document hierarchy, the fullscreen element (or its container) is not
15034   // visible there. Stop when we reach the root document.
15035   Document* child = this;
15036   while (true) {
15037     child->SetFullscreenRoot(fullScreenRootDoc);
15038 
15039     // When entering fullscreen, reset the RCD's resolution to the intrinsic
15040     // resolution, otherwise the fullscreen content could be sized larger than
15041     // the screen (since fullscreen is implemented using position:fixed and
15042     // fixed elements are sized to the layout viewport).
15043     // This also ensures that things like video controls aren't zoomed in
15044     // when in fullscreen mode.
15045     if (PresShell* presShell = child->GetPresShell()) {
15046       if (RefPtr<MobileViewportManager> manager =
15047               presShell->GetMobileViewportManager()) {
15048         // Save the previous resolution so it can be restored.
15049         child->mSavedResolution = presShell->GetResolution();
15050         presShell->SetResolutionAndScaleTo(
15051             manager->ComputeIntrinsicResolution(),
15052             ResolutionChangeOrigin::MainThreadRestore);
15053       }
15054     }
15055 
15056     NS_ASSERTION(child->GetFullscreenRoot() == fullScreenRootDoc,
15057                  "Fullscreen root should be set!");
15058     if (child == fullScreenRootDoc) {
15059       break;
15060     }
15061 
15062     Element* element = child->GetEmbedderElement();
15063     if (!element) {
15064       // We've reached the root.No more changes need to be made
15065       // to the top layer stacks of documents further up the tree.
15066       break;
15067     }
15068 
15069     Document* parent = child->GetInProcessParentDocument();
15070     parent->SetFullscreenElement(element);
15071     changed.AppendElement(parent);
15072     child = parent;
15073   }
15074 
15075   FullscreenRoots::Add(this);
15076 
15077   // If it is the first entry of the fullscreen, trigger an event so
15078   // that the UI can response to this change, e.g. hide chrome, or
15079   // notifying parent process to enter fullscreen. Note that chrome
15080   // code may also want to listen to MozDOMFullscreen:NewOrigin event
15081   // to pop up warning UI.
15082   if (!previousFullscreenDoc) {
15083     nsContentUtils::DispatchEventOnlyToChrome(
15084         this, ToSupports(elem), u"MozDOMFullscreen:Entered"_ns, CanBubble::eYes,
15085         Cancelable::eNo, /* DefaultAction */ nullptr);
15086   }
15087 
15088   // The origin which is fullscreen gets changed. Trigger an event so
15089   // that the chrome knows to pop up a warning UI. Note that
15090   // previousFullscreenDoc == nullptr upon first entry, so we always
15091   // take this path on the first entry. Also note that, in a multi-
15092   // process browser, the code in content process is responsible for
15093   // sending message with the origin to its parent, and the parent
15094   // shouldn't rely on this event itself.
15095   if (aRequest->mShouldNotifyNewOrigin &&
15096       !nsContentUtils::HaveEqualPrincipals(previousFullscreenDoc, this)) {
15097     DispatchFullscreenNewOriginEvent(this);
15098   }
15099 
15100   // Dispatch "fullscreenchange" events. Note that the loop order is
15101   // reversed so that events are dispatched in the tree order as
15102   // indicated in the spec.
15103   for (Document* d : Reversed(changed)) {
15104     DispatchFullscreenChange(*d, d->GetUnretargetedFullScreenElement());
15105   }
15106   aRequest->MayResolvePromise();
15107   return true;
15108 }
15109 
ClearOrientationPendingPromise()15110 void Document::ClearOrientationPendingPromise() {
15111   mOrientationPendingPromise = nullptr;
15112 }
15113 
SetOrientationPendingPromise(Promise * aPromise)15114 bool Document::SetOrientationPendingPromise(Promise* aPromise) {
15115   if (mIsGoingAway) {
15116     return false;
15117   }
15118 
15119   mOrientationPendingPromise = aPromise;
15120   return true;
15121 }
15122 
UpdateVisibilityState(DispatchVisibilityChange aDispatchEvent)15123 void Document::UpdateVisibilityState(DispatchVisibilityChange aDispatchEvent) {
15124   dom::VisibilityState oldState = mVisibilityState;
15125   mVisibilityState = ComputeVisibilityState();
15126   if (oldState != mVisibilityState) {
15127     if (aDispatchEvent == DispatchVisibilityChange::Yes) {
15128       nsContentUtils::DispatchTrustedEvent(this, ToSupports(this),
15129                                            u"visibilitychange"_ns,
15130                                            CanBubble::eYes, Cancelable::eNo);
15131     }
15132     NotifyActivityChanged();
15133     if (mVisibilityState == dom::VisibilityState::Visible) {
15134       MaybeActiveMediaComponents();
15135     }
15136 
15137     bool visible = !Hidden();
15138     for (auto* listener : mWorkerListeners) {
15139       listener->OnVisible(visible);
15140     }
15141   }
15142 }
15143 
AddWorkerDocumentListener(WorkerDocumentListener * aListener)15144 void Document::AddWorkerDocumentListener(WorkerDocumentListener* aListener) {
15145   mWorkerListeners.Insert(aListener);
15146   aListener->OnVisible(!Hidden());
15147 }
15148 
RemoveWorkerDocumentListener(WorkerDocumentListener * aListener)15149 void Document::RemoveWorkerDocumentListener(WorkerDocumentListener* aListener) {
15150   mWorkerListeners.Remove(aListener);
15151 }
15152 
ComputeVisibilityState() const15153 VisibilityState Document::ComputeVisibilityState() const {
15154   // We have to check a few pieces of information here:
15155   // 1)  Are we in bfcache (!IsVisible())?  If so, nothing else matters.
15156   // 2)  Do we have an outer window?  If not, we're hidden.  Note that we don't
15157   //     want to use GetWindow here because it does weird groveling for windows
15158   //     in some cases.
15159   // 3)  Is our outer window background?  If so, we're hidden.
15160   // Otherwise, we're visible.
15161   if (!IsVisible() || !mWindow || !mWindow->GetOuterWindow() ||
15162       mWindow->GetOuterWindow()->IsBackground()) {
15163     return dom::VisibilityState::Hidden;
15164   }
15165 
15166   return dom::VisibilityState::Visible;
15167 }
15168 
PostVisibilityUpdateEvent()15169 void Document::PostVisibilityUpdateEvent() {
15170   nsCOMPtr<nsIRunnable> event = NewRunnableMethod<DispatchVisibilityChange>(
15171       "Document::UpdateVisibilityState", this, &Document::UpdateVisibilityState,
15172       DispatchVisibilityChange::Yes);
15173   Dispatch(TaskCategory::Other, event.forget());
15174 }
15175 
MaybeActiveMediaComponents()15176 void Document::MaybeActiveMediaComponents() {
15177   auto* window = GetWindow();
15178   if (!window || !window->ShouldDelayMediaFromStart()) {
15179     return;
15180   }
15181   window->ActivateMediaComponents();
15182 }
15183 
DocAddSizeOfExcludingThis(nsWindowSizes & aWindowSizes) const15184 void Document::DocAddSizeOfExcludingThis(nsWindowSizes& aWindowSizes) const {
15185   nsINode::AddSizeOfExcludingThis(aWindowSizes,
15186                                   &aWindowSizes.mDOMSizes.mDOMOtherSize);
15187 
15188   for (nsIContent* kid = GetFirstChild(); kid; kid = kid->GetNextSibling()) {
15189     AddSizeOfNodeTree(*kid, aWindowSizes);
15190   }
15191 
15192   // IMPORTANT: for our ComputedValues measurements, we want to measure
15193   // ComputedValues accessible from DOM elements before ComputedValues not
15194   // accessible from DOM elements (i.e. accessible only from the frame tree).
15195   //
15196   // Therefore, the measurement of the Document superclass must happen after
15197   // the measurement of DOM nodes (above), because Document contains the
15198   // PresShell, which contains the frame tree.
15199   if (mPresShell) {
15200     mPresShell->AddSizeOfIncludingThis(aWindowSizes);
15201   }
15202 
15203   mStyleSet->AddSizeOfIncludingThis(aWindowSizes);
15204 
15205   aWindowSizes.mPropertyTablesSize +=
15206       mPropertyTable.SizeOfExcludingThis(aWindowSizes.mState.mMallocSizeOf);
15207 
15208   if (EventListenerManager* elm = GetExistingListenerManager()) {
15209     aWindowSizes.mDOMEventListenersCount += elm->ListenerCount();
15210   }
15211 
15212   if (mNodeInfoManager) {
15213     mNodeInfoManager->AddSizeOfIncludingThis(aWindowSizes);
15214   }
15215 
15216   aWindowSizes.mDOMSizes.mDOMMediaQueryLists +=
15217       mDOMMediaQueryLists.sizeOfExcludingThis(
15218           aWindowSizes.mState.mMallocSizeOf);
15219 
15220   for (const MediaQueryList* mql : mDOMMediaQueryLists) {
15221     aWindowSizes.mDOMSizes.mDOMMediaQueryLists +=
15222         mql->SizeOfExcludingThis(aWindowSizes.mState.mMallocSizeOf);
15223   }
15224 
15225   DocumentOrShadowRoot::AddSizeOfExcludingThis(aWindowSizes);
15226 
15227   for (auto& sheetArray : mAdditionalSheets) {
15228     AddSizeOfOwnedSheetArrayExcludingThis(aWindowSizes, sheetArray);
15229   }
15230   // Lumping in the loader with the style-sheets size is not ideal,
15231   // but most of the things in there are in fact stylesheets, so it
15232   // doesn't seem worthwhile to separate it out.
15233   // This can be null if we've already been unlinked.
15234   if (mCSSLoader) {
15235     aWindowSizes.mLayoutStyleSheetsSize +=
15236         mCSSLoader->SizeOfIncludingThis(aWindowSizes.mState.mMallocSizeOf);
15237   }
15238 
15239   if (mResizeObserverController) {
15240     mResizeObserverController->AddSizeOfIncludingThis(aWindowSizes);
15241   }
15242 
15243   aWindowSizes.mDOMSizes.mDOMOtherSize +=
15244       mAttrStyleSheet ? mAttrStyleSheet->DOMSizeOfIncludingThis(
15245                             aWindowSizes.mState.mMallocSizeOf)
15246                       : 0;
15247 
15248   aWindowSizes.mDOMSizes.mDOMOtherSize +=
15249       mStyledLinks.ShallowSizeOfExcludingThis(
15250           aWindowSizes.mState.mMallocSizeOf);
15251 
15252   // Measurement of the following members may be added later if DMD finds it
15253   // is worthwhile:
15254   // - mMidasCommandManager
15255   // - many!
15256 }
15257 
DocAddSizeOfIncludingThis(nsWindowSizes & aWindowSizes) const15258 void Document::DocAddSizeOfIncludingThis(nsWindowSizes& aWindowSizes) const {
15259   aWindowSizes.mDOMSizes.mDOMOtherSize +=
15260       aWindowSizes.mState.mMallocSizeOf(this);
15261   DocAddSizeOfExcludingThis(aWindowSizes);
15262 }
15263 
AddSizeOfExcludingThis(nsWindowSizes & aSizes,size_t * aNodeSize) const15264 void Document::AddSizeOfExcludingThis(nsWindowSizes& aSizes,
15265                                       size_t* aNodeSize) const {
15266   // This AddSizeOfExcludingThis() overrides the one from nsINode.  But
15267   // nsDocuments can only appear at the top of the DOM tree, and we use the
15268   // specialized DocAddSizeOfExcludingThis() in that case.  So this should never
15269   // be called.
15270   MOZ_CRASH();
15271 }
15272 
15273 /* static */
AddSizeOfNodeTree(nsINode & aNode,nsWindowSizes & aWindowSizes)15274 void Document::AddSizeOfNodeTree(nsINode& aNode, nsWindowSizes& aWindowSizes) {
15275   size_t nodeSize = 0;
15276   aNode.AddSizeOfIncludingThis(aWindowSizes, &nodeSize);
15277 
15278   // This is where we transfer the nodeSize obtained from
15279   // nsINode::AddSizeOfIncludingThis() to a value in nsWindowSizes.
15280   switch (aNode.NodeType()) {
15281     case nsINode::ELEMENT_NODE:
15282       aWindowSizes.mDOMSizes.mDOMElementNodesSize += nodeSize;
15283       break;
15284     case nsINode::TEXT_NODE:
15285       aWindowSizes.mDOMSizes.mDOMTextNodesSize += nodeSize;
15286       break;
15287     case nsINode::CDATA_SECTION_NODE:
15288       aWindowSizes.mDOMSizes.mDOMCDATANodesSize += nodeSize;
15289       break;
15290     case nsINode::COMMENT_NODE:
15291       aWindowSizes.mDOMSizes.mDOMCommentNodesSize += nodeSize;
15292       break;
15293     default:
15294       aWindowSizes.mDOMSizes.mDOMOtherSize += nodeSize;
15295       break;
15296   }
15297 
15298   if (EventListenerManager* elm = aNode.GetExistingListenerManager()) {
15299     aWindowSizes.mDOMEventListenersCount += elm->ListenerCount();
15300   }
15301 
15302   if (aNode.IsContent()) {
15303     nsTArray<nsIContent*> anonKids;
15304     nsContentUtils::AppendNativeAnonymousChildren(aNode.AsContent(), anonKids,
15305                                                   nsIContent::eAllChildren);
15306     for (nsIContent* anonKid : anonKids) {
15307       AddSizeOfNodeTree(*anonKid, aWindowSizes);
15308     }
15309 
15310     if (auto* element = Element::FromNode(aNode)) {
15311       if (ShadowRoot* shadow = element->GetShadowRoot()) {
15312         AddSizeOfNodeTree(*shadow, aWindowSizes);
15313       }
15314     }
15315   }
15316 
15317   // NOTE(emilio): If you feel smart and want to change this function to use
15318   // GetNextNode(), think twice, since you'd need to handle <xbl:content> in a
15319   // sane way, and kids of <content> won't point to the parent, so we'd never
15320   // find the root node where we should stop at.
15321   for (nsIContent* kid = aNode.GetFirstChild(); kid;
15322        kid = kid->GetNextSibling()) {
15323     AddSizeOfNodeTree(*kid, aWindowSizes);
15324   }
15325 }
15326 
Constructor(const GlobalObject & aGlobal,ErrorResult & rv)15327 already_AddRefed<Document> Document::Constructor(const GlobalObject& aGlobal,
15328                                                  ErrorResult& rv) {
15329   nsCOMPtr<nsIScriptGlobalObject> global =
15330       do_QueryInterface(aGlobal.GetAsSupports());
15331   if (!global) {
15332     rv.Throw(NS_ERROR_UNEXPECTED);
15333     return nullptr;
15334   }
15335 
15336   nsCOMPtr<nsIScriptObjectPrincipal> prin =
15337       do_QueryInterface(aGlobal.GetAsSupports());
15338   if (!prin) {
15339     rv.Throw(NS_ERROR_UNEXPECTED);
15340     return nullptr;
15341   }
15342 
15343   nsCOMPtr<nsIURI> uri;
15344   NS_NewURI(getter_AddRefs(uri), "about:blank");
15345   if (!uri) {
15346     rv.Throw(NS_ERROR_OUT_OF_MEMORY);
15347     return nullptr;
15348   }
15349 
15350   nsCOMPtr<Document> doc;
15351   nsresult res = NS_NewDOMDocument(getter_AddRefs(doc), VoidString(), u""_ns,
15352                                    nullptr, uri, uri, prin->GetPrincipal(),
15353                                    true, global, DocumentFlavorPlain);
15354   if (NS_FAILED(res)) {
15355     rv.Throw(res);
15356     return nullptr;
15357   }
15358 
15359   doc->SetReadyStateInternal(Document::READYSTATE_COMPLETE);
15360 
15361   return doc.forget();
15362 }
15363 
CreateExpression(const nsAString & aExpression,XPathNSResolver * aResolver,ErrorResult & rv)15364 XPathExpression* Document::CreateExpression(const nsAString& aExpression,
15365                                             XPathNSResolver* aResolver,
15366                                             ErrorResult& rv) {
15367   return XPathEvaluator()->CreateExpression(aExpression, aResolver, rv);
15368 }
15369 
CreateNSResolver(nsINode & aNodeResolver)15370 nsINode* Document::CreateNSResolver(nsINode& aNodeResolver) {
15371   return XPathEvaluator()->CreateNSResolver(aNodeResolver);
15372 }
15373 
Evaluate(JSContext * aCx,const nsAString & aExpression,nsINode & aContextNode,XPathNSResolver * aResolver,uint16_t aType,JS::Handle<JSObject * > aResult,ErrorResult & rv)15374 already_AddRefed<XPathResult> Document::Evaluate(
15375     JSContext* aCx, const nsAString& aExpression, nsINode& aContextNode,
15376     XPathNSResolver* aResolver, uint16_t aType, JS::Handle<JSObject*> aResult,
15377     ErrorResult& rv) {
15378   return XPathEvaluator()->Evaluate(aCx, aExpression, aContextNode, aResolver,
15379                                     aType, aResult, rv);
15380 }
15381 
GetAppWindowIfToplevelChrome() const15382 already_AddRefed<nsIAppWindow> Document::GetAppWindowIfToplevelChrome() const {
15383   nsCOMPtr<nsIDocShellTreeItem> item = GetDocShell();
15384   if (!item) {
15385     return nullptr;
15386   }
15387   nsCOMPtr<nsIDocShellTreeOwner> owner;
15388   item->GetTreeOwner(getter_AddRefs(owner));
15389   nsCOMPtr<nsIAppWindow> appWin = do_GetInterface(owner);
15390   if (!appWin) {
15391     return nullptr;
15392   }
15393   nsCOMPtr<nsIDocShell> appWinShell;
15394   appWin->GetDocShell(getter_AddRefs(appWinShell));
15395   if (!SameCOMIdentity(appWinShell, item)) {
15396     return nullptr;
15397   }
15398   return appWin.forget();
15399 }
15400 
GetTopLevelWindowContext() const15401 WindowContext* Document::GetTopLevelWindowContext() const {
15402   WindowContext* windowContext = GetWindowContext();
15403   return windowContext ? windowContext->TopWindowContext() : nullptr;
15404 }
15405 
GetTopLevelContentDocumentIfSameProcess()15406 Document* Document::GetTopLevelContentDocumentIfSameProcess() {
15407   Document* parent;
15408 
15409   if (!mLoadedAsData) {
15410     parent = this;
15411   } else {
15412     nsCOMPtr<nsPIDOMWindowInner> window = do_QueryInterface(GetScopeObject());
15413     if (!window) {
15414       return nullptr;
15415     }
15416 
15417     parent = window->GetExtantDoc();
15418     if (!parent) {
15419       return nullptr;
15420     }
15421   }
15422 
15423   do {
15424     if (parent->IsTopLevelContentDocument()) {
15425       break;
15426     }
15427 
15428     // If we ever have a non-content parent before we hit a toplevel content
15429     // parent, then we're never going to find one.  Just bail.
15430     if (!parent->IsContentDocument()) {
15431       return nullptr;
15432     }
15433 
15434     parent = parent->GetInProcessParentDocument();
15435   } while (parent);
15436 
15437   return parent;
15438 }
15439 
GetTopLevelContentDocumentIfSameProcess() const15440 const Document* Document::GetTopLevelContentDocumentIfSameProcess() const {
15441   return const_cast<Document*>(this)->GetTopLevelContentDocumentIfSameProcess();
15442 }
15443 
PropagateImageUseCounters(Document * aReferencingDocument)15444 void Document::PropagateImageUseCounters(Document* aReferencingDocument) {
15445   MOZ_ASSERT(IsBeingUsedAsImage());
15446   MOZ_ASSERT(aReferencingDocument);
15447 
15448   if (!aReferencingDocument->mShouldReportUseCounters) {
15449     // No need to propagate use counters to a document that itself won't report
15450     // use counters.
15451     return;
15452   }
15453 
15454   MOZ_LOG(gUseCountersLog, LogLevel::Debug,
15455           ("PropagateImageUseCounters from %s to %s",
15456            nsContentUtils::TruncatedURLForDisplay(mDocumentURI).get(),
15457            nsContentUtils::TruncatedURLForDisplay(
15458                aReferencingDocument->mDocumentURI)
15459                .get()));
15460 
15461   if (aReferencingDocument->IsBeingUsedAsImage()) {
15462     NS_WARNING(
15463         "Page use counters from nested image documents may not "
15464         "propagate to the top-level document (bug 1657805)");
15465   }
15466 
15467   SetCssUseCounterBits();
15468   aReferencingDocument->mChildDocumentUseCounters |= mUseCounters;
15469   aReferencingDocument->mChildDocumentUseCounters |= mChildDocumentUseCounters;
15470 }
15471 
HasScriptsBlockedBySandbox()15472 bool Document::HasScriptsBlockedBySandbox() {
15473   return mSandboxFlags & SANDBOXED_SCRIPTS;
15474 }
15475 
15476 // Some use-counter sanity-checking.
15477 static_assert(size_t(eUseCounter_EndCSSProperties) -
15478                       size_t(eUseCounter_FirstCSSProperty) ==
15479                   size_t(eCSSProperty_COUNT_with_aliases),
15480               "We should have the right amount of CSS property use counters");
15481 static_assert(size_t(eUseCounter_Count) -
15482                       size_t(eUseCounter_FirstCountedUnknownProperty) ==
15483                   size_t(CountedUnknownProperty::Count),
15484               "We should have the right amount of counted unknown properties"
15485               " use counters");
15486 static_assert(size_t(eUseCounter_Count) * 2 ==
15487                   size_t(Telemetry::HistogramUseCounterCount),
15488               "There should be two histograms (document and page)"
15489               " for each use counter");
15490 
15491 #define ASSERT_CSS_COUNTER(id_, method_)                        \
15492   static_assert(size_t(eUseCounter_property_##method_) -        \
15493                         size_t(eUseCounter_FirstCSSProperty) == \
15494                     size_t(id_),                                \
15495                 "Order for CSS counters and CSS property id should match");
15496 #define CSS_PROP_PUBLIC_OR_PRIVATE(publicname_, privatename_) privatename_
15497 #define CSS_PROP_LONGHAND(name_, id_, method_, ...) \
15498   ASSERT_CSS_COUNTER(eCSSProperty_##id_, method_)
15499 #define CSS_PROP_SHORTHAND(name_, id_, method_, ...) \
15500   ASSERT_CSS_COUNTER(eCSSProperty_##id_, method_)
15501 #define CSS_PROP_ALIAS(name_, aliasid_, id_, method_, ...) \
15502   ASSERT_CSS_COUNTER(eCSSPropertyAlias_##aliasid_, method_)
15503 #include "mozilla/ServoCSSPropList.h"
15504 #undef CSS_PROP_ALIAS
15505 #undef CSS_PROP_SHORTHAND
15506 #undef CSS_PROP_LONGHAND
15507 #undef CSS_PROP_PUBLIC_OR_PRIVATE
15508 #undef ASSERT_CSS_COUNTER
15509 
SetCssUseCounterBits()15510 void Document::SetCssUseCounterBits() {
15511   if (StaticPrefs::layout_css_use_counters_enabled()) {
15512     for (size_t i = 0; i < eCSSProperty_COUNT_with_aliases; ++i) {
15513       auto id = nsCSSPropertyID(i);
15514       if (Servo_IsPropertyIdRecordedInUseCounter(mStyleUseCounters.get(), id)) {
15515         SetUseCounter(nsCSSProps::UseCounterFor(id));
15516       }
15517     }
15518   }
15519 
15520   if (StaticPrefs::layout_css_use_counters_unimplemented_enabled()) {
15521     for (size_t i = 0; i < size_t(CountedUnknownProperty::Count); ++i) {
15522       auto id = CountedUnknownProperty(i);
15523       if (Servo_IsUnknownPropertyRecordedInUseCounter(mStyleUseCounters.get(),
15524                                                       id)) {
15525         SetUseCounter(UseCounter(eUseCounter_FirstCountedUnknownProperty + i));
15526       }
15527     }
15528   }
15529 }
15530 
InitUseCounters()15531 void Document::InitUseCounters() {
15532   // We can be called more than once, e.g. when session history navigation shows
15533   // us a second time.
15534   if (mUseCountersInitialized) {
15535     return;
15536   }
15537   mUseCountersInitialized = true;
15538 
15539   static_assert(Telemetry::HistogramUseCounterCount > 0);
15540 
15541   if (!ShouldIncludeInTelemetry(/* aAllowExtensionURIs = */ true)) {
15542     return;
15543   }
15544 
15545   // Now we know for sure that we should report use counters from this document.
15546   mShouldReportUseCounters = true;
15547 
15548   WindowContext* top = GetWindowContextForPageUseCounters();
15549   if (!top) {
15550     // This is the case for SVG image documents.  They are not displayed in a
15551     // window, but we still do want to record document use counters for them.
15552     //
15553     // Page use counter propagation is handled in PropagateImageUseCounters,
15554     // so there is no need to use the cross-process machinery to send them.
15555     MOZ_LOG(gUseCountersLog, LogLevel::Debug,
15556             ("InitUseCounters for a non-displayed document [%s]",
15557              nsContentUtils::TruncatedURLForDisplay(mDocumentURI).get()));
15558     return;
15559   }
15560 
15561   RefPtr<WindowGlobalChild> wgc = GetWindowGlobalChild();
15562   if (!wgc) {
15563     return;
15564   }
15565 
15566   MOZ_LOG(gUseCountersLog, LogLevel::Debug,
15567           ("InitUseCounters for a displayed document: %" PRIu64 " -> %" PRIu64
15568            " [from %s]",
15569            wgc->InnerWindowId(), top->Id(),
15570            nsContentUtils::TruncatedURLForDisplay(mDocumentURI).get()));
15571 
15572   // Inform the parent process that we will send it page use counters later on.
15573   wgc->SendExpectPageUseCounters(top);
15574   mShouldSendPageUseCounters = true;
15575 }
15576 
15577 // We keep separate counts for individual documents and top-level
15578 // pages to more accurately track how many web pages might break if
15579 // certain features were removed.  Consider the case of a single
15580 // HTML document with several SVG images and/or iframes with
15581 // sub-documents of their own.  If we maintained a single set of use
15582 // counters and all the sub-documents use a particular feature, then
15583 // telemetry would indicate that we would be breaking N documents if
15584 // that feature were removed.  Whereas with a document/top-level
15585 // page split, we can see that N documents would be affected, but
15586 // only a single web page would be affected.
15587 //
15588 // The difference between the values of these two histograms and the
15589 // related use counters below tell us how many pages did *not* use
15590 // the feature in question.  For instance, if we see that a given
15591 // session has destroyed 30 content documents, but a particular use
15592 // counter shows only a count of 5, we can infer that the use
15593 // counter was *not* used in 25 of those 30 documents.
15594 //
15595 // We do things this way, rather than accumulating a boolean flag
15596 // for each use counter, to avoid sending histograms for features
15597 // that don't get widely used.  Doing things in this fashion means
15598 // smaller telemetry payloads and faster processing on the server
15599 // side.
ReportDocumentUseCounters()15600 void Document::ReportDocumentUseCounters() {
15601   if (!mShouldReportUseCounters || mReportedDocumentUseCounters) {
15602     return;
15603   }
15604 
15605   mReportedDocumentUseCounters = true;
15606 
15607   // Note that a document is being destroyed.  See the comment above for how
15608   // use counter histograms are interpreted relative to this measurement.
15609   // TOP_LEVEL_CONTENT_DOCUMENTS_DESTROYED is recorded in
15610   // WindowGlobalParent::FinishAccumulatingPageUseCounters.
15611   Telemetry::Accumulate(Telemetry::CONTENT_DOCUMENTS_DESTROYED, 1);
15612 
15613   // Ask all of our resource documents to report their own document use
15614   // counters.
15615   EnumerateExternalResources([](Document& aDoc) {
15616     aDoc.ReportDocumentUseCounters();
15617     return CallState::Continue;
15618   });
15619 
15620   // Copy StyleUseCounters into our document use counters.
15621   SetCssUseCounterBits();
15622 
15623   // Report our per-document use counters.
15624   MOZ_LOG(gUseCountersLog, LogLevel::Debug,
15625           ("Reporting document use counters [%s]",
15626            nsContentUtils::TruncatedURLForDisplay(GetDocumentURI()).get()));
15627   for (int32_t c = 0; c < eUseCounter_Count; ++c) {
15628     auto uc = static_cast<UseCounter>(c);
15629     if (!mUseCounters[uc]) {
15630       continue;
15631     }
15632 
15633     auto id = static_cast<Telemetry::HistogramID>(
15634         Telemetry::HistogramFirstUseCounter + uc * 2);
15635     MOZ_LOG(gUseCountersLog, LogLevel::Debug,
15636             (" > %s\n", Telemetry::GetHistogramName(id)));
15637     Telemetry::Accumulate(id, 1);
15638   }
15639 
15640   ReportDocumentLazyLoadCounters();
15641 }
15642 
ReportDocumentLazyLoadCounters()15643 void Document::ReportDocumentLazyLoadCounters() {
15644   if (!mLazyLoadImageCount) {
15645     return;
15646   }
15647   Telemetry::Accumulate(Telemetry::LAZYLOAD_IMAGE_TOTAL, mLazyLoadImageCount);
15648   Telemetry::Accumulate(Telemetry::LAZYLOAD_IMAGE_STARTED,
15649                         mLazyLoadImageStarted);
15650   Telemetry::Accumulate(Telemetry::LAZYLOAD_IMAGE_NOT_VIEWPORT,
15651                         mLazyLoadImageStarted -
15652                             mLazyLoadImageReachViewportLoading -
15653                             mLazyLoadImageReachViewportLoaded);
15654   Telemetry::Accumulate(Telemetry::LAZYLOAD_IMAGE_VIEWPORT_LOADING,
15655                         mLazyLoadImageReachViewportLoading);
15656   Telemetry::Accumulate(Telemetry::LAZYLOAD_IMAGE_VIEWPORT_LOADED,
15657                         mLazyLoadImageReachViewportLoaded);
15658 }
15659 
SendPageUseCounters()15660 void Document::SendPageUseCounters() {
15661   if (!mShouldReportUseCounters || !mShouldSendPageUseCounters) {
15662     return;
15663   }
15664 
15665   // Ask all of our resource documents to send their own document use
15666   // counters to the parent process to be counted as page use counters.
15667   EnumerateExternalResources([](Document& aDoc) {
15668     aDoc.SendPageUseCounters();
15669     return CallState::Continue;
15670   });
15671 
15672   // Send our use counters to the parent process to accumulate them towards the
15673   // page use counters for the top-level document.
15674   //
15675   // We take our own document use counters (those in mUseCounters) and any child
15676   // document use counters (those in mChildDocumentUseCounters) that have been
15677   // explicitly propagated up to us, which includes resource documents, static
15678   // clones, and SVG images.
15679   RefPtr<WindowGlobalChild> wgc = GetWindowGlobalChild();
15680   if (!wgc) {
15681     MOZ_ASSERT_UNREACHABLE(
15682         "SendPageUseCounters should be called while we still have access "
15683         "to our WindowContext");
15684     MOZ_LOG(gUseCountersLog, LogLevel::Debug,
15685             (" > too late to send page use counters"));
15686     return;
15687   }
15688 
15689   MOZ_LOG(gUseCountersLog, LogLevel::Debug,
15690           ("Sending page use counters: from WindowContext %" PRIu64 " [%s]",
15691            wgc->WindowContext()->Id(),
15692            nsContentUtils::TruncatedURLForDisplay(GetDocumentURI()).get()));
15693 
15694   // Copy StyleUseCounters into our document use counters.
15695   SetCssUseCounterBits();
15696 
15697   UseCounters counters = mUseCounters | mChildDocumentUseCounters;
15698   wgc->SendAccumulatePageUseCounters(counters);
15699 }
15700 
GetWindowContextForPageUseCounters() const15701 WindowContext* Document::GetWindowContextForPageUseCounters() const {
15702   if (mDisplayDocument) {
15703     // If we are a resource document, then go through it to find the
15704     // top-level document.
15705     return mDisplayDocument->GetWindowContextForPageUseCounters();
15706   }
15707 
15708   if (mOriginalDocument) {
15709     // For static clones (print preview documents), contribute page use counters
15710     // towards the original document.
15711     return mOriginalDocument->GetWindowContextForPageUseCounters();
15712   }
15713 
15714   WindowContext* wc = GetTopLevelWindowContext();
15715   if (!wc || !wc->GetBrowsingContext()->IsContent()) {
15716     return nullptr;
15717   }
15718 
15719   return wc;
15720 }
15721 
UpdateIntersectionObservations(TimeStamp aNowTime)15722 void Document::UpdateIntersectionObservations(TimeStamp aNowTime) {
15723   if (mIntersectionObservers.IsEmpty()) {
15724     return;
15725   }
15726 
15727   DOMHighResTimeStamp time = 0;
15728   if (nsPIDOMWindowInner* win = GetInnerWindow()) {
15729     if (Performance* perf = win->GetPerformance()) {
15730       time = perf->TimeStampToDOMHighResForRendering(aNowTime);
15731     }
15732   }
15733 
15734   const auto observers = ToTArray<nsTArray<RefPtr<DOMIntersectionObserver>>>(
15735       mIntersectionObservers);
15736   for (const auto& observer : observers) {
15737     if (observer) {
15738       observer->Update(this, time);
15739     }
15740   }
15741 }
15742 
ScheduleIntersectionObserverNotification()15743 void Document::ScheduleIntersectionObserverNotification() {
15744   if (mIntersectionObservers.IsEmpty()) {
15745     return;
15746   }
15747   MOZ_RELEASE_ASSERT(NS_IsMainThread());
15748   nsCOMPtr<nsIRunnable> notification =
15749       NewRunnableMethod("Document::NotifyIntersectionObservers", this,
15750                         &Document::NotifyIntersectionObservers);
15751   Dispatch(TaskCategory::Other, notification.forget());
15752 }
15753 
NotifyIntersectionObservers()15754 void Document::NotifyIntersectionObservers() {
15755   const auto observers = ToTArray<nsTArray<RefPtr<DOMIntersectionObserver>>>(
15756       mIntersectionObservers);
15757   for (const auto& observer : observers) {
15758     if (observer) {
15759       // MOZ_KnownLive because the 'observers' array guarantees to keep it
15760       // alive.
15761       MOZ_KnownLive(observer)->Notify();
15762     }
15763   }
15764 }
15765 
EnsureLazyLoadImageObserver()15766 DOMIntersectionObserver& Document::EnsureLazyLoadImageObserver() {
15767   if (!mLazyLoadImageObserver) {
15768     mLazyLoadImageObserver =
15769         DOMIntersectionObserver::CreateLazyLoadObserver(*this);
15770   }
15771   return *mLazyLoadImageObserver;
15772 }
15773 
EnsureLazyLoadImageObserverViewport()15774 DOMIntersectionObserver& Document::EnsureLazyLoadImageObserverViewport() {
15775   if (!mLazyLoadImageObserverViewport) {
15776     mLazyLoadImageObserverViewport =
15777         DOMIntersectionObserver::CreateLazyLoadObserverViewport(*this);
15778   }
15779   return *mLazyLoadImageObserverViewport;
15780 }
15781 
IncLazyLoadImageReachViewport(bool aLoading)15782 void Document::IncLazyLoadImageReachViewport(bool aLoading) {
15783   if (aLoading) {
15784     ++mLazyLoadImageReachViewportLoading;
15785   } else {
15786     ++mLazyLoadImageReachViewportLoaded;
15787   }
15788 }
15789 
NotifyLayerManagerRecreated()15790 void Document::NotifyLayerManagerRecreated() {
15791   NotifyActivityChanged();
15792   EnumerateSubDocuments([](Document& aSubDoc) {
15793     aSubDoc.NotifyLayerManagerRecreated();
15794     return CallState::Continue;
15795   });
15796 }
15797 
XPathEvaluator()15798 XPathEvaluator* Document::XPathEvaluator() {
15799   if (!mXPathEvaluator) {
15800     mXPathEvaluator.reset(new dom::XPathEvaluator(this));
15801   }
15802   return mXPathEvaluator.get();
15803 }
15804 
GetCachedEncoder()15805 already_AddRefed<nsIDocumentEncoder> Document::GetCachedEncoder() {
15806   return mCachedEncoder.forget();
15807 }
15808 
SetCachedEncoder(already_AddRefed<nsIDocumentEncoder> aEncoder)15809 void Document::SetCachedEncoder(already_AddRefed<nsIDocumentEncoder> aEncoder) {
15810   mCachedEncoder = aEncoder;
15811 }
15812 
GetLoadContext() const15813 nsILoadContext* Document::GetLoadContext() const { return mDocumentContainer; }
15814 
GetDocShell() const15815 nsIDocShell* Document::GetDocShell() const { return mDocumentContainer; }
15816 
SetStateObject(nsIStructuredCloneContainer * scContainer)15817 void Document::SetStateObject(nsIStructuredCloneContainer* scContainer) {
15818   mStateObjectContainer = scContainer;
15819   mStateObjectCached = nullptr;
15820 }
15821 
GetDocumentLWTheme() const15822 Document::DocumentTheme Document::GetDocumentLWTheme() const {
15823   if (!NodePrincipal()->IsSystemPrincipal()) {
15824     return DocumentTheme::None;
15825   }
15826 
15827   auto theme = DocumentTheme::None;  // No lightweight theme by default
15828   Element* element = GetRootElement();
15829   if (element && element->AttrValueIs(kNameSpaceID_None, nsGkAtoms::lwtheme,
15830                                       nsGkAtoms::_true, eCaseMatters)) {
15831     theme = DocumentTheme::Neutral;
15832     nsAutoString lwTheme;
15833     element->GetAttr(kNameSpaceID_None, nsGkAtoms::lwthemetextcolor, lwTheme);
15834     if (lwTheme.EqualsLiteral("dark")) {
15835       theme = DocumentTheme::Dark;
15836     } else if (lwTheme.EqualsLiteral("bright")) {
15837       theme = DocumentTheme::Bright;
15838     }
15839   }
15840 
15841   return theme;
15842 }
15843 
CreateHTMLElement(nsAtom * aTag)15844 already_AddRefed<Element> Document::CreateHTMLElement(nsAtom* aTag) {
15845   RefPtr<mozilla::dom::NodeInfo> nodeInfo;
15846   nodeInfo = mNodeInfoManager->GetNodeInfo(aTag, nullptr, kNameSpaceID_XHTML,
15847                                            ELEMENT_NODE);
15848   MOZ_ASSERT(nodeInfo, "GetNodeInfo should never fail");
15849 
15850   nsCOMPtr<Element> element;
15851   DebugOnly<nsresult> rv =
15852       NS_NewHTMLElement(getter_AddRefs(element), nodeInfo.forget(),
15853                         mozilla::dom::NOT_FROM_PARSER);
15854 
15855   MOZ_ASSERT(NS_SUCCEEDED(rv), "NS_NewHTMLElement should never fail");
15856   return element.forget();
15857 }
15858 
SuppressBrowsingContext(BrowsingContext * aContext)15859 void AutoWalkBrowsingContextGroup::SuppressBrowsingContext(
15860     BrowsingContext* aContext) {
15861   aContext->PreOrderWalk([&](BrowsingContext* aBC) {
15862     if (nsCOMPtr<nsPIDOMWindowOuter> win = aBC->GetDOMWindow()) {
15863       if (RefPtr<Document> doc = win->GetExtantDoc()) {
15864         SuppressDocument(doc);
15865         mDocuments.AppendElement(doc);
15866       }
15867     }
15868   });
15869 }
15870 
SuppressBrowsingContextGroup(BrowsingContextGroup * aGroup)15871 void AutoWalkBrowsingContextGroup::SuppressBrowsingContextGroup(
15872     BrowsingContextGroup* aGroup) {
15873   for (const auto& bc : aGroup->Toplevels()) {
15874     SuppressBrowsingContext(bc);
15875   }
15876 }
15877 
nsAutoSyncOperation(Document * aDoc,SyncOperationBehavior aSyncBehavior)15878 nsAutoSyncOperation::nsAutoSyncOperation(Document* aDoc,
15879                                          SyncOperationBehavior aSyncBehavior)
15880     : mSyncBehavior(aSyncBehavior) {
15881   mMicroTaskLevel = 0;
15882   if (CycleCollectedJSContext* ccjs = CycleCollectedJSContext::Get()) {
15883     mMicroTaskLevel = ccjs->MicroTaskLevel();
15884     ccjs->SetMicroTaskLevel(0);
15885   }
15886   if (aDoc) {
15887     mBrowsingContext = aDoc->GetBrowsingContext();
15888     if (InputTaskManager::CanSuspendInputEvent()) {
15889       if (auto* bcg = aDoc->GetDocGroup()->GetBrowsingContextGroup()) {
15890         SuppressBrowsingContextGroup(bcg);
15891       }
15892     } else if (mBrowsingContext) {
15893       SuppressBrowsingContext(mBrowsingContext->Top());
15894     }
15895     if (mBrowsingContext &&
15896         mSyncBehavior == SyncOperationBehavior::eSuspendInput &&
15897         InputTaskManager::CanSuspendInputEvent()) {
15898       mBrowsingContext->Group()->IncInputEventSuspensionLevel();
15899     }
15900   }
15901 }
15902 
SuppressDocument(Document * aDoc)15903 void nsAutoSyncOperation::SuppressDocument(Document* aDoc) {
15904   if (nsCOMPtr<nsPIDOMWindowInner> win = aDoc->GetInnerWindow()) {
15905     win->TimeoutManager().BeginSyncOperation();
15906   }
15907   aDoc->SetIsInSyncOperation(true);
15908 }
15909 
UnsuppressDocument(Document * aDoc)15910 void nsAutoSyncOperation::UnsuppressDocument(Document* aDoc) {
15911   if (nsCOMPtr<nsPIDOMWindowInner> win = aDoc->GetInnerWindow()) {
15912     win->TimeoutManager().EndSyncOperation();
15913   }
15914   aDoc->SetIsInSyncOperation(false);
15915 }
15916 
~nsAutoSyncOperation()15917 nsAutoSyncOperation::~nsAutoSyncOperation() {
15918   UnsuppressDocuments();
15919   CycleCollectedJSContext* ccjs = CycleCollectedJSContext::Get();
15920   if (ccjs) {
15921     ccjs->SetMicroTaskLevel(mMicroTaskLevel);
15922   }
15923   if (mBrowsingContext &&
15924       mSyncBehavior == SyncOperationBehavior::eSuspendInput &&
15925       InputTaskManager::CanSuspendInputEvent()) {
15926     mBrowsingContext->Group()->DecInputEventSuspensionLevel();
15927   }
15928 }
15929 
SetIsInSyncOperation(bool aSync)15930 void Document::SetIsInSyncOperation(bool aSync) {
15931   if (CycleCollectedJSContext* ccjs = CycleCollectedJSContext::Get()) {
15932     ccjs->UpdateMicroTaskSuppressionGeneration();
15933   }
15934 
15935   if (aSync) {
15936     ++mInSyncOperationCount;
15937   } else {
15938     --mInSyncOperationCount;
15939   }
15940 }
15941 
GetUserFontSet()15942 gfxUserFontSet* Document::GetUserFontSet() {
15943   if (!mFontFaceSet) {
15944     return nullptr;
15945   }
15946 
15947   return mFontFaceSet->GetUserFontSet();
15948 }
15949 
FlushUserFontSet()15950 void Document::FlushUserFontSet() {
15951   if (!mFontFaceSetDirty) {
15952     return;
15953   }
15954 
15955   mFontFaceSetDirty = false;
15956 
15957   if (gfxPlatform::GetPlatform()->DownloadableFontsEnabled()) {
15958     nsTArray<nsFontFaceRuleContainer> rules;
15959     RefPtr<PresShell> presShell = GetPresShell();
15960     if (presShell) {
15961       MOZ_ASSERT(mStyleSetFilled);
15962       mStyleSet->AppendFontFaceRules(rules);
15963     }
15964 
15965     if (!mFontFaceSet && !rules.IsEmpty()) {
15966       nsCOMPtr<nsPIDOMWindowInner> window = do_QueryInterface(GetScopeObject());
15967       mFontFaceSet = new FontFaceSet(window, this);
15968     }
15969 
15970     bool changed = false;
15971     if (mFontFaceSet) {
15972       changed = mFontFaceSet->UpdateRules(rules);
15973     }
15974 
15975     // We need to enqueue a style change reflow (for later) to
15976     // reflect that we're modifying @font-face rules.  (However,
15977     // without a reflow, nothing will happen to start any downloads
15978     // that are needed.)
15979     if (changed && presShell) {
15980       if (nsPresContext* presContext = presShell->GetPresContext()) {
15981         presContext->UserFontSetUpdated();
15982       }
15983     }
15984   }
15985 }
15986 
MarkUserFontSetDirty()15987 void Document::MarkUserFontSetDirty() {
15988   if (mFontFaceSetDirty) {
15989     return;
15990   }
15991   mFontFaceSetDirty = true;
15992   if (PresShell* presShell = GetPresShell()) {
15993     presShell->EnsureStyleFlush();
15994   }
15995 }
15996 
Fonts()15997 FontFaceSet* Document::Fonts() {
15998   if (!mFontFaceSet) {
15999     nsCOMPtr<nsPIDOMWindowInner> window = do_QueryInterface(GetScopeObject());
16000     mFontFaceSet = new FontFaceSet(window, this);
16001     FlushUserFontSet();
16002   }
16003   return mFontFaceSet;
16004 }
16005 
ReportHasScrollLinkedEffect()16006 void Document::ReportHasScrollLinkedEffect() {
16007   if (mHasScrollLinkedEffect) {
16008     // We already did this once for this document, don't do it again.
16009     return;
16010   }
16011   mHasScrollLinkedEffect = true;
16012   nsContentUtils::ReportToConsole(
16013       nsIScriptError::warningFlag, "Async Pan/Zoom"_ns, this,
16014       nsContentUtils::eLAYOUT_PROPERTIES, "ScrollLinkedEffectFound3");
16015 }
16016 
SetSHEntryHasUserInteraction(bool aHasInteraction)16017 void Document::SetSHEntryHasUserInteraction(bool aHasInteraction) {
16018   if (RefPtr<WindowContext> topWc = GetTopLevelWindowContext()) {
16019     // Setting has user interction on a discarded browsing context has
16020     // no effect.
16021     Unused << topWc->SetSHEntryHasUserInteraction(aHasInteraction);
16022   }
16023 }
16024 
GetSHEntryHasUserInteraction()16025 bool Document::GetSHEntryHasUserInteraction() {
16026   if (RefPtr<WindowContext> topWc = GetTopLevelWindowContext()) {
16027     return topWc->GetSHEntryHasUserInteraction();
16028   }
16029   return false;
16030 }
16031 
SetUserHasInteracted()16032 void Document::SetUserHasInteracted() {
16033   MOZ_LOG(gUserInteractionPRLog, LogLevel::Debug,
16034           ("Document %p has been interacted by user.", this));
16035 
16036   // We maybe need to update the user-interaction permission.
16037   MaybeStoreUserInteractionAsPermission();
16038 
16039   // For purposes of reducing irrelevant session history entries on
16040   // the back button, we annotate entries with whether they had user
16041   // interaction. This is gated on its own flag on the WindowContext
16042   // (instead of mUserHasInteracted) to account for the fact that multiple
16043   // top-level SH entries can be associated with the same document.
16044   // Thus, whenever we create a new SH entry for this document,
16045   // this flag is reset.
16046   if (!GetSHEntryHasUserInteraction()) {
16047     nsIDocShell* docShell = this->GetDocShell();
16048     if (docShell) {
16049       nsCOMPtr<nsISHEntry> currentEntry;
16050       bool oshe;
16051       nsresult rv =
16052           docShell->GetCurrentSHEntry(getter_AddRefs(currentEntry), &oshe);
16053       if (!NS_WARN_IF(NS_FAILED(rv)) && currentEntry) {
16054         currentEntry->SetHasUserInteraction(true);
16055       }
16056     }
16057     SetSHEntryHasUserInteraction(true);
16058   }
16059 
16060   if (mUserHasInteracted) {
16061     return;
16062   }
16063 
16064   mUserHasInteracted = true;
16065 
16066   if (mChannel) {
16067     nsCOMPtr<nsILoadInfo> loadInfo = mChannel->LoadInfo();
16068     loadInfo->SetDocumentHasUserInteracted(true);
16069   }
16070   // Tell the parent process about user interaction
16071   if (auto* wgc = GetWindowGlobalChild()) {
16072     wgc->SendUpdateDocumentHasUserInteracted(true);
16073   }
16074 
16075   MaybeAllowStorageForOpenerAfterUserInteraction();
16076 }
16077 
GetBrowsingContext() const16078 BrowsingContext* Document::GetBrowsingContext() const {
16079   nsCOMPtr<nsIDocShell> docshell(mDocumentContainer);
16080   return docshell ? docshell->GetBrowsingContext() : nullptr;
16081 }
16082 
NotifyUserGestureActivation()16083 void Document::NotifyUserGestureActivation() {
16084   if (RefPtr<BrowsingContext> bc = GetBrowsingContext()) {
16085     bc->PreOrderWalk([&](BrowsingContext* aBC) {
16086       WindowContext* windowContext = aBC->GetCurrentWindowContext();
16087       if (!windowContext) {
16088         return;
16089       }
16090 
16091       nsIDocShell* docShell = aBC->GetDocShell();
16092       if (!docShell) {
16093         return;
16094       }
16095 
16096       Document* document = docShell->GetDocument();
16097       if (!document) {
16098         return;
16099       }
16100 
16101       // XXXedgar we probably could just check `IsInProcess()` after fission
16102       // enable.
16103       if (NodePrincipal()->Equals(document->NodePrincipal())) {
16104         windowContext->NotifyUserGestureActivation();
16105       }
16106     });
16107 
16108     for (bc = bc->GetParent(); bc; bc = bc->GetParent()) {
16109       if (WindowContext* windowContext = bc->GetCurrentWindowContext()) {
16110         windowContext->NotifyUserGestureActivation();
16111       }
16112     }
16113   }
16114 }
16115 
HasBeenUserGestureActivated()16116 bool Document::HasBeenUserGestureActivated() {
16117   RefPtr<WindowContext> wc = GetWindowContext();
16118   return wc && wc->HasBeenUserGestureActivated();
16119 }
16120 
ClearUserGestureActivation()16121 void Document::ClearUserGestureActivation() {
16122   if (RefPtr<BrowsingContext> bc = GetBrowsingContext()) {
16123     bc = bc->Top();
16124     bc->PreOrderWalk([&](BrowsingContext* aBC) {
16125       if (WindowContext* windowContext = aBC->GetCurrentWindowContext()) {
16126         windowContext->NotifyResetUserGestureActivation();
16127       }
16128     });
16129   }
16130 }
16131 
HasValidTransientUserGestureActivation() const16132 bool Document::HasValidTransientUserGestureActivation() const {
16133   RefPtr<WindowContext> wc = GetWindowContext();
16134   return wc && wc->HasValidTransientUserGestureActivation();
16135 }
16136 
ConsumeTransientUserGestureActivation()16137 bool Document::ConsumeTransientUserGestureActivation() {
16138   RefPtr<WindowContext> wc = GetWindowContext();
16139   return wc && wc->ConsumeTransientUserGestureActivation();
16140 }
16141 
IncLazyLoadImageCount()16142 void Document::IncLazyLoadImageCount() {
16143   if (!mLazyLoadImageCount) {
16144     if (WindowContext* wc = GetTopLevelWindowContext()) {
16145       if (!wc->HadLazyLoadImage()) {
16146         Unused << wc->SetHadLazyLoadImage(true);
16147       }
16148     }
16149   }
16150   ++mLazyLoadImageCount;
16151 }
16152 
SetDocTreeHadMedia()16153 void Document::SetDocTreeHadMedia() {
16154   RefPtr<WindowContext> topWc = GetTopLevelWindowContext();
16155   if (topWc && !topWc->IsDiscarded() && !topWc->GetDocTreeHadMedia()) {
16156     MOZ_ALWAYS_SUCCEEDS(topWc->SetDocTreeHadMedia(true));
16157   }
16158 }
16159 
AutoplayPolicy() const16160 DocumentAutoplayPolicy Document::AutoplayPolicy() const {
16161   return AutoplayPolicy::IsAllowedToPlay(*this);
16162 }
16163 
MaybeAllowStorageForOpenerAfterUserInteraction()16164 void Document::MaybeAllowStorageForOpenerAfterUserInteraction() {
16165   if (!CookieJarSettings()->GetRejectThirdPartyContexts()) {
16166     return;
16167   }
16168 
16169   // This will probably change for project fission, but currently this document
16170   // and the opener are on the same process. In the future, we should make this
16171   // part async.
16172   nsPIDOMWindowInner* inner = GetInnerWindow();
16173   if (NS_WARN_IF(!inner)) {
16174     return;
16175   }
16176 
16177   uint32_t cookieBehavior = CookieJarSettings()->GetCookieBehavior();
16178   if (cookieBehavior == nsICookieService::BEHAVIOR_REJECT_TRACKER ||
16179       cookieBehavior ==
16180           nsICookieService::BEHAVIOR_REJECT_TRACKER_AND_PARTITION_FOREIGN) {
16181     // We care about first-party tracking resources only.
16182     if (!nsContentUtils::IsFirstPartyTrackingResourceWindow(inner)) {
16183       return;
16184     }
16185   } else {
16186     MOZ_ASSERT(net::CookieJarSettings::IsRejectThirdPartyWithExceptions(
16187         cookieBehavior));
16188   }
16189 
16190   auto* outer = nsGlobalWindowOuter::Cast(inner->GetOuterWindow());
16191   if (NS_WARN_IF(!outer)) {
16192     return;
16193   }
16194 
16195   RefPtr<BrowsingContext> openerBC = outer->GetOpenerBrowsingContext();
16196   if (!openerBC) {
16197     // No opener.
16198     return;
16199   }
16200 
16201   // We want to ensure the following check works for both fission mode and
16202   // non-fission mode:
16203   // "If the opener is not a 3rd party and if this window is not a 3rd party
16204   // with respect to the opener, we should not continue."
16205   //
16206   // In non-fission mode, the opener and the opened window are in the same
16207   // process, we can use AntiTrackingUtils::IsThirdPartyWindow to do the check.
16208   // In fission mode, if this window is not a 3rd party with respect to the
16209   // opener, they must be in the same process, so we can still use
16210   // IsThirdPartyWindow(openerInner) to continue to check if the opener is a 3rd
16211   // party.
16212   if (openerBC->IsInProcess()) {
16213     nsCOMPtr<nsPIDOMWindowOuter> outerOpener = openerBC->GetDOMWindow();
16214     if (NS_WARN_IF(!outerOpener)) {
16215       return;
16216     }
16217 
16218     nsCOMPtr<nsPIDOMWindowInner> openerInner =
16219         outerOpener->GetCurrentInnerWindow();
16220     if (NS_WARN_IF(!openerInner)) {
16221       return;
16222     }
16223 
16224     RefPtr<Document> openerDocument = openerInner->GetExtantDoc();
16225     if (NS_WARN_IF(!openerDocument)) {
16226       return;
16227     }
16228 
16229     nsCOMPtr<nsIURI> openerURI = openerDocument->GetDocumentURI();
16230     if (NS_WARN_IF(!openerURI)) {
16231       return;
16232     }
16233 
16234     // If the opener is not a 3rd party and if this window is not
16235     // a 3rd party with respect to the opener, we should not continue.
16236     if (!AntiTrackingUtils::IsThirdPartyWindow(inner, openerURI) &&
16237         !AntiTrackingUtils::IsThirdPartyWindow(openerInner, nullptr)) {
16238       return;
16239     }
16240   }
16241 
16242   // We don't care when the asynchronous work finishes here.
16243   Unused << ContentBlocking::AllowAccessFor(
16244       NodePrincipal(), openerBC,
16245       ContentBlockingNotifier::eOpenerAfterUserInteraction);
16246 }
16247 
16248 namespace {
16249 
16250 // Documents can stay alive for days. We don't want to update the permission
16251 // value at any user-interaction, and, using a timer triggered any X seconds
16252 // should be good enough. 'X' is taken from
16253 // privacy.userInteraction.document.interval pref.
16254 //  We also want to store the user-interaction before shutting down, and, for
16255 //  this reason, this class implements nsIAsyncShutdownBlocker interface.
16256 class UserInteractionTimer final : public Runnable,
16257                                    public nsITimerCallback,
16258                                    public nsIAsyncShutdownBlocker {
16259  public:
16260   NS_DECL_ISUPPORTS_INHERITED
16261 
UserInteractionTimer(Document * aDocument)16262   explicit UserInteractionTimer(Document* aDocument)
16263       : Runnable("UserInteractionTimer"),
16264         mPrincipal(aDocument->NodePrincipal()),
16265         mDocument(do_GetWeakReference(aDocument)) {
16266     static int32_t userInteractionTimerId = 0;
16267     // Blocker names must be unique. Let's create it now because when needed,
16268     // the document could be already gone.
16269     mBlockerName.AppendPrintf("UserInteractionTimer %d for document %p",
16270                               ++userInteractionTimerId, aDocument);
16271   }
16272 
16273   // Runnable interface
16274 
16275   NS_IMETHOD
Run()16276   Run() override {
16277     uint32_t interval =
16278         StaticPrefs::privacy_userInteraction_document_interval();
16279     if (!interval) {
16280       return NS_OK;
16281     }
16282 
16283     RefPtr<UserInteractionTimer> self = this;
16284     auto raii =
16285         MakeScopeExit([self] { self->CancelTimerAndStoreUserInteraction(); });
16286 
16287     nsresult rv = NS_NewTimerWithCallback(
16288         getter_AddRefs(mTimer), this, interval * 1000, nsITimer::TYPE_ONE_SHOT);
16289     NS_ENSURE_SUCCESS(rv, NS_OK);
16290 
16291     nsCOMPtr<nsIAsyncShutdownClient> phase = GetShutdownPhase();
16292     NS_ENSURE_TRUE(!!phase, NS_OK);
16293 
16294     rv = phase->AddBlocker(this, NS_LITERAL_STRING_FROM_CSTRING(__FILE__),
16295                            __LINE__, u"UserInteractionTimer shutdown"_ns);
16296     NS_ENSURE_SUCCESS(rv, NS_OK);
16297 
16298     raii.release();
16299     return NS_OK;
16300   }
16301 
16302   // nsITimerCallback interface
16303 
16304   NS_IMETHOD
Notify(nsITimer * aTimer)16305   Notify(nsITimer* aTimer) override {
16306     StoreUserInteraction();
16307     return NS_OK;
16308   }
16309 
16310 #ifdef MOZ_COLLECTING_RUNNABLE_TELEMETRY
16311   using nsINamed::GetName;
16312 #endif
16313 
16314   // nsIAsyncShutdownBlocker interface
16315 
16316   NS_IMETHOD
GetName(nsAString & aName)16317   GetName(nsAString& aName) override {
16318     aName = mBlockerName;
16319     return NS_OK;
16320   }
16321 
16322   NS_IMETHOD
BlockShutdown(nsIAsyncShutdownClient * aClient)16323   BlockShutdown(nsIAsyncShutdownClient* aClient) override {
16324     CancelTimerAndStoreUserInteraction();
16325     return NS_OK;
16326   }
16327 
16328   NS_IMETHOD
GetState(nsIPropertyBag **)16329   GetState(nsIPropertyBag**) override { return NS_OK; }
16330 
16331  private:
16332   ~UserInteractionTimer() = default;
16333 
StoreUserInteraction()16334   void StoreUserInteraction() {
16335     // Remove the shutting down blocker
16336     nsCOMPtr<nsIAsyncShutdownClient> phase = GetShutdownPhase();
16337     if (phase) {
16338       phase->RemoveBlocker(this);
16339     }
16340 
16341     // If the document is not gone, let's reset its timer flag.
16342     nsCOMPtr<Document> document = do_QueryReferent(mDocument);
16343     if (document) {
16344       ContentBlockingUserInteraction::Observe(mPrincipal);
16345       document->ResetUserInteractionTimer();
16346     }
16347   }
16348 
CancelTimerAndStoreUserInteraction()16349   void CancelTimerAndStoreUserInteraction() {
16350     if (mTimer) {
16351       mTimer->Cancel();
16352       mTimer = nullptr;
16353     }
16354 
16355     StoreUserInteraction();
16356   }
16357 
GetShutdownPhase()16358   static already_AddRefed<nsIAsyncShutdownClient> GetShutdownPhase() {
16359     nsCOMPtr<nsIAsyncShutdownService> svc = services::GetAsyncShutdownService();
16360     NS_ENSURE_TRUE(!!svc, nullptr);
16361 
16362     nsCOMPtr<nsIAsyncShutdownClient> phase;
16363     nsresult rv = svc->GetXpcomWillShutdown(getter_AddRefs(phase));
16364     NS_ENSURE_SUCCESS(rv, nullptr);
16365 
16366     return phase.forget();
16367   }
16368 
16369   nsCOMPtr<nsIPrincipal> mPrincipal;
16370   nsWeakPtr mDocument;
16371 
16372   nsCOMPtr<nsITimer> mTimer;
16373 
16374   nsString mBlockerName;
16375 };
16376 
16377 NS_IMPL_ISUPPORTS_INHERITED(UserInteractionTimer, Runnable, nsITimerCallback,
16378                             nsIAsyncShutdownBlocker)
16379 
16380 }  // namespace
16381 
MaybeStoreUserInteractionAsPermission()16382 void Document::MaybeStoreUserInteractionAsPermission() {
16383   // We care about user-interaction stored only for top-level documents
16384   // and documents with access to the Storage Access API
16385   if (!IsTopLevelContentDocument()) {
16386     bool hasSA;
16387     nsresult rv = HasStorageAccessSync(hasSA);
16388     if (NS_FAILED(rv) || !hasSA) {
16389       return;
16390     }
16391   }
16392 
16393   if (!mUserHasInteracted) {
16394     // First interaction, let's store this info now.
16395     ContentBlockingUserInteraction::Observe(NodePrincipal());
16396     return;
16397   }
16398 
16399   if (mHasUserInteractionTimerScheduled) {
16400     return;
16401   }
16402 
16403   nsCOMPtr<nsIRunnable> task = new UserInteractionTimer(this);
16404   nsresult rv = NS_DispatchToCurrentThreadQueue(task.forget(), 2500,
16405                                                 EventQueuePriority::Idle);
16406   if (NS_WARN_IF(NS_FAILED(rv))) {
16407     return;
16408   }
16409 
16410   // This value will be reset by the timer.
16411   mHasUserInteractionTimerScheduled = true;
16412 }
16413 
ResetUserInteractionTimer()16414 void Document::ResetUserInteractionTimer() {
16415   mHasUserInteractionTimerScheduled = false;
16416 }
16417 
IsExtensionPage() const16418 bool Document::IsExtensionPage() const {
16419   return Preferences::GetBool("media.autoplay.allow-extension-background-pages",
16420                               true) &&
16421          BasePrincipal::Cast(NodePrincipal())->AddonPolicy();
16422 }
16423 
16424 /**
16425  * Retrieves the classification of the Flash plugins in the document based on
16426  * the classification lists. For more information, see
16427  * toolkit/components/url-classifier/flash-block-lists.rst
16428  */
DocumentFlashClassification()16429 FlashClassification Document::DocumentFlashClassification() {
16430   // Disable flash blocking when fission is enabled(See Bug 1584931).
16431   const auto fnIsFlashBlockingEnabled = [] {
16432     return StaticPrefs::plugins_flashBlock_enabled() && !FissionAutostart();
16433   };
16434 
16435   // If neither pref is on, skip the null-principal and principal URI checks.
16436   if (!StaticPrefs::plugins_http_https_only() && !fnIsFlashBlockingEnabled()) {
16437     return FlashClassification::Unknown;
16438   }
16439 
16440   if (!NodePrincipal()->GetIsContentPrincipal()) {
16441     return FlashClassification::Denied;
16442   }
16443 
16444   if (StaticPrefs::plugins_http_https_only()) {
16445     // Only allow plugins for documents from an HTTP/HTTPS origin. This should
16446     // allow dependent data: URIs to load plugins, but not:
16447     // * chrome documents
16448     // * "bare" data: loads
16449     // * FTP/gopher/file
16450 
16451     if (!(NodePrincipal()->SchemeIs("http") ||
16452           NodePrincipal()->SchemeIs("https"))) {
16453       return FlashClassification::Denied;
16454     }
16455   }
16456 
16457   // If flash blocking is disabled, it is equivalent to all sites being
16458   // on neither list.
16459   if (!fnIsFlashBlockingEnabled()) {
16460     return FlashClassification::Unknown;
16461   }
16462 
16463   if (mFlashClassification == FlashClassification::Unknown) {
16464     mFlashClassification = DocumentFlashClassificationInternal();
16465   }
16466 
16467   return mFlashClassification;
16468 }
16469 
AddResizeObserver(ResizeObserver & aObserver)16470 void Document::AddResizeObserver(ResizeObserver& aObserver) {
16471   if (!mResizeObserverController) {
16472     mResizeObserverController = MakeUnique<ResizeObserverController>(this);
16473   }
16474   mResizeObserverController->AddResizeObserver(aObserver);
16475 }
16476 
RemoveResizeObserver(ResizeObserver & aObserver)16477 void Document::RemoveResizeObserver(ResizeObserver& aObserver) {
16478   MOZ_DIAGNOSTIC_ASSERT(mResizeObserverController, "No controller?");
16479   if (MOZ_UNLIKELY(!mResizeObserverController)) {
16480     return;
16481   }
16482   mResizeObserverController->RemoveResizeObserver(aObserver);
16483 }
16484 
GetPermissionDelegateHandler()16485 PermissionDelegateHandler* Document::GetPermissionDelegateHandler() {
16486   if (!mPermissionDelegateHandler) {
16487     mPermissionDelegateHandler =
16488         mozilla::MakeAndAddRef<PermissionDelegateHandler>(this);
16489   }
16490 
16491   if (!mPermissionDelegateHandler->Initialize()) {
16492     mPermissionDelegateHandler = nullptr;
16493   }
16494 
16495   return mPermissionDelegateHandler;
16496 }
16497 
ScheduleResizeObserversNotification() const16498 void Document::ScheduleResizeObserversNotification() const {
16499   if (!mResizeObserverController) {
16500     return;
16501   }
16502 
16503   mResizeObserverController->ScheduleNotification();
16504 }
16505 
16506 /**
16507  * Initializes |mIsThirdPartyForFlashClassifier| if necessary and returns its
16508  * value. The value returned represents whether this document should be
16509  * considered Third-Party.
16510  *
16511  * A top-level document cannot be a considered Third-Party; only subdocuments
16512  * may. For a subdocument to be considered Third-Party, it must meet ANY ONE
16513  * of the following requirements:
16514  *  - The document's parent is Third-Party
16515  *  - The document has a different scheme (http/https) than its parent document
16516  *  - The document's domain and subdomain do not match those of its parent
16517  *    document.
16518  *
16519  * If there is an error in determining whether the document is Third-Party,
16520  * it will be assumed to be Third-Party for security reasons.
16521  */
IsThirdPartyForFlashClassifier()16522 bool Document::IsThirdPartyForFlashClassifier() {
16523   if (mIsThirdPartyForFlashClassifier.isSome()) {
16524     return mIsThirdPartyForFlashClassifier.value();
16525   }
16526 
16527   BrowsingContext* browsingContext = this->GetBrowsingContext();
16528   if (!browsingContext) {
16529     mIsThirdPartyForFlashClassifier.emplace(true);
16530     return mIsThirdPartyForFlashClassifier.value();
16531   }
16532 
16533   if (browsingContext->IsTop()) {
16534     mIsThirdPartyForFlashClassifier.emplace(false);
16535     return mIsThirdPartyForFlashClassifier.value();
16536   }
16537 
16538   nsCOMPtr<Document> parentDocument = GetInProcessParentDocument();
16539   if (!parentDocument) {
16540     // Failure
16541     mIsThirdPartyForFlashClassifier.emplace(true);
16542     return mIsThirdPartyForFlashClassifier.value();
16543   }
16544 
16545   if (parentDocument->IsThirdPartyForFlashClassifier()) {
16546     mIsThirdPartyForFlashClassifier.emplace(true);
16547     return mIsThirdPartyForFlashClassifier.value();
16548   }
16549 
16550   nsCOMPtr<nsIPrincipal> principal = NodePrincipal();
16551   nsCOMPtr<nsIPrincipal> parentPrincipal = parentDocument->GetPrincipal();
16552 
16553   bool principalsMatch = false;
16554   nsresult rv = principal->Equals(parentPrincipal, &principalsMatch);
16555 
16556   if (NS_WARN_IF(NS_FAILED(rv))) {
16557     // Failure
16558     mIsThirdPartyForFlashClassifier.emplace(true);
16559     return mIsThirdPartyForFlashClassifier.value();
16560   }
16561 
16562   if (!principalsMatch) {
16563     mIsThirdPartyForFlashClassifier.emplace(true);
16564     return mIsThirdPartyForFlashClassifier.value();
16565   }
16566 
16567   // Fall-through. Document is not a Third-Party Document.
16568   mIsThirdPartyForFlashClassifier.emplace(false);
16569   return mIsThirdPartyForFlashClassifier.value();
16570 }
16571 
DocumentFlashClassificationInternal()16572 FlashClassification Document::DocumentFlashClassificationInternal() {
16573   FlashClassification classification = FlashClassification::Unknown;
16574 
16575   nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(GetChannel());
16576   if (httpChannel) {
16577     nsIHttpChannel::FlashPluginState state = nsIHttpChannel::FlashPluginUnknown;
16578     httpChannel->GetFlashPluginState(&state);
16579 
16580     // Allow unknown children to inherit allowed status from parent, but do not
16581     // allow denied children to do so.
16582 
16583     if (state == nsIHttpChannel::FlashPluginDeniedInSubdocuments &&
16584         IsThirdPartyForFlashClassifier()) {
16585       return FlashClassification::Denied;
16586     }
16587 
16588     if (state == nsIHttpChannel::FlashPluginDenied) {
16589       return FlashClassification::Denied;
16590     }
16591 
16592     if (state == nsIHttpChannel::FlashPluginAllowed) {
16593       classification = FlashClassification::Allowed;
16594     }
16595   }
16596 
16597   if (IsTopLevelContentDocument()) {
16598     return classification;
16599   }
16600 
16601   Document* parentDocument = GetInProcessParentDocument();
16602   if (!parentDocument) {
16603     return FlashClassification::Denied;
16604   }
16605 
16606   FlashClassification parentClassification =
16607       parentDocument->DocumentFlashClassification();
16608 
16609   if (parentClassification == FlashClassification::Denied) {
16610     return FlashClassification::Denied;
16611   }
16612 
16613   // Allow unknown children to inherit allowed status from parent, but
16614   // do not allow denied children to do so.
16615   if (classification == FlashClassification::Unknown &&
16616       parentClassification == FlashClassification::Allowed) {
16617     return FlashClassification::Allowed;
16618   }
16619 
16620   return classification;
16621 }
16622 
ClearStaleServoData()16623 void Document::ClearStaleServoData() {
16624   DocumentStyleRootIterator iter(this);
16625   while (Element* root = iter.GetNextStyleRoot()) {
16626     RestyleManager::ClearServoDataFromSubtree(root);
16627   }
16628 }
16629 
GetSelection(ErrorResult & aRv)16630 Selection* Document::GetSelection(ErrorResult& aRv) {
16631   nsCOMPtr<nsPIDOMWindowInner> window = GetInnerWindow();
16632   if (!window) {
16633     return nullptr;
16634   }
16635 
16636   if (!window->IsCurrentInnerWindow()) {
16637     return nullptr;
16638   }
16639 
16640   return nsGlobalWindowInner::Cast(window)->GetSelection(aRv);
16641 }
16642 
HasStorageAccessSync(bool & aHasStorageAccess)16643 nsresult Document::HasStorageAccessSync(bool& aHasStorageAccess) {
16644   if (NodePrincipal()->GetIsNullPrincipal()) {
16645     aHasStorageAccess = false;
16646     return NS_OK;
16647   }
16648 
16649   if (CookieJarSettings()->GetBlockingAllContexts()) {
16650     aHasStorageAccess = false;
16651     return NS_OK;
16652   }
16653 
16654   if (IsTopLevelContentDocument()) {
16655     aHasStorageAccess = true;
16656     return NS_OK;
16657   }
16658 
16659   RefPtr<BrowsingContext> bc = GetBrowsingContext();
16660   if (!bc) {
16661     aHasStorageAccess = false;
16662     return NS_OK;
16663   }
16664 
16665   RefPtr<BrowsingContext> topBC = bc->Top();
16666   // We check if the document is a first-party document here by testing if the
16667   // top-level window is same-origin. In non-Fission mode, we can directly get
16668   // the top-level window through the top browsing context since it should be
16669   // in-process. And test their principals.
16670   //
16671   // In Fission mode, we can also directly get the top-level window. If we
16672   // cannot get it, this means the top-level window is cross-origin. Then, we
16673   // know our answer.
16674   if (auto* topOuterWindow = topBC->GetDOMWindow()) {
16675     if (nsGlobalWindowOuter::Cast(topOuterWindow)
16676             ->GetPrincipal()
16677             ->Equals(NodePrincipal())) {
16678       aHasStorageAccess = true;
16679       return NS_OK;
16680     }
16681   }
16682 
16683   nsPIDOMWindowInner* inner = GetInnerWindow();
16684   nsGlobalWindowOuter* outer = nullptr;
16685   NS_ENSURE_TRUE(inner, NS_ERROR_FAILURE);
16686   outer = nsGlobalWindowOuter::Cast(inner->GetOuterWindow());
16687   NS_ENSURE_TRUE(outer, NS_ERROR_FAILURE);
16688   aHasStorageAccess = outer->IsStorageAccessPermissionGranted();
16689   return NS_OK;
16690 }
16691 
HasStorageAccess(mozilla::ErrorResult & aRv)16692 already_AddRefed<mozilla::dom::Promise> Document::HasStorageAccess(
16693     mozilla::ErrorResult& aRv) {
16694   nsIGlobalObject* global = GetScopeObject();
16695   if (!global) {
16696     aRv.Throw(NS_ERROR_NOT_AVAILABLE);
16697     return nullptr;
16698   }
16699 
16700   RefPtr<Promise> promise =
16701       Promise::Create(global, aRv, Promise::ePropagateUserInteraction);
16702   if (aRv.Failed()) {
16703     return nullptr;
16704   }
16705 
16706   bool hasStorageAccess;
16707   nsresult rv = HasStorageAccessSync(hasStorageAccess);
16708   if (NS_FAILED(rv)) {
16709     promise->MaybeRejectWithUndefined();
16710   } else {
16711     promise->MaybeResolve(hasStorageAccess);
16712   }
16713 
16714   return promise.forget();
16715 }
16716 
16717 RefPtr<Document::GetContentBlockingEventsPromise>
GetContentBlockingEvents()16718 Document::GetContentBlockingEvents() {
16719   RefPtr<nsPIDOMWindowInner> inner = GetInnerWindow();
16720   if (!inner) {
16721     return nullptr;
16722   }
16723 
16724   RefPtr<WindowGlobalChild> wgc = inner->GetWindowGlobalChild();
16725   if (!wgc) {
16726     return nullptr;
16727   }
16728 
16729   return wgc->SendGetContentBlockingEvents()->Then(
16730       GetCurrentSerialEventTarget(), __func__,
16731       [](const WindowGlobalChild::GetContentBlockingEventsPromise::
16732              ResolveOrRejectValue& aValue) {
16733         if (aValue.IsResolve()) {
16734           return Document::GetContentBlockingEventsPromise::CreateAndResolve(
16735               aValue.ResolveValue(), __func__);
16736         }
16737 
16738         return Document::GetContentBlockingEventsPromise::CreateAndReject(
16739             false, __func__);
16740       });
16741 }
16742 
RequestStorageAccess(mozilla::ErrorResult & aRv)16743 already_AddRefed<mozilla::dom::Promise> Document::RequestStorageAccess(
16744     mozilla::ErrorResult& aRv) {
16745   nsIGlobalObject* global = GetScopeObject();
16746   if (!global) {
16747     aRv.Throw(NS_ERROR_NOT_AVAILABLE);
16748     return nullptr;
16749   }
16750 
16751   RefPtr<Promise> promise = Promise::Create(global, aRv);
16752   if (aRv.Failed()) {
16753     return nullptr;
16754   }
16755 
16756   // Window doesn't have user activation, reject.
16757   if (!this->HasValidTransientUserGestureActivation()) {
16758     nsContentUtils::ReportToConsole(nsIScriptError::errorFlag,
16759                                     nsLiteralCString("requestStorageAccess"),
16760                                     this, nsContentUtils::eDOM_PROPERTIES,
16761                                     "RequestStorageAccessUserGesture");
16762     promise->MaybeRejectWithUndefined();
16763     return promise.forget();
16764   }
16765 
16766   nsCOMPtr<nsPIDOMWindowInner> inner = GetInnerWindow();
16767   if (!inner) {
16768     this->ConsumeTransientUserGestureActivation();
16769     promise->MaybeRejectWithUndefined();
16770     return promise.forget();
16771   }
16772 
16773   // Step 0. If the browser forbids any storage access, reject.
16774   if (CookieJarSettings()->GetBlockingAllContexts()) {
16775     this->ConsumeTransientUserGestureActivation();
16776     promise->MaybeRejectWithUndefined();
16777     return promise.forget();
16778   }
16779 
16780   // Step 1. If the document already has been granted access, resolve.
16781   RefPtr<nsGlobalWindowOuter> outer =
16782       nsGlobalWindowOuter::Cast(inner->GetOuterWindow());
16783   if (!outer) {
16784     this->ConsumeTransientUserGestureActivation();
16785     promise->MaybeRejectWithUndefined();
16786     return promise.forget();
16787   }
16788 
16789   if (outer->IsStorageAccessPermissionGranted()) {
16790     promise->MaybeResolveWithUndefined();
16791     return promise.forget();
16792   }
16793 
16794   // Step 2. If the document has a null origin, reject.
16795   if (NodePrincipal()->GetIsNullPrincipal()) {
16796     nsContentUtils::ReportToConsole(nsIScriptError::errorFlag,
16797                                     nsLiteralCString("requestStorageAccess"),
16798                                     this, nsContentUtils::eDOM_PROPERTIES,
16799                                     "RequestStorageAccessNullPrincipal");
16800     this->ConsumeTransientUserGestureActivation();
16801     promise->MaybeRejectWithUndefined();
16802     return promise.forget();
16803   }
16804 
16805   RefPtr<BrowsingContext> bc = GetBrowsingContext();
16806   if (!bc) {
16807     this->ConsumeTransientUserGestureActivation();
16808     promise->MaybeRejectWithUndefined();
16809     return promise.forget();
16810   }
16811 
16812   // Only enforce third-party checks when there is a reason to enforce them.
16813   if (!CookieJarSettings()->GetRejectThirdPartyContexts()) {
16814     // Step 3. If the document's frame is the main frame, resolve.
16815     if (IsTopLevelContentDocument()) {
16816       promise->MaybeResolveWithUndefined();
16817       return promise.forget();
16818     }
16819 
16820     // Step 4. If the sub frame's origin is equal to the main frame's, resolve.
16821 
16822     // In fission, if the sub frame's origin differs from the main frame's
16823     // origin, they will be in different processes. We use IsInProcess()
16824     // check here to deterimine whether they have the same origin. In
16825     // non-fission mode, it is always in-process so we need to compare their
16826     // principals.
16827     if (bc->Top()->IsInProcess()) {
16828       nsCOMPtr<nsPIDOMWindowOuter> topOuter = bc->Top()->GetDOMWindow();
16829       if (!topOuter) {
16830         this->ConsumeTransientUserGestureActivation();
16831         promise->MaybeRejectWithUndefined();
16832         return promise.forget();
16833       }
16834 
16835       nsCOMPtr<Document> topLevelDoc = topOuter->GetExtantDoc();
16836       if (!topLevelDoc) {
16837         this->ConsumeTransientUserGestureActivation();
16838         promise->MaybeRejectWithUndefined();
16839         return promise.forget();
16840       }
16841 
16842       if (topLevelDoc->NodePrincipal()->Equals(NodePrincipal())) {
16843         promise->MaybeResolveWithUndefined();
16844         return promise.forget();
16845       }
16846     }
16847   }
16848 
16849   // Step 5. If the sub frame is not sandboxed, skip to step 7.
16850   // Step 6. If the sub frame doesn't have the token
16851   //         "allow-storage-access-by-user-activation", reject.
16852   if (StorageAccessSandboxed()) {
16853     nsContentUtils::ReportToConsole(
16854         nsIScriptError::errorFlag, nsLiteralCString("requestStorageAccess"),
16855         this, nsContentUtils::eDOM_PROPERTIES, "RequestStorageAccessSandboxed");
16856     this->ConsumeTransientUserGestureActivation();
16857     promise->MaybeRejectWithUndefined();
16858     return promise.forget();
16859   }
16860 
16861   // Step 7. If the sub frame's parent frame is not the top frame, reject.
16862   RefPtr<BrowsingContext> parentBC = bc->GetParent();
16863   if (parentBC && !parentBC->IsTopContent()) {
16864     nsContentUtils::ReportToConsole(
16865         nsIScriptError::errorFlag, nsLiteralCString("requestStorageAccess"),
16866         this, nsContentUtils::eDOM_PROPERTIES, "RequestStorageAccessNested");
16867     this->ConsumeTransientUserGestureActivation();
16868     promise->MaybeRejectWithUndefined();
16869     return promise.forget();
16870   }
16871 
16872   // Step 9. Check any additional rules that the browser has.
16873   //         Examples: skip-lists, on-device classification,
16874   //         user settings, anti-clickjacking heuristics, or prompting the
16875   //         user for explicit permission. Reject if some rule is not fulfilled.
16876 
16877   if (CookieJarSettings()->GetBlockingAllThirdPartyContexts()) {
16878     this->ConsumeTransientUserGestureActivation();
16879     promise->MaybeRejectWithUndefined();
16880     return promise.forget();
16881   }
16882 
16883   if (CookieJarSettings()->GetRejectThirdPartyContexts()) {
16884     // Only do something special for third-party tracking content.
16885     uint32_t antiTrackingRejectedReason = 0;
16886     if (StorageDisabledByAntiTracking(this, nullptr,
16887                                       antiTrackingRejectedReason)) {
16888       // If storage is disabled because of a custom cookie permission for the
16889       // site, reject.
16890       if (antiTrackingRejectedReason ==
16891           nsIWebProgressListener::STATE_COOKIES_BLOCKED_BY_PERMISSION) {
16892         this->ConsumeTransientUserGestureActivation();
16893         promise->MaybeRejectWithUndefined();
16894         return promise.forget();
16895       }
16896 
16897       // Note: If this has returned true, the top-level document is guaranteed
16898       // to not be on the Content Blocking allow list.
16899       MOZ_ASSERT(!CookieJarSettings()->GetIsOnContentBlockingAllowList());
16900 
16901       RefPtr<Document> self(this);
16902 
16903       auto performFinalChecks =
16904           [inner,
16905            self]() -> RefPtr<ContentBlocking::StorageAccessFinalCheckPromise> {
16906         RefPtr<ContentBlocking::StorageAccessFinalCheckPromise::Private> p =
16907             new ContentBlocking::StorageAccessFinalCheckPromise::Private(
16908                 __func__);
16909         RefPtr<StorageAccessPermissionRequest> sapr =
16910             StorageAccessPermissionRequest::Create(
16911                 inner,
16912                 // Allow
16913                 [p] {
16914                   Telemetry::AccumulateCategorical(
16915                       Telemetry::LABELS_STORAGE_ACCESS_API_UI::Allow);
16916                   p->Resolve(ContentBlocking::eAllow, __func__);
16917                 },
16918                 // Block
16919                 [p] {
16920                   Telemetry::AccumulateCategorical(
16921                       Telemetry::LABELS_STORAGE_ACCESS_API_UI::Deny);
16922                   p->Reject(false, __func__);
16923                 });
16924 
16925         using PromptResult = ContentPermissionRequestBase::PromptResult;
16926         PromptResult pr = sapr->CheckPromptPrefs();
16927 
16928         if (pr == PromptResult::Pending) {
16929           // We're about to show a prompt, record the request attempt
16930           Telemetry::AccumulateCategorical(
16931               Telemetry::LABELS_STORAGE_ACCESS_API_UI::Request);
16932         }
16933 
16934         self->AutomaticStorageAccessPermissionCanBeGranted(true)->Then(
16935             GetCurrentSerialEventTarget(), __func__,
16936             [p, pr, sapr,
16937              inner](const AutomaticStorageAccessPermissionGrantPromise::
16938                         ResolveOrRejectValue& aValue) -> void {
16939               // Make a copy because we can't modified copy-captured lambda
16940               // variables.
16941               PromptResult pr2 = pr;
16942 
16943               bool storageAccessCanBeGrantedAutomatically =
16944                   aValue.IsResolve() && aValue.ResolveValue();
16945 
16946               bool autoGrant = false;
16947               if (pr2 == PromptResult::Pending &&
16948                   storageAccessCanBeGrantedAutomatically) {
16949                 pr2 = PromptResult::Granted;
16950                 autoGrant = true;
16951 
16952                 Telemetry::AccumulateCategorical(
16953                     Telemetry::LABELS_STORAGE_ACCESS_API_UI::
16954                         AllowAutomatically);
16955               }
16956 
16957               if (pr2 != PromptResult::Pending) {
16958                 MOZ_ASSERT_IF(pr2 != PromptResult::Granted,
16959                               pr2 == PromptResult::Denied);
16960                 if (pr2 == PromptResult::Granted) {
16961                   ContentBlocking::StorageAccessPromptChoices choice =
16962                       ContentBlocking::eAllow;
16963                   if (autoGrant) {
16964                     choice = ContentBlocking::eAllowAutoGrant;
16965                   }
16966                   if (!autoGrant) {
16967                     p->Resolve(choice, __func__);
16968                   } else {
16969                     sapr->MaybeDelayAutomaticGrants()->Then(
16970                         GetCurrentSerialEventTarget(), __func__,
16971                         [p, choice] { p->Resolve(choice, __func__); },
16972                         [p] { p->Reject(false, __func__); });
16973                   }
16974                   return;
16975                 }
16976                 p->Reject(false, __func__);
16977                 return;
16978               }
16979 
16980               sapr->RequestDelayedTask(
16981                   inner->EventTargetFor(TaskCategory::Other),
16982                   ContentPermissionRequestBase::DelayedTaskType::Request);
16983             });
16984 
16985         return p;
16986       };
16987 
16988       // Consume user activation before entering the async part of this method.
16989       // This prevents usage of other transient activation-gated APIs.
16990       this->ConsumeTransientUserGestureActivation();
16991 
16992       ContentBlocking::AllowAccessFor(
16993           NodePrincipal(), bc, ContentBlockingNotifier::eStorageAccessAPI,
16994           performFinalChecks)
16995           ->Then(
16996               GetCurrentSerialEventTarget(), __func__,
16997               [self, outer, promise] {
16998                 // Step 10. Grant the document access to cookies and store
16999                 // that fact for
17000                 //          the purposes of future calls to
17001                 //          hasStorageAccess() and requestStorageAccess().
17002                 outer->SetStorageAccessPermissionGranted(true);
17003                 self->NotifyUserGestureActivation();
17004                 promise->MaybeResolveWithUndefined();
17005               },
17006               [outer, promise] {
17007                 outer->SetStorageAccessPermissionGranted(false);
17008                 promise->MaybeRejectWithUndefined();
17009               });
17010 
17011       return promise.forget();
17012     }
17013   }
17014 
17015   outer->SetStorageAccessPermissionGranted(true);
17016   promise->MaybeResolveWithUndefined();
17017   return promise.forget();
17018 }
17019 
RequestStorageAccessForOrigin(const nsAString & aThirdPartyOrigin,const bool aRequireUserActivation,mozilla::ErrorResult & aRv)17020 already_AddRefed<mozilla::dom::Promise> Document::RequestStorageAccessForOrigin(
17021     const nsAString& aThirdPartyOrigin, const bool aRequireUserActivation,
17022     mozilla::ErrorResult& aRv) {
17023   nsIGlobalObject* global = GetScopeObject();
17024   if (!global) {
17025     aRv.Throw(NS_ERROR_NOT_AVAILABLE);
17026     return nullptr;
17027   }
17028 
17029   RefPtr<Promise> promise = Promise::Create(global, aRv);
17030   if (aRv.Failed()) {
17031     return nullptr;
17032   }
17033 
17034   // Window doesn't have user activation, reject.
17035   bool hasUserActivation = this->HasValidTransientUserGestureActivation();
17036   if (aRequireUserActivation && !hasUserActivation) {
17037     nsContentUtils::ReportToConsole(nsIScriptError::errorFlag,
17038                                     nsLiteralCString("requestStorageAccess"),
17039                                     this, nsContentUtils::eDOM_PROPERTIES,
17040                                     "RequestStorageAccessUserGesture");
17041     promise->MaybeRejectWithUndefined();
17042     return promise.forget();
17043   }
17044 
17045   nsCOMPtr<nsPIDOMWindowInner> inner = GetInnerWindow();
17046   if (!inner) {
17047     this->ConsumeTransientUserGestureActivation();
17048     promise->MaybeRejectWithUndefined();
17049     return promise.forget();
17050   }
17051 
17052   // We only allow request storage access for third-party origin from the
17053   // first-party context.
17054   if (AntiTrackingUtils::IsThirdPartyWindow(inner, nullptr)) {
17055     this->ConsumeTransientUserGestureActivation();
17056     promise->MaybeRejectWithUndefined();
17057     return promise.forget();
17058   }
17059 
17060   // If the document has a null origin, reject.
17061   if (NodePrincipal()->GetIsNullPrincipal()) {
17062     nsContentUtils::ReportToConsole(nsIScriptError::errorFlag,
17063                                     nsLiteralCString("requestStorageAccess"),
17064                                     this, nsContentUtils::eDOM_PROPERTIES,
17065                                     "RequestStorageAccessNullPrincipal");
17066     this->ConsumeTransientUserGestureActivation();
17067     promise->MaybeRejectWithUndefined();
17068     return promise.forget();
17069   }
17070 
17071   nsCOMPtr<nsIURI> thirdPartyURI;
17072   nsresult rv = NS_NewURI(getter_AddRefs(thirdPartyURI), aThirdPartyOrigin);
17073   if (NS_WARN_IF(NS_FAILED(rv))) {
17074     aRv.Throw(rv);
17075     return nullptr;
17076   }
17077 
17078   // If the browser forbids any storage access, reject.
17079   if (CookieJarSettings()->GetBlockingAllContexts()) {
17080     this->ConsumeTransientUserGestureActivation();
17081     promise->MaybeRejectWithUndefined();
17082     return promise.forget();
17083   }
17084 
17085   // Only enforce third-party checks when there is a reason to enforce them.
17086   if (!CookieJarSettings()->GetRejectThirdPartyContexts()) {
17087     // If the the thrid party origin is equal to the window's, resolve.
17088     if (NodePrincipal()->IsSameOrigin(thirdPartyURI)) {
17089       promise->MaybeResolveWithUndefined();
17090       return promise.forget();
17091     }
17092   }
17093 
17094   // Check any additional rules that the browser has.
17095 
17096   if (CookieJarSettings()->GetBlockingAllThirdPartyContexts()) {
17097     this->ConsumeTransientUserGestureActivation();
17098     promise->MaybeRejectWithUndefined();
17099     return promise.forget();
17100   }
17101 
17102   if (CookieJarSettings()->GetRejectThirdPartyContexts()) {
17103     RefPtr<BrowsingContext> bc = GetBrowsingContext();
17104     if (!bc) {
17105       this->ConsumeTransientUserGestureActivation();
17106       promise->MaybeRejectWithUndefined();
17107       return promise.forget();
17108     }
17109 
17110     nsCOMPtr<nsIPrincipal> principal = BasePrincipal::CreateContentPrincipal(
17111         thirdPartyURI, NodePrincipal()->OriginAttributesRef());
17112 
17113     if (!principal) {
17114       this->ConsumeTransientUserGestureActivation();
17115       promise->MaybeRejectWithUndefined();
17116       return promise.forget();
17117     }
17118 
17119     RefPtr<Document> self(this);
17120 
17121     // Consume user activation before entering the async part of this method.
17122     // This prevents usage of other transient activation-gated APIs.
17123     this->ConsumeTransientUserGestureActivation();
17124 
17125     auto performFinalChecks = [inner, self, principal, hasUserActivation]() {
17126       RefPtr<ContentBlocking::StorageAccessFinalCheckPromise::Private> p =
17127           new ContentBlocking::StorageAccessFinalCheckPromise::Private(
17128               __func__);
17129       RefPtr<StorageAccessPermissionRequest> sapr =
17130           StorageAccessPermissionRequest::Create(
17131               inner, principal,
17132               // Allow
17133               [p] {
17134                 Telemetry::AccumulateCategorical(
17135                     Telemetry::LABELS_STORAGE_ACCESS_API_UI::Allow);
17136                 p->Resolve(ContentBlocking::eAllow, __func__);
17137               },
17138               // Block
17139               [p] {
17140                 Telemetry::AccumulateCategorical(
17141                     Telemetry::LABELS_STORAGE_ACCESS_API_UI::Deny);
17142                 p->Reject(false, __func__);
17143               });
17144 
17145       using PromptResult = ContentPermissionRequestBase::PromptResult;
17146       PromptResult pr = sapr->CheckPromptPrefs();
17147 
17148       if (pr == PromptResult::Pending) {
17149         // We're about to show a prompt, record the request attempt
17150         Telemetry::AccumulateCategorical(
17151             Telemetry::LABELS_STORAGE_ACCESS_API_UI::Request);
17152       }
17153 
17154       self->AutomaticStorageAccessPermissionCanBeGranted(hasUserActivation)
17155           ->Then(GetCurrentSerialEventTarget(), __func__,
17156                  [p, pr, sapr,
17157                   inner](const AutomaticStorageAccessPermissionGrantPromise::
17158                              ResolveOrRejectValue& aValue) -> void {
17159                    // Make a copy because we can't modified copy-captured lambda
17160                    // variables.
17161                    PromptResult pr2 = pr;
17162 
17163                    bool storageAccessCanBeGrantedAutomatically =
17164                        aValue.IsResolve() && aValue.ResolveValue();
17165 
17166                    bool autoGrant = false;
17167                    if (pr2 == PromptResult::Pending &&
17168                        storageAccessCanBeGrantedAutomatically) {
17169                      pr2 = PromptResult::Granted;
17170                      autoGrant = true;
17171 
17172                      Telemetry::AccumulateCategorical(
17173                          Telemetry::LABELS_STORAGE_ACCESS_API_UI::
17174                              AllowAutomatically);
17175                    }
17176 
17177                    if (pr2 != PromptResult::Pending) {
17178                      MOZ_ASSERT_IF(pr2 != PromptResult::Granted,
17179                                    pr2 == PromptResult::Denied);
17180                      if (pr2 == PromptResult::Granted) {
17181                        ContentBlocking::StorageAccessPromptChoices choice =
17182                            ContentBlocking::eAllow;
17183                        if (autoGrant) {
17184                          choice = ContentBlocking::eAllowAutoGrant;
17185                        }
17186                        if (!autoGrant) {
17187                          p->Resolve(choice, __func__);
17188                        } else {
17189                          sapr->MaybeDelayAutomaticGrants()->Then(
17190                              GetCurrentSerialEventTarget(), __func__,
17191                              [p, choice] { p->Resolve(choice, __func__); },
17192                              [p] { p->Reject(false, __func__); });
17193                        }
17194                        return;
17195                      }
17196                      p->Reject(false, __func__);
17197                      return;
17198                    }
17199 
17200                    sapr->RequestDelayedTask(
17201                        inner->EventTargetFor(TaskCategory::Other),
17202                        ContentPermissionRequestBase::DelayedTaskType::Request);
17203                  });
17204 
17205       return p;
17206     };
17207 
17208     // Only do something special for the third party that storage has been
17209     // disabled by anti-tracking feature.
17210     AsyncStorageDisabledByAntiTracking(bc, principal)
17211         ->Then(
17212             GetCurrentSerialEventTarget(), __func__,
17213             [performFinalChecks, promise, bc, principal,
17214              self](AsyncStorageDisabledByAntiTrackingPromise::
17215                        ResolveOrRejectValue&& aValue) {
17216               if (aValue.IsReject()) {
17217                 // Storage was enabled by anti-tracking feature.
17218                 return ContentBlocking::StorageAccessPermissionGrantPromise::
17219                     CreateAndResolve(0, __func__);
17220               }
17221 
17222               MOZ_ASSERT(aValue.IsResolve());
17223               // Storage was disabled by anti-tracking feature.
17224 
17225               // If the storage was disabled by the cookie permission, we don't
17226               // bother to show the prompt.
17227               uint32_t rejectReason = aValue.ResolveValue();
17228               if (rejectReason ==
17229                   nsIWebProgressListener::STATE_COOKIES_BLOCKED_BY_PERMISSION) {
17230                 return ContentBlocking::StorageAccessPermissionGrantPromise::
17231                     CreateAndReject(true, __func__);
17232                 ;
17233               }
17234 
17235               MOZ_ASSERT(!self->CookieJarSettings()
17236                               ->GetIsOnContentBlockingAllowList());
17237 
17238               return ContentBlocking::AllowAccessFor(
17239                   principal, bc,
17240                   ContentBlockingNotifier::ePrivilegeStorageAccessForOriginAPI,
17241                   performFinalChecks);
17242             })
17243         ->Then(
17244             GetCurrentSerialEventTarget(), __func__,
17245             [self, promise] {
17246               self->NotifyUserGestureActivation();
17247               promise->MaybeResolveWithUndefined();
17248             },
17249             [promise] { promise->MaybeRejectWithUndefined(); });
17250 
17251     return promise.forget();
17252   }
17253 
17254   promise->MaybeResolveWithUndefined();
17255   return promise.forget();
17256 }
17257 
17258 RefPtr<Document::AutomaticStorageAccessPermissionGrantPromise>
AutomaticStorageAccessPermissionCanBeGranted(bool hasUserActivation)17259 Document::AutomaticStorageAccessPermissionCanBeGranted(bool hasUserActivation) {
17260   // requestStorageAccessForOrigin may not require user activation. If we don't
17261   // have user activation at this point we should always show the prompt.
17262   if (!hasUserActivation ||
17263       !StaticPrefs::privacy_antitracking_enableWebcompat()) {
17264     return AutomaticStorageAccessPermissionGrantPromise::CreateAndResolve(
17265         false, __func__);
17266   }
17267   if (XRE_IsContentProcess()) {
17268     // In the content process, we need to ask the parent process to compute
17269     // this.  The reason is that nsIPermissionManager::GetAllWithTypePrefix()
17270     // isn't accessible in the content process.
17271     ContentChild* cc = ContentChild::GetSingleton();
17272     MOZ_ASSERT(cc);
17273 
17274     return cc
17275         ->SendAutomaticStorageAccessPermissionCanBeGranted(
17276             IPC::Principal(NodePrincipal()))
17277         ->Then(GetCurrentSerialEventTarget(), __func__,
17278                [](const ContentChild::
17279                       AutomaticStorageAccessPermissionCanBeGrantedPromise::
17280                           ResolveOrRejectValue& aValue) {
17281                  if (aValue.IsResolve()) {
17282                    return AutomaticStorageAccessPermissionGrantPromise::
17283                        CreateAndResolve(aValue.ResolveValue(), __func__);
17284                  }
17285 
17286                  return AutomaticStorageAccessPermissionGrantPromise::
17287                      CreateAndReject(false, __func__);
17288                });
17289   }
17290 
17291   if (XRE_IsParentProcess()) {
17292     // In the parent process, we can directly compute this.
17293     return AutomaticStorageAccessPermissionGrantPromise::CreateAndResolve(
17294         AutomaticStorageAccessPermissionCanBeGranted(NodePrincipal()),
17295         __func__);
17296   }
17297 
17298   return AutomaticStorageAccessPermissionGrantPromise::CreateAndReject(
17299       false, __func__);
17300 }
17301 
AutomaticStorageAccessPermissionCanBeGranted(nsIPrincipal * aPrincipal)17302 bool Document::AutomaticStorageAccessPermissionCanBeGranted(
17303     nsIPrincipal* aPrincipal) {
17304   nsAutoCString prefix;
17305   AntiTrackingUtils::CreateStoragePermissionKey(aPrincipal, prefix);
17306 
17307   if (!ContentBlockingUserInteraction::Exists(aPrincipal)) {
17308     return false;
17309   }
17310 
17311   PermissionManager* permManager = PermissionManager::GetInstance();
17312   if (NS_WARN_IF(!permManager)) {
17313     return false;
17314   }
17315 
17316   using Permissions = nsTArray<RefPtr<nsIPermission>>;
17317   Permissions perms;
17318   nsresult rv = permManager->GetAllWithTypePrefix(prefix, perms);
17319   if (NS_WARN_IF(NS_FAILED(rv))) {
17320     return false;
17321   }
17322 
17323   nsAutoCString prefix2(prefix);
17324   prefix2.Append('^');
17325   using Origins = nsTArray<nsCString>;
17326   Origins origins;
17327 
17328   for (const auto& perm : perms) {
17329     nsAutoCString type;
17330     rv = perm->GetType(type);
17331     if (NS_WARN_IF(NS_FAILED(rv))) {
17332       return false;
17333     }
17334     // Let's make sure that we're not looking at a permission for
17335     // https://exampletracker.company when we mean to look for the
17336     // permission for https://exampletracker.com!
17337     if (type != prefix && StringHead(type, prefix2.Length()) != prefix2) {
17338       continue;
17339     }
17340 
17341     nsCOMPtr<nsIPrincipal> principal;
17342     rv = perm->GetPrincipal(getter_AddRefs(principal));
17343     if (NS_WARN_IF(NS_FAILED(rv))) {
17344       return false;
17345     }
17346 
17347     nsAutoCString origin;
17348     rv = principal->GetOrigin(origin);
17349     if (NS_WARN_IF(NS_FAILED(rv))) {
17350       return false;
17351     }
17352 
17353     ToLowerCase(origin);
17354 
17355     if (origins.IndexOf(origin) == Origins::NoIndex) {
17356       origins.AppendElement(origin);
17357     }
17358   }
17359 
17360   nsCOMPtr<nsIBrowserUsage> bu = do_ImportModule(
17361       "resource:///modules/BrowserUsageTelemetry.jsm", fallible);
17362   if (NS_WARN_IF(!bu)) {
17363     return false;
17364   }
17365 
17366   uint32_t uniqueDomainsVisitedInPast24Hours = 0;
17367   rv = bu->GetUniqueDomainsVisitedInPast24Hours(
17368       &uniqueDomainsVisitedInPast24Hours);
17369   if (NS_WARN_IF(NS_FAILED(rv))) {
17370     return false;
17371   }
17372 
17373   // one percent of the number of top-levels origins visited in the current
17374   // session (but not to exceed 24 hours), or the value of the
17375   // dom.storage_access.max_concurrent_auto_grants preference, whichever is
17376   // higher.
17377   size_t maxConcurrentAutomaticGrants = std::max(
17378       std::max(int(std::floor(uniqueDomainsVisitedInPast24Hours / 100)),
17379                StaticPrefs::dom_storage_access_max_concurrent_auto_grants()),
17380       0);
17381 
17382   size_t originsThirdPartyHasAccessTo = origins.Length();
17383 
17384   return StaticPrefs::dom_storage_access_auto_grants() &&
17385          originsThirdPartyHasAccessTo < maxConcurrentAutomaticGrants;
17386 }
17387 
RecordNavigationTiming(ReadyState aReadyState)17388 void Document::RecordNavigationTiming(ReadyState aReadyState) {
17389   if (!XRE_IsContentProcess()) {
17390     return;
17391   }
17392   if (!IsTopLevelContentDocument()) {
17393     return;
17394   }
17395   // If we dont have the timing yet (mostly because the doc is still loading),
17396   // get it from docshell.
17397   RefPtr<nsDOMNavigationTiming> timing = mTiming;
17398   if (!timing) {
17399     if (!mDocumentContainer) {
17400       return;
17401     }
17402     timing = mDocumentContainer->GetNavigationTiming();
17403     if (!timing) {
17404       return;
17405     }
17406   }
17407   TimeStamp startTime = timing->GetNavigationStartTimeStamp();
17408   switch (aReadyState) {
17409     case READYSTATE_LOADING:
17410       if (!mDOMLoadingSet) {
17411         Telemetry::AccumulateTimeDelta(Telemetry::TIME_TO_DOM_LOADING_MS,
17412                                        startTime);
17413         mDOMLoadingSet = true;
17414       }
17415       break;
17416     case READYSTATE_INTERACTIVE:
17417       if (!mDOMInteractiveSet) {
17418         Telemetry::AccumulateTimeDelta(Telemetry::TIME_TO_DOM_INTERACTIVE_MS,
17419                                        startTime);
17420         mDOMInteractiveSet = true;
17421       }
17422       break;
17423     case READYSTATE_COMPLETE:
17424       if (!mDOMCompleteSet) {
17425         Telemetry::AccumulateTimeDelta(Telemetry::TIME_TO_DOM_COMPLETE_MS,
17426                                        startTime);
17427         mDOMCompleteSet = true;
17428       }
17429       break;
17430     default:
17431       NS_WARNING("Unexpected ReadyState value");
17432       break;
17433   }
17434 }
17435 
ModuleScriptsEnabled()17436 bool Document::ModuleScriptsEnabled() {
17437   return nsContentUtils::IsChromeDoc(this) ||
17438          StaticPrefs::dom_moduleScripts_enabled();
17439 }
17440 
ReportShadowDOMUsage()17441 void Document::ReportShadowDOMUsage() {
17442   nsPIDOMWindowInner* inner = GetInnerWindow();
17443   if (NS_WARN_IF(!inner)) {
17444     return;
17445   }
17446 
17447   WindowContext* wc = inner->GetWindowContext();
17448   if (NS_WARN_IF(!wc || wc->IsDiscarded())) {
17449     return;
17450   }
17451 
17452   WindowContext* topWc = wc->TopWindowContext();
17453   if (topWc->GetHasReportedShadowDOMUsage()) {
17454     return;
17455   }
17456 
17457   MOZ_ALWAYS_SUCCEEDS(topWc->SetHasReportedShadowDOMUsage(true));
17458 }
17459 
17460 // static
StorageAccessSandboxed(uint32_t aSandboxFlags)17461 bool Document::StorageAccessSandboxed(uint32_t aSandboxFlags) {
17462   return StaticPrefs::dom_storage_access_enabled() &&
17463          (aSandboxFlags & SANDBOXED_STORAGE_ACCESS) != 0;
17464 }
17465 
StorageAccessSandboxed() const17466 bool Document::StorageAccessSandboxed() const {
17467   return Document::StorageAccessSandboxed(GetSandboxFlags());
17468 }
17469 
GetCachedSizes(nsTabSizes * aSizes)17470 bool Document::GetCachedSizes(nsTabSizes* aSizes) {
17471   if (mCachedTabSizeGeneration == 0 ||
17472       GetGeneration() != mCachedTabSizeGeneration) {
17473     return false;
17474   }
17475   aSizes->mDom += mCachedTabSizes.mDom;
17476   aSizes->mStyle += mCachedTabSizes.mStyle;
17477   aSizes->mOther += mCachedTabSizes.mOther;
17478   return true;
17479 }
17480 
SetCachedSizes(nsTabSizes * aSizes)17481 void Document::SetCachedSizes(nsTabSizes* aSizes) {
17482   mCachedTabSizes.mDom = aSizes->mDom;
17483   mCachedTabSizes.mStyle = aSizes->mStyle;
17484   mCachedTabSizes.mOther = aSizes->mOther;
17485   mCachedTabSizeGeneration = GetGeneration();
17486 }
17487 
GetContentLanguageAsAtomForStyle() const17488 already_AddRefed<nsAtom> Document::GetContentLanguageAsAtomForStyle() const {
17489   nsAutoString contentLang;
17490   GetContentLanguage(contentLang);
17491   contentLang.StripWhitespace();
17492 
17493   // Content-Language may be a comma-separated list of language codes,
17494   // in which case the HTML5 spec says to treat it as unknown
17495   if (!contentLang.IsEmpty() && !contentLang.Contains(char16_t(','))) {
17496     return NS_Atomize(contentLang);
17497   }
17498 
17499   return nullptr;
17500 }
17501 
GetLanguageForStyle() const17502 already_AddRefed<nsAtom> Document::GetLanguageForStyle() const {
17503   RefPtr<nsAtom> lang = GetContentLanguageAsAtomForStyle();
17504   if (!lang) {
17505     lang = mLanguageFromCharset;
17506   }
17507   return lang.forget();
17508 }
17509 
GetFontPrefsForLang(nsAtom * aLanguage,bool * aNeedsToCache) const17510 const LangGroupFontPrefs* Document::GetFontPrefsForLang(
17511     nsAtom* aLanguage, bool* aNeedsToCache) const {
17512   nsAtom* lang = aLanguage ? aLanguage : mLanguageFromCharset.get();
17513   return StaticPresData::Get()->GetFontPrefsForLang(lang, aNeedsToCache);
17514 }
17515 
DoCacheAllKnownLangPrefs()17516 void Document::DoCacheAllKnownLangPrefs() {
17517   MOZ_ASSERT(mMayNeedFontPrefsUpdate);
17518   RefPtr<nsAtom> lang = GetLanguageForStyle();
17519   StaticPresData* data = StaticPresData::Get();
17520   data->GetFontPrefsForLang(lang ? lang.get() : mLanguageFromCharset.get());
17521   data->GetFontPrefsForLang(nsGkAtoms::x_math);
17522   // https://bugzilla.mozilla.org/show_bug.cgi?id=1362599#c12
17523   data->GetFontPrefsForLang(nsGkAtoms::Unicode);
17524   for (const auto& key : mLanguagesUsed) {
17525     data->GetFontPrefsForLang(key);
17526   }
17527   mMayNeedFontPrefsUpdate = false;
17528 }
17529 
RecomputeLanguageFromCharset()17530 void Document::RecomputeLanguageFromCharset() {
17531   nsLanguageAtomService* service = nsLanguageAtomService::GetService();
17532   RefPtr<nsAtom> language = service->LookupCharSet(mCharacterSet);
17533   if (language == nsGkAtoms::Unicode) {
17534     language = service->GetLocaleLanguage();
17535   }
17536 
17537   if (language == mLanguageFromCharset) {
17538     return;
17539   }
17540 
17541   mMayNeedFontPrefsUpdate = true;
17542   mLanguageFromCharset = std::move(language);
17543 }
17544 
CookieJarSettings()17545 nsICookieJarSettings* Document::CookieJarSettings() {
17546   // If we are here, this is probably a javascript: URL document. In any case,
17547   // we must have a nsCookieJarSettings. Let's create it.
17548   if (!mCookieJarSettings) {
17549     Document* inProcessParent = GetInProcessParentDocument();
17550 
17551     mCookieJarSettings =
17552         inProcessParent
17553             ? net::CookieJarSettings::Create(
17554                   inProcessParent->CookieJarSettings()->GetCookieBehavior(),
17555                   mozilla::net::CookieJarSettings::Cast(
17556                       inProcessParent->CookieJarSettings())
17557                       ->GetPartitionKey(),
17558                   inProcessParent->CookieJarSettings()
17559                       ->GetIsFirstPartyIsolated(),
17560                   inProcessParent->CookieJarSettings()
17561                       ->GetIsOnContentBlockingAllowList())
17562             : net::CookieJarSettings::Create(NodePrincipal());
17563 
17564     if (auto* wgc = GetWindowGlobalChild()) {
17565       net::CookieJarSettingsArgs csArgs;
17566       net::CookieJarSettings::Cast(mCookieJarSettings)->Serialize(csArgs);
17567       // Update cookie settings in the parent process
17568       if (!wgc->SendUpdateCookieJarSettings(csArgs)) {
17569         NS_WARNING(
17570             "Failed to update document's cookie jar settings on the "
17571             "WindowGlobalParent");
17572       }
17573     }
17574   }
17575 
17576   return mCookieJarSettings;
17577 }
17578 
HasStorageAccessPermissionGranted()17579 bool Document::HasStorageAccessPermissionGranted() {
17580   // The HasStoragePermission flag in LoadInfo remains fixed when
17581   // it is set in the parent process, so we need to check the cache
17582   // to see if the permission is granted afterwards.
17583   nsPIDOMWindowInner* inner = GetInnerWindow();
17584   if (inner && inner->HasStorageAccessPermissionGranted()) {
17585     return true;
17586   }
17587 
17588   if (!mChannel) {
17589     return false;
17590   }
17591 
17592   nsCOMPtr<nsILoadInfo> loadInfo = mChannel->LoadInfo();
17593   return loadInfo->GetStoragePermission() != nsILoadInfo::NoStoragePermission;
17594 }
17595 
HasStorageAccessPermissionGrantedByAllowList()17596 bool Document::HasStorageAccessPermissionGrantedByAllowList() {
17597   // We only care about if the document gets the storage permission via the
17598   // allow list here. So we don't check the storage access cache in the inner
17599   // window.
17600 
17601   if (!mChannel) {
17602     return false;
17603   }
17604 
17605   nsCOMPtr<nsILoadInfo> loadInfo = mChannel->LoadInfo();
17606   return loadInfo->GetStoragePermission() ==
17607          nsILoadInfo::StoragePermissionAllowListed;
17608 }
17609 
EffectiveStoragePrincipal() const17610 nsIPrincipal* Document::EffectiveStoragePrincipal() const {
17611   nsPIDOMWindowInner* inner = GetInnerWindow();
17612   if (!inner) {
17613     return NodePrincipal();
17614   }
17615 
17616   // Return our cached storage principal if one exists.
17617   if (mActiveStoragePrincipal) {
17618     return mActiveStoragePrincipal;
17619   }
17620 
17621   // We use the lower-level ContentBlocking API here to ensure this
17622   // check doesn't send notifications.
17623   uint32_t rejectedReason = 0;
17624   if (ContentBlocking::ShouldAllowAccessFor(inner, GetDocumentURI(),
17625                                             &rejectedReason)) {
17626     return mActiveStoragePrincipal = NodePrincipal();
17627   }
17628 
17629   // Let's use the storage principal only if we need to partition the cookie
17630   // jar. When the permission is granted, access will be different and the
17631   // normal principal will be used.
17632   if (ShouldPartitionStorage(rejectedReason) &&
17633       !StoragePartitioningEnabled(
17634           rejectedReason, const_cast<Document*>(this)->CookieJarSettings())) {
17635     return mActiveStoragePrincipal = NodePrincipal();
17636   }
17637 
17638   return mActiveStoragePrincipal = mPartitionedPrincipal;
17639 }
17640 
GetPrincipalForPrefBasedHacks() const17641 nsIPrincipal* Document::GetPrincipalForPrefBasedHacks() const {
17642   // If the document is sandboxed document or data: document, we should
17643   // get URI of the parent document.
17644   for (const Document* document = this;
17645        document && document->IsContentDocument();
17646        document = document->GetInProcessParentDocument()) {
17647     // The document URI may be about:blank even if it comes from actual web
17648     // site.  Therefore, we need to check the URI of its principal.
17649     nsIPrincipal* principal = document->NodePrincipal();
17650     if (principal->GetIsNullPrincipal()) {
17651       continue;
17652     }
17653     return principal;
17654   }
17655   return nullptr;
17656 }
17657 
SetIsInitialDocument(bool aIsInitialDocument)17658 void Document::SetIsInitialDocument(bool aIsInitialDocument) {
17659   mIsInitialDocumentInWindow = aIsInitialDocument;
17660 
17661   // Asynchronously tell the parent process that we are, or are no longer, the
17662   // initial document. This happens async.
17663   if (auto* wgc = GetWindowGlobalChild()) {
17664     wgc->SendSetIsInitialDocument(aIsInitialDocument);
17665   }
17666 }
17667 
17668 // static
AddToplevelLoadingDocument(Document * aDoc)17669 void Document::AddToplevelLoadingDocument(Document* aDoc) {
17670   MOZ_ASSERT(aDoc && aDoc->IsTopLevelContentDocument());
17671   // Currently we're interested in foreground documents only, so bail out early.
17672   if (aDoc->IsInBackgroundWindow() || !XRE_IsContentProcess()) {
17673     return;
17674   }
17675 
17676   if (!sLoadingForegroundTopLevelContentDocument) {
17677     sLoadingForegroundTopLevelContentDocument = new AutoTArray<Document*, 8>();
17678     mozilla::ipc::IdleSchedulerChild* idleScheduler =
17679         mozilla::ipc::IdleSchedulerChild::GetMainThreadIdleScheduler();
17680     if (idleScheduler) {
17681       idleScheduler->SendRunningPrioritizedOperation();
17682     }
17683   }
17684   if (!sLoadingForegroundTopLevelContentDocument->Contains(aDoc)) {
17685     sLoadingForegroundTopLevelContentDocument->AppendElement(aDoc);
17686   }
17687 }
17688 
17689 // static
RemoveToplevelLoadingDocument(Document * aDoc)17690 void Document::RemoveToplevelLoadingDocument(Document* aDoc) {
17691   MOZ_ASSERT(aDoc && aDoc->IsTopLevelContentDocument());
17692   if (sLoadingForegroundTopLevelContentDocument) {
17693     sLoadingForegroundTopLevelContentDocument->RemoveElement(aDoc);
17694     if (sLoadingForegroundTopLevelContentDocument->IsEmpty()) {
17695       delete sLoadingForegroundTopLevelContentDocument;
17696       sLoadingForegroundTopLevelContentDocument = nullptr;
17697 
17698       mozilla::ipc::IdleSchedulerChild* idleScheduler =
17699           mozilla::ipc::IdleSchedulerChild::GetMainThreadIdleScheduler();
17700       if (idleScheduler) {
17701         idleScheduler->SendPrioritizedOperationDone();
17702       }
17703     }
17704   }
17705 }
17706 
DefaultColorScheme() const17707 ColorScheme Document::DefaultColorScheme() const {
17708   return LookAndFeel::ColorSchemeForStyle(*this, {GetColorSchemeBits()});
17709 }
17710 
PreferredColorScheme(IgnoreRFP aIgnoreRFP) const17711 ColorScheme Document::PreferredColorScheme(IgnoreRFP aIgnoreRFP) const {
17712   if (aIgnoreRFP == IgnoreRFP::No &&
17713       nsContentUtils::ShouldResistFingerprinting(this)) {
17714     return ColorScheme::Light;
17715   }
17716 
17717   if (nsPresContext* pc = GetPresContext()) {
17718     if (auto scheme = pc->GetOverriddenColorScheme()) {
17719       return *scheme;
17720     }
17721   }
17722 
17723   // NOTE(emilio): We use IsInChromeDocShell rather than IsChromeDoc
17724   // intentionally, to make chrome documents in content docshells (like about
17725   // pages) use the content color scheme.
17726   if (IsInChromeDocShell()) {
17727     return LookAndFeel::ColorSchemeForChrome();
17728   }
17729   return LookAndFeel::PreferredColorSchemeForContent();
17730 }
17731 
HasRecentlyStartedForegroundLoads()17732 bool Document::HasRecentlyStartedForegroundLoads() {
17733   if (!sLoadingForegroundTopLevelContentDocument) {
17734     return false;
17735   }
17736 
17737   for (size_t i = 0; i < sLoadingForegroundTopLevelContentDocument->Length();
17738        ++i) {
17739     Document* doc = sLoadingForegroundTopLevelContentDocument->ElementAt(i);
17740     // A page loaded in foreground could be in background now.
17741     if (!doc->IsInBackgroundWindow()) {
17742       nsPIDOMWindowInner* win = doc->GetInnerWindow();
17743       if (win) {
17744         Performance* perf = win->GetPerformance();
17745         if (perf &&
17746             perf->Now() < StaticPrefs::page_load_deprioritization_period()) {
17747           return true;
17748         }
17749       }
17750     }
17751   }
17752 
17753   // Didn't find any loading foreground documents, just clear the array.
17754   delete sLoadingForegroundTopLevelContentDocument;
17755   sLoadingForegroundTopLevelContentDocument = nullptr;
17756 
17757   mozilla::ipc::IdleSchedulerChild* idleScheduler =
17758       mozilla::ipc::IdleSchedulerChild::GetMainThreadIdleScheduler();
17759   if (idleScheduler) {
17760     idleScheduler->SendPrioritizedOperationDone();
17761   }
17762   return false;
17763 }
17764 
AddPendingFrameStaticClone(nsFrameLoaderOwner * aElement,nsFrameLoader * aStaticCloneOf)17765 void Document::AddPendingFrameStaticClone(nsFrameLoaderOwner* aElement,
17766                                           nsFrameLoader* aStaticCloneOf) {
17767   PendingFrameStaticClone* clone = mPendingFrameStaticClones.AppendElement();
17768   clone->mElement = aElement;
17769   clone->mStaticCloneOf = aStaticCloneOf;
17770 }
17771 
ShouldAvoidNativeTheme() const17772 bool Document::ShouldAvoidNativeTheme() const {
17773   return StaticPrefs::widget_non_native_theme_enabled() &&
17774          (!IsInChromeDocShell() || XRE_IsContentProcess());
17775 }
17776 
UseRegularPrincipal() const17777 bool Document::UseRegularPrincipal() const {
17778   return EffectiveStoragePrincipal() == NodePrincipal();
17779 }
17780 
HasThirdPartyChannel()17781 bool Document::HasThirdPartyChannel() {
17782   nsCOMPtr<nsIChannel> channel = GetChannel();
17783   if (channel) {
17784     // We assume that the channel is a third-party by default.
17785     bool thirdParty = true;
17786 
17787     nsCOMPtr<mozIThirdPartyUtil> thirdPartyUtil =
17788         components::ThirdPartyUtil::Service();
17789     if (!thirdPartyUtil) {
17790       return thirdParty;
17791     }
17792 
17793     // Check that if the channel is a third-party to its parent.
17794     nsresult rv =
17795         thirdPartyUtil->IsThirdPartyChannel(channel, nullptr, &thirdParty);
17796     if (NS_FAILED(rv)) {
17797       // Assume third-party in case of failure
17798       thirdParty = true;
17799     }
17800 
17801     return thirdParty;
17802   }
17803 
17804   if (mParentDocument) {
17805     return mParentDocument->HasThirdPartyChannel();
17806   }
17807 
17808   return false;
17809 }
17810 
ShouldIncludeInTelemetry(bool aAllowExtensionURIs)17811 bool Document::ShouldIncludeInTelemetry(bool aAllowExtensionURIs) {
17812   if (!(IsContentDocument() || IsResourceDoc())) {
17813     return false;
17814   }
17815 
17816   if (!aAllowExtensionURIs &&
17817       NodePrincipal()->GetIsAddonOrExpandedAddonPrincipal()) {
17818     return false;
17819   }
17820 
17821   return !NodePrincipal()->SchemeIs("about") &&
17822          !NodePrincipal()->SchemeIs("chrome") &&
17823          !NodePrincipal()->SchemeIs("resource");
17824 }
17825 
GetConnectedShadowRoots(nsTArray<RefPtr<ShadowRoot>> & aOut) const17826 void Document::GetConnectedShadowRoots(
17827     nsTArray<RefPtr<ShadowRoot>>& aOut) const {
17828   AppendToArray(aOut, mComposedShadowRoots);
17829 }
17830 
HasPictureInPictureChildElement() const17831 bool Document::HasPictureInPictureChildElement() const {
17832   return mPictureInPictureChildElementCount > 0;
17833 }
17834 
EnableChildElementInPictureInPictureMode()17835 void Document::EnableChildElementInPictureInPictureMode() {
17836   mPictureInPictureChildElementCount++;
17837   MOZ_ASSERT(mPictureInPictureChildElementCount >= 0);
17838 }
17839 
DisableChildElementInPictureInPictureMode()17840 void Document::DisableChildElementInPictureInPictureMode() {
17841   mPictureInPictureChildElementCount--;
17842   MOZ_ASSERT(mPictureInPictureChildElementCount >= 0);
17843 }
17844 
AddMediaElementWithMSE()17845 void Document::AddMediaElementWithMSE() {
17846   if (mMediaElementWithMSECount++ == 0) {
17847     WindowGlobalChild* wgc = GetWindowGlobalChild();
17848     if (wgc) {
17849       wgc->BlockBFCacheFor(BFCacheStatus::CONTAINS_MSE_CONTENT);
17850     }
17851   }
17852 }
17853 
RemoveMediaElementWithMSE()17854 void Document::RemoveMediaElementWithMSE() {
17855   MOZ_ASSERT(mMediaElementWithMSECount > 0);
17856   if (--mMediaElementWithMSECount == 0) {
17857     WindowGlobalChild* wgc = GetWindowGlobalChild();
17858     if (wgc) {
17859       wgc->UnblockBFCacheFor(BFCacheStatus::CONTAINS_MSE_CONTENT);
17860     }
17861   }
17862 }
17863 
UnregisterFromMemoryReportingForDataDocument()17864 void Document::UnregisterFromMemoryReportingForDataDocument() {
17865   if (!mAddedToMemoryReportingAsDataDocument) {
17866     return;
17867   }
17868   mAddedToMemoryReportingAsDataDocument = false;
17869   nsIGlobalObject* global = GetScopeObject();
17870   if (global) {
17871     if (nsPIDOMWindowInner* win = global->AsInnerWindow()) {
17872       nsGlobalWindowInner::Cast(win)->UnregisterDataDocumentForMemoryReporting(
17873           this);
17874     }
17875   }
17876 }
OOPChildLoadStarted(BrowserBridgeChild * aChild)17877 void Document::OOPChildLoadStarted(BrowserBridgeChild* aChild) {
17878   MOZ_DIAGNOSTIC_ASSERT(!mOOPChildrenLoading.Contains(aChild));
17879   mOOPChildrenLoading.AppendElement(aChild);
17880   if (mOOPChildrenLoading.Length() == 1) {
17881     // Let's block unload so that we're blocked from going into the BFCache
17882     // until the child has actually notified us that it has done loading.
17883     BlockOnload();
17884   }
17885 }
17886 
OOPChildLoadDone(BrowserBridgeChild * aChild)17887 void Document::OOPChildLoadDone(BrowserBridgeChild* aChild) {
17888   // aChild will not be in the list if nsDocLoader::Stop() was called, since
17889   // that clears mOOPChildrenLoading.  It also dispatches the 'load' event,
17890   // so we don't need to call DocLoaderIsEmpty in that case.
17891   if (mOOPChildrenLoading.RemoveElement(aChild)) {
17892     if (mOOPChildrenLoading.IsEmpty()) {
17893       UnblockOnload(false);
17894     }
17895     RefPtr<nsDocLoader> docLoader(mDocumentContainer);
17896     if (docLoader) {
17897       docLoader->OOPChildrenLoadingIsEmpty();
17898     }
17899   }
17900 }
17901 
ClearOOPChildrenLoading()17902 void Document::ClearOOPChildrenLoading() {
17903   nsTArray<const BrowserBridgeChild*> oopChildrenLoading;
17904   mOOPChildrenLoading.SwapElements(oopChildrenLoading);
17905   if (!oopChildrenLoading.IsEmpty()) {
17906     UnblockOnload(false);
17907   }
17908 }
17909 
17910 }  // namespace mozilla::dom
17911