tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

Document.cpp (730068B)


      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 
     13 #include <inttypes.h>
     14 #include <stdlib.h>
     15 #include <string.h>
     16 
     17 #include <algorithm>
     18 #include <cstddef>
     19 #include <cstdint>
     20 #include <limits>
     21 
     22 #include "AnchorPositioningUtils.h"
     23 #include "Attr.h"
     24 #include "ErrorList.h"
     25 #include "ExpandedPrincipal.h"
     26 #include "MainThreadUtils.h"
     27 #include "MobileViewportManager.h"
     28 #include "NSSErrorsService.h"
     29 #include "NodeUbiReporting.h"
     30 #include "NonCustomCSSPropertyId.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/TelemetryTimers.h"
     40 #include "js/Value.h"
     41 #include "jsapi.h"
     42 #include "mozAutoDocUpdate.h"
     43 #include "mozIDOMWindow.h"
     44 #include "mozIThirdPartyUtil.h"
     45 #include "mozilla/AntiTrackingUtils.h"
     46 #include "mozilla/ArrayIterator.h"
     47 #include "mozilla/AsyncEventDispatcher.h"
     48 #include "mozilla/AttributeStyles.h"
     49 #include "mozilla/Base64.h"
     50 #include "mozilla/BasePrincipal.h"
     51 #include "mozilla/BounceTrackingProtection.h"
     52 #include "mozilla/CSSEnabledState.h"
     53 #include "mozilla/Components.h"
     54 #include "mozilla/ContentBlockingAllowList.h"
     55 #include "mozilla/ContentBlockingNotifier.h"
     56 #include "mozilla/ContentBlockingUserInteraction.h"
     57 #include "mozilla/ContentPrincipal.h"
     58 #include "mozilla/CycleCollectedJSContext.h"
     59 #include "mozilla/DebugOnly.h"
     60 #include "mozilla/DocumentStyleRootIterator.h"
     61 #include "mozilla/EditorBase.h"
     62 #include "mozilla/EditorCommands.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/Likely.h"
     77 #include "mozilla/Logging.h"
     78 #include "mozilla/LookAndFeel.h"
     79 #include "mozilla/MappedDeclarationsBuilder.h"
     80 #include "mozilla/Maybe.h"
     81 #include "mozilla/MediaFeatureChange.h"
     82 #include "mozilla/MediaManager.h"
     83 #include "mozilla/MemoryReporting.h"
     84 #include "mozilla/NeverDestroyed.h"
     85 #include "mozilla/NullPrincipal.h"
     86 #include "mozilla/OriginAttributes.h"
     87 #include "mozilla/OwningNonNull.h"
     88 #include "mozilla/PendingFullscreenEvent.h"
     89 #include "mozilla/PermissionDelegateHandler.h"
     90 #include "mozilla/PermissionManager.h"
     91 #include "mozilla/Preferences.h"
     92 #include "mozilla/PreloadHashKey.h"
     93 #include "mozilla/PresShell.h"
     94 #include "mozilla/PresShellForwards.h"
     95 #include "mozilla/PresShellInlines.h"
     96 #include "mozilla/ProfilerMarkers.h"
     97 #include "mozilla/PseudoStyleType.h"
     98 #include "mozilla/RelativeTo.h"
     99 #include "mozilla/RestyleManager.h"
    100 #include "mozilla/ReverseIterator.h"
    101 #include "mozilla/SMILAnimationController.h"
    102 #include "mozilla/SMILTimeContainer.h"
    103 #include "mozilla/SVGUtils.h"
    104 #include "mozilla/SchedulerGroup.h"
    105 #include "mozilla/ScopeExit.h"
    106 #include "mozilla/ScrollContainerFrame.h"
    107 #include "mozilla/ScrollTimelineAnimationTracker.h"
    108 #include "mozilla/ServoStyleConsts.h"
    109 #include "mozilla/ServoTypes.h"
    110 #include "mozilla/SizeOfState.h"
    111 #include "mozilla/Sprintf.h"
    112 #include "mozilla/StaticAnalysisFunctions.h"
    113 #include "mozilla/StaticPrefs_apz.h"
    114 #include "mozilla/StaticPrefs_browser.h"
    115 #include "mozilla/StaticPrefs_docshell.h"
    116 #include "mozilla/StaticPrefs_dom.h"
    117 #include "mozilla/StaticPrefs_fission.h"
    118 #include "mozilla/StaticPrefs_full_screen_api.h"
    119 #include "mozilla/StaticPrefs_layout.h"
    120 #include "mozilla/StaticPrefs_network.h"
    121 #include "mozilla/StaticPrefs_page_load.h"
    122 #include "mozilla/StaticPrefs_privacy.h"
    123 #include "mozilla/StaticPrefs_security.h"
    124 #include "mozilla/StaticPrefs_widget.h"
    125 #include "mozilla/StaticPresData.h"
    126 #include "mozilla/StorageAccess.h"
    127 #include "mozilla/StoragePrincipalHelper.h"
    128 #include "mozilla/StyleSheet.h"
    129 #include "mozilla/TelemetryScalarEnums.h"
    130 #include "mozilla/TextControlElement.h"
    131 #include "mozilla/TextEditor.h"
    132 #include "mozilla/URLDecorationStripper.h"
    133 #include "mozilla/URLExtraData.h"
    134 #include "mozilla/css/ImageLoader.h"
    135 #include "mozilla/css/Loader.h"
    136 #include "mozilla/css/Rule.h"
    137 #include "mozilla/css/SheetParsingMode.h"
    138 #include "mozilla/dom/AncestorIterator.h"
    139 #include "mozilla/dom/AnonymousContent.h"
    140 #include "mozilla/dom/BindContext.h"
    141 #include "mozilla/dom/BlobURLProtocolHandler.h"
    142 #include "mozilla/dom/BrowserChild.h"
    143 #include "mozilla/dom/BrowsingContext.h"
    144 #include "mozilla/dom/BrowsingContextGroup.h"
    145 #include "mozilla/dom/CDATASection.h"
    146 #include "mozilla/dom/CSPDictionariesBinding.h"
    147 #include "mozilla/dom/CSSBinding.h"
    148 #include "mozilla/dom/CSSCustomPropertyRegisteredEvent.h"
    149 #include "mozilla/dom/CanonicalBrowsingContext.h"
    150 #include "mozilla/dom/CanvasRenderingContextHelper.h"
    151 #include "mozilla/dom/ChromeObserver.h"
    152 #include "mozilla/dom/ClientInfo.h"
    153 #include "mozilla/dom/ClientState.h"
    154 #include "mozilla/dom/CloseWatcherManager.h"
    155 #include "mozilla/dom/Comment.h"
    156 #include "mozilla/dom/ContentChild.h"
    157 #include "mozilla/dom/DOMImplementation.h"
    158 #include "mozilla/dom/DOMIntersectionObserver.h"
    159 #include "mozilla/dom/DOMStringList.h"
    160 #include "mozilla/dom/DocGroup.h"
    161 #include "mozilla/dom/DocumentBinding.h"
    162 #include "mozilla/dom/DocumentFragment.h"
    163 #include "mozilla/dom/DocumentInlines.h"
    164 #include "mozilla/dom/DocumentL10n.h"
    165 #include "mozilla/dom/DocumentPictureInPicture.h"
    166 #include "mozilla/dom/DocumentTimeline.h"
    167 #include "mozilla/dom/DocumentType.h"
    168 #include "mozilla/dom/ElementBinding.h"
    169 #include "mozilla/dom/ErrorEvent.h"
    170 #include "mozilla/dom/Event.h"
    171 #include "mozilla/dom/EventListenerBinding.h"
    172 #include "mozilla/dom/FailedCertSecurityInfoBinding.h"
    173 #include "mozilla/dom/FeaturePolicy.h"
    174 #include "mozilla/dom/FeaturePolicyUtils.h"
    175 #include "mozilla/dom/FontFaceSet.h"
    176 #include "mozilla/dom/FragmentDirective.h"
    177 #include "mozilla/dom/FromParser.h"
    178 #include "mozilla/dom/HTMLAllCollection.h"
    179 #include "mozilla/dom/HTMLBodyElement.h"
    180 #include "mozilla/dom/HTMLCollectionBinding.h"
    181 #include "mozilla/dom/HTMLDialogElement.h"
    182 #include "mozilla/dom/HTMLEmbedElement.h"
    183 #include "mozilla/dom/HTMLFormElement.h"
    184 #include "mozilla/dom/HTMLIFrameElement.h"
    185 #include "mozilla/dom/HTMLImageElement.h"
    186 #include "mozilla/dom/HTMLInputElement.h"
    187 #include "mozilla/dom/HTMLLinkElement.h"
    188 #include "mozilla/dom/HTMLMediaElement.h"
    189 #include "mozilla/dom/HTMLMetaElement.h"
    190 #include "mozilla/dom/HTMLObjectElement.h"
    191 #include "mozilla/dom/HTMLSharedElement.h"
    192 #include "mozilla/dom/HTMLTextAreaElement.h"
    193 #include "mozilla/dom/HighlightRegistry.h"
    194 #include "mozilla/dom/InspectorUtils.h"
    195 #include "mozilla/dom/IntegrityPolicy.h"
    196 #include "mozilla/dom/InteractiveWidget.h"
    197 #include "mozilla/dom/Link.h"
    198 #include "mozilla/dom/MediaQueryList.h"
    199 #include "mozilla/dom/MediaSource.h"
    200 #include "mozilla/dom/MutationObservers.h"
    201 #include "mozilla/dom/NameSpaceConstants.h"
    202 #include "mozilla/dom/NavigationBinding.h"
    203 #include "mozilla/dom/Navigator.h"
    204 #include "mozilla/dom/NetErrorInfoBinding.h"
    205 #include "mozilla/dom/NodeInfo.h"
    206 #include "mozilla/dom/NodeIterator.h"
    207 #include "mozilla/dom/PContentChild.h"
    208 #include "mozilla/dom/PWindowGlobalChild.h"
    209 #include "mozilla/dom/PageLoadEventUtils.h"
    210 #include "mozilla/dom/PageTransitionEvent.h"
    211 #include "mozilla/dom/PageTransitionEventBinding.h"
    212 #include "mozilla/dom/Performance.h"
    213 #include "mozilla/dom/PermissionMessageUtils.h"
    214 #include "mozilla/dom/PolicyContainer.h"
    215 #include "mozilla/dom/PopoverData.h"
    216 #include "mozilla/dom/PostMessageEvent.h"
    217 #include "mozilla/dom/ProcessingInstruction.h"
    218 #include "mozilla/dom/Promise.h"
    219 #include "mozilla/dom/PromiseNativeHandler.h"
    220 #include "mozilla/dom/RemoteBrowser.h"
    221 #include "mozilla/dom/ResizeObserver.h"
    222 #include "mozilla/dom/RustTypes.h"
    223 #include "mozilla/dom/SVGDocument.h"
    224 #include "mozilla/dom/SVGElement.h"
    225 #include "mozilla/dom/SVGSVGElement.h"
    226 #include "mozilla/dom/SVGUseElement.h"
    227 #include "mozilla/dom/Sanitizer.h"
    228 #include "mozilla/dom/ScriptLoader.h"
    229 #include "mozilla/dom/ScriptSettings.h"
    230 #include "mozilla/dom/Selection.h"
    231 #include "mozilla/dom/ServiceWorkerContainer.h"
    232 #include "mozilla/dom/ServiceWorkerDescriptor.h"
    233 #include "mozilla/dom/ServiceWorkerManager.h"
    234 #include "mozilla/dom/ShadowIncludingTreeIterator.h"
    235 #include "mozilla/dom/ShadowRoot.h"
    236 #include "mozilla/dom/StyleSheetApplicableStateChangeEvent.h"
    237 #include "mozilla/dom/StyleSheetApplicableStateChangeEventBinding.h"
    238 #include "mozilla/dom/StyleSheetList.h"
    239 #include "mozilla/dom/StyleSheetRemovedEvent.h"
    240 #include "mozilla/dom/StyleSheetRemovedEventBinding.h"
    241 #include "mozilla/dom/TimeoutManager.h"
    242 #include "mozilla/dom/ToggleEvent.h"
    243 #include "mozilla/dom/Touch.h"
    244 #include "mozilla/dom/TouchEvent.h"
    245 #include "mozilla/dom/TreeOrderedArrayInlines.h"
    246 #include "mozilla/dom/TreeWalker.h"
    247 #include "mozilla/dom/TrustedHTML.h"
    248 #include "mozilla/dom/TrustedTypeUtils.h"
    249 #include "mozilla/dom/TrustedTypesConstants.h"
    250 #include "mozilla/dom/URL.h"
    251 #include "mozilla/dom/UseCounterMetrics.h"
    252 #include "mozilla/dom/UserActivation.h"
    253 #include "mozilla/dom/ViewTransition.h"
    254 #include "mozilla/dom/WakeLockJS.h"
    255 #include "mozilla/dom/WakeLockSentinel.h"
    256 #include "mozilla/dom/WebIdentityHandler.h"
    257 #include "mozilla/dom/WindowBinding.h"
    258 #include "mozilla/dom/WindowContext.h"
    259 #include "mozilla/dom/WindowGlobalChild.h"
    260 #include "mozilla/dom/WindowProxyHolder.h"
    261 #include "mozilla/dom/WorkerDocumentListener.h"
    262 #include "mozilla/dom/XPathEvaluator.h"
    263 #include "mozilla/dom/XPathExpression.h"
    264 #include "mozilla/dom/XULBroadcastManager.h"
    265 #include "mozilla/dom/XULPersist.h"
    266 #include "mozilla/dom/fragmentdirectives_ffi_generated.h"
    267 #include "mozilla/dom/nsCSPContext.h"
    268 #include "mozilla/dom/nsCSPUtils.h"
    269 #include "mozilla/dom/nsHTTPSOnlyUtils.h"
    270 #include "mozilla/extensions/WebExtensionPolicy.h"
    271 #include "mozilla/fallible.h"
    272 #include "mozilla/gfx/BaseCoord.h"
    273 #include "mozilla/gfx/BaseSize.h"
    274 #include "mozilla/gfx/Coord.h"
    275 #include "mozilla/gfx/Point.h"
    276 #include "mozilla/gfx/ScaleFactor.h"
    277 #include "mozilla/glean/DomMetrics.h"
    278 #include "mozilla/glean/DomUseCounterMetrics.h"
    279 #include "mozilla/intl/EncodingToLang.h"
    280 #include "mozilla/intl/LocaleService.h"
    281 #include "mozilla/ipc/IdleSchedulerChild.h"
    282 #include "mozilla/ipc/MessageChannel.h"
    283 #include "mozilla/net/ChannelEventQueue.h"
    284 #include "mozilla/net/Cookie.h"
    285 #include "mozilla/net/CookieCommons.h"
    286 #include "mozilla/net/CookieJarSettings.h"
    287 #include "mozilla/net/CookieParser.h"
    288 #include "mozilla/net/NeckoChannelParams.h"
    289 #include "mozilla/net/RequestContextService.h"
    290 #include "nsAboutProtocolUtils.h"
    291 #include "nsAtom.h"
    292 #include "nsAttrValue.h"
    293 #include "nsAttrValueInlines.h"
    294 #include "nsBaseHashtable.h"
    295 #include "nsBidiUtils.h"
    296 #include "nsCRT.h"
    297 #include "nsCSSProps.h"
    298 #include "nsCSSPseudoElements.h"
    299 #include "nsCSSRendering.h"
    300 #include "nsCanvasFrame.h"
    301 #include "nsCaseTreatment.h"
    302 #include "nsCharsetSource.h"
    303 #include "nsCommandManager.h"
    304 #include "nsCommandParams.h"
    305 #include "nsComponentManagerUtils.h"
    306 #include "nsContentCreatorFunctions.h"
    307 #include "nsContentList.h"
    308 #include "nsContentPermissionHelper.h"
    309 #include "nsContentSecurityUtils.h"
    310 #include "nsContentUtils.h"
    311 #include "nsCoord.h"
    312 #include "nsCycleCollectionNoteChild.h"
    313 #include "nsCycleCollectionTraversalCallback.h"
    314 #include "nsDOMAttributeMap.h"
    315 #include "nsDOMCaretPosition.h"
    316 #include "nsDOMNavigationTiming.h"
    317 #include "nsDOMString.h"
    318 #include "nsDeviceContext.h"
    319 #include "nsDocShell.h"
    320 #include "nsDocShellLoadTypes.h"
    321 #include "nsError.h"
    322 #include "nsEscape.h"
    323 #include "nsFocusManager.h"
    324 #include "nsFrameLoader.h"
    325 #include "nsFrameLoaderOwner.h"
    326 #include "nsGenericHTMLElement.h"
    327 #include "nsGlobalWindowInner.h"
    328 #include "nsGlobalWindowOuter.h"
    329 #include "nsHTMLDocument.h"
    330 #include "nsHtml5Module.h"
    331 #include "nsHtml5Parser.h"
    332 #include "nsHtml5TreeOpExecutor.h"
    333 #include "nsIAppWindow.h"
    334 #include "nsIAsyncShutdown.h"
    335 #include "nsIAuthPrompt.h"
    336 #include "nsIAuthPrompt2.h"
    337 #include "nsIBFCacheEntry.h"
    338 #include "nsIBaseWindow.h"
    339 #include "nsIBrowserChild.h"
    340 #include "nsIBrowserUsage.h"
    341 #include "nsICSSLoaderObserver.h"
    342 #include "nsICategoryManager.h"
    343 #include "nsICertOverrideService.h"
    344 #include "nsIClassifiedChannel.h"
    345 #include "nsIContent.h"
    346 #include "nsIContentInlines.h"
    347 #include "nsIContentPolicy.h"
    348 #include "nsIContentSecurityPolicy.h"
    349 #include "nsIContentSink.h"
    350 #include "nsICookieJarSettings.h"
    351 #include "nsICookieService.h"
    352 #include "nsIDNSService.h"
    353 #include "nsIDOMXULCommandDispatcher.h"
    354 #include "nsIDocShell.h"
    355 #include "nsIDocShellTreeItem.h"
    356 #include "nsIDocShellTreeOwner.h"
    357 #include "nsIDocumentActivity.h"
    358 #include "nsIDocumentEncoder.h"
    359 #include "nsIDocumentLoader.h"
    360 #include "nsIDocumentLoaderFactory.h"
    361 #include "nsIDocumentObserver.h"
    362 #include "nsIEditingSession.h"
    363 #include "nsIEditor.h"
    364 #include "nsIEffectiveTLDService.h"
    365 #include "nsIFile.h"
    366 #include "nsIFileChannel.h"
    367 #include "nsIFrame.h"
    368 #include "nsIGlobalObject.h"
    369 #include "nsIHTMLCollection.h"
    370 #include "nsIHttpChannel.h"
    371 #include "nsIHttpChannelInternal.h"
    372 #include "nsIIOService.h"
    373 #include "nsIImageLoadingContent.h"
    374 #include "nsIInlineSpellChecker.h"
    375 #include "nsIInputStreamChannel.h"
    376 #include "nsIInterfaceRequestorUtils.h"
    377 #include "nsILayoutHistoryState.h"
    378 #include "nsIMultiPartChannel.h"
    379 #include "nsIMutationObserver.h"
    380 #include "nsINSSErrorsService.h"
    381 #include "nsINamed.h"
    382 #include "nsINodeList.h"
    383 #include "nsIObjectLoadingContent.h"
    384 #include "nsIObserverService.h"
    385 #include "nsIParentalControlsService.h"
    386 #include "nsIPermission.h"
    387 #include "nsIPrompt.h"
    388 #include "nsIPropertyBag2.h"
    389 #include "nsIPublicKeyPinningService.h"
    390 #include "nsIReferrerInfo.h"
    391 #include "nsIRefreshURI.h"
    392 #include "nsIRequest.h"
    393 #include "nsIRequestContext.h"
    394 #include "nsIRunnable.h"
    395 #include "nsISHEntry.h"
    396 #include "nsIScriptElement.h"
    397 #include "nsIScriptError.h"
    398 #include "nsIScriptGlobalObject.h"
    399 #include "nsIScriptSecurityManager.h"
    400 #include "nsISecurityConsoleMessage.h"
    401 #include "nsISelectionController.h"
    402 #include "nsISerialEventTarget.h"
    403 #include "nsISimpleEnumerator.h"
    404 #include "nsISiteSecurityService.h"
    405 #include "nsISocketProvider.h"
    406 #include "nsISpeculativeConnect.h"
    407 #include "nsIStructuredCloneContainer.h"
    408 #include "nsIThread.h"
    409 #include "nsITimedChannel.h"
    410 #include "nsITimer.h"
    411 #include "nsITransportSecurityInfo.h"
    412 #include "nsIURIMutator.h"
    413 #include "nsIVariant.h"
    414 #include "nsIWeakReference.h"
    415 #include "nsIWebNavigation.h"
    416 #include "nsIWidget.h"
    417 #include "nsIX509Cert.h"
    418 #include "nsIX509CertValidity.h"
    419 #include "nsIXMLContentSink.h"
    420 #include "nsIXULRuntime.h"
    421 #include "nsImageLoadingContent.h"
    422 #include "nsImportModule.h"
    423 #include "nsLayoutUtils.h"
    424 #include "nsMenuPopupFrame.h"
    425 #include "nsMimeTypes.h"
    426 #include "nsNetCID.h"
    427 #include "nsNetUtil.h"
    428 #include "nsNodeInfoManager.h"
    429 #include "nsObjectLoadingContent.h"
    430 #include "nsPIDOMWindowInlines.h"
    431 #include "nsPIWindowRoot.h"
    432 #include "nsPoint.h"
    433 #include "nsPointerHashKeys.h"
    434 #include "nsPresContext.h"
    435 #include "nsQueryFrame.h"
    436 #include "nsQueryObject.h"
    437 #include "nsRange.h"
    438 #include "nsRect.h"
    439 #include "nsRefreshDriver.h"
    440 #include "nsSandboxFlags.h"
    441 #include "nsSerializationHelper.h"
    442 #include "nsServiceManagerUtils.h"
    443 #include "nsStringFlags.h"
    444 #include "nsStringIterator.h"
    445 #include "nsStyleSheetService.h"
    446 #include "nsStyleStruct.h"
    447 #include "nsStyleUtil.h"
    448 #include "nsSubDocumentFrame.h"
    449 #include "nsTextControlFrame.h"
    450 #include "nsTextNode.h"
    451 #include "nsURLHelper.h"
    452 #include "nsUnicharUtils.h"
    453 #include "nsWrapperCache.h"
    454 #include "nsWrapperCacheInlines.h"
    455 #include "nsXPCOMCID.h"
    456 #include "nsXULAppAPI.h"
    457 #include "nsXULCommandDispatcher.h"
    458 #include "nsXULElement.h"
    459 #include "nsXULPopupManager.h"
    460 #include "nsXULPrototypeDocument.h"
    461 #include "prthread.h"
    462 #include "prtime.h"
    463 #include "prtypes.h"
    464 #include "xpcpublic.h"
    465 
    466 // clang-format off
    467 #include "mozilla/Encoding.h"
    468 #include "encoding_rs.h"
    469 // clang-format on
    470 
    471 #define XML_DECLARATION_BITS_DECLARATION_EXISTS (1 << 0)
    472 #define XML_DECLARATION_BITS_ENCODING_EXISTS (1 << 1)
    473 #define XML_DECLARATION_BITS_STANDALONE_EXISTS (1 << 2)
    474 #define XML_DECLARATION_BITS_STANDALONE_YES (1 << 3)
    475 
    476 #define NS_MAX_DOCUMENT_WRITE_DEPTH 20
    477 
    478 mozilla::LazyLogModule gPageCacheLog("PageCache");
    479 mozilla::LazyLogModule gSHIPBFCacheLog("SHIPBFCache");
    480 mozilla::LazyLogModule gTimeoutDeferralLog("TimeoutDefer");
    481 mozilla::LazyLogModule gUseCountersLog("UseCounters");
    482 static mozilla::LazyLogModule gFingerprinterDetection("FingerprinterDetection");
    483 
    484 namespace mozilla {
    485 
    486 using namespace net;
    487 
    488 using performance::pageload_event::PageloadEventType;
    489 
    490 namespace dom {
    491 
    492 class Document::HeaderData {
    493 public:
    494  HeaderData(nsAtom* aField, const nsAString& aData)
    495      : mField(aField), mData(aData) {}
    496 
    497  ~HeaderData() {
    498    // Delete iteratively to avoid blowing up the stack, though it shouldn't
    499    // happen in practice.
    500    UniquePtr<HeaderData> next = std::move(mNext);
    501    while (next) {
    502      next = std::move(next->mNext);
    503    }
    504  }
    505 
    506  RefPtr<nsAtom> mField;
    507  nsString mData;
    508  UniquePtr<HeaderData> mNext;
    509 };
    510 
    511 AutoTArray<Document*, 8>* Document::sLoadingForegroundTopLevelContentDocument =
    512    nullptr;
    513 
    514 static LinkedList<Document>& AllDocumentsList() {
    515  static NeverDestroyed<LinkedList<Document>> sAllDocuments;
    516  return *sAllDocuments;
    517 }
    518 
    519 static LazyLogModule gDocumentLeakPRLog("DocumentLeak");
    520 static LazyLogModule gCspPRLog("CSP");
    521 LazyLogModule gUserInteractionPRLog("UserInteraction");
    522 
    523 static nsresult GetHttpChannelHelper(nsIChannel* aChannel,
    524                                     nsIHttpChannel** aHttpChannel) {
    525  nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(aChannel);
    526  if (httpChannel) {
    527    httpChannel.forget(aHttpChannel);
    528    return NS_OK;
    529  }
    530 
    531  nsCOMPtr<nsIMultiPartChannel> multipart = do_QueryInterface(aChannel);
    532  if (!multipart) {
    533    *aHttpChannel = nullptr;
    534    return NS_OK;
    535  }
    536 
    537  nsCOMPtr<nsIChannel> baseChannel;
    538  nsresult rv = multipart->GetBaseChannel(getter_AddRefs(baseChannel));
    539  if (NS_WARN_IF(NS_FAILED(rv))) {
    540    return rv;
    541  }
    542 
    543  httpChannel = do_QueryInterface(baseChannel);
    544  httpChannel.forget(aHttpChannel);
    545 
    546  return NS_OK;
    547 }
    548 
    549 }  // namespace dom
    550 
    551 #define NAME_NOT_VALID ((nsSimpleContentList*)1)
    552 
    553 IdentifierMapEntry::IdentifierMapEntry(
    554    const IdentifierMapEntry::DependentAtomOrString* aKey)
    555    : mKey(aKey ? *aKey : nullptr) {}
    556 
    557 void IdentifierMapEntry::Traverse(
    558    nsCycleCollectionTraversalCallback* aCallback) {
    559  NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*aCallback,
    560                                     "mIdentifierMap mNameContentList");
    561  aCallback->NoteXPCOMChild(static_cast<nsINodeList*>(mNameContentList));
    562 
    563  NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*aCallback,
    564                                     "mIdentifierMap mDocumentNameContentList");
    565  aCallback->NoteXPCOMChild(
    566      static_cast<nsINodeList*>(mDocumentNameContentList));
    567 
    568  if (mImageElement) {
    569    NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*aCallback,
    570                                       "mIdentifierMap mImageElement element");
    571    nsIContent* imageElement = mImageElement;
    572    aCallback->NoteXPCOMChild(imageElement);
    573  }
    574 }
    575 
    576 bool IdentifierMapEntry::IsEmpty() {
    577  return mIdContentList.IsEmpty() && !mNameContentList &&
    578         !mDocumentNameContentList && !mChangeCallbacks && !mImageElement;
    579 }
    580 
    581 bool IdentifierMapEntry::HasNameElement() const {
    582  return mNameContentList && mNameContentList->Length() != 0;
    583 }
    584 
    585 void IdentifierMapEntry::AddContentChangeCallback(
    586    Document::IDTargetObserver aCallback, void* aData, bool aForImage) {
    587  if (!mChangeCallbacks) {
    588    mChangeCallbacks = MakeUnique<nsTHashtable<ChangeCallbackEntry>>();
    589  }
    590 
    591  ChangeCallback cc = {aCallback, aData, aForImage};
    592  mChangeCallbacks->PutEntry(cc);
    593 }
    594 
    595 void IdentifierMapEntry::RemoveContentChangeCallback(
    596    Document::IDTargetObserver aCallback, void* aData, bool aForImage) {
    597  if (!mChangeCallbacks) return;
    598  ChangeCallback cc = {aCallback, aData, aForImage};
    599  mChangeCallbacks->RemoveEntry(cc);
    600  if (mChangeCallbacks->Count() == 0) {
    601    mChangeCallbacks = nullptr;
    602  }
    603 }
    604 
    605 void IdentifierMapEntry::FireChangeCallbacks(Element* aOldElement,
    606                                             Element* aNewElement,
    607                                             bool aImageOnly) {
    608  if (!mChangeCallbacks) return;
    609 
    610  for (auto iter = mChangeCallbacks->Iter(); !iter.Done(); iter.Next()) {
    611    IdentifierMapEntry::ChangeCallbackEntry* entry = iter.Get();
    612    // Don't fire image changes for non-image observers, and don't fire element
    613    // changes for image observers when an image override is active.
    614    if (entry->mKey.mForImage ? (mImageElement && !aImageOnly) : aImageOnly) {
    615      continue;
    616    }
    617 
    618    if (!entry->mKey.mCallback(aOldElement, aNewElement, entry->mKey.mData)) {
    619      iter.Remove();
    620    }
    621  }
    622 }
    623 
    624 void IdentifierMapEntry::AddIdElement(Element* aElement) {
    625  MOZ_ASSERT(aElement, "Must have element");
    626  MOZ_ASSERT(!mIdContentList.Contains(nullptr), "Why is null in our list?");
    627 
    628  size_t index = mIdContentList.Insert(*aElement);
    629  if (index == 0) {
    630    Element* oldElement = mIdContentList.SafeElementAt(1, nullptr);
    631    FireChangeCallbacks(oldElement, aElement);
    632  }
    633 }
    634 
    635 void IdentifierMapEntry::RemoveIdElement(Element* aElement) {
    636  MOZ_ASSERT(aElement, "Missing element");
    637 
    638  // This should only be called while the document is in an update.
    639  // Assertions near the call to this method guarantee this.
    640 
    641  // This could fire in OOM situations
    642  // Only assert this in HTML documents for now as XUL does all sorts of weird
    643  // crap.
    644  NS_ASSERTION(!aElement->OwnerDoc()->IsHTMLDocument() ||
    645                   mIdContentList.Contains(aElement),
    646               "Removing id entry that doesn't exist");
    647 
    648  // XXXbz should this ever Compact() I guess when all the content is gone
    649  // we'll just get cleaned up in the natural order of things...
    650  Element* currentElement = mIdContentList.SafeElementAt(0, nullptr);
    651  mIdContentList.RemoveElement(*aElement);
    652  if (currentElement == aElement) {
    653    FireChangeCallbacks(currentElement,
    654                        mIdContentList.SafeElementAt(0, nullptr));
    655  }
    656 }
    657 
    658 void IdentifierMapEntry::SetImageElement(Element* aElement) {
    659  Element* oldElement = GetImageIdElement();
    660  mImageElement = aElement;
    661  Element* newElement = GetImageIdElement();
    662  if (oldElement != newElement) {
    663    FireChangeCallbacks(oldElement, newElement, true);
    664  }
    665 }
    666 
    667 void IdentifierMapEntry::ClearAndNotify() {
    668  Element* currentElement = mIdContentList.SafeElementAt(0, nullptr);
    669  mIdContentList.Clear();
    670  if (currentElement) {
    671    FireChangeCallbacks(currentElement, nullptr);
    672  }
    673  mNameContentList = nullptr;
    674  mDocumentNameContentList = nullptr;
    675  if (mImageElement) {
    676    SetImageElement(nullptr);
    677  }
    678  mChangeCallbacks = nullptr;
    679 }
    680 
    681 namespace dom {
    682 
    683 class SimpleHTMLCollection final : public nsSimpleContentList,
    684                                   public nsIHTMLCollection {
    685 public:
    686  explicit SimpleHTMLCollection(nsINode* aRoot) : nsSimpleContentList(aRoot) {}
    687 
    688  NS_DECL_ISUPPORTS_INHERITED
    689 
    690  virtual nsINode* GetParentObject() override {
    691    return nsSimpleContentList::GetParentObject();
    692  }
    693  virtual uint32_t Length() override { return nsSimpleContentList::Length(); }
    694  virtual Element* GetElementAt(uint32_t aIndex) override {
    695    return mElements.SafeElementAt(aIndex)->AsElement();
    696  }
    697 
    698  virtual Element* GetFirstNamedElement(const nsAString& aName,
    699                                        bool& aFound) override {
    700    aFound = false;
    701    RefPtr<nsAtom> name = NS_Atomize(aName);
    702    for (uint32_t i = 0; i < mElements.Length(); i++) {
    703      MOZ_DIAGNOSTIC_ASSERT(mElements[i]);
    704      Element* element = mElements[i]->AsElement();
    705      if (element->GetID() == name ||
    706          (element->HasName() &&
    707           element->GetParsedAttr(nsGkAtoms::name)->GetAtomValue() == name)) {
    708        aFound = true;
    709        return element;
    710      }
    711    }
    712    return nullptr;
    713  }
    714 
    715  virtual void GetSupportedNames(nsTArray<nsString>& aNames) override {
    716    AutoTArray<nsAtom*, 8> atoms;
    717    for (uint32_t i = 0; i < mElements.Length(); i++) {
    718      MOZ_DIAGNOSTIC_ASSERT(mElements[i]);
    719      Element* element = mElements[i]->AsElement();
    720 
    721      nsAtom* id = element->GetID();
    722      MOZ_ASSERT(id != nsGkAtoms::_empty);
    723      if (id && !atoms.Contains(id)) {
    724        atoms.AppendElement(id);
    725      }
    726 
    727      if (element->HasName()) {
    728        nsAtom* name = element->GetParsedAttr(nsGkAtoms::name)->GetAtomValue();
    729        MOZ_ASSERT(name && name != nsGkAtoms::_empty);
    730        if (name && !atoms.Contains(name)) {
    731          atoms.AppendElement(name);
    732        }
    733      }
    734    }
    735 
    736    nsString* names = aNames.AppendElements(atoms.Length());
    737    for (uint32_t i = 0; i < atoms.Length(); i++) {
    738      atoms[i]->ToString(names[i]);
    739    }
    740  }
    741 
    742  virtual JSObject* GetWrapperPreserveColorInternal() override {
    743    return nsWrapperCache::GetWrapperPreserveColor();
    744  }
    745  virtual void PreserveWrapperInternal(
    746      nsISupports* aScriptObjectHolder) override {
    747    nsWrapperCache::PreserveWrapper(aScriptObjectHolder);
    748  }
    749  virtual JSObject* WrapObject(JSContext* aCx,
    750                               JS::Handle<JSObject*> aGivenProto) override {
    751    return HTMLCollection_Binding::Wrap(aCx, this, aGivenProto);
    752  }
    753 
    754  using nsBaseContentList::Item;
    755 
    756 private:
    757  virtual ~SimpleHTMLCollection() = default;
    758 };
    759 
    760 NS_IMPL_ISUPPORTS_INHERITED(SimpleHTMLCollection, nsSimpleContentList,
    761                            nsIHTMLCollection)
    762 
    763 }  // namespace dom
    764 
    765 void IdentifierMapEntry::AddNameElement(nsINode* aNode, Element* aElement) {
    766  if (!mNameContentList) {
    767    mNameContentList = new dom::SimpleHTMLCollection(aNode);
    768  }
    769 
    770  mNameContentList->AppendElement(aElement);
    771 }
    772 
    773 void IdentifierMapEntry::RemoveNameElement(Element* aElement) {
    774  if (mNameContentList) {
    775    mNameContentList->RemoveElement(aElement);
    776  }
    777 }
    778 
    779 void IdentifierMapEntry::AddDocumentNameElement(
    780    Document* aDocument, nsGenericHTMLElement* aElement) {
    781  if (!mDocumentNameContentList) {
    782    mDocumentNameContentList = new dom::SimpleHTMLCollection(aDocument);
    783  }
    784 
    785  mDocumentNameContentList->AppendElement(aElement);
    786 }
    787 
    788 void IdentifierMapEntry::RemoveDocumentNameElement(
    789    nsGenericHTMLElement* aElement) {
    790  if (mDocumentNameContentList) {
    791    mDocumentNameContentList->RemoveElement(aElement);
    792  }
    793 }
    794 
    795 bool IdentifierMapEntry::HasDocumentNameElement() const {
    796  return mDocumentNameContentList && mDocumentNameContentList->Length() != 0;
    797 }
    798 
    799 bool IdentifierMapEntry::HasIdElementExposedAsHTMLDocumentProperty() const {
    800  Element* idElement = GetIdElement();
    801  return idElement &&
    802         nsGenericHTMLElement::ShouldExposeIdAsHTMLDocumentProperty(idElement);
    803 }
    804 
    805 size_t IdentifierMapEntry::SizeOfExcludingThis(
    806    MallocSizeOf aMallocSizeOf) const {
    807  return mKey.mString.SizeOfExcludingThisIfUnshared(aMallocSizeOf);
    808 }
    809 
    810 // Helper structs for the content->subdoc map
    811 
    812 class SubDocMapEntry : public PLDHashEntryHdr {
    813 public:
    814  // Both of these are strong references
    815  dom::Element* mKey;  // must be first, to look like PLDHashEntryStub
    816  dom::Document* mSubDocument;
    817 };
    818 
    819 class OnloadBlocker final : public nsIRequest {
    820 public:
    821  OnloadBlocker() = default;
    822 
    823  NS_DECL_ISUPPORTS
    824  NS_DECL_NSIREQUEST
    825 
    826 private:
    827  ~OnloadBlocker() = default;
    828 };
    829 
    830 NS_IMPL_ISUPPORTS(OnloadBlocker, nsIRequest)
    831 
    832 NS_IMETHODIMP
    833 OnloadBlocker::GetName(nsACString& aResult) {
    834  aResult.AssignLiteral("about:document-onload-blocker");
    835  return NS_OK;
    836 }
    837 
    838 NS_IMETHODIMP
    839 OnloadBlocker::IsPending(bool* _retval) {
    840  *_retval = true;
    841  return NS_OK;
    842 }
    843 
    844 NS_IMETHODIMP
    845 OnloadBlocker::GetStatus(nsresult* status) {
    846  *status = NS_OK;
    847  return NS_OK;
    848 }
    849 
    850 NS_IMETHODIMP OnloadBlocker::SetCanceledReason(const nsACString& aReason) {
    851  return SetCanceledReasonImpl(aReason);
    852 }
    853 
    854 NS_IMETHODIMP OnloadBlocker::GetCanceledReason(nsACString& aReason) {
    855  return GetCanceledReasonImpl(aReason);
    856 }
    857 
    858 NS_IMETHODIMP OnloadBlocker::CancelWithReason(nsresult aStatus,
    859                                              const nsACString& aReason) {
    860  return CancelWithReasonImpl(aStatus, aReason);
    861 }
    862 NS_IMETHODIMP
    863 OnloadBlocker::Cancel(nsresult status) { return NS_OK; }
    864 NS_IMETHODIMP
    865 OnloadBlocker::Suspend(void) { return NS_OK; }
    866 NS_IMETHODIMP
    867 OnloadBlocker::Resume(void) { return NS_OK; }
    868 
    869 NS_IMETHODIMP
    870 OnloadBlocker::GetLoadGroup(nsILoadGroup** aLoadGroup) {
    871  *aLoadGroup = nullptr;
    872  return NS_OK;
    873 }
    874 
    875 NS_IMETHODIMP
    876 OnloadBlocker::SetLoadGroup(nsILoadGroup* aLoadGroup) { return NS_OK; }
    877 
    878 NS_IMETHODIMP
    879 OnloadBlocker::GetLoadFlags(nsLoadFlags* aLoadFlags) {
    880  *aLoadFlags = nsIRequest::LOAD_NORMAL;
    881  return NS_OK;
    882 }
    883 
    884 NS_IMETHODIMP
    885 OnloadBlocker::GetTRRMode(nsIRequest::TRRMode* aTRRMode) {
    886  return GetTRRModeImpl(aTRRMode);
    887 }
    888 
    889 NS_IMETHODIMP
    890 OnloadBlocker::SetTRRMode(nsIRequest::TRRMode aTRRMode) {
    891  return SetTRRModeImpl(aTRRMode);
    892 }
    893 
    894 NS_IMETHODIMP
    895 OnloadBlocker::SetLoadFlags(nsLoadFlags aLoadFlags) { return NS_OK; }
    896 
    897 // ==================================================================
    898 
    899 namespace dom {
    900 
    901 ExternalResourceMap::ExternalResourceMap() : mHaveShutDown(false) {}
    902 
    903 Document* ExternalResourceMap::RequestResource(
    904    nsIURI* aURI, nsIReferrerInfo* aReferrerInfo, nsINode* aRequestingNode,
    905    Document* aDisplayDocument, ExternalResourceLoad** aPendingLoad) {
    906  // If we ever start allowing non-same-origin loads here, we might need to do
    907  // something interesting with aRequestingPrincipal even for the hashtable
    908  // gets.
    909  MOZ_ASSERT(aURI, "Must have a URI");
    910  MOZ_ASSERT(aRequestingNode, "Must have a node");
    911  MOZ_ASSERT(aReferrerInfo, "Must have a referrerInfo");
    912  *aPendingLoad = nullptr;
    913  if (mHaveShutDown) {
    914    return nullptr;
    915  }
    916 
    917  // First, make sure we strip the ref from aURI.
    918  nsCOMPtr<nsIURI> clone;
    919  nsresult rv = NS_GetURIWithoutRef(aURI, getter_AddRefs(clone));
    920  if (NS_FAILED(rv) || !clone) {
    921    return nullptr;
    922  }
    923 
    924  ExternalResource* resource;
    925  mMap.Get(clone, &resource);
    926  if (resource) {
    927    return resource->mDocument;
    928  }
    929 
    930  bool loadStartSucceeded =
    931      mPendingLoads.WithEntryHandle(clone, [&](auto&& loadEntry) {
    932        if (!loadEntry) {
    933          loadEntry.Insert(MakeRefPtr<PendingLoad>(aDisplayDocument));
    934 
    935          if (NS_FAILED(loadEntry.Data()->StartLoad(clone, aReferrerInfo,
    936                                                    aRequestingNode))) {
    937            return false;
    938          }
    939        }
    940 
    941        RefPtr<PendingLoad> load(loadEntry.Data());
    942        load.forget(aPendingLoad);
    943        return true;
    944      });
    945  if (!loadStartSucceeded) {
    946    // Make sure we don't thrash things by trying this load again, since
    947    // chances are it failed for good reasons (security check, etc).
    948    // This must be done outside the WithEntryHandle functor, as it accesses
    949    // mPendingLoads.
    950    AddExternalResource(clone, nullptr, nullptr, aDisplayDocument);
    951  }
    952 
    953  return nullptr;
    954 }
    955 
    956 void ExternalResourceMap::EnumerateResources(SubDocEnumFunc aCallback) const {
    957  nsTArray<RefPtr<Document>> docs(mMap.Count());
    958  for (const auto& entry : mMap.Values()) {
    959    if (Document* doc = entry->mDocument) {
    960      docs.AppendElement(doc);
    961    }
    962  }
    963  for (auto& doc : docs) {
    964    if (aCallback(*doc) == CallState::Stop) {
    965      return;
    966    }
    967  }
    968 }
    969 
    970 void ExternalResourceMap::CollectDescendantDocuments(
    971    nsTArray<RefPtr<Document>>& aDocs, SubDocTestFunc aCallback) const {
    972  for (const auto& entry : mMap.Values()) {
    973    if (Document* doc = entry->mDocument) {
    974      if (aCallback(doc)) {
    975        aDocs.AppendElement(doc);
    976      }
    977      doc->CollectDescendantDocuments(aDocs, Document::IncludeSubResources::Yes,
    978                                      aCallback);
    979    }
    980  }
    981 }
    982 
    983 void ExternalResourceMap::Traverse(
    984    nsCycleCollectionTraversalCallback* aCallback) const {
    985  // mPendingLoads will get cleared out as the requests complete, so
    986  // no need to worry about those here.
    987  for (const auto& entry : mMap) {
    988    ExternalResourceMap::ExternalResource* resource = entry.GetWeak();
    989 
    990    NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*aCallback,
    991                                       "mExternalResourceMap.mMap entry"
    992                                       "->mDocument");
    993    aCallback->NoteXPCOMChild(ToSupports(resource->mDocument));
    994 
    995    NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*aCallback,
    996                                       "mExternalResourceMap.mMap entry"
    997                                       "->mViewer");
    998    aCallback->NoteXPCOMChild(resource->mViewer);
    999 
   1000    NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*aCallback,
   1001                                       "mExternalResourceMap.mMap entry"
   1002                                       "->mLoadGroup");
   1003    aCallback->NoteXPCOMChild(resource->mLoadGroup);
   1004  }
   1005 }
   1006 
   1007 void ExternalResourceMap::HideViewers() {
   1008  for (const auto& entry : mMap) {
   1009    nsCOMPtr<nsIDocumentViewer> viewer = entry.GetData()->mViewer;
   1010    if (viewer) {
   1011      viewer->Hide();
   1012    }
   1013  }
   1014 }
   1015 
   1016 void ExternalResourceMap::ShowViewers() {
   1017  for (const auto& entry : mMap) {
   1018    nsCOMPtr<nsIDocumentViewer> viewer = entry.GetData()->mViewer;
   1019    if (viewer) {
   1020      viewer->Show();
   1021    }
   1022  }
   1023 }
   1024 
   1025 void TransferShowingState(Document* aFromDoc, Document* aToDoc) {
   1026  MOZ_ASSERT(aFromDoc && aToDoc, "transferring showing state from/to null doc");
   1027 
   1028  if (aFromDoc->IsShowing()) {
   1029    aToDoc->OnPageShow(true, nullptr);
   1030  }
   1031 }
   1032 
   1033 nsresult ExternalResourceMap::AddExternalResource(nsIURI* aURI,
   1034                                                  nsIDocumentViewer* aViewer,
   1035                                                  nsILoadGroup* aLoadGroup,
   1036                                                  Document* aDisplayDocument) {
   1037  MOZ_ASSERT(aURI, "Unexpected call");
   1038  MOZ_ASSERT((aViewer && aLoadGroup) || (!aViewer && !aLoadGroup),
   1039             "Must have both or neither");
   1040 
   1041  RefPtr<PendingLoad> load;
   1042  mPendingLoads.Remove(aURI, getter_AddRefs(load));
   1043 
   1044  nsresult rv = NS_OK;
   1045 
   1046  nsCOMPtr<Document> doc;
   1047  if (aViewer) {
   1048    doc = aViewer->GetDocument();
   1049    NS_ASSERTION(doc, "Must have a document");
   1050 
   1051    doc->SetDisplayDocument(aDisplayDocument);
   1052 
   1053    // Make sure that hiding our viewer will tear down its presentation.
   1054    aViewer->SetSticky(false);
   1055 
   1056    rv = aViewer->Init(nullptr, LayoutDeviceIntRect(), nullptr);
   1057    if (NS_SUCCEEDED(rv)) {
   1058      rv = aViewer->Open(nullptr, nullptr);
   1059    }
   1060 
   1061    if (NS_FAILED(rv)) {
   1062      doc = nullptr;
   1063      aViewer = nullptr;
   1064      aLoadGroup = nullptr;
   1065    }
   1066  }
   1067 
   1068  ExternalResource* newResource =
   1069      mMap.InsertOrUpdate(aURI, MakeUnique<ExternalResource>()).get();
   1070 
   1071  newResource->mDocument = doc;
   1072  newResource->mViewer = aViewer;
   1073  newResource->mLoadGroup = aLoadGroup;
   1074  if (doc) {
   1075    if (nsPresContext* pc = doc->GetPresContext()) {
   1076      pc->RecomputeBrowsingContextDependentData();
   1077    }
   1078    TransferShowingState(aDisplayDocument, doc);
   1079  }
   1080 
   1081  const nsTArray<nsCOMPtr<nsIObserver>>& obs = load->Observers();
   1082  for (uint32_t i = 0; i < obs.Length(); ++i) {
   1083    obs[i]->Observe(ToSupports(doc), "external-resource-document-created",
   1084                    nullptr);
   1085  }
   1086 
   1087  return rv;
   1088 }
   1089 
   1090 NS_IMPL_ISUPPORTS(ExternalResourceMap::PendingLoad, nsIStreamListener,
   1091                  nsIRequestObserver)
   1092 
   1093 NS_IMETHODIMP
   1094 ExternalResourceMap::PendingLoad::OnStartRequest(nsIRequest* aRequest) {
   1095  ExternalResourceMap& map = mDisplayDocument->ExternalResourceMap();
   1096  if (map.HaveShutDown()) {
   1097    return NS_BINDING_ABORTED;
   1098  }
   1099 
   1100  nsCOMPtr<nsIDocumentViewer> viewer;
   1101  nsCOMPtr<nsILoadGroup> loadGroup;
   1102  nsresult rv =
   1103      SetupViewer(aRequest, getter_AddRefs(viewer), getter_AddRefs(loadGroup));
   1104 
   1105  // Make sure to do this no matter what
   1106  nsresult rv2 =
   1107      map.AddExternalResource(mURI, viewer, loadGroup, mDisplayDocument);
   1108  if (NS_FAILED(rv)) {
   1109    return rv;
   1110  }
   1111  if (NS_FAILED(rv2)) {
   1112    mTargetListener = nullptr;
   1113    return rv2;
   1114  }
   1115 
   1116  return mTargetListener->OnStartRequest(aRequest);
   1117 }
   1118 
   1119 nsresult ExternalResourceMap::PendingLoad::SetupViewer(
   1120    nsIRequest* aRequest, nsIDocumentViewer** aViewer,
   1121    nsILoadGroup** aLoadGroup) {
   1122  MOZ_ASSERT(!mTargetListener, "Unexpected call to OnStartRequest");
   1123  *aViewer = nullptr;
   1124  *aLoadGroup = nullptr;
   1125 
   1126  nsCOMPtr<nsIChannel> chan(do_QueryInterface(aRequest));
   1127  NS_ENSURE_TRUE(chan, NS_ERROR_UNEXPECTED);
   1128 
   1129  nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(aRequest));
   1130  if (httpChannel) {
   1131    bool requestSucceeded;
   1132    if (NS_FAILED(httpChannel->GetRequestSucceeded(&requestSucceeded)) ||
   1133        !requestSucceeded) {
   1134      // Bail out on this load, since it looks like we have an HTTP error page
   1135      return NS_BINDING_ABORTED;
   1136    }
   1137  }
   1138 
   1139  nsAutoCString type;
   1140  chan->GetContentType(type);
   1141 
   1142  nsCOMPtr<nsILoadGroup> loadGroup;
   1143  chan->GetLoadGroup(getter_AddRefs(loadGroup));
   1144 
   1145  // Give this document its own loadgroup
   1146  nsCOMPtr<nsILoadGroup> newLoadGroup =
   1147      do_CreateInstance(NS_LOADGROUP_CONTRACTID);
   1148  NS_ENSURE_TRUE(newLoadGroup, NS_ERROR_OUT_OF_MEMORY);
   1149  newLoadGroup->SetLoadGroup(loadGroup);
   1150 
   1151  nsCOMPtr<nsIInterfaceRequestor> callbacks;
   1152  loadGroup->GetNotificationCallbacks(getter_AddRefs(callbacks));
   1153 
   1154  nsCOMPtr<nsIInterfaceRequestor> newCallbacks =
   1155      new LoadgroupCallbacks(callbacks);
   1156  newLoadGroup->SetNotificationCallbacks(newCallbacks);
   1157 
   1158  nsCOMPtr<nsIDocumentLoaderFactory> docLoaderFactory =
   1159      nsContentUtils::FindInternalDocumentViewer(type);
   1160  NS_ENSURE_TRUE(docLoaderFactory, NS_ERROR_NOT_AVAILABLE);
   1161 
   1162  nsCOMPtr<nsIDocumentViewer> viewer;
   1163  nsCOMPtr<nsIStreamListener> listener;
   1164  nsresult rv = docLoaderFactory->CreateInstance(
   1165      "external-resource", chan, newLoadGroup, type, nullptr, nullptr,
   1166      getter_AddRefs(listener), getter_AddRefs(viewer));
   1167  NS_ENSURE_SUCCESS(rv, rv);
   1168  NS_ENSURE_TRUE(viewer, NS_ERROR_UNEXPECTED);
   1169 
   1170  nsCOMPtr<nsIParser> parser = do_QueryInterface(listener);
   1171  if (!parser) {
   1172    /// We don't want to deal with the various fake documents yet
   1173    return NS_ERROR_NOT_IMPLEMENTED;
   1174  }
   1175 
   1176  // We can't handle HTML and other weird things here yet.
   1177  nsIContentSink* sink = parser->GetContentSink();
   1178  nsCOMPtr<nsIXMLContentSink> xmlSink = do_QueryInterface(sink);
   1179  if (!xmlSink) {
   1180    return NS_ERROR_NOT_IMPLEMENTED;
   1181  }
   1182 
   1183  listener.swap(mTargetListener);
   1184  viewer.forget(aViewer);
   1185  newLoadGroup.forget(aLoadGroup);
   1186  return NS_OK;
   1187 }
   1188 
   1189 NS_IMETHODIMP
   1190 ExternalResourceMap::PendingLoad::OnDataAvailable(nsIRequest* aRequest,
   1191                                                  nsIInputStream* aStream,
   1192                                                  uint64_t aOffset,
   1193                                                  uint32_t aCount) {
   1194  // mTargetListener might be null if SetupViewer or AddExternalResource failed.
   1195  NS_ENSURE_TRUE(mTargetListener, NS_ERROR_FAILURE);
   1196  if (mDisplayDocument->ExternalResourceMap().HaveShutDown()) {
   1197    return NS_BINDING_ABORTED;
   1198  }
   1199  return mTargetListener->OnDataAvailable(aRequest, aStream, aOffset, aCount);
   1200 }
   1201 
   1202 NS_IMETHODIMP
   1203 ExternalResourceMap::PendingLoad::OnStopRequest(nsIRequest* aRequest,
   1204                                                nsresult aStatus) {
   1205  // mTargetListener might be null if SetupViewer or AddExternalResource failed
   1206  if (mTargetListener) {
   1207    nsCOMPtr<nsIStreamListener> listener;
   1208    mTargetListener.swap(listener);
   1209    return listener->OnStopRequest(aRequest, aStatus);
   1210  }
   1211 
   1212  return NS_OK;
   1213 }
   1214 
   1215 nsresult ExternalResourceMap::PendingLoad::StartLoad(
   1216    nsIURI* aURI, nsIReferrerInfo* aReferrerInfo, nsINode* aRequestingNode) {
   1217  MOZ_ASSERT(aURI, "Must have a URI");
   1218  MOZ_ASSERT(aRequestingNode, "Must have a node");
   1219  MOZ_ASSERT(aReferrerInfo, "Must have a referrerInfo");
   1220 
   1221  nsCOMPtr<nsILoadGroup> loadGroup =
   1222      aRequestingNode->OwnerDoc()->GetDocumentLoadGroup();
   1223 
   1224  nsresult rv = NS_OK;
   1225  nsCOMPtr<nsIChannel> channel;
   1226  rv = NS_NewChannel(getter_AddRefs(channel), aURI, aRequestingNode,
   1227                     nsILoadInfo::SEC_REQUIRE_SAME_ORIGIN_INHERITS_SEC_CONTEXT,
   1228                     nsIContentPolicy::TYPE_INTERNAL_EXTERNAL_RESOURCE,
   1229                     nullptr,  // aPerformanceStorage
   1230                     loadGroup);
   1231  NS_ENSURE_SUCCESS(rv, rv);
   1232 
   1233  nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(channel));
   1234  if (httpChannel) {
   1235    rv = httpChannel->SetReferrerInfo(aReferrerInfo);
   1236    (void)NS_WARN_IF(NS_FAILED(rv));
   1237  }
   1238 
   1239  mURI = aURI;
   1240 
   1241  return channel->AsyncOpen(this);
   1242 }
   1243 
   1244 NS_IMPL_ISUPPORTS(ExternalResourceMap::LoadgroupCallbacks,
   1245                  nsIInterfaceRequestor)
   1246 
   1247 #define IMPL_SHIM(_i) \
   1248  NS_IMPL_ISUPPORTS(ExternalResourceMap::LoadgroupCallbacks::_i##Shim, _i)
   1249 
   1250 IMPL_SHIM(nsILoadContext)
   1251 IMPL_SHIM(nsIProgressEventSink)
   1252 IMPL_SHIM(nsIChannelEventSink)
   1253 
   1254 #undef IMPL_SHIM
   1255 
   1256 #define IID_IS(_i) aIID.Equals(NS_GET_IID(_i))
   1257 
   1258 #define TRY_SHIM(_i)                                 \
   1259  PR_BEGIN_MACRO                                     \
   1260  if (IID_IS(_i)) {                                  \
   1261    nsCOMPtr<_i> real = do_GetInterface(mCallbacks); \
   1262    if (!real) {                                     \
   1263      return NS_NOINTERFACE;                         \
   1264    }                                                \
   1265    nsCOMPtr<_i> shim = new _i##Shim(this, real);    \
   1266    shim.forget(aSink);                              \
   1267    return NS_OK;                                    \
   1268  }                                                  \
   1269  PR_END_MACRO
   1270 
   1271 NS_IMETHODIMP
   1272 ExternalResourceMap::LoadgroupCallbacks::GetInterface(const nsIID& aIID,
   1273                                                      void** aSink) {
   1274  if (mCallbacks && (IID_IS(nsIPrompt) || IID_IS(nsIAuthPrompt) ||
   1275                     IID_IS(nsIAuthPrompt2) || IID_IS(nsIBrowserChild))) {
   1276    return mCallbacks->GetInterface(aIID, aSink);
   1277  }
   1278 
   1279  *aSink = nullptr;
   1280 
   1281  TRY_SHIM(nsILoadContext);
   1282  TRY_SHIM(nsIProgressEventSink);
   1283  TRY_SHIM(nsIChannelEventSink);
   1284 
   1285  return NS_NOINTERFACE;
   1286 }
   1287 
   1288 #undef TRY_SHIM
   1289 #undef IID_IS
   1290 
   1291 ExternalResourceMap::ExternalResource::~ExternalResource() {
   1292  if (mViewer) {
   1293    mViewer->Close(nullptr);
   1294    mViewer->Destroy();
   1295  }
   1296 }
   1297 
   1298 // ==================================================================
   1299 // =
   1300 // ==================================================================
   1301 
   1302 // If we ever have an nsIDocumentObserver notification for stylesheet title
   1303 // changes we should update the list from that instead of overriding
   1304 // EnsureFresh.
   1305 class DOMStyleSheetSetList final : public DOMStringList {
   1306 public:
   1307  explicit DOMStyleSheetSetList(Document* aDocument);
   1308 
   1309  void Disconnect() { mDocument = nullptr; }
   1310 
   1311  virtual void EnsureFresh() override;
   1312 
   1313 protected:
   1314  Document* mDocument;  // Our document; weak ref.  It'll let us know if it
   1315                        // dies.
   1316 };
   1317 
   1318 DOMStyleSheetSetList::DOMStyleSheetSetList(Document* aDocument)
   1319    : mDocument(aDocument) {
   1320  NS_ASSERTION(mDocument, "Must have document!");
   1321 }
   1322 
   1323 void DOMStyleSheetSetList::EnsureFresh() {
   1324  MOZ_ASSERT(NS_IsMainThread());
   1325 
   1326  mNames.Clear();
   1327 
   1328  if (!mDocument) {
   1329    return;  // Spec says "no exceptions", and we have no style sets if we have
   1330             // no document, for sure
   1331  }
   1332 
   1333  size_t count = mDocument->SheetCount();
   1334  nsAutoString title;
   1335  for (size_t index = 0; index < count; index++) {
   1336    StyleSheet* sheet = mDocument->SheetAt(index);
   1337    NS_ASSERTION(sheet, "Null sheet in sheet list!");
   1338    sheet->GetTitle(title);
   1339    if (!title.IsEmpty() && !mNames.Contains(title) && !Add(title)) {
   1340      return;
   1341    }
   1342  }
   1343 }
   1344 
   1345 Document::PendingFrameStaticClone::~PendingFrameStaticClone() = default;
   1346 
   1347 // ==================================================================
   1348 // =
   1349 // ==================================================================
   1350 
   1351 Document::InternalCommandDataHashtable*
   1352    Document::sInternalCommandDataHashtable = nullptr;
   1353 
   1354 // static
   1355 void Document::Shutdown() {
   1356  if (sInternalCommandDataHashtable) {
   1357    sInternalCommandDataHashtable->Clear();
   1358    delete sInternalCommandDataHashtable;
   1359    sInternalCommandDataHashtable = nullptr;
   1360  }
   1361 }
   1362 
   1363 Document::Document(const char* aContentType,
   1364                   mozilla::dom::LoadedAsData aLoadedAsData)
   1365    : nsINode(nullptr),
   1366      DocumentOrShadowRoot(this),
   1367      mCharacterSet(WINDOWS_1252_ENCODING),
   1368      mCharacterSetSource(0),
   1369      mParentDocument(nullptr),
   1370      mCachedRootElement(nullptr),
   1371      mNodeInfoManager(nullptr),
   1372 #ifdef DEBUG
   1373      mStyledLinksCleared(false),
   1374 #endif
   1375      mInitialStatus(Document::InitialStatus::NeverInitial),
   1376      mCachedStateObjectValid(false),
   1377      mBlockAllMixedContent(false),
   1378      mBlockAllMixedContentPreloads(false),
   1379      mUpgradeInsecureRequests(false),
   1380      mUpgradeInsecurePreloads(false),
   1381      mDevToolsWatchingDOMMutations(false),
   1382      mLoadedAsData(aLoadedAsData == LoadedAsData::AsData),
   1383      mRenderingSuppressedForViewTransitions(false),
   1384      mBidiEnabled(false),
   1385      mMayNeedFontPrefsUpdate(true),
   1386      mInitialAboutBlankLoadCompleting(false),
   1387      mIgnoreDocGroupMismatches(false),
   1388      mAddedToMemoryReportingAsDataDocument(false),
   1389      mMayStartLayout(true),
   1390      mHaveFiredTitleChange(false),
   1391      mIsShowing(false),
   1392      mVisible(true),
   1393      mIsCompletelyLoaded(false),
   1394      mRemovedFromDocShell(false),
   1395      // mAllowDNSPrefetch starts true, so that we can always reliably && it
   1396      // with various values that might disable it.  Since we never prefetch
   1397      // unless we get a window, and in that case the docshell value will get
   1398      // &&-ed in, this is safe.
   1399      mAllowDNSPrefetch(true),
   1400      mIsStaticDocument(false),
   1401      mCreatingStaticClone(false),
   1402      mHasPrintCallbacks(false),
   1403      mInUnlinkOrDeletion(false),
   1404      mHasHadScriptHandlingObject(false),
   1405      mIsBeingUsedAsImage(false),
   1406      mChromeRulesEnabled(false),
   1407      mInChromeDocShell(false),
   1408      mIsSyntheticDocument(false),
   1409      mHasLinksToUpdateRunnable(false),
   1410      mFlushingPendingLinkUpdates(false),
   1411      mMayHaveDOMMutationObservers(false),
   1412      mMayHaveAnimationObservers(false),
   1413      mHasCSPDeliveredThroughHeader(false),
   1414      mBFCacheDisallowed(false),
   1415      mHasHadDefaultView(false),
   1416      mStyleSheetChangeEventsEnabled(false),
   1417      mDevToolsAnonymousAndShadowEventsEnabled(false),
   1418      mPausedByDevTools(false),
   1419      mForceNonNativeTheme(false),
   1420      mIsSrcdocDocument(false),
   1421      mHasDisplayDocument(false),
   1422      mFontFaceSetDirty(true),
   1423      mDidFireDOMContentLoaded(true),
   1424      mIsTopLevelContentDocument(false),
   1425      mIsContentDocument(false),
   1426      mDidCallBeginLoad(false),
   1427      mEncodingMenuDisabled(false),
   1428      mLinksEnabled(true),
   1429      mIsSVGGlyphsDocument(false),
   1430      mInDestructor(false),
   1431      mIsGoingAway(false),
   1432      mStyleSetFilled(false),
   1433      mQuirkSheetAdded(false),
   1434      mMayHaveTitleElement(false),
   1435      mDOMLoadingSet(false),
   1436      mDOMInteractiveSet(false),
   1437      mDOMCompleteSet(false),
   1438      mAutoFocusFired(false),
   1439      mScrolledToRefAlready(false),
   1440      mChangeScrollPosWhenScrollingToRef(false),
   1441      mDelayFrameLoaderInitialization(false),
   1442      mSynchronousDOMContentLoaded(false),
   1443      mMaybeServiceWorkerControlled(false),
   1444      mAllowZoom(false),
   1445      mValidScaleFloat(false),
   1446      mValidMinScale(false),
   1447      mValidMaxScale(false),
   1448      mWidthStrEmpty(false),
   1449      mLockingImages(false),
   1450      mAnimatingImages(true),
   1451      mParserAborted(false),
   1452      mReportedDocumentUseCounters(false),
   1453      mHasReportedShadowDOMUsage(false),
   1454      mLoadEventFiring(false),
   1455      mSkipLoadEventAfterClose(false),
   1456      mDisableCookieAccess(false),
   1457      mDisableDocWrite(false),
   1458      mTooDeepWriteRecursion(false),
   1459      mPendingMaybeEditingStateChanged(false),
   1460      mHasBeenEditable(false),
   1461      mIsRunningExecCommandByContent(false),
   1462      mIsRunningExecCommandByChromeOrAddon(false),
   1463      mSetCompleteAfterDOMContentLoaded(false),
   1464      mDidHitCompleteSheetCache(false),
   1465      mUseCountersInitialized(false),
   1466      mShouldReportUseCounters(false),
   1467      mShouldSendPageUseCounters(false),
   1468      mUserHasInteracted(false),
   1469      mHasUserInteractionTimerScheduled(false),
   1470      mShouldResistFingerprinting(false),
   1471      mIsInPrivateBrowsing(false),
   1472      mCloningForSVGUse(false),
   1473      mAllowDeclarativeShadowRoots(false),
   1474      mSuspendDOMNotifications(false),
   1475      mForceLoadAtTop(false),
   1476      mSuppressNotifyingDevToolsOfNodeRemovals(false),
   1477      mHasPolicyWithRequireTrustedTypesForDirective(false),
   1478      mClipboardCopyTriggered(false),
   1479      mXMLDeclarationBits(0),
   1480      mOnloadBlockCount(0),
   1481      mWriteLevel(0),
   1482      mContentEditableCount(0),
   1483      mEditingState(EditingState::eOff),
   1484      mCompatMode(eCompatibility_FullStandards),
   1485      mReadyState(ReadyState::READYSTATE_UNINITIALIZED),
   1486      mAncestorIsLoading(false),
   1487      mVisibilityState(dom::VisibilityState::Hidden),
   1488      mType(eUnknown),
   1489      mDefaultElementType(0),
   1490      mAllowXULXBL(eTriUnset),
   1491      mSkipDTDSecurityChecks(false),
   1492      mBidiOptions(IBMBIDI_DEFAULT_BIDI_OPTIONS),
   1493      mSandboxFlags(0),
   1494      mPartID(0),
   1495      mMarkedCCGeneration(0),
   1496      mPresShell(nullptr),
   1497      mPreloadPictureDepth(0),
   1498      mEventsSuppressed(0),
   1499      mIgnoreDestructiveWritesCounter(0),
   1500      mStaticCloneCount(0),
   1501      mWindow(nullptr),
   1502      mBFCacheEntry(nullptr),
   1503      mInSyncOperationCount(0),
   1504      mBlockDOMContentLoaded(0),
   1505      mUpdateNestLevel(0),
   1506      mHttpsOnlyStatus(nsILoadInfo::HTTPS_ONLY_UNINITIALIZED),
   1507      mViewportType(Unknown),
   1508      mViewportFit(ViewportFitType::Auto),
   1509      mInteractiveWidgetMode(
   1510          InteractiveWidgetUtils::DefaultInteractiveWidgetMode()),
   1511      mHeaderData(nullptr),
   1512      mLanguageFromCharset(nullptr),
   1513      mServoRestyleRootDirtyBits(0),
   1514      mThrowOnDynamicMarkupInsertionCounter(0),
   1515      mIgnoreOpensDuringUnloadCounter(0),
   1516      mSavedResolution(1.0f),
   1517      mClassificationFlags({0, 0}),
   1518      mGeneration(0),
   1519      mCachedTabSizeGeneration(0),
   1520      mNextFormNumber(0),
   1521      mNextControlNumber(0),
   1522      mPreloadService(this),
   1523      mShouldNotifyFetchSuccess(false),
   1524      mShouldNotifyFormOrPasswordRemoved(false) {
   1525  MOZ_LOG(gDocumentLeakPRLog, LogLevel::Debug, ("DOCUMENT %p created", this));
   1526 
   1527  SetIsInDocument();
   1528  SetIsConnected(true);
   1529 
   1530  // Create these unconditionally, they will be used to warn about the `zoom`
   1531  // property, even if use counters are disabled.
   1532  mStyleUseCounters.reset(Servo_UseCounters_Create());
   1533 
   1534  SetContentType(nsDependentCString(aContentType));
   1535 
   1536  // Start out mLastStyleSheetSet as null, per spec
   1537  SetDOMStringToNull(mLastStyleSheetSet);
   1538 
   1539  // void state used to differentiate an empty source from an unselected source
   1540  mPreloadPictureFoundSource.SetIsVoid(true);
   1541 
   1542  RecomputeLanguageFromCharset();
   1543 
   1544  mPreloadReferrerInfo = new dom::ReferrerInfo(nullptr);
   1545  mReferrerInfo = new dom::ReferrerInfo(nullptr);
   1546 }
   1547 
   1548 #ifndef ANDROID
   1549 // unused by GeckoView
   1550 static bool IsAboutErrorPage(nsGlobalWindowInner* aWin, const char* aSpec) {
   1551  if (NS_WARN_IF(!aWin)) {
   1552    return false;
   1553  }
   1554 
   1555  nsIURI* uri = aWin->GetDocumentURI();
   1556  if (NS_WARN_IF(!uri)) {
   1557    return false;
   1558  }
   1559  // getSpec is an expensive operation, hence we first check the scheme
   1560  // to see if the caller is actually an about: page.
   1561  if (!uri->SchemeIs("about")) {
   1562    return false;
   1563  }
   1564 
   1565  nsAutoCString aboutSpec;
   1566  nsresult rv = NS_GetAboutModuleName(uri, aboutSpec);
   1567  NS_ENSURE_SUCCESS(rv, false);
   1568 
   1569  return aboutSpec.EqualsASCII(aSpec);
   1570 }
   1571 #endif
   1572 
   1573 bool Document::CallerIsTrustedAboutNetError(JSContext* aCx, JSObject* aObject) {
   1574  nsGlobalWindowInner* win = xpc::WindowOrNull(aObject);
   1575 #ifdef ANDROID
   1576  // GeckoView uses data URLs for error pages, so for now just check for any
   1577  // error page
   1578  return win && win->GetDocument() && win->GetDocument()->IsErrorPage();
   1579 #else
   1580  return win && IsAboutErrorPage(win, "neterror");
   1581 #endif
   1582 }
   1583 
   1584 bool Document::CallerIsTrustedAboutHttpsOnlyError(JSContext* aCx,
   1585                                                  JSObject* aObject) {
   1586  nsGlobalWindowInner* win = xpc::WindowOrNull(aObject);
   1587 #ifdef ANDROID
   1588  // GeckoView uses data URLs for error pages, so for now just check for any
   1589  // error page
   1590  return win && win->GetDocument() && win->GetDocument()->IsErrorPage();
   1591 #else
   1592  return win && IsAboutErrorPage(win, "httpsonlyerror");
   1593 #endif
   1594 }
   1595 
   1596 already_AddRefed<mozilla::dom::Promise> Document::AddCertException(
   1597    bool aIsTemporary, ErrorResult& aError) {
   1598  RefPtr<Promise> promise = Promise::Create(GetScopeObject(), aError,
   1599                                            Promise::ePropagateUserInteraction);
   1600  if (aError.Failed()) {
   1601    return nullptr;
   1602  }
   1603 
   1604  nsresult rv = NS_OK;
   1605  if (NS_WARN_IF(!mFailedChannel)) {
   1606    promise->MaybeReject(NS_ERROR_DOM_INVALID_STATE_ERR);
   1607    return promise.forget();
   1608  }
   1609 
   1610  nsCOMPtr<nsIURI> failedChannelURI;
   1611  NS_GetFinalChannelURI(mFailedChannel, getter_AddRefs(failedChannelURI));
   1612  if (!failedChannelURI) {
   1613    promise->MaybeReject(NS_ERROR_DOM_INVALID_STATE_ERR);
   1614    return promise.forget();
   1615  }
   1616 
   1617  nsCOMPtr<nsIURI> innerURI = NS_GetInnermostURI(failedChannelURI);
   1618  if (!innerURI) {
   1619    promise->MaybeReject(NS_ERROR_DOM_INVALID_STATE_ERR);
   1620    return promise.forget();
   1621  }
   1622 
   1623  nsAutoCString host;
   1624  innerURI->GetAsciiHost(host);
   1625  int32_t port;
   1626  innerURI->GetPort(&port);
   1627 
   1628  nsCOMPtr<nsITransportSecurityInfo> tsi;
   1629  rv = mFailedChannel->GetSecurityInfo(getter_AddRefs(tsi));
   1630  if (NS_WARN_IF(NS_FAILED(rv))) {
   1631    promise->MaybeReject(rv);
   1632    return promise.forget();
   1633  }
   1634  if (NS_WARN_IF(!tsi)) {
   1635    promise->MaybeReject(NS_ERROR_DOM_INVALID_STATE_ERR);
   1636    return promise.forget();
   1637  }
   1638 
   1639  nsCOMPtr<nsIX509Cert> cert;
   1640  rv = tsi->GetServerCert(getter_AddRefs(cert));
   1641  if (NS_WARN_IF(NS_FAILED(rv))) {
   1642    promise->MaybeReject(rv);
   1643    return promise.forget();
   1644  }
   1645  if (NS_WARN_IF(!cert)) {
   1646    promise->MaybeReject(NS_ERROR_DOM_INVALID_STATE_ERR);
   1647    return promise.forget();
   1648  }
   1649 
   1650  if (XRE_IsContentProcess()) {
   1651    ContentChild* cc = ContentChild::GetSingleton();
   1652    MOZ_ASSERT(cc);
   1653    OriginAttributes const& attrs = NodePrincipal()->OriginAttributesRef();
   1654    cc->SendAddCertException(cert, host, port, attrs, aIsTemporary)
   1655        ->Then(GetCurrentSerialEventTarget(), __func__,
   1656               [promise](const mozilla::MozPromise<
   1657                         nsresult, mozilla::ipc::ResponseRejectReason,
   1658                         true>::ResolveOrRejectValue& aValue) {
   1659                 if (aValue.IsResolve()) {
   1660                   promise->MaybeResolve(aValue.ResolveValue());
   1661                 } else {
   1662                   promise->MaybeRejectWithUndefined();
   1663                 }
   1664               });
   1665    return promise.forget();
   1666  }
   1667 
   1668  if (XRE_IsParentProcess()) {
   1669    nsCOMPtr<nsICertOverrideService> overrideService =
   1670        do_GetService(NS_CERTOVERRIDE_CONTRACTID);
   1671    if (!overrideService) {
   1672      promise->MaybeReject(NS_ERROR_FAILURE);
   1673      return promise.forget();
   1674    }
   1675 
   1676    OriginAttributes const& attrs = NodePrincipal()->OriginAttributesRef();
   1677    rv = overrideService->RememberValidityOverride(host, port, attrs, cert,
   1678                                                   aIsTemporary);
   1679    if (NS_WARN_IF(NS_FAILED(rv))) {
   1680      promise->MaybeReject(rv);
   1681      return promise.forget();
   1682    }
   1683 
   1684    promise->MaybeResolveWithUndefined();
   1685    return promise.forget();
   1686  }
   1687 
   1688  promise->MaybeReject(NS_ERROR_FAILURE);
   1689  return promise.forget();
   1690 }
   1691 
   1692 void Document::ReloadWithHttpsOnlyException() {
   1693  if (WindowGlobalChild* wgc = GetWindowGlobalChild()) {
   1694    wgc->SendReloadWithHttpsOnlyException();
   1695  }
   1696 }
   1697 
   1698 // Given an nsresult that is assumed to be synthesized by PSM and describes a
   1699 // certificate or TLS error, attempts to convert it into a string
   1700 // representation of the underlying NSS error.
   1701 // `aErrorCodeString` will be an empty string if `aResult` is not an error from
   1702 // PSM or it does not represent a valid NSS error.
   1703 void GetErrorCodeStringFromNSResult(nsresult aResult,
   1704                                    nsAString& aErrorCodeString) {
   1705  aErrorCodeString.Truncate();
   1706 
   1707  if (NS_ERROR_GET_MODULE(aResult) != NS_ERROR_MODULE_SECURITY ||
   1708      NS_ERROR_GET_SEVERITY(aResult) != NS_ERROR_SEVERITY_ERROR) {
   1709    return;
   1710  }
   1711 
   1712  PRErrorCode errorCode = -1 * NS_ERROR_GET_CODE(aResult);
   1713  if (!mozilla::psm::IsNSSErrorCode(errorCode)) {
   1714    return;
   1715  }
   1716 
   1717  const char* errorCodeString = PR_ErrorToName(errorCode);
   1718  if (!errorCodeString) {
   1719    return;
   1720  }
   1721 
   1722  aErrorCodeString.AssignASCII(errorCodeString);
   1723 }
   1724 
   1725 void Document::GetNetErrorInfo(NetErrorInfo& aInfo, ErrorResult& aRv) {
   1726  nsresult rv = NS_OK;
   1727  if (NS_WARN_IF(!mFailedChannel)) {
   1728    aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
   1729    return;
   1730  }
   1731 
   1732  nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(mFailedChannel));
   1733 
   1734  // We don't throw even if httpChannel is null, we just keep responseStatus and
   1735  // responseStatusText empty
   1736  if (httpChannel) {
   1737    uint32_t responseStatus;
   1738    nsAutoCString responseStatusText;
   1739    rv = httpChannel->GetResponseStatus(&responseStatus);
   1740    if (NS_SUCCEEDED(rv)) {
   1741      aInfo.mResponseStatus = responseStatus;
   1742    }
   1743 
   1744    rv = httpChannel->GetResponseStatusText(responseStatusText);
   1745    if (NS_FAILED(rv) || responseStatusText.IsEmpty()) {
   1746      net_GetDefaultStatusTextForCode(responseStatus, responseStatusText);
   1747    }
   1748    aInfo.mResponseStatusText.AssignASCII(responseStatusText);
   1749  }
   1750 
   1751  nsCOMPtr<nsITransportSecurityInfo> tsi;
   1752  rv = mFailedChannel->GetSecurityInfo(getter_AddRefs(tsi));
   1753  if (NS_WARN_IF(NS_FAILED(rv))) {
   1754    aRv.Throw(rv);
   1755    return;
   1756  }
   1757 
   1758  nsresult channelStatus;
   1759  rv = mFailedChannel->GetStatus(&channelStatus);
   1760  if (NS_WARN_IF(NS_FAILED(rv))) {
   1761    aRv.Throw(rv);
   1762    return;
   1763  }
   1764  aInfo.mChannelStatus = static_cast<uint32_t>(channelStatus);
   1765 
   1766  // If nsITransportSecurityInfo is not set, simply keep the remaining fields
   1767  // empty (to make responseStatus and responseStatusText accessible).
   1768  if (!tsi) {
   1769    return;
   1770  }
   1771 
   1772  // TransportSecurityInfo::GetErrorCodeString always returns NS_OK
   1773  (void)tsi->GetErrorCodeString(aInfo.mErrorCodeString);
   1774  if (aInfo.mErrorCodeString.IsEmpty()) {
   1775    GetErrorCodeStringFromNSResult(channelStatus, aInfo.mErrorCodeString);
   1776  }
   1777 }
   1778 
   1779 bool Document::CallerIsTrustedAboutCertError(JSContext* aCx,
   1780                                             JSObject* aObject) {
   1781  nsGlobalWindowInner* win = xpc::WindowOrNull(aObject);
   1782 #ifdef ANDROID
   1783  // GeckoView uses data URLs for error pages, so for now just check for any
   1784  // error page
   1785  return win && win->GetDocument() && win->GetDocument()->IsErrorPage();
   1786 #else
   1787  return win && IsAboutErrorPage(win, "certerror");
   1788 #endif
   1789 }
   1790 
   1791 bool Document::CallerIsSystemPrincipalOrWebCompatAddon(JSContext* aCx,
   1792                                                       JSObject* aObject) {
   1793  RefPtr<BasePrincipal> principal =
   1794      BasePrincipal::Cast(nsContentUtils::SubjectPrincipal(aCx));
   1795 
   1796  if (!principal) {
   1797    return false;
   1798  }
   1799 
   1800  // We allow the privileged APIs to be called from system principal.
   1801  if (principal->IsSystemPrincipal()) {
   1802    return true;
   1803  }
   1804 
   1805  // We only allow calling privileged APIs from the webcompat extension.
   1806  if (auto* policy = principal->ContentScriptAddonPolicy()) {
   1807    nsAutoString addonID;
   1808    policy->GetId(addonID);
   1809 
   1810    return addonID.EqualsLiteral("webcompat@mozilla.org");
   1811  }
   1812 
   1813  return false;
   1814 }
   1815 
   1816 bool Document::IsErrorPage() const {
   1817  nsCOMPtr<nsILoadInfo> loadInfo = mChannel ? mChannel->LoadInfo() : nullptr;
   1818  return loadInfo && loadInfo->GetLoadErrorPage();
   1819 }
   1820 
   1821 void Document::GetFailedCertSecurityInfo(FailedCertSecurityInfo& aInfo,
   1822                                         ErrorResult& aRv) {
   1823  nsresult rv = NS_OK;
   1824  if (NS_WARN_IF(!mFailedChannel)) {
   1825    aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
   1826    return;
   1827  }
   1828 
   1829  nsCOMPtr<nsITransportSecurityInfo> tsi;
   1830  rv = mFailedChannel->GetSecurityInfo(getter_AddRefs(tsi));
   1831  if (NS_WARN_IF(NS_FAILED(rv))) {
   1832    aRv.Throw(rv);
   1833    return;
   1834  }
   1835  if (NS_WARN_IF(!tsi)) {
   1836    aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
   1837    return;
   1838  }
   1839 
   1840  nsresult channelStatus;
   1841  rv = mFailedChannel->GetStatus(&channelStatus);
   1842  if (NS_WARN_IF(NS_FAILED(rv))) {
   1843    aRv.Throw(rv);
   1844    return;
   1845  }
   1846  aInfo.mChannelStatus = static_cast<uint32_t>(channelStatus);
   1847 
   1848  // TransportSecurityInfo::GetErrorCodeString always returns NS_OK
   1849  (void)tsi->GetErrorCodeString(aInfo.mErrorCodeString);
   1850  if (aInfo.mErrorCodeString.IsEmpty()) {
   1851    GetErrorCodeStringFromNSResult(channelStatus, aInfo.mErrorCodeString);
   1852  }
   1853 
   1854  nsITransportSecurityInfo::OverridableErrorCategory errorCategory;
   1855  rv = tsi->GetOverridableErrorCategory(&errorCategory);
   1856  if (NS_WARN_IF(NS_FAILED(rv))) {
   1857    aRv.Throw(rv);
   1858    return;
   1859  }
   1860  switch (errorCategory) {
   1861    case nsITransportSecurityInfo::OverridableErrorCategory::ERROR_TRUST:
   1862      aInfo.mOverridableErrorCategory =
   1863          dom::OverridableErrorCategory::Trust_error;
   1864      break;
   1865    case nsITransportSecurityInfo::OverridableErrorCategory::ERROR_DOMAIN:
   1866      aInfo.mOverridableErrorCategory =
   1867          dom::OverridableErrorCategory::Domain_mismatch;
   1868      break;
   1869    case nsITransportSecurityInfo::OverridableErrorCategory::ERROR_TIME:
   1870      aInfo.mOverridableErrorCategory =
   1871          dom::OverridableErrorCategory::Expired_or_not_yet_valid;
   1872      break;
   1873    default:
   1874      aInfo.mOverridableErrorCategory = dom::OverridableErrorCategory::Unset;
   1875      break;
   1876  }
   1877 
   1878  nsCOMPtr<nsIX509Cert> cert;
   1879  nsCOMPtr<nsIX509CertValidity> validity;
   1880  rv = tsi->GetServerCert(getter_AddRefs(cert));
   1881  if (NS_WARN_IF(NS_FAILED(rv))) {
   1882    aRv.Throw(rv);
   1883    return;
   1884  }
   1885  if (NS_WARN_IF(!cert)) {
   1886    aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
   1887    return;
   1888  }
   1889 
   1890  rv = cert->GetValidity(getter_AddRefs(validity));
   1891  if (NS_WARN_IF(NS_FAILED(rv)) || NS_WARN_IF(!validity)) {
   1892    aInfo.mValidNotBefore = 0;
   1893    aInfo.mValidNotAfter = 0;
   1894  } else {
   1895    PRTime validityResult;
   1896    rv = validity->GetNotBefore(&validityResult);
   1897    if (NS_WARN_IF(NS_FAILED(rv))) {
   1898      aInfo.mValidNotBefore = 0;
   1899    } else {
   1900      aInfo.mValidNotBefore = DOMTimeStamp(validityResult / PR_USEC_PER_MSEC);
   1901    }
   1902 
   1903    rv = validity->GetNotAfter(&validityResult);
   1904    if (NS_WARN_IF(NS_FAILED(rv))) {
   1905      aInfo.mValidNotAfter = 0;
   1906    } else {
   1907      aInfo.mValidNotAfter = DOMTimeStamp(validityResult / PR_USEC_PER_MSEC);
   1908    }
   1909  }
   1910 
   1911  nsAutoString issuerCommonName;
   1912  nsAutoString certChainPEMString;
   1913  Sequence<nsString>& certChainStrings = aInfo.mCertChainStrings.Construct();
   1914  int64_t maxValidity = std::numeric_limits<int64_t>::max();
   1915  int64_t minValidity = 0;
   1916  PRTime notBefore, notAfter;
   1917  nsTArray<RefPtr<nsIX509Cert>> handshakeCertificates;
   1918  rv = tsi->GetHandshakeCertificates(handshakeCertificates);
   1919  if (NS_WARN_IF(NS_FAILED(rv))) {
   1920    aRv.Throw(rv);
   1921    return;
   1922  }
   1923 
   1924  if (NS_WARN_IF(handshakeCertificates.IsEmpty())) {
   1925    aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
   1926    return;
   1927  }
   1928 
   1929  for (const auto& certificate : handshakeCertificates) {
   1930    rv = certificate->GetIssuerCommonName(issuerCommonName);
   1931    if (NS_WARN_IF(NS_FAILED(rv))) {
   1932      aRv.Throw(rv);
   1933      return;
   1934    }
   1935 
   1936    rv = certificate->GetValidity(getter_AddRefs(validity));
   1937    if (NS_WARN_IF(NS_FAILED(rv)) || NS_WARN_IF(!validity)) {
   1938      notBefore = 0;
   1939      notAfter = 0;
   1940    } else {
   1941      rv = validity->GetNotBefore(&notBefore);
   1942      if (NS_WARN_IF(NS_FAILED(rv))) {
   1943        notBefore = 0;
   1944      }
   1945      rv = validity->GetNotAfter(&notAfter);
   1946      if (NS_WARN_IF(NS_FAILED(rv))) {
   1947        notAfter = 0;
   1948      }
   1949    }
   1950 
   1951    notBefore = std::max(minValidity, notBefore);
   1952    notAfter = std::min(maxValidity, notAfter);
   1953    nsTArray<uint8_t> certArray;
   1954    rv = certificate->GetRawDER(certArray);
   1955    if (NS_WARN_IF(NS_FAILED(rv))) {
   1956      aRv.Throw(rv);
   1957      return;
   1958    }
   1959 
   1960    nsAutoString der64;
   1961    rv = Base64Encode(reinterpret_cast<const char*>(certArray.Elements()),
   1962                      certArray.Length(), der64);
   1963    if (NS_WARN_IF(NS_FAILED(rv))) {
   1964      aRv.Throw(rv);
   1965      return;
   1966    }
   1967    if (!certChainStrings.AppendElement(der64, fallible)) {
   1968      aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
   1969      return;
   1970    }
   1971  }
   1972 
   1973  aInfo.mIssuerCommonName.Assign(issuerCommonName);
   1974  aInfo.mCertValidityRangeNotAfter = DOMTimeStamp(notAfter / PR_USEC_PER_MSEC);
   1975  aInfo.mCertValidityRangeNotBefore =
   1976      DOMTimeStamp(notBefore / PR_USEC_PER_MSEC);
   1977 
   1978  int32_t errorCode;
   1979  rv = tsi->GetErrorCode(&errorCode);
   1980  if (NS_WARN_IF(NS_FAILED(rv))) {
   1981    aRv.Throw(rv);
   1982    return;
   1983  }
   1984 
   1985  aInfo.mErrorIsOverridable = mozilla::psm::ErrorIsOverridable(errorCode);
   1986 
   1987  nsCOMPtr<nsINSSErrorsService> nsserr =
   1988      do_GetService("@mozilla.org/nss_errors_service;1");
   1989  if (NS_WARN_IF(!nsserr)) {
   1990    aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
   1991    return;
   1992  }
   1993  nsresult res;
   1994  rv = nsserr->GetXPCOMFromNSSError(errorCode, &res);
   1995  if (NS_WARN_IF(NS_FAILED(rv))) {
   1996    aRv.Throw(rv);
   1997    return;
   1998  }
   1999  rv = nsserr->GetErrorMessage(res, aInfo.mErrorMessage);
   2000  if (NS_WARN_IF(NS_FAILED(rv))) {
   2001    aRv.Throw(rv);
   2002    return;
   2003  }
   2004 
   2005  OriginAttributes attrs;
   2006  StoragePrincipalHelper::GetRegularPrincipalOriginAttributes(this, attrs);
   2007  nsCOMPtr<nsIURI> aURI;
   2008  mFailedChannel->GetURI(getter_AddRefs(aURI));
   2009  if (XRE_IsContentProcess()) {
   2010    ContentChild* cc = ContentChild::GetSingleton();
   2011    MOZ_ASSERT(cc);
   2012    cc->SendIsSecureURI(aURI, attrs, &aInfo.mHasHSTS);
   2013  } else {
   2014    nsCOMPtr<nsISiteSecurityService> sss =
   2015        do_GetService(NS_SSSERVICE_CONTRACTID);
   2016    if (NS_WARN_IF(!sss)) {
   2017      return;
   2018    }
   2019    (void)NS_WARN_IF(NS_FAILED(sss->IsSecureURI(aURI, attrs, &aInfo.mHasHSTS)));
   2020  }
   2021  nsCOMPtr<nsIPublicKeyPinningService> pkps =
   2022      do_GetService(NS_PKPSERVICE_CONTRACTID);
   2023  if (NS_WARN_IF(!pkps)) {
   2024    return;
   2025  }
   2026  (void)NS_WARN_IF(NS_FAILED(pkps->HostHasPins(aURI, &aInfo.mHasHPKP)));
   2027 }
   2028 
   2029 bool Document::IsAboutPage() const {
   2030  return NodePrincipal()->SchemeIs("about");
   2031 }
   2032 
   2033 void Document::ConstructUbiNode(void* storage) {
   2034  JS::ubi::Concrete<Document>::construct(storage, this);
   2035 }
   2036 
   2037 void Document::LoadEventFired() {
   2038  // Collect page load timings
   2039  AccumulatePageLoadTelemetry();
   2040 
   2041  // Record page load event
   2042  RecordPageLoadEventTelemetry();
   2043 
   2044  // Release the JS bytecode cache from its wait on the load event, and
   2045  // potentially dispatch the encoding of the bytecode.
   2046  if (mScriptLoader) {
   2047    mScriptLoader->LoadEventFired();
   2048  }
   2049 }
   2050 
   2051 void Document::RecordPageLoadEventTelemetry() {
   2052  // If the page load time is empty, then the content wasn't something we want
   2053  // to report (i.e. not a top level document).
   2054  if (!mPageloadEventData.HasLoadTime()) {
   2055    return;
   2056  }
   2057  MOZ_ASSERT(IsTopLevelContentDocument());
   2058 
   2059  nsPIDOMWindowOuter* window = GetWindow();
   2060  if (!window) {
   2061    return;
   2062  }
   2063 
   2064  nsIDocShell* docshell = window->GetDocShell();
   2065  if (!docshell) {
   2066    return;
   2067  }
   2068 
   2069  // Don't send any event telemetry for private browsing.
   2070  if (IsInPrivateBrowsing()) {
   2071    return;
   2072  }
   2073 
   2074  if (!GetChannel()) {
   2075    return;
   2076  }
   2077 
   2078  auto pageloadEventType = performance::pageload_event::GetPageloadEventType();
   2079 
   2080  // Return if we are not sending an event for this pageload.
   2081  if (pageloadEventType == mozilla::PageloadEventType::kNone) {
   2082    return;
   2083  }
   2084 
   2085 #ifdef ACCESSIBILITY
   2086  if (GetAccService() != nullptr) {
   2087    mPageloadEventData.SetUserFeature(
   2088        performance::pageload_event::UserFeature::USING_A11Y);
   2089  }
   2090 #endif
   2091 
   2092  if (GetChannel()) {
   2093    nsCOMPtr<nsICacheInfoChannel> cacheInfoChannel =
   2094        do_QueryInterface(GetChannel());
   2095    if (cacheInfoChannel) {
   2096      nsICacheInfoChannel::CacheDisposition disposition =
   2097          nsICacheInfoChannel::kCacheUnknown;
   2098      nsresult rv = cacheInfoChannel->GetCacheDisposition(&disposition);
   2099      if (NS_SUCCEEDED(rv)) {
   2100        mPageloadEventData.set_cacheDisposition(disposition);
   2101      }
   2102    }
   2103  }
   2104 
   2105  nsAutoCString loadTypeStr;
   2106  switch (docshell->GetLoadType()) {
   2107    case LOAD_NORMAL:
   2108    case LOAD_NORMAL_REPLACE:
   2109    case LOAD_NORMAL_BYPASS_CACHE:
   2110    case LOAD_NORMAL_BYPASS_PROXY:
   2111    case LOAD_NORMAL_BYPASS_PROXY_AND_CACHE:
   2112      loadTypeStr.Append("NORMAL");
   2113      break;
   2114    case LOAD_HISTORY:
   2115      loadTypeStr.Append("HISTORY");
   2116      break;
   2117    case LOAD_RELOAD_NORMAL:
   2118    case LOAD_RELOAD_BYPASS_CACHE:
   2119    case LOAD_RELOAD_BYPASS_PROXY:
   2120    case LOAD_RELOAD_BYPASS_PROXY_AND_CACHE:
   2121    case LOAD_REFRESH:
   2122    case LOAD_REFRESH_REPLACE:
   2123    case LOAD_RELOAD_CHARSET_CHANGE:
   2124    case LOAD_RELOAD_CHARSET_CHANGE_BYPASS_PROXY_AND_CACHE:
   2125    case LOAD_RELOAD_CHARSET_CHANGE_BYPASS_CACHE:
   2126      loadTypeStr.Append("RELOAD");
   2127      break;
   2128    case LOAD_LINK:
   2129      loadTypeStr.Append("LINK");
   2130      break;
   2131    case LOAD_STOP_CONTENT:
   2132    case LOAD_STOP_CONTENT_AND_REPLACE:
   2133      loadTypeStr.Append("STOP");
   2134      break;
   2135    case LOAD_ERROR_PAGE:
   2136      loadTypeStr.Append("ERROR");
   2137      break;
   2138    default:
   2139      loadTypeStr.Append("OTHER");
   2140      break;
   2141  }
   2142  mPageloadEventData.set_loadType(loadTypeStr);
   2143 
   2144  nsCOMPtr<nsIEffectiveTLDService> tldService =
   2145      mozilla::components::EffectiveTLD::Service();
   2146 
   2147  nsresult rv = NS_OK;
   2148  if (tldService) {
   2149    if (mReferrerInfo &&
   2150        (docshell->GetLoadType() & nsIDocShell::LOAD_CMD_NORMAL)) {
   2151      nsAutoCString currentBaseDomain, referrerBaseDomain;
   2152      nsCOMPtr<nsIURI> referrerURI = mReferrerInfo->GetComputedReferrer();
   2153      if (referrerURI) {
   2154        rv = tldService->GetBaseDomain(referrerURI, 0, referrerBaseDomain);
   2155        if (NS_SUCCEEDED(rv)) {
   2156          bool sameOrigin = false;
   2157          NodePrincipal()->IsSameOrigin(referrerURI, &sameOrigin);
   2158          mPageloadEventData.set_sameOriginNav(sameOrigin);
   2159        }
   2160      }
   2161    }
   2162  }
   2163 
   2164  if (pageloadEventType == PageloadEventType::kDomain) {
   2165    // Do not record anything if we failed to assign the domain.
   2166    if (!mPageloadEventData.MaybeSetPublicRegistrableDomain(GetDocumentURI(),
   2167                                                            GetChannel())) {
   2168      return;
   2169    }
   2170  }
   2171 
   2172  // Collect any JS timers that were measured during pageload.
   2173  if (GetScopeObject() && GetScopeObject()->GetGlobalJSObject()) {
   2174    AutoJSContext cx;
   2175    JSObject* globalObject = GetScopeObject()->GetGlobalJSObject();
   2176    JSAutoRealm ar(cx, globalObject);
   2177    JS::JSTimers timers = JS::GetJSTimers(cx);
   2178 
   2179    if (!timers.executionTime.IsZero()) {
   2180      mPageloadEventData.set_jsExecTime(
   2181          static_cast<uint32_t>(timers.executionTime.ToMilliseconds()));
   2182    }
   2183 
   2184    if (!timers.delazificationTime.IsZero()) {
   2185      mPageloadEventData.set_delazifyTime(
   2186          static_cast<uint32_t>(timers.delazificationTime.ToMilliseconds()));
   2187    }
   2188  }
   2189 
   2190  // Sending a glean ping must be done on the parent process.
   2191  if (ContentChild* cc = ContentChild::GetSingleton()) {
   2192    if (GetNavigationTiming()) {
   2193      uint64_t androidAppLinkLoadIdentifier = 0;
   2194 #ifdef ANDROID
   2195      if (BrowsingContext* bc = GetBrowsingContext()) {
   2196        Maybe<uint64_t> contextAppLinkLoadIdentifier =
   2197            bc->GetAndroidAppLinkLoadIdentifier();
   2198        if (contextAppLinkLoadIdentifier.isSome()) {
   2199          androidAppLinkLoadIdentifier = contextAppLinkLoadIdentifier.value();
   2200        }
   2201      }
   2202 #endif
   2203      cc->SendRecordPageLoadEvent(
   2204          mPageloadEventData,
   2205          GetNavigationTiming()->GetNavigationStartTimeStamp(),
   2206          androidAppLinkLoadIdentifier);
   2207    }
   2208  }
   2209 }
   2210 
   2211 #ifndef ANDROID
   2212 static void AccumulatePriorityFcpGleanPref(
   2213    const nsCString& http3WithPriorityKey, const TimeDuration& duration) {
   2214  if (http3WithPriorityKey == "with_priority"_ns) {
   2215    glean::performance_pageload::h3p_fcp_with_priority.AccumulateRawDuration(
   2216        duration);
   2217  } else if (http3WithPriorityKey == "without_priority"_ns) {
   2218    glean::performance_pageload::http3_fcp_without_priority
   2219        .AccumulateRawDuration(duration);
   2220  } else {
   2221    MOZ_ASSERT_UNREACHABLE("Unknown value for http3WithPriorityKey");
   2222  }
   2223 }
   2224 #endif
   2225 
   2226 void Document::AccumulatePageLoadTelemetry() {
   2227  // Interested only in top level documents for real websites that are in the
   2228  // foreground.
   2229  if (!ShouldIncludeInTelemetry() || !IsTopLevelContentDocument() ||
   2230      !GetNavigationTiming() ||
   2231      !GetNavigationTiming()->DocShellHasBeenActiveSinceNavigationStart()) {
   2232    return;
   2233  }
   2234 
   2235  if (!GetChannel()) {
   2236    return;
   2237  }
   2238 
   2239  nsCOMPtr<nsITimedChannel> timedChannel(do_QueryInterface(GetChannel()));
   2240  if (!timedChannel) {
   2241    return;
   2242  }
   2243 
   2244  // Default duration is 0, use this to check for bogus negative values.
   2245  const TimeDuration zeroDuration;
   2246 
   2247  TimeStamp responseStart;
   2248  timedChannel->GetResponseStart(&responseStart);
   2249 
   2250  TimeStamp redirectStart, redirectEnd;
   2251  timedChannel->GetRedirectStart(&redirectStart);
   2252  timedChannel->GetRedirectEnd(&redirectEnd);
   2253 
   2254  uint8_t redirectCount;
   2255  timedChannel->GetRedirectCount(&redirectCount);
   2256  if (redirectCount) {
   2257    mPageloadEventData.set_redirectCount(static_cast<uint32_t>(redirectCount));
   2258  }
   2259 
   2260  if (!redirectStart.IsNull() && !redirectEnd.IsNull()) {
   2261    TimeDuration redirectTime = redirectEnd - redirectStart;
   2262    if (redirectTime > zeroDuration) {
   2263      mPageloadEventData.set_redirectTime(
   2264          static_cast<uint32_t>(redirectTime.ToMilliseconds()));
   2265    }
   2266  }
   2267 
   2268  TimeStamp dnsLookupStart, dnsLookupEnd;
   2269  timedChannel->GetDomainLookupStart(&dnsLookupStart);
   2270  timedChannel->GetDomainLookupEnd(&dnsLookupEnd);
   2271 
   2272  if (!dnsLookupStart.IsNull() && !dnsLookupEnd.IsNull()) {
   2273    TimeDuration dnsLookupTime = dnsLookupEnd - dnsLookupStart;
   2274    if (dnsLookupTime > zeroDuration) {
   2275      mPageloadEventData.set_dnsLookupTime(
   2276          static_cast<uint32_t>(dnsLookupTime.ToMilliseconds()));
   2277    }
   2278  }
   2279 
   2280  TimeStamp navigationStart =
   2281      GetNavigationTiming()->GetNavigationStartTimeStamp();
   2282 
   2283  if (!navigationStart) {
   2284    return;
   2285  }
   2286 
   2287  if (!responseStart) {
   2288    // This happens when getting a response from the cache.
   2289    responseStart = navigationStart;
   2290  }
   2291 
   2292  nsAutoCString dnsKey("Native");
   2293  nsAutoCString http3Key;
   2294  nsAutoCString http3WithPriorityKey;
   2295  nsAutoCString earlyHintKey;
   2296  nsCOMPtr<nsIHttpChannelInternal> httpChannel =
   2297      do_QueryInterface(GetChannel());
   2298  if (httpChannel) {
   2299    bool resolvedByTRR = false;
   2300    (void)httpChannel->GetIsResolvedByTRR(&resolvedByTRR);
   2301    if (resolvedByTRR) {
   2302      if (nsCOMPtr<nsIDNSService> dns =
   2303              do_GetService(NS_DNSSERVICE_CONTRACTID)) {
   2304        dns->GetTRRDomainKey(dnsKey);
   2305      } else {
   2306        // Failed to get the DNS service.
   2307        dnsKey = "(fail)"_ns;
   2308      }
   2309      mPageloadEventData.set_trrDomain(dnsKey);
   2310    }
   2311 
   2312    uint32_t major;
   2313    uint32_t minor;
   2314    if (NS_SUCCEEDED(httpChannel->GetResponseVersion(&major, &minor))) {
   2315      if (major == 3) {
   2316        http3Key = "http3"_ns;
   2317        nsCOMPtr<nsIHttpChannel> httpChannel2 = do_QueryInterface(GetChannel());
   2318        nsCString header;
   2319        if (httpChannel2 &&
   2320            NS_SUCCEEDED(
   2321                httpChannel2->GetResponseHeader("priority"_ns, header)) &&
   2322            !header.IsEmpty()) {
   2323          http3WithPriorityKey = "with_priority"_ns;
   2324        } else {
   2325          http3WithPriorityKey = "without_priority"_ns;
   2326        }
   2327      } else if (major == 2) {
   2328        bool supportHttp3 = false;
   2329        if (NS_FAILED(httpChannel->GetSupportsHTTP3(&supportHttp3))) {
   2330          supportHttp3 = false;
   2331        }
   2332        if (supportHttp3) {
   2333          http3Key = "supports_http3"_ns;
   2334        }
   2335      }
   2336 
   2337      mPageloadEventData.set_httpVer(major);
   2338    }
   2339 
   2340    uint32_t earlyHintType = 0;
   2341    (void)httpChannel->GetEarlyHintLinkType(&earlyHintType);
   2342    if (earlyHintType & LinkStyle::ePRECONNECT) {
   2343      earlyHintKey.Append("preconnect_"_ns);
   2344    }
   2345    if (earlyHintType & LinkStyle::ePRELOAD) {
   2346      earlyHintKey.Append("preload_"_ns);
   2347      earlyHintKey.Append(mPreloadService.GetEarlyHintUsed() ? "1"_ns : "0"_ns);
   2348    }
   2349  }
   2350 
   2351  TimeStamp asyncOpen;
   2352  timedChannel->GetAsyncOpen(&asyncOpen);
   2353  if (asyncOpen) {
   2354    glean::perf::dns_first_byte.Get(dnsKey).AccumulateRawDuration(
   2355        responseStart - asyncOpen);
   2356  }
   2357 
   2358  // First Contentful Composite
   2359  if (TimeStamp firstContentfulComposite =
   2360          GetNavigationTiming()->GetFirstContentfulCompositeTimeStamp()) {
   2361    glean::performance_pageload::fcp.AccumulateRawDuration(
   2362        firstContentfulComposite - navigationStart);
   2363 
   2364    if (!http3WithPriorityKey.IsEmpty()) {
   2365      glean::perf::h3p_first_contentful_paint.Get(http3WithPriorityKey)
   2366          .AccumulateRawDuration(firstContentfulComposite - navigationStart);
   2367 #ifndef ANDROID
   2368      AccumulatePriorityFcpGleanPref(
   2369          http3WithPriorityKey, firstContentfulComposite - navigationStart);
   2370 #endif
   2371    }
   2372 
   2373    glean::performance_pageload::fcp_responsestart.AccumulateRawDuration(
   2374        firstContentfulComposite - responseStart);
   2375 
   2376    TimeDuration fcpTime = firstContentfulComposite - navigationStart;
   2377    if (fcpTime > zeroDuration) {
   2378      mPageloadEventData.set_fcpTime(
   2379          static_cast<uint32_t>(fcpTime.ToMilliseconds()));
   2380    }
   2381  }
   2382 
   2383  // Report the most up to date LCP time. For our histogram we actually report
   2384  // this on page unload.
   2385  if (TimeStamp lcpTime =
   2386          GetNavigationTiming()->GetLargestContentfulRenderTimeStamp()) {
   2387    mPageloadEventData.set_lcpTime(
   2388        static_cast<uint32_t>((lcpTime - navigationStart).ToMilliseconds()));
   2389  }
   2390 
   2391  // Load event
   2392  if (TimeStamp loadEventStart =
   2393          GetNavigationTiming()->GetLoadEventStartTimeStamp()) {
   2394    glean::performance_pageload::load_time.AccumulateRawDuration(
   2395        loadEventStart - navigationStart);
   2396 
   2397    if (!http3WithPriorityKey.IsEmpty()) {
   2398      glean::perf::h3p_page_load_time.Get(http3WithPriorityKey)
   2399          .AccumulateRawDuration(loadEventStart - navigationStart);
   2400    }
   2401 
   2402    glean::performance_pageload::load_time_responsestart.AccumulateRawDuration(
   2403        loadEventStart - responseStart);
   2404 
   2405    TimeDuration responseTime = responseStart - navigationStart;
   2406    if (responseTime > zeroDuration) {
   2407      mPageloadEventData.set_responseTime(
   2408          static_cast<uint32_t>(responseTime.ToMilliseconds()));
   2409    }
   2410 
   2411    TimeDuration loadTime = loadEventStart - navigationStart;
   2412    if (loadTime > zeroDuration) {
   2413      mPageloadEventData.set_loadTime(
   2414          static_cast<uint32_t>(loadTime.ToMilliseconds()));
   2415    }
   2416 
   2417    TimeStamp requestStart;
   2418    timedChannel->GetRequestStart(&requestStart);
   2419    if (requestStart) {
   2420      TimeDuration timeToRequestStart = requestStart - navigationStart;
   2421      if (timeToRequestStart > zeroDuration) {
   2422        mPageloadEventData.set_timeToRequestStart(
   2423            static_cast<uint32_t>(timeToRequestStart.ToMilliseconds()));
   2424      } else {
   2425        // Speculative and pre-established connections may yield zero or
   2426        // slightly negative timeToRequestStart timings. We record these as zero
   2427        // to maintain consistent, non-negative timing data, while still
   2428        // capturing the impact of early connection establishment.
   2429        mPageloadEventData.set_timeToRequestStart(0);
   2430      }
   2431    }
   2432 
   2433    TimeStamp secureConnectStart;
   2434    TimeStamp connectEnd;
   2435    timedChannel->GetSecureConnectionStart(&secureConnectStart);
   2436    timedChannel->GetConnectEnd(&connectEnd);
   2437    if (secureConnectStart && connectEnd) {
   2438      TimeDuration tlsHandshakeTime = connectEnd - secureConnectStart;
   2439      if (tlsHandshakeTime > zeroDuration) {
   2440        mPageloadEventData.set_tlsHandshakeTime(
   2441            static_cast<uint32_t>(tlsHandshakeTime.ToMilliseconds()));
   2442      }
   2443    }
   2444  }
   2445 }
   2446 
   2447 Document::~Document() {
   2448  MOZ_LOG(gDocumentLeakPRLog, LogLevel::Debug, ("DOCUMENT %p destroyed", this));
   2449  MOZ_ASSERT(!IsTopLevelContentDocument() || !IsResourceDoc(),
   2450             "Can't be top-level and a resource doc at the same time");
   2451 
   2452  NS_ASSERTION(!mIsShowing, "Destroying a currently-showing document");
   2453 
   2454  if (IsTopLevelContentDocument()) {
   2455    RemoveToplevelLoadingDocument(this);
   2456  }
   2457 
   2458  mInDestructor = true;
   2459  mInUnlinkOrDeletion = true;
   2460 
   2461  mozilla::DropJSObjects(this);
   2462 
   2463  // Clear mObservers to keep it in sync with the mutationobserver list
   2464  mObservers.Clear();
   2465 
   2466  mIntersectionObservers.Clear();
   2467 
   2468  if (mStyleSheetSetList) {
   2469    mStyleSheetSetList->Disconnect();
   2470  }
   2471 
   2472  if (mAnimationController) {
   2473    mAnimationController->Disconnect();
   2474  }
   2475 
   2476  mParentDocument = nullptr;
   2477 
   2478  // Kill the subdocument map, doing this will release its strong
   2479  // references, if any.
   2480  mSubDocuments = nullptr;
   2481 
   2482  nsAutoScriptBlocker scriptBlocker;
   2483 
   2484  WillRemoveRoot();
   2485 
   2486  // Invalidate cached array of child nodes
   2487  InvalidateChildNodes();
   2488 
   2489  // We should not have child nodes when destructor is called,
   2490  // since child nodes keep their owner document alive.
   2491  MOZ_ASSERT(!HasChildren());
   2492 
   2493  mCachedRootElement = nullptr;
   2494 
   2495  for (auto& sheets : mAdditionalSheets) {
   2496    UnlinkStyleSheets(sheets);
   2497  }
   2498 
   2499  if (mAttributeStyles) {
   2500    mAttributeStyles->SetOwningDocument(nullptr);
   2501  }
   2502 
   2503  if (mListenerManager) {
   2504    mListenerManager->Disconnect();
   2505    UnsetFlags(NODE_HAS_LISTENERMANAGER);
   2506  }
   2507 
   2508  if (mScriptLoader) {
   2509    mScriptLoader->DropDocumentReference();
   2510  }
   2511 
   2512  if (mCSSLoader) {
   2513    // Could be null here if Init() failed or if we have been unlinked.
   2514    mCSSLoader->DropDocumentReference();
   2515  }
   2516 
   2517  if (mStyleImageLoader) {
   2518    mStyleImageLoader->DropDocumentReference();
   2519  }
   2520 
   2521  if (mXULBroadcastManager) {
   2522    mXULBroadcastManager->DropDocumentReference();
   2523  }
   2524 
   2525  if (mXULPersist) {
   2526    mXULPersist->DropDocumentReference();
   2527  }
   2528 
   2529  if (mPermissionDelegateHandler) {
   2530    mPermissionDelegateHandler->DropDocumentReference();
   2531  }
   2532 
   2533  SetLockingImages(false);
   2534  SetImageAnimationState(false);
   2535 
   2536  mHeaderData = nullptr;
   2537 
   2538  mPendingTitleChangeEvent.Revoke();
   2539 
   2540  MOZ_ASSERT(mDOMMediaQueryLists.isEmpty(),
   2541             "must not have media query lists left");
   2542 
   2543  if (mNodeInfoManager) {
   2544    mNodeInfoManager->DropDocumentReference();
   2545  }
   2546 
   2547  if (mDocGroup) {
   2548    MOZ_ASSERT(mDocGroup->GetBrowsingContextGroup());
   2549    mDocGroup->GetBrowsingContextGroup()->RemoveDocument(this, mDocGroup);
   2550  }
   2551 
   2552  UnlinkOriginalDocumentIfStatic();
   2553 
   2554  UnregisterFromMemoryReportingForDataDocument();
   2555 
   2556  if (isInList()) {
   2557    MOZ_ASSERT(AllDocumentsList().contains(this));
   2558    remove();
   2559  }
   2560 }
   2561 
   2562 void Document::DropStyleSet() { mStyleSet = nullptr; }
   2563 
   2564 NS_INTERFACE_TABLE_HEAD(Document)
   2565  NS_WRAPPERCACHE_INTERFACE_TABLE_ENTRY
   2566  NS_INTERFACE_TABLE_BEGIN
   2567    NS_INTERFACE_TABLE_ENTRY_AMBIGUOUS(Document, nsISupports, nsINode)
   2568    NS_INTERFACE_TABLE_ENTRY(Document, nsINode)
   2569    NS_INTERFACE_TABLE_ENTRY(Document, Document)
   2570    NS_INTERFACE_TABLE_ENTRY(Document, nsIScriptObjectPrincipal)
   2571    NS_INTERFACE_TABLE_ENTRY(Document, EventTarget)
   2572    NS_INTERFACE_TABLE_ENTRY(Document, nsISupportsWeakReference)
   2573  NS_INTERFACE_TABLE_END
   2574  NS_INTERFACE_TABLE_TO_MAP_SEGUE_CYCLE_COLLECTION(Document)
   2575 NS_INTERFACE_MAP_END
   2576 
   2577 NS_IMPL_CYCLE_COLLECTING_ADDREF(Document)
   2578 NS_IMPL_CYCLE_COLLECTING_RELEASE_WITH_LAST_RELEASE(Document, LastRelease())
   2579 
   2580 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_BEGIN(Document)
   2581  if (Element::CanSkip(tmp, aRemovingAllowed)) {
   2582    EventListenerManager* elm = tmp->GetExistingListenerManager();
   2583    if (elm) {
   2584      elm->MarkForCC();
   2585    }
   2586    return true;
   2587  }
   2588 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_END
   2589 
   2590 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_BEGIN(Document)
   2591  return Element::CanSkipInCC(tmp);
   2592 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_END
   2593 
   2594 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_BEGIN(Document)
   2595  return Element::CanSkipThis(tmp);
   2596 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_END
   2597 
   2598 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INTERNAL(Document)
   2599  if (MOZ_UNLIKELY(cb.WantDebugInfo())) {
   2600    char name[512];
   2601    nsAutoCString loadedAsData;
   2602    if (tmp->IsLoadedAsData()) {
   2603      loadedAsData.AssignLiteral("data");
   2604    } else {
   2605      loadedAsData.AssignLiteral("normal");
   2606    }
   2607    uint32_t nsid = tmp->GetDefaultNamespaceID();
   2608    nsAutoCString uri;
   2609    if (tmp->mDocumentURI) uri = tmp->mDocumentURI->GetSpecOrDefault();
   2610    static const char* kNSURIs[] = {"([none])", "(xmlns)", "(xml)",
   2611                                    "(xhtml)",  "(XLink)", "(XSLT)",
   2612                                    "(MathML)", "(RDF)",   "(XUL)"};
   2613    if (nsid < std::size(kNSURIs)) {
   2614      SprintfLiteral(name, "Document %s %s %s", loadedAsData.get(),
   2615                     kNSURIs[nsid], uri.get());
   2616    } else {
   2617      SprintfLiteral(name, "Document %s %s", loadedAsData.get(), uri.get());
   2618    }
   2619    cb.DescribeRefCountedNode(tmp->mRefCnt.get(), name);
   2620  } else {
   2621    NS_IMPL_CYCLE_COLLECTION_DESCRIBE(Document, tmp->mRefCnt.get())
   2622  }
   2623 
   2624  if (!nsINode::Traverse(tmp, cb)) {
   2625    return NS_SUCCESS_INTERRUPTED_TRAVERSE;
   2626  }
   2627 
   2628  tmp->mExternalResourceMap.Traverse(&cb);
   2629 
   2630  // Traverse all Document pointer members.
   2631  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSecurityInfo)
   2632  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDisplayDocument)
   2633  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFontFaceSet)
   2634  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mReadyForIdle)
   2635  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocumentL10n)
   2636  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFragmentDirective)
   2637  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mHighlightRegistry)
   2638  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPendingFullscreenEvents)
   2639  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mParser)
   2640  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mScriptGlobalObject)
   2641  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mListenerManager)
   2642  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mStyleSheetSetList)
   2643  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mScriptLoader)
   2644  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCustomContentContainer)
   2645 
   2646  DocumentOrShadowRoot::Traverse(tmp, cb);
   2647 
   2648  if (tmp->mRadioGroupContainer) {
   2649    RadioGroupContainer::Traverse(tmp->mRadioGroupContainer.get(), cb);
   2650  }
   2651 
   2652  for (auto& sheets : tmp->mAdditionalSheets) {
   2653    tmp->TraverseStyleSheets(sheets, "mAdditionalSheets[<origin>][i]", cb);
   2654  }
   2655 
   2656  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mOnloadBlocker)
   2657  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mLazyLoadObserver)
   2658  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mElementsObservedForLastRememberedSize)
   2659  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDOMImplementation)
   2660  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mImageMaps)
   2661  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mOrientationPendingPromise)
   2662  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mOriginalDocument)
   2663  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCachedEncoder)
   2664  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocumentTimeline)
   2665  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mScrollTimelineAnimationTracker)
   2666  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mTemplateContentsOwner)
   2667  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mChildrenCollection)
   2668  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mImages);
   2669  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mEmbeds);
   2670  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mLinks);
   2671  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mForms);
   2672  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mScripts);
   2673  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mApplets);
   2674  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mAnchors);
   2675  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mAnonymousContents)
   2676  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCommandDispatcher)
   2677  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFeaturePolicy)
   2678  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPermissionDelegateHandler)
   2679  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSuppressedEventListener)
   2680  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPrototypeDocument)
   2681  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mMidasCommandManager)
   2682  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mAll)
   2683  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mActiveViewTransition)
   2684  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mViewTransitionUpdateCallbacks)
   2685  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocGroup)
   2686  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFrameRequestManager)
   2687 
   2688  // Traverse all our nsCOMArrays.
   2689  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPreloadingImages)
   2690 
   2691  // Traverse animation components
   2692  if (tmp->mAnimationController) {
   2693    tmp->mAnimationController->Traverse(&cb);
   2694  }
   2695 
   2696  if (tmp->mSubDocuments) {
   2697    for (auto iter = tmp->mSubDocuments->Iter(); !iter.Done(); iter.Next()) {
   2698      auto entry = static_cast<SubDocMapEntry*>(iter.Get());
   2699 
   2700      NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mSubDocuments entry->mKey");
   2701      cb.NoteXPCOMChild(entry->mKey);
   2702      NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb,
   2703                                         "mSubDocuments entry->mSubDocument");
   2704      cb.NoteXPCOMChild(ToSupports(entry->mSubDocument));
   2705    }
   2706  }
   2707 
   2708  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCSSLoader)
   2709 
   2710  // We own only the items in mDOMMediaQueryLists that have listeners;
   2711  // this reference is managed by their AddListener and RemoveListener
   2712  // methods.
   2713  for (MediaQueryList* mql = tmp->mDOMMediaQueryLists.getFirst(); mql;
   2714       mql = static_cast<LinkedListElement<MediaQueryList>*>(mql)->getNext()) {
   2715    if (mql->HasListeners() &&
   2716        NS_SUCCEEDED(mql->CheckCurrentGlobalCorrectness())) {
   2717      NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mDOMMediaQueryLists item");
   2718      cb.NoteXPCOMChild(static_cast<EventTarget*>(mql));
   2719    }
   2720  }
   2721 
   2722  // XXX: This should be not needed once bug 1569185 lands.
   2723  for (const auto& entry : tmp->mL10nProtoElements) {
   2724    NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mL10nProtoElements key");
   2725    cb.NoteXPCOMChild(entry.GetKey());
   2726    CycleCollectionNoteChild(cb, entry.GetWeak(), "mL10nProtoElements value");
   2727  }
   2728 
   2729  for (size_t i = 0; i < tmp->mPendingFrameStaticClones.Length(); ++i) {
   2730    NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPendingFrameStaticClones[i].mElement);
   2731    NS_IMPL_CYCLE_COLLECTION_TRAVERSE(
   2732        mPendingFrameStaticClones[i].mStaticCloneOf);
   2733  }
   2734 
   2735  for (auto& tableEntry : tmp->mActiveLocks) {
   2736    ImplCycleCollectionTraverse(cb, *tableEntry.GetModifiableData(),
   2737                                "mActiveLocks entry", 0);
   2738  }
   2739 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
   2740 
   2741 NS_IMPL_CYCLE_COLLECTION_CLASS(Document)
   2742 
   2743 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(Document)
   2744  NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER
   2745  NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mCachedStateObject)
   2746 NS_IMPL_CYCLE_COLLECTION_TRACE_END
   2747 
   2748 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(Document)
   2749  tmp->mInUnlinkOrDeletion = true;
   2750 
   2751  tmp->SetStateObject(nullptr);
   2752 
   2753  // Clear out our external resources
   2754  tmp->mExternalResourceMap.Shutdown();
   2755 
   2756  nsAutoScriptBlocker scriptBlocker;
   2757 
   2758  tmp->RemoveCustomContentContainer();
   2759 
   2760  nsINode::Unlink(tmp);
   2761 
   2762  BatchRemovalState state{};
   2763  while (nsCOMPtr<nsIContent> child = tmp->GetLastChild()) {
   2764    // Hold a strong ref to the node when we remove it, because we may be
   2765    // the last reference to it.
   2766    // If this code changes, change the corresponding code in Document's
   2767    // unlink impl and ContentUnbinder::UnbindSubtree.
   2768    tmp->DisconnectChild(child);
   2769    child->UnbindFromTree(/* aNewParent=*/nullptr, &state);
   2770    state.mIsFirst = false;
   2771  }
   2772 
   2773  tmp->UnlinkOriginalDocumentIfStatic();
   2774 
   2775  tmp->mCachedRootElement = nullptr;  // Avoid a dangling pointer
   2776 
   2777  tmp->SetScriptGlobalObject(nullptr);
   2778 
   2779  for (auto& sheets : tmp->mAdditionalSheets) {
   2780    tmp->UnlinkStyleSheets(sheets);
   2781  }
   2782 
   2783  NS_IMPL_CYCLE_COLLECTION_UNLINK(mSecurityInfo)
   2784  NS_IMPL_CYCLE_COLLECTION_UNLINK(mDisplayDocument)
   2785  NS_IMPL_CYCLE_COLLECTION_UNLINK(mLazyLoadObserver)
   2786  NS_IMPL_CYCLE_COLLECTION_UNLINK(mElementsObservedForLastRememberedSize);
   2787  NS_IMPL_CYCLE_COLLECTION_UNLINK(mFontFaceSet)
   2788  NS_IMPL_CYCLE_COLLECTION_UNLINK(mReadyForIdle)
   2789  NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocumentL10n)
   2790  NS_IMPL_CYCLE_COLLECTION_UNLINK(mFragmentDirective)
   2791  NS_IMPL_CYCLE_COLLECTION_UNLINK(mHighlightRegistry)
   2792  NS_IMPL_CYCLE_COLLECTION_UNLINK(mPendingFullscreenEvents)
   2793  NS_IMPL_CYCLE_COLLECTION_UNLINK(mParser)
   2794  NS_IMPL_CYCLE_COLLECTION_UNLINK(mOnloadBlocker)
   2795  NS_IMPL_CYCLE_COLLECTION_UNLINK(mDOMImplementation)
   2796  NS_IMPL_CYCLE_COLLECTION_UNLINK(mImageMaps)
   2797  NS_IMPL_CYCLE_COLLECTION_UNLINK(mOrientationPendingPromise)
   2798  NS_IMPL_CYCLE_COLLECTION_UNLINK(mOriginalDocument)
   2799  NS_IMPL_CYCLE_COLLECTION_UNLINK(mCachedEncoder)
   2800  NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocumentTimeline)
   2801  NS_IMPL_CYCLE_COLLECTION_UNLINK(mScrollTimelineAnimationTracker)
   2802  NS_IMPL_CYCLE_COLLECTION_UNLINK(mTemplateContentsOwner)
   2803  NS_IMPL_CYCLE_COLLECTION_UNLINK(mChildrenCollection)
   2804  NS_IMPL_CYCLE_COLLECTION_UNLINK(mImages);
   2805  NS_IMPL_CYCLE_COLLECTION_UNLINK(mEmbeds);
   2806  NS_IMPL_CYCLE_COLLECTION_UNLINK(mLinks);
   2807  NS_IMPL_CYCLE_COLLECTION_UNLINK(mForms);
   2808  NS_IMPL_CYCLE_COLLECTION_UNLINK(mScripts);
   2809  NS_IMPL_CYCLE_COLLECTION_UNLINK(mApplets);
   2810  NS_IMPL_CYCLE_COLLECTION_UNLINK(mAnchors);
   2811  NS_IMPL_CYCLE_COLLECTION_UNLINK(mAnonymousContents)
   2812  NS_IMPL_CYCLE_COLLECTION_UNLINK(mCommandDispatcher)
   2813  NS_IMPL_CYCLE_COLLECTION_UNLINK(mFeaturePolicy)
   2814  NS_IMPL_CYCLE_COLLECTION_UNLINK(mPermissionDelegateHandler)
   2815  NS_IMPL_CYCLE_COLLECTION_UNLINK(mSuppressedEventListener)
   2816  NS_IMPL_CYCLE_COLLECTION_UNLINK(mPrototypeDocument)
   2817  NS_IMPL_CYCLE_COLLECTION_UNLINK(mMidasCommandManager)
   2818  NS_IMPL_CYCLE_COLLECTION_UNLINK(mAll)
   2819  NS_IMPL_CYCLE_COLLECTION_UNLINK(mActiveViewTransition)
   2820  NS_IMPL_CYCLE_COLLECTION_UNLINK(mViewTransitionUpdateCallbacks)
   2821  NS_IMPL_CYCLE_COLLECTION_UNLINK(mReferrerInfo)
   2822  NS_IMPL_CYCLE_COLLECTION_UNLINK(mPreloadReferrerInfo)
   2823 
   2824  if (tmp->mDocGroup && tmp->mDocGroup->GetBrowsingContextGroup()) {
   2825    tmp->mDocGroup->GetBrowsingContextGroup()->RemoveDocument(tmp,
   2826                                                              tmp->mDocGroup);
   2827  }
   2828  tmp->mDocGroup = nullptr;
   2829 
   2830  if (tmp->IsTopLevelContentDocument()) {
   2831    RemoveToplevelLoadingDocument(tmp);
   2832  }
   2833 
   2834  tmp->mParentDocument = nullptr;
   2835 
   2836  NS_IMPL_CYCLE_COLLECTION_UNLINK(mPreloadingImages)
   2837 
   2838  NS_IMPL_CYCLE_COLLECTION_UNLINK(mIntersectionObservers)
   2839 
   2840  if (tmp->mListenerManager) {
   2841    tmp->mListenerManager->Disconnect();
   2842    tmp->UnsetFlags(NODE_HAS_LISTENERMANAGER);
   2843    tmp->mListenerManager = nullptr;
   2844  }
   2845 
   2846  if (tmp->mStyleSheetSetList) {
   2847    tmp->mStyleSheetSetList->Disconnect();
   2848    tmp->mStyleSheetSetList = nullptr;
   2849  }
   2850 
   2851  tmp->mSubDocuments = nullptr;
   2852 
   2853  NS_IMPL_CYCLE_COLLECTION_UNLINK(mFrameRequestManager)
   2854 
   2855  DocumentOrShadowRoot::Unlink(tmp);
   2856 
   2857  tmp->mRadioGroupContainer = nullptr;
   2858 
   2859  // Document has a pretty complex destructor, so we're going to
   2860  // assume that *most* cycles you actually want to break somewhere
   2861  // else, and not unlink an awful lot here.
   2862 
   2863  tmp->mExpandoAndGeneration.OwnerUnlinked();
   2864 
   2865  if (tmp->mAnimationController) {
   2866    tmp->mAnimationController->Unlink();
   2867  }
   2868 
   2869  tmp->mPendingTitleChangeEvent.Revoke();
   2870 
   2871  if (tmp->mCSSLoader) {
   2872    tmp->mCSSLoader->DropDocumentReference();
   2873    NS_IMPL_CYCLE_COLLECTION_UNLINK(mCSSLoader)
   2874  }
   2875 
   2876  if (tmp->mScriptLoader) {
   2877    tmp->mScriptLoader->DropDocumentReference();
   2878    NS_IMPL_CYCLE_COLLECTION_UNLINK(mScriptLoader)
   2879  }
   2880 
   2881  // We own only the items in mDOMMediaQueryLists that have listeners;
   2882  // this reference is managed by their AddListener and RemoveListener
   2883  // methods.
   2884  for (MediaQueryList* mql = tmp->mDOMMediaQueryLists.getFirst(); mql;) {
   2885    MediaQueryList* next =
   2886        static_cast<LinkedListElement<MediaQueryList>*>(mql)->getNext();
   2887    mql->Disconnect();
   2888    mql = next;
   2889  }
   2890 
   2891  tmp->mPendingFrameStaticClones.Clear();
   2892 
   2893  tmp->mActiveLocks.Clear();
   2894 
   2895  if (tmp->isInList()) {
   2896    MOZ_ASSERT(AllDocumentsList().contains(tmp));
   2897    tmp->remove();
   2898  }
   2899 
   2900  tmp->mInUnlinkOrDeletion = false;
   2901 
   2902  tmp->UnregisterFromMemoryReportingForDataDocument();
   2903 
   2904  NS_IMPL_CYCLE_COLLECTION_UNLINK(mL10nProtoElements)
   2905  NS_IMPL_CYCLE_COLLECTION_UNLINK_WEAK_PTR
   2906  NS_IMPL_CYCLE_COLLECTION_UNLINK_WEAK_REFERENCE
   2907 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
   2908 
   2909 nsresult Document::Init(nsIPrincipal* aPrincipal,
   2910                        nsIPrincipal* aPartitionedPrincipal) {
   2911  if (mCSSLoader || mStyleImageLoader || mNodeInfoManager || mScriptLoader) {
   2912    return NS_ERROR_ALREADY_INITIALIZED;
   2913  }
   2914 
   2915  // Force initialization.
   2916  mOnloadBlocker = new OnloadBlocker();
   2917 
   2918  mNodeInfoManager = new nsNodeInfoManager(this, aPrincipal);
   2919 
   2920  // mNodeInfo keeps NodeInfoManager alive!
   2921  mNodeInfo = mNodeInfoManager->GetDocumentNodeInfo();
   2922  NS_ENSURE_TRUE(mNodeInfo, NS_ERROR_OUT_OF_MEMORY);
   2923  MOZ_ASSERT(mNodeInfo->NodeType() == DOCUMENT_NODE,
   2924             "Bad NodeType in aNodeInfo");
   2925 
   2926  NS_ASSERTION(OwnerDoc() == this, "Our nodeinfo is busted!");
   2927 
   2928  if (!mLoadedAsData) {
   2929    CreateCSSAndStyleImageLoaders(false);
   2930  }
   2931 
   2932  // If after creation the owner js global is not set for a document
   2933  // we use the default compartment for this document, instead of creating
   2934  // wrapper in some random compartment when the document is exposed to js
   2935  // via some events.
   2936  nsCOMPtr<nsIGlobalObject> global =
   2937      xpc::NativeGlobal(xpc::PrivilegedJunkScope());
   2938  NS_ENSURE_TRUE(global, NS_ERROR_FAILURE);
   2939  mScopeObject = do_GetWeakReference(global);
   2940  MOZ_ASSERT(mScopeObject);
   2941 
   2942  if (!mLoadedAsData) {
   2943    mScriptLoader = new dom::ScriptLoader(this);
   2944  }
   2945 
   2946  // we need to create a policy here so getting the policy within
   2947  // ::Policy() can *always* return a non null policy
   2948  mFeaturePolicy = new dom::FeaturePolicy(this);
   2949  mFeaturePolicy->SetDefaultOrigin(NodePrincipal());
   2950 
   2951  if (aPrincipal) {
   2952    SetPrincipals(aPrincipal, aPartitionedPrincipal);
   2953  } else {
   2954    RecomputeResistFingerprinting();
   2955  }
   2956 
   2957  AllDocumentsList().insertBack(this);
   2958 
   2959  return NS_OK;
   2960 }
   2961 
   2962 void Document::RemoveAllProperties() { PropertyTable().RemoveAllProperties(); }
   2963 
   2964 void Document::RemoveAllPropertiesFor(nsINode* aNode) {
   2965  PropertyTable().RemoveAllPropertiesFor(aNode);
   2966 }
   2967 
   2968 void Document::Reset(nsIChannel* aChannel, nsILoadGroup* aLoadGroup) {
   2969  nsCOMPtr<nsIURI> uri;
   2970  nsCOMPtr<nsIPrincipal> principal;
   2971  nsCOMPtr<nsIPrincipal> partitionedPrincipal;
   2972  if (aChannel) {
   2973    mIsInPrivateBrowsing = NS_UsePrivateBrowsing(aChannel);
   2974 
   2975    // Note: this code is duplicated in PrototypeDocumentContentSink::Init and
   2976    // nsScriptSecurityManager::GetChannelResultPrincipals.
   2977    // Note: this should match the uri used for the OnNewURI call in
   2978    //       nsDocShell::CreateDocumentViewer.
   2979    NS_GetFinalChannelURI(aChannel, getter_AddRefs(uri));
   2980 
   2981    nsIScriptSecurityManager* securityManager =
   2982        nsContentUtils::GetSecurityManager();
   2983    if (securityManager) {
   2984      securityManager->GetChannelResultPrincipals(
   2985          aChannel, getter_AddRefs(principal),
   2986          getter_AddRefs(partitionedPrincipal));
   2987    }
   2988  }
   2989 
   2990  bool equal = principal->Equals(partitionedPrincipal);
   2991 
   2992  principal = MaybeDowngradePrincipal(principal);
   2993  if (equal) {
   2994    partitionedPrincipal = principal;
   2995  } else {
   2996    partitionedPrincipal = MaybeDowngradePrincipal(partitionedPrincipal);
   2997  }
   2998 
   2999  ResetToURI(uri, aLoadGroup, principal, partitionedPrincipal);
   3000 
   3001  // Note that, since mTiming does not change during a reset, the
   3002  // navigationStart time remains unchanged and therefore any future new
   3003  // timeline will have the same global clock time as the old one.
   3004  mDocumentTimeline = nullptr;
   3005 
   3006  if (nsCOMPtr<nsIPropertyBag2> bag = do_QueryInterface(aChannel)) {
   3007    if (nsCOMPtr<nsIURI> baseURI = do_GetProperty(bag, u"baseURI"_ns)) {
   3008      mDocumentBaseURI = baseURI.forget();
   3009      mChromeXHRDocBaseURI = nullptr;
   3010    }
   3011  }
   3012 
   3013  mChannel = aChannel;
   3014  RecomputeResistFingerprinting();
   3015  MaybeRecomputePartitionKey();
   3016 }
   3017 
   3018 void Document::DisconnectNodeTree() {
   3019  // Delete references to sub-documents and kill the subdocument map,
   3020  // if any. This is not strictly needed, but makes the node tree
   3021  // teardown a bit faster.
   3022  mSubDocuments = nullptr;
   3023 
   3024  bool oldVal = mInUnlinkOrDeletion;
   3025  mInUnlinkOrDeletion = true;
   3026  {  // Scope for update
   3027    MOZ_AUTO_DOC_UPDATE(this, true);
   3028 
   3029    WillRemoveRoot();
   3030 
   3031    // Invalidate cached array of child nodes
   3032    InvalidateChildNodes();
   3033 
   3034    while (nsCOMPtr<nsIContent> content = GetLastChild()) {
   3035      nsMutationGuard::DidMutate();
   3036      MutationObservers::NotifyContentWillBeRemoved(this, content, {});
   3037      DisconnectChild(content);
   3038      if (content == mCachedRootElement) {
   3039        // Immediately clear mCachedRootElement, now that it's been removed
   3040        // from mChildren, so that GetRootElement() will stop returning this
   3041        // now-stale value.
   3042        mCachedRootElement = nullptr;
   3043      }
   3044      content->UnbindFromTree();
   3045    }
   3046    MOZ_ASSERT(!mCachedRootElement,
   3047               "After removing all children, there should be no root elem");
   3048  }
   3049  mInUnlinkOrDeletion = oldVal;
   3050 }
   3051 
   3052 void Document::ResetToURI(nsIURI* aURI, nsILoadGroup* aLoadGroup,
   3053                          nsIPrincipal* aPrincipal,
   3054                          nsIPrincipal* aPartitionedPrincipal) {
   3055  MOZ_ASSERT(aURI, "Null URI passed to ResetToURI");
   3056  MOZ_ASSERT(!!aPrincipal == !!aPartitionedPrincipal);
   3057 
   3058  MOZ_LOG(gDocumentLeakPRLog, LogLevel::Debug,
   3059          ("DOCUMENT %p ResetToURI %s", this, aURI->GetSpecOrDefault().get()));
   3060 
   3061  mSecurityInfo = nullptr;
   3062 
   3063  nsCOMPtr<nsILoadGroup> group = do_QueryReferent(mDocumentLoadGroup);
   3064  if (!aLoadGroup || group != aLoadGroup) {
   3065    mDocumentLoadGroup = nullptr;
   3066  }
   3067 
   3068  DisconnectNodeTree();
   3069 
   3070  // Reset our stylesheets
   3071  ResetStylesheetsToURI(aURI);
   3072 
   3073  // Release the listener manager
   3074  if (mListenerManager) {
   3075    mListenerManager->Disconnect();
   3076    mListenerManager = nullptr;
   3077  }
   3078 
   3079  // Release the stylesheets list.
   3080  mDOMStyleSheets = nullptr;
   3081 
   3082  // Release our principal after tearing down the document, rather than before.
   3083  // This ensures that, during teardown, the document and the dying window
   3084  // (which already nulled out its document pointer and cached the principal)
   3085  // have matching principals.
   3086  SetPrincipals(nullptr, nullptr);
   3087 
   3088  // Clear the original URI so SetDocumentURI sets it.
   3089  mOriginalURI = nullptr;
   3090 
   3091  SetDocumentURI(aURI);
   3092  mChromeXHRDocURI = nullptr;
   3093  // If mDocumentBaseURI is null, Document::GetBaseURI() returns
   3094  // mDocumentURI.
   3095  mDocumentBaseURI = nullptr;
   3096  mChromeXHRDocBaseURI = nullptr;
   3097  mOnionLocationURI = nullptr;
   3098 
   3099  if (aLoadGroup) {
   3100    nsCOMPtr<nsIInterfaceRequestor> callbacks;
   3101    aLoadGroup->GetNotificationCallbacks(getter_AddRefs(callbacks));
   3102    if (callbacks) {
   3103      nsCOMPtr<nsILoadContext> loadContext = do_GetInterface(callbacks);
   3104      if (loadContext) {
   3105        // This is asserting that if we previously set mIsInPrivateBrowsing
   3106        // to true from the channel in Document::Reset, that the loadContext
   3107        // also believes it to be true.
   3108        MOZ_ASSERT(!mIsInPrivateBrowsing ||
   3109                   mIsInPrivateBrowsing == loadContext->UsePrivateBrowsing());
   3110        mIsInPrivateBrowsing = loadContext->UsePrivateBrowsing();
   3111      }
   3112    }
   3113 
   3114    mDocumentLoadGroup = do_GetWeakReference(aLoadGroup);
   3115    // there was an assertion here that aLoadGroup was not null.  This
   3116    // is no longer valid: nsDocShell::SetDocument does not create a
   3117    // load group, and it works just fine
   3118 
   3119    // XXXbz what does "just fine" mean exactly?  And given that there
   3120    // is no nsDocShell::SetDocument, what is this talking about?
   3121 
   3122    if (IsContentDocument()) {
   3123      // Inform the associated request context about this load start so
   3124      // any of its internal load progress flags gets reset.
   3125      nsCOMPtr<nsIRequestContextService> rcsvc =
   3126          net::RequestContextService::GetOrCreate();
   3127      if (rcsvc) {
   3128        nsCOMPtr<nsIRequestContext> rc;
   3129        rcsvc->GetRequestContextFromLoadGroup(aLoadGroup, getter_AddRefs(rc));
   3130        if (rc) {
   3131          rc->BeginLoad();
   3132        }
   3133      }
   3134    }
   3135  }
   3136 
   3137  mLastModified.Truncate();
   3138  // XXXbz I guess we're assuming that the caller will either pass in
   3139  // a channel with a useful type or call SetContentType?
   3140  SetContentType(""_ns);
   3141  mContentLanguage = nullptr;
   3142  mBaseTarget.Truncate();
   3143 
   3144  mXMLDeclarationBits = 0;
   3145 
   3146  // Now get our new principal
   3147  if (aPrincipal) {
   3148    SetPrincipals(aPrincipal, aPartitionedPrincipal);
   3149  } else {
   3150    nsIScriptSecurityManager* securityManager =
   3151        nsContentUtils::GetSecurityManager();
   3152    if (securityManager) {
   3153      nsCOMPtr<nsILoadContext> loadContext(mDocumentContainer);
   3154 
   3155      if (!loadContext && aLoadGroup) {
   3156        nsCOMPtr<nsIInterfaceRequestor> cbs;
   3157        aLoadGroup->GetNotificationCallbacks(getter_AddRefs(cbs));
   3158        loadContext = do_GetInterface(cbs);
   3159      }
   3160 
   3161      MOZ_ASSERT(loadContext,
   3162                 "must have a load context or pass in an explicit principal");
   3163 
   3164      nsCOMPtr<nsIPrincipal> principal;
   3165      nsresult rv = securityManager->GetLoadContextContentPrincipal(
   3166          mDocumentURI, loadContext, getter_AddRefs(principal));
   3167      if (NS_SUCCEEDED(rv)) {
   3168        SetPrincipals(principal, principal);
   3169      }
   3170    }
   3171  }
   3172 
   3173  if (mFontFaceSet) {
   3174    mFontFaceSet->RefreshStandardFontLoadPrincipal();
   3175  }
   3176 
   3177  // Refresh the principal on the realm.
   3178  if (nsPIDOMWindowInner* win = GetInnerWindow()) {
   3179    nsGlobalWindowInner::Cast(win)->RefreshRealmPrincipal();
   3180  }
   3181 }
   3182 
   3183 already_AddRefed<nsIPrincipal> Document::MaybeDowngradePrincipal(
   3184    nsIPrincipal* aPrincipal) {
   3185  if (!aPrincipal) {
   3186    return nullptr;
   3187  }
   3188 
   3189  // We can't load a document with an expanded principal. If we're given one,
   3190  // automatically downgrade it to the last principal it subsumes (which is the
   3191  // extension principal, in the case of extension content scripts).
   3192  auto* basePrin = BasePrincipal::Cast(aPrincipal);
   3193  if (basePrin->Is<ExpandedPrincipal>()) {
   3194    MOZ_DIAGNOSTIC_CRASH(
   3195        "Should never try to create a document with "
   3196        "an expanded principal");
   3197 
   3198    auto* expanded = basePrin->As<ExpandedPrincipal>();
   3199    return do_AddRef(expanded->AllowList().LastElement());
   3200  }
   3201 
   3202  if (aPrincipal->IsSystemPrincipal() && mDocumentContainer) {
   3203    // We basically want the parent document here, but because this is very
   3204    // early in the load, GetInProcessParentDocument() returns null, so we use
   3205    // the docshell hierarchy to get this information instead.
   3206    if (RefPtr<BrowsingContext> parent =
   3207            mDocumentContainer->GetBrowsingContext()->GetParent()) {
   3208      auto* parentWin = nsGlobalWindowOuter::Cast(parent->GetDOMWindow());
   3209      if (!parentWin || !parentWin->GetPrincipal()->IsSystemPrincipal()) {
   3210        nsCOMPtr<nsIPrincipal> nullPrincipal =
   3211            NullPrincipal::CreateWithoutOriginAttributes();
   3212        return nullPrincipal.forget();
   3213      }
   3214    }
   3215  }
   3216  nsCOMPtr<nsIPrincipal> principal(aPrincipal);
   3217  return principal.forget();
   3218 }
   3219 
   3220 size_t Document::FindDocStyleSheetInsertionPoint(const StyleSheet& aSheet) {
   3221  ServoStyleSet& styleSet = EnsureStyleSet();
   3222 
   3223  // lowest index first
   3224  const size_t newDocIndex = StyleOrderIndexOfSheet(aSheet);
   3225  MOZ_ASSERT(newDocIndex != mStyleSheets.NoIndex);
   3226 
   3227  size_t index = styleSet.SheetCount(StyleOrigin::Author);
   3228  while (index--) {
   3229    auto* sheet = styleSet.SheetAt(StyleOrigin::Author, index);
   3230    MOZ_ASSERT(sheet);
   3231    if (!sheet->GetAssociatedDocumentOrShadowRoot()) {
   3232      // If the sheet is not owned by the document it should be an author sheet
   3233      // registered at nsStyleSheetService, or an additional sheet. In that case
   3234      // the doc sheet should end up before it.
   3235      // FIXME(emilio): Additional stylesheets inconsistently end up with
   3236      // associated document, depending on which code-path adds them. Fix this.
   3237      MOZ_ASSERT(
   3238          nsStyleSheetService::GetInstance()->AuthorStyleSheets()->Contains(
   3239              sheet) ||
   3240          mAdditionalSheets[eAuthorSheet].Contains(sheet));
   3241      continue;
   3242    }
   3243    size_t sheetDocIndex = StyleOrderIndexOfSheet(*sheet);
   3244    if (MOZ_UNLIKELY(sheetDocIndex == mStyleSheets.NoIndex)) {
   3245      MOZ_ASSERT_UNREACHABLE("Which stylesheet can hit this?");
   3246      continue;
   3247    }
   3248    MOZ_ASSERT(sheetDocIndex != newDocIndex);
   3249    if (sheetDocIndex < newDocIndex) {
   3250      // We found a document-owned sheet. All of them go together, so if the
   3251      // current sheet goes before ours, we're at the right index already.
   3252      return index + 1;
   3253    }
   3254    // Otherwise keep looking. Unfortunately we can't do something clever like:
   3255    //
   3256    // return index - sheetDocIndex + newDocIndex;
   3257    //
   3258    // Or so, because we need to deal with disabled / non-applicable sheets
   3259    // which are not in the styleset, even though they're in the document.
   3260  }
   3261  // We found no sheet that goes before us, so we're index 0.
   3262  return 0;
   3263 }
   3264 
   3265 void Document::ResetStylesheetsToURI(nsIURI* aURI) {
   3266  MOZ_ASSERT(aURI);
   3267 
   3268  ClearAdoptedStyleSheets();
   3269  ServoStyleSet& styleSet = EnsureStyleSet();
   3270 
   3271  auto ClearSheetList = [&](nsTArray<RefPtr<StyleSheet>>& aSheetList) {
   3272    for (auto& sheet : Reversed(aSheetList)) {
   3273      sheet->ClearAssociatedDocumentOrShadowRoot();
   3274      if (mStyleSetFilled) {
   3275        styleSet.RemoveStyleSheet(*sheet);
   3276      }
   3277    }
   3278    aSheetList.Clear();
   3279  };
   3280  ClearSheetList(mStyleSheets);
   3281  for (auto& sheets : mAdditionalSheets) {
   3282    ClearSheetList(sheets);
   3283  }
   3284  if (mStyleSetFilled) {
   3285    if (auto* ss = nsStyleSheetService::GetInstance()) {
   3286      for (auto& sheet : Reversed(*ss->AuthorStyleSheets())) {
   3287        MOZ_ASSERT(!sheet->GetAssociatedDocumentOrShadowRoot());
   3288        if (sheet->IsApplicable()) {
   3289          styleSet.RemoveStyleSheet(*sheet);
   3290        }
   3291      }
   3292    }
   3293  }
   3294 
   3295  // Now reset our inline style and attribute sheets.
   3296  if (mAttributeStyles) {
   3297    mAttributeStyles->Reset();
   3298    mAttributeStyles->SetOwningDocument(this);
   3299  } else {
   3300    mAttributeStyles = new AttributeStyles(this);
   3301  }
   3302 
   3303  if (mStyleSetFilled) {
   3304    FillStyleSetDocumentSheets();
   3305 
   3306    if (styleSet.StyleSheetsHaveChanged()) {
   3307      ApplicableStylesChanged();
   3308    }
   3309  }
   3310 }
   3311 
   3312 void Document::FillStyleSetUserAndUASheets() {
   3313  // Make sure this does the same thing as PresShell::Add{User,Agent}Sheet wrt
   3314  // ordering.
   3315 
   3316  // The document will fill in the document sheets when we create the presshell
   3317  auto* cache = GlobalStyleSheetCache::Singleton();
   3318 
   3319  nsStyleSheetService* sheetService = nsStyleSheetService::GetInstance();
   3320  MOZ_ASSERT(sheetService,
   3321             "should never be creating a StyleSet after the style sheet "
   3322             "service has gone");
   3323 
   3324  ServoStyleSet& styleSet = EnsureStyleSet();
   3325  for (StyleSheet* sheet : *sheetService->UserStyleSheets()) {
   3326    styleSet.AppendStyleSheet(*sheet);
   3327  }
   3328 
   3329  StyleSheet* sheet = IsInChromeDocShell() ? cache->GetUserChromeSheet()
   3330                                           : cache->GetUserContentSheet();
   3331  if (sheet) {
   3332    styleSet.AppendStyleSheet(*sheet);
   3333  }
   3334 
   3335  styleSet.AppendStyleSheet(*cache->UASheet());
   3336 
   3337  if (MOZ_LIKELY(NodeInfoManager()->MathMLEnabled())) {
   3338    styleSet.AppendStyleSheet(*cache->MathMLSheet());
   3339  }
   3340 
   3341  if (MOZ_LIKELY(NodeInfoManager()->SVGEnabled())) {
   3342    styleSet.AppendStyleSheet(*cache->SVGSheet());
   3343  }
   3344 
   3345  styleSet.AppendStyleSheet(*cache->HTMLSheet());
   3346 
   3347  if (nsLayoutUtils::ShouldUseNoFramesSheet(this)) {
   3348    styleSet.AppendStyleSheet(*cache->NoFramesSheet());
   3349  }
   3350 
   3351  styleSet.AppendStyleSheet(*cache->CounterStylesSheet());
   3352 
   3353  // Only load the full XUL sheet if we'll need it.
   3354  if (LoadsFullXULStyleSheetUpFront()) {
   3355    styleSet.AppendStyleSheet(*cache->XULSheet());
   3356  }
   3357 
   3358  styleSet.AppendStyleSheet(*cache->FormsSheet());
   3359  styleSet.AppendStyleSheet(*cache->ScrollbarsSheet());
   3360 
   3361  for (StyleSheet* sheet : *sheetService->AgentStyleSheets()) {
   3362    styleSet.AppendStyleSheet(*sheet);
   3363  }
   3364 
   3365  MOZ_ASSERT(!mQuirkSheetAdded);
   3366  if (NeedsQuirksSheet()) {
   3367    styleSet.AppendStyleSheet(*cache->QuirkSheet());
   3368    mQuirkSheetAdded = true;
   3369  }
   3370 }
   3371 
   3372 void Document::FillStyleSet() {
   3373  MOZ_ASSERT(!mStyleSetFilled);
   3374  FillStyleSetUserAndUASheets();
   3375  FillStyleSetDocumentSheets();
   3376  mStyleSetFilled = true;
   3377 }
   3378 
   3379 void Document::FillStyleSetDocumentSheets() {
   3380  ServoStyleSet& styleSet = EnsureStyleSet();
   3381  MOZ_ASSERT(styleSet.SheetCount(StyleOrigin::Author) == 0,
   3382             "Style set already has document sheets?");
   3383 
   3384  // Sheets are added in reverse order to avoid worst-case time complexity when
   3385  // looking up the index of a sheet.
   3386  //
   3387  // Note that usually appending is faster (rebuilds less stuff in the
   3388  // styleset), but in this case it doesn't matter since we're filling the
   3389  // styleset from scratch anyway.
   3390  for (StyleSheet* sheet : Reversed(mStyleSheets)) {
   3391    if (sheet->IsApplicable()) {
   3392      styleSet.AddDocStyleSheet(*sheet);
   3393    }
   3394  }
   3395 
   3396  EnumerateUniqueAdoptedStyleSheetsBackToFront([&](StyleSheet& aSheet) {
   3397    if (aSheet.IsApplicable()) {
   3398      styleSet.AddDocStyleSheet(aSheet);
   3399    }
   3400  });
   3401 
   3402  nsStyleSheetService* sheetService = nsStyleSheetService::GetInstance();
   3403  for (StyleSheet* sheet : *sheetService->AuthorStyleSheets()) {
   3404    styleSet.AppendStyleSheet(*sheet);
   3405  }
   3406 
   3407  for (auto& sheets : mAdditionalSheets) {
   3408    for (StyleSheet* sheet : sheets) {
   3409      styleSet.AppendStyleSheet(*sheet);
   3410    }
   3411  }
   3412 }
   3413 
   3414 void Document::CompatibilityModeChanged() {
   3415  MOZ_ASSERT(IsHTMLOrXHTML());
   3416  if (mCSSLoader) {
   3417    mCSSLoader->SetCompatibilityMode(mCompatMode);
   3418  }
   3419 
   3420  if (mStyleSet) {
   3421    mStyleSet->CompatibilityModeChanged();
   3422  }
   3423  if (!mStyleSetFilled) {
   3424    MOZ_ASSERT(!mQuirkSheetAdded);
   3425    return;
   3426  }
   3427 
   3428  MOZ_ASSERT(mStyleSet);
   3429  if (PresShell* presShell = GetPresShell()) {
   3430    // Selectors may have become case-sensitive / case-insensitive, the stylist
   3431    // has already performed the relevant invalidation.
   3432    presShell->EnsureStyleFlush();
   3433  }
   3434  if (mQuirkSheetAdded == NeedsQuirksSheet()) {
   3435    return;
   3436  }
   3437  auto* cache = GlobalStyleSheetCache::Singleton();
   3438  StyleSheet* sheet = cache->QuirkSheet();
   3439  if (mQuirkSheetAdded) {
   3440    mStyleSet->RemoveStyleSheet(*sheet);
   3441  } else {
   3442    mStyleSet->AppendStyleSheet(*sheet);
   3443  }
   3444  mQuirkSheetAdded = !mQuirkSheetAdded;
   3445  ApplicableStylesChanged();
   3446 }
   3447 
   3448 void Document::SetCompatibilityMode(nsCompatibility aMode) {
   3449  NS_ASSERTION(IsHTMLDocument() || aMode == eCompatibility_FullStandards,
   3450               "Bad compat mode for XHTML document!");
   3451 
   3452  if (mCompatMode == aMode) {
   3453    return;
   3454  }
   3455  mCompatMode = aMode;
   3456  CompatibilityModeChanged();
   3457  // Trigger recomputation of the nsViewportInfo the next time it's queried.
   3458  mViewportType = Unknown;
   3459 }
   3460 
   3461 static void WarnIfSandboxIneffective(nsIDocShell* aDocShell,
   3462                                     uint32_t aSandboxFlags,
   3463                                     nsIChannel* aChannel) {
   3464  // If the document permits allow-top-navigation and
   3465  // allow-top-navigation-by-user-activation this will permit all top
   3466  // navigation.
   3467  if (aSandboxFlags != SANDBOXED_NONE &&
   3468      !(aSandboxFlags & SANDBOXED_TOPLEVEL_NAVIGATION) &&
   3469      !(aSandboxFlags & SANDBOXED_TOPLEVEL_NAVIGATION_USER_ACTIVATION)) {
   3470    nsContentUtils::ReportToConsole(
   3471        nsIScriptError::warningFlag, "Iframe Sandbox"_ns,
   3472        aDocShell->GetDocument(), nsContentUtils::eSECURITY_PROPERTIES,
   3473        "BothAllowTopNavigationAndUserActivationPresent");
   3474  }
   3475  // If the document is sandboxed (via the HTML5 iframe sandbox
   3476  // attribute) and both the allow-scripts and allow-same-origin
   3477  // keywords are supplied, the sandboxed document can call into its
   3478  // parent document and remove its sandboxing entirely - we print a
   3479  // warning to the web console in this case.
   3480  if (aSandboxFlags & SANDBOXED_NAVIGATION &&
   3481      !(aSandboxFlags & SANDBOXED_SCRIPTS) &&
   3482      !(aSandboxFlags & SANDBOXED_ORIGIN)) {
   3483    RefPtr<BrowsingContext> bc = aDocShell->GetBrowsingContext();
   3484    MOZ_ASSERT(bc->IsInProcess());
   3485 
   3486    RefPtr<BrowsingContext> parentBC = bc->GetParent();
   3487    if (!parentBC || !parentBC->IsInProcess()) {
   3488      // If parent document is not in process, then by construction it
   3489      // cannot be same origin.
   3490      return;
   3491    }
   3492 
   3493    // Don't warn if our parent is not the top-level document.
   3494    if (!parentBC->IsTopContent()) {
   3495      return;
   3496    }
   3497 
   3498    nsCOMPtr<nsIDocShell> parentDocShell = parentBC->GetDocShell();
   3499    MOZ_ASSERT(parentDocShell);
   3500 
   3501    nsCOMPtr<nsIChannel> parentChannel;
   3502    parentDocShell->GetCurrentDocumentChannel(getter_AddRefs(parentChannel));
   3503    if (!parentChannel) {
   3504      return;
   3505    }
   3506    nsresult rv = nsContentUtils::CheckSameOrigin(aChannel, parentChannel);
   3507    if (NS_FAILED(rv)) {
   3508      return;
   3509    }
   3510 
   3511    nsCOMPtr<Document> parentDocument = parentDocShell->GetDocument();
   3512    nsCOMPtr<nsIURI> iframeUri;
   3513    parentChannel->GetURI(getter_AddRefs(iframeUri));
   3514    nsContentUtils::ReportToConsole(
   3515        nsIScriptError::warningFlag, "Iframe Sandbox"_ns, parentDocument,
   3516        nsContentUtils::eSECURITY_PROPERTIES,
   3517        "BothAllowScriptsAndSameOriginPresent", nsTArray<nsString>(),
   3518        SourceLocation(iframeUri.get()));
   3519  }
   3520 }
   3521 
   3522 bool Document::IsSynthesized() {
   3523  nsCOMPtr<nsILoadInfo> loadInfo = mChannel ? mChannel->LoadInfo() : nullptr;
   3524  return loadInfo && loadInfo->GetServiceWorkerTaintingSynthesized();
   3525 }
   3526 
   3527 // static
   3528 bool Document::IsCallerChromeOrAddon(JSContext* aCx, JSObject* aObject) {
   3529  nsIPrincipal* principal = nsContentUtils::SubjectPrincipal(aCx);
   3530  return principal && (principal->IsSystemPrincipal() ||
   3531                       principal->GetIsAddonOrExpandedAddonPrincipal());
   3532 }
   3533 
   3534 static void CheckIsBadPolicy(nsILoadInfo::CrossOriginOpenerPolicy aPolicy,
   3535                             BrowsingContext* aContext, nsIChannel* aChannel) {
   3536 #if defined(EARLY_BETA_OR_EARLIER)
   3537  auto requireCORP =
   3538      nsILoadInfo::OPENER_POLICY_SAME_ORIGIN_EMBEDDER_POLICY_REQUIRE_CORP;
   3539 
   3540  if (aContext->GetOpenerPolicy() == aPolicy ||
   3541      (aContext->GetOpenerPolicy() != requireCORP && aPolicy != requireCORP)) {
   3542    return;
   3543  }
   3544 
   3545  nsCOMPtr<nsIURI> uri;
   3546  bool hasURI = NS_SUCCEEDED(aChannel->GetOriginalURI(getter_AddRefs(uri)));
   3547 
   3548  bool isViewSource = hasURI && uri->SchemeIs("view-source");
   3549 
   3550  nsCString contentType;
   3551  nsCOMPtr<nsIPropertyBag2> bag = do_QueryInterface(aChannel);
   3552  bool isPDFJS = bag &&
   3553                 NS_SUCCEEDED(bag->GetPropertyAsACString(u"contentType"_ns,
   3554                                                         contentType)) &&
   3555                 contentType.EqualsLiteral(APPLICATION_PDF);
   3556 
   3557  MOZ_DIAGNOSTIC_ASSERT(!isViewSource,
   3558                        "Bug 1834864: Assert due to view-source.");
   3559  MOZ_DIAGNOSTIC_ASSERT(!isPDFJS, "Bug 1834864: Assert due to  pdfjs.");
   3560  MOZ_DIAGNOSTIC_ASSERT(aPolicy == requireCORP,
   3561                        "Assert due to clearing REQUIRE_CORP.");
   3562  MOZ_DIAGNOSTIC_ASSERT(aContext->GetOpenerPolicy() == requireCORP,
   3563                        "Assert due to setting REQUIRE_CORP.");
   3564 #endif  // defined(EARLY_BETA_OR_EARLIER)
   3565 }
   3566 
   3567 void Document::ApplyCspFromLoadInfo(nsILoadInfo* aLoadInfo) {
   3568  // The CSP directives upgrade-insecure-requests as well as
   3569  // block-all-mixed-content not only apply to the toplevel document,
   3570  // but also to nested documents. The loadInfo of a subdocument
   3571  // load already holds the correct flag, so let's just set it here
   3572  // on the document. Please note that we set the appropriate preload
   3573  // bits just for the sake of completeness here, because the preloader
   3574  // does not reach into subdocuments.
   3575  mUpgradeInsecureRequests = aLoadInfo->GetUpgradeInsecureRequests();
   3576  mUpgradeInsecurePreloads = mUpgradeInsecureRequests;
   3577  mBlockAllMixedContent = aLoadInfo->GetBlockAllMixedContent();
   3578  mBlockAllMixedContentPreloads = mBlockAllMixedContent;
   3579 }
   3580 
   3581 nsresult Document::StartDocumentLoad(const char* aCommand, nsIChannel* aChannel,
   3582                                     nsILoadGroup* aLoadGroup,
   3583                                     nsISupports* aContainer,
   3584                                     nsIStreamListener** aDocListener,
   3585                                     bool aReset) {
   3586  if (MOZ_LOG_TEST(gDocumentLeakPRLog, LogLevel::Debug)) {
   3587    nsCOMPtr<nsIURI> uri;
   3588    aChannel->GetURI(getter_AddRefs(uri));
   3589    MOZ_LOG(gDocumentLeakPRLog, LogLevel::Debug,
   3590            ("DOCUMENT %p StartDocumentLoad %s", this,
   3591             uri ? uri->GetSpecOrDefault().get() : ""));
   3592  }
   3593 
   3594  MOZ_ASSERT(GetReadyStateEnum() == Document::READYSTATE_UNINITIALIZED,
   3595             "Bad readyState");
   3596  SetReadyStateInternal(READYSTATE_LOADING);
   3597 
   3598  if (nsCRT::strcmp(kLoadAsData, aCommand) == 0) {
   3599    MOZ_RELEASE_ASSERT(mLoadedAsData);
   3600    SetLoadedAsData(true, /* aConsiderForMemoryReporting */ true);
   3601    // We need to disable script & style loading in this case.
   3602    // We leave them disabled even in EndLoad(), and let anyone
   3603    // who puts the document on display to worry about enabling.
   3604  } else if (nsCRT::strcmp("external-resource", aCommand) == 0) {
   3605    // Allow CSS, but not scripts
   3606    // TODO: Enforce this via the constructor and make mScriptLoader null here.
   3607    MOZ_ASSERT(mScriptLoader);
   3608    mScriptLoader->SetEnabled(false);
   3609  }
   3610 
   3611  mMayStartLayout = false;
   3612  MOZ_ASSERT(!mReadyForIdle,
   3613             "We should never hit DOMContentLoaded before this point");
   3614 
   3615  if (aReset) {
   3616    Reset(aChannel, aLoadGroup);
   3617  }
   3618 
   3619  nsAutoCString contentType;
   3620  nsCOMPtr<nsIPropertyBag2> bag = do_QueryInterface(aChannel);
   3621  if ((bag && NS_SUCCEEDED(bag->GetPropertyAsACString(u"contentType"_ns,
   3622                                                      contentType))) ||
   3623      NS_SUCCEEDED(aChannel->GetContentType(contentType))) {
   3624    // XXX this is only necessary for viewsource:
   3625    nsACString::const_iterator start, end, semicolon;
   3626    contentType.BeginReading(start);
   3627    contentType.EndReading(end);
   3628    semicolon = start;
   3629    FindCharInReadable(';', semicolon, end);
   3630    SetContentType(Substring(start, semicolon));
   3631  }
   3632 
   3633  RetrieveRelevantHeaders(aChannel);
   3634 
   3635  mChannel = aChannel;
   3636  RecomputeResistFingerprinting();
   3637  nsCOMPtr<nsIInputStreamChannel> inStrmChan = do_QueryInterface(mChannel);
   3638  if (inStrmChan) {
   3639    bool isSrcdocChannel;
   3640    inStrmChan->GetIsSrcdocChannel(&isSrcdocChannel);
   3641    if (isSrcdocChannel) {
   3642      mIsSrcdocDocument = true;
   3643    }
   3644  }
   3645 
   3646  if (mChannel) {
   3647    nsLoadFlags loadFlags;
   3648    mChannel->GetLoadFlags(&loadFlags);
   3649    bool isDocument = false;
   3650    mChannel->GetIsDocument(&isDocument);
   3651    if (loadFlags & nsIRequest::LOAD_DOCUMENT_NEEDS_COOKIE && isDocument &&
   3652        IsSynthesized() && XRE_IsContentProcess()) {
   3653      ContentChild::UpdateCookieStatus(mChannel);
   3654    }
   3655 
   3656    // Store the security info for future use.
   3657    mChannel->GetSecurityInfo(getter_AddRefs(mSecurityInfo));
   3658  }
   3659 
   3660  // If this document is being loaded by a docshell, copy its sandbox flags
   3661  // to the document, and store the fullscreen enabled flag. These are
   3662  // immutable after being set here.
   3663  nsCOMPtr<nsIDocShell> docShell = do_QueryInterface(aContainer);
   3664 
   3665  // If this is an error page, don't inherit sandbox flags
   3666  nsCOMPtr<nsILoadInfo> loadInfo = aChannel->LoadInfo();
   3667 
   3668  if (!IsTopLevelContentDocument()) {
   3669    SetAncestorOriginsList(
   3670        ProduceAncestorOriginsList(loadInfo->AncestorPrincipals()));
   3671  }
   3672 
   3673  if (docShell && !loadInfo->GetLoadErrorPage()) {
   3674    mSandboxFlags = loadInfo->GetSandboxFlags();
   3675    WarnIfSandboxIneffective(docShell, mSandboxFlags, GetChannel());
   3676  }
   3677 
   3678  nsCOMPtr<nsIClassifiedChannel> classifiedChannel =
   3679      do_QueryInterface(aChannel);
   3680 
   3681  if (classifiedChannel) {
   3682    mClassificationFlags = {
   3683        classifiedChannel->GetFirstPartyClassificationFlags(),
   3684        classifiedChannel->GetThirdPartyClassificationFlags()};
   3685  }
   3686 
   3687  // Set the opener policy for the top level content document.
   3688  nsCOMPtr<nsIHttpChannelInternal> httpChan = do_QueryInterface(mChannel);
   3689  nsILoadInfo::CrossOriginOpenerPolicy policy =
   3690      nsILoadInfo::OPENER_POLICY_UNSAFE_NONE;
   3691  if (IsTopLevelContentDocument() && httpChan &&
   3692      NS_SUCCEEDED(httpChan->GetCrossOriginOpenerPolicy(&policy)) && docShell &&
   3693      docShell->GetBrowsingContext()) {
   3694    CheckIsBadPolicy(policy, docShell->GetBrowsingContext(), aChannel);
   3695 
   3696    // Setting the opener policy on a discarded context has no effect.
   3697    (void)docShell->GetBrowsingContext()->SetOpenerPolicy(policy);
   3698  }
   3699 
   3700  ApplyCspFromLoadInfo(loadInfo);
   3701 
   3702  // HTTPS-Only Mode flags
   3703  // The HTTPS_ONLY_EXEMPT flag of the HTTPS-Only state gets propagated to all
   3704  // sub-resources and sub-documents.
   3705  mHttpsOnlyStatus = loadInfo->GetHttpsOnlyStatus();
   3706 
   3707  nsresult rv = InitReferrerInfo(aChannel);
   3708  NS_ENSURE_SUCCESS(rv, rv);
   3709 
   3710  rv = InitCOEP(aChannel);
   3711  NS_ENSURE_SUCCESS(rv, rv);
   3712 
   3713  // HACK: Calling EnsureIPCPoliciesRead() here will parse the CSP using the
   3714  // context's current mSelfURI (which is still the previous mSelfURI),
   3715  // bypassing some internal bugs with 'self' and iframe inheritance.
   3716  // Not calling it here results in the mSelfURI being the current mSelfURI and
   3717  // not the previous which breaks said inheritance.
   3718  // https://bugzilla.mozilla.org/show_bug.cgi?id=1793560#ch-8
   3719  nsCOMPtr<nsIPolicyContainer> policyContainer =
   3720      loadInfo->GetPolicyContainerToInherit();
   3721  nsCOMPtr<nsIContentSecurityPolicy> cspToInherit =
   3722      PolicyContainer::GetCSP(policyContainer);
   3723  if (cspToInherit) {
   3724    cspToInherit->EnsureIPCPoliciesRead();
   3725  }
   3726 
   3727  rv = InitPolicyContainer(aChannel);
   3728  NS_ENSURE_SUCCESS(rv, rv);
   3729 
   3730  rv = InitCSP(aChannel);
   3731  NS_ENSURE_SUCCESS(rv, rv);
   3732 
   3733  rv = InitIntegrityPolicy(aChannel);
   3734  NS_ENSURE_SUCCESS(rv, rv);
   3735 
   3736  rv = InitDocPolicy(aChannel);
   3737  NS_ENSURE_SUCCESS(rv, rv);
   3738 
   3739  // Initialize FeaturePolicy
   3740  rv = InitFeaturePolicy(aChannel);
   3741  NS_ENSURE_SUCCESS(rv, rv);
   3742 
   3743  rv = InitTLSCertificateBinding(aChannel);
   3744  NS_ENSURE_SUCCESS(rv, rv);
   3745 
   3746  rv = loadInfo->GetCookieJarSettings(getter_AddRefs(mCookieJarSettings));
   3747  NS_ENSURE_SUCCESS(rv, rv);
   3748 
   3749  MaybeRecomputePartitionKey();
   3750 
   3751  // Generally XFO and CSP frame-ancestors is handled within
   3752  // DocumentLoadListener. However, the DocumentLoadListener can not handle
   3753  // object and embed. Until then we have to enforce it here (See Bug 1646899).
   3754  nsContentPolicyType internalContentType =
   3755      loadInfo->InternalContentPolicyType();
   3756  if (internalContentType == nsIContentPolicy::TYPE_INTERNAL_OBJECT ||
   3757      internalContentType == nsIContentPolicy::TYPE_INTERNAL_EMBED) {
   3758    nsContentSecurityUtils::PerformCSPFrameAncestorAndXFOCheck(aChannel);
   3759 
   3760    nsresult status;
   3761    aChannel->GetStatus(&status);
   3762    if (status == NS_ERROR_XFO_VIOLATION) {
   3763      // stop!  ERROR page!
   3764      // But before we have to reset the principal of the document
   3765      // because the onload() event fires before the error page
   3766      // is displayed and we do not want the enclosing document
   3767      // to access the contentDocument.
   3768      RefPtr<NullPrincipal> nullPrincipal =
   3769          NullPrincipal::CreateWithInheritedAttributes(NodePrincipal());
   3770      // Before calling SetPrincipals() we should ensure that mFontFaceSet
   3771      // and also GetInnerWindow() is still null at this point, before
   3772      // we can fix Bug 1614735: Evaluate calls to SetPrincipal
   3773      // within Document.cpp
   3774      MOZ_ASSERT(!mFontFaceSet && !GetInnerWindow());
   3775      SetPrincipals(nullPrincipal, nullPrincipal);
   3776    }
   3777  }
   3778 
   3779  return NS_OK;
   3780 }
   3781 
   3782 void Document::SetLoadedAsData(bool aLoadedAsData,
   3783                               bool aConsiderForMemoryReporting) {
   3784  MOZ_RELEASE_ASSERT(aLoadedAsData == mLoadedAsData);
   3785  if (aConsiderForMemoryReporting) {
   3786    nsIGlobalObject* global = GetScopeObject();
   3787    if (global) {
   3788      if (nsPIDOMWindowInner* window = global->GetAsInnerWindow()) {
   3789        nsGlobalWindowInner::Cast(window)
   3790            ->RegisterDataDocumentForMemoryReporting(this);
   3791      }
   3792    }
   3793  }
   3794 }
   3795 
   3796 nsIContentSecurityPolicy* Document::GetPreloadCsp() const {
   3797  return mPreloadCSP;
   3798 }
   3799 
   3800 void Document::SetPreloadCsp(nsIContentSecurityPolicy* aPreloadCSP) {
   3801  mPreloadCSP = aPreloadCSP;
   3802 }
   3803 
   3804 void Document::GetCspJSON(nsString& aJSON) {
   3805  aJSON.Truncate();
   3806 
   3807  nsIContentSecurityPolicy* csp = PolicyContainer::GetCSP(mPolicyContainer);
   3808  if (!csp) {
   3809    dom::CSPPolicies jsonPolicies;
   3810    jsonPolicies.ToJSON(aJSON);
   3811    return;
   3812  }
   3813  csp->ToJSON(aJSON);
   3814 }
   3815 
   3816 void Document::SendToConsole(nsCOMArray<nsISecurityConsoleMessage>& aMessages) {
   3817  for (uint32_t i = 0; i < aMessages.Length(); ++i) {
   3818    nsAutoString messageTag;
   3819    aMessages[i]->GetTag(messageTag);
   3820 
   3821    nsAutoString category;
   3822    aMessages[i]->GetCategory(category);
   3823 
   3824    nsContentUtils::ReportToConsole(nsIScriptError::warningFlag,
   3825                                    NS_ConvertUTF16toUTF8(category), this,
   3826                                    nsContentUtils::eSECURITY_PROPERTIES,
   3827                                    NS_ConvertUTF16toUTF8(messageTag).get());
   3828  }
   3829 }
   3830 
   3831 void Document::ApplySettingsFromCSP(bool aSpeculative) {
   3832  nsresult rv = NS_OK;
   3833  if (!aSpeculative) {
   3834    nsIContentSecurityPolicy* csp = PolicyContainer::GetCSP(mPolicyContainer);
   3835    // 1) apply settings from regular CSP
   3836    if (csp) {
   3837      // Set up 'block-all-mixed-content' if not already inherited
   3838      // from the parent context or set by any other CSP.
   3839      if (!mBlockAllMixedContent) {
   3840        bool block = false;
   3841        rv = csp->GetBlockAllMixedContent(&block);
   3842        NS_ENSURE_SUCCESS_VOID(rv);
   3843        mBlockAllMixedContent = block;
   3844      }
   3845      if (!mBlockAllMixedContentPreloads) {
   3846        mBlockAllMixedContentPreloads = mBlockAllMixedContent;
   3847      }
   3848 
   3849      // Set up 'upgrade-insecure-requests' if not already inherited
   3850      // from the parent context or set by any other CSP.
   3851      if (!mUpgradeInsecureRequests) {
   3852        bool upgrade = false;
   3853        rv = csp->GetUpgradeInsecureRequests(&upgrade);
   3854        NS_ENSURE_SUCCESS_VOID(rv);
   3855        mUpgradeInsecureRequests = upgrade;
   3856      }
   3857      if (!mUpgradeInsecurePreloads) {
   3858        mUpgradeInsecurePreloads = mUpgradeInsecureRequests;
   3859      }
   3860      // Update csp settings in the parent process
   3861      if (auto* wgc = GetWindowGlobalChild()) {
   3862        wgc->SendUpdateDocumentCspSettings(mBlockAllMixedContent,
   3863                                           mUpgradeInsecureRequests);
   3864      }
   3865    }
   3866    return;
   3867  }
   3868 
   3869  // 2) apply settings from speculative csp
   3870  if (mPreloadCSP) {
   3871    if (!mBlockAllMixedContentPreloads) {
   3872      bool block = false;
   3873      rv = mPreloadCSP->GetBlockAllMixedContent(&block);
   3874      NS_ENSURE_SUCCESS_VOID(rv);
   3875      mBlockAllMixedContent = block;
   3876    }
   3877    if (!mUpgradeInsecurePreloads) {
   3878      bool upgrade = false;
   3879      rv = mPreloadCSP->GetUpgradeInsecureRequests(&upgrade);
   3880      NS_ENSURE_SUCCESS_VOID(rv);
   3881      mUpgradeInsecurePreloads = upgrade;
   3882    }
   3883  }
   3884 }
   3885 
   3886 nsresult Document::InitPolicyContainer(nsIChannel* aChannel) {
   3887  bool shouldInherit = CSP_ShouldResponseInheritCSP(aChannel);
   3888  if (shouldInherit) {
   3889    nsCOMPtr<nsILoadInfo> loadInfo = aChannel->LoadInfo();
   3890    nsCOMPtr<nsIPolicyContainer> policyContainer =
   3891        loadInfo->GetPolicyContainerToInherit();
   3892    mPolicyContainer = PolicyContainer::Cast(policyContainer);
   3893  }
   3894 
   3895  if (!mPolicyContainer) {
   3896    mPolicyContainer = new PolicyContainer();
   3897  }
   3898 
   3899  return NS_OK;
   3900 }
   3901 
   3902 void Document::SetPolicyContainer(nsIPolicyContainer* aPolicyContainer) {
   3903  mPolicyContainer = PolicyContainer::Cast(aPolicyContainer);
   3904  nsIContentSecurityPolicy* csp = PolicyContainer::GetCSP(mPolicyContainer);
   3905  mHasPolicyWithRequireTrustedTypesForDirective =
   3906      csp && csp->GetRequireTrustedTypesForDirectiveState() !=
   3907                 RequireTrustedTypesForDirectiveState::NONE;
   3908 }
   3909 
   3910 nsIPolicyContainer* Document::GetPolicyContainer() const {
   3911  return mPolicyContainer;
   3912 }
   3913 
   3914 nsresult Document::InitCSP(nsIChannel* aChannel) {
   3915  MOZ_ASSERT(!mScriptGlobalObject,
   3916             "CSP must be initialized before mScriptGlobalObject is set!");
   3917  MOZ_ASSERT(mPolicyContainer,
   3918             "Policy container must be initialized before CSP!");
   3919 
   3920  // If this is a data document - no need to set CSP.
   3921  if (mLoadedAsData) {
   3922    return NS_OK;
   3923  }
   3924 
   3925  // If this is an image, no need to set a CSP. Otherwise SVG images
   3926  // served with a CSP might block internally applied inline styles.
   3927  nsCOMPtr<nsILoadInfo> loadInfo = aChannel->LoadInfo();
   3928  if (loadInfo->GetExternalContentPolicyType() ==
   3929          ExtContentPolicy::TYPE_IMAGE ||
   3930      loadInfo->GetExternalContentPolicyType() ==
   3931          ExtContentPolicy::TYPE_IMAGESET) {
   3932    return NS_OK;
   3933  }
   3934 
   3935  nsIContentSecurityPolicy* csp = PolicyContainer::GetCSP(mPolicyContainer);
   3936  bool inheritedCSP = !!csp;
   3937 
   3938  // If there is no CSP to inherit, then we create a new CSP here so
   3939  // that history entries always have the right reference in case a
   3940  // Meta CSP gets dynamically added after the history entry has
   3941  // already been created.
   3942  if (!csp) {
   3943    csp = new nsCSPContext();
   3944    mPolicyContainer->SetCSP(csp);
   3945    mHasPolicyWithRequireTrustedTypesForDirective = false;
   3946  } else {
   3947    mHasPolicyWithRequireTrustedTypesForDirective =
   3948        csp->GetRequireTrustedTypesForDirectiveState() !=
   3949        RequireTrustedTypesForDirectiveState::NONE;
   3950  }
   3951 
   3952  // Always overwrite the requesting context of the CSP so that any new
   3953  // 'self' keyword added to an inherited CSP translates correctly.
   3954  nsresult rv = csp->SetRequestContextWithDocument(this);
   3955  if (NS_WARN_IF(NS_FAILED(rv))) {
   3956    return rv;
   3957  }
   3958 
   3959  nsAutoCString tCspHeaderValue, tCspROHeaderValue;
   3960 
   3961  nsCOMPtr<nsIHttpChannel> httpChannel;
   3962  rv = GetHttpChannelHelper(aChannel, getter_AddRefs(httpChannel));
   3963  if (NS_WARN_IF(NS_FAILED(rv))) {
   3964    return rv;
   3965  }
   3966 
   3967  if (httpChannel) {
   3968    (void)httpChannel->GetResponseHeader("content-security-policy"_ns,
   3969                                         tCspHeaderValue);
   3970 
   3971    (void)httpChannel->GetResponseHeader(
   3972        "content-security-policy-report-only"_ns, tCspROHeaderValue);
   3973  }
   3974  NS_ConvertASCIItoUTF16 cspHeaderValue(tCspHeaderValue);
   3975  NS_ConvertASCIItoUTF16 cspROHeaderValue(tCspROHeaderValue);
   3976 
   3977  // Check if this is a document from a WebExtension.
   3978  nsCOMPtr<nsIPrincipal> principal = NodePrincipal();
   3979  MOZ_ASSERT(!BasePrincipal::Cast(principal)->Is<ExpandedPrincipal>());
   3980  auto addonPolicy = BasePrincipal::Cast(principal)->AddonPolicy();
   3981 
   3982  // If there's no CSP to apply, go ahead and return early
   3983  if (!inheritedCSP && !addonPolicy && cspHeaderValue.IsEmpty() &&
   3984      cspROHeaderValue.IsEmpty()) {
   3985    if (MOZ_LOG_TEST(gCspPRLog, LogLevel::Debug)) {
   3986      nsCOMPtr<nsIURI> chanURI;
   3987      aChannel->GetURI(getter_AddRefs(chanURI));
   3988      nsAutoCString aspec;
   3989      chanURI->GetAsciiSpec(aspec);
   3990      MOZ_LOG(gCspPRLog, LogLevel::Debug,
   3991              ("no CSP for document, %s", aspec.get()));
   3992    }
   3993 
   3994    return NS_OK;
   3995  }
   3996 
   3997  MOZ_LOG(gCspPRLog, LogLevel::Debug,
   3998          ("Document is an add-on or CSP header specified %p", this));
   3999 
   4000  // ----- if the doc is an addon, apply its CSP.
   4001  if (addonPolicy) {
   4002    csp->AppendPolicy(addonPolicy->BaseCSP(), false, false);
   4003 
   4004    csp->AppendPolicy(addonPolicy->ExtensionPageCSP(), false, false);
   4005  }
   4006 
   4007  // ----- if there's a full-strength CSP header, apply it.
   4008  if (!cspHeaderValue.IsEmpty()) {
   4009    mHasCSPDeliveredThroughHeader = true;
   4010    rv = CSP_AppendCSPFromHeader(csp, cspHeaderValue, false);
   4011    NS_ENSURE_SUCCESS(rv, rv);
   4012  }
   4013 
   4014  // ----- if there's a report-only CSP header, apply it.
   4015  if (!cspROHeaderValue.IsEmpty()) {
   4016    rv = CSP_AppendCSPFromHeader(csp, cspROHeaderValue, true);
   4017    NS_ENSURE_SUCCESS(rv, rv);
   4018  }
   4019 
   4020  // ----- Enforce sandbox policy if supplied in CSP header
   4021  // The document may already have some sandbox flags set (e.g. if the document
   4022  // is an iframe with the sandbox attribute set). If we have a CSP sandbox
   4023  // directive, intersect the CSP sandbox flags with the existing flags. This
   4024  // corresponds to the _least_ permissive policy.
   4025  uint32_t cspSandboxFlags = SANDBOXED_NONE;
   4026  rv = csp->GetCSPSandboxFlags(&cspSandboxFlags);
   4027  NS_ENSURE_SUCCESS(rv, rv);
   4028 
   4029  // Probably the iframe sandbox attribute already caused the creation of a
   4030  // new NullPrincipal. Only create a new NullPrincipal if CSP requires so
   4031  // and no one has been created yet.
   4032  bool needNewNullPrincipal = (cspSandboxFlags & SANDBOXED_ORIGIN) &&
   4033                              !(mSandboxFlags & SANDBOXED_ORIGIN);
   4034 
   4035  mSandboxFlags |= cspSandboxFlags;
   4036 
   4037  if (needNewNullPrincipal) {
   4038    principal = NullPrincipal::CreateWithInheritedAttributes(principal);
   4039    // Skip setting the content blocking allowlist principal to NullPrincipal.
   4040    // The principal is only used to enable/disable trackingprotection via
   4041    // permission and can be shared with the top level sandboxed site.
   4042    // See Bug 1654546.
   4043    SetPrincipals(principal, principal);
   4044  }
   4045 
   4046  ApplySettingsFromCSP(false);
   4047  return NS_OK;
   4048 }
   4049 
   4050 nsresult Document::InitIntegrityPolicy(nsIChannel* aChannel) {
   4051  MOZ_ASSERT(!mScriptGlobalObject,
   4052             "Integrity Policy must be initialized before mScriptGlobalObject "
   4053             "is set!");
   4054  MOZ_ASSERT(mPolicyContainer,
   4055             "Policy container must be initialized before IntegrityPolicy!");
   4056 
   4057  if (mPolicyContainer->GetIntegrityPolicy()) {
   4058    // We inherited the integrity policy.
   4059    return NS_OK;
   4060  }
   4061 
   4062  nsAutoCString headerValue, headerROValue;
   4063  nsCOMPtr<nsIHttpChannel> httpChannel;
   4064  nsresult rv = GetHttpChannelHelper(aChannel, getter_AddRefs(httpChannel));
   4065  if (NS_WARN_IF(NS_FAILED(rv))) {
   4066    return rv;
   4067  }
   4068 
   4069  if (httpChannel) {
   4070    (void)httpChannel->GetResponseHeader("integrity-policy"_ns, headerValue);
   4071 
   4072    (void)httpChannel->GetResponseHeader("integrity-policy-report-only"_ns,
   4073                                         headerROValue);
   4074  }
   4075 
   4076  RefPtr<IntegrityPolicy> integrityPolicy;
   4077  rv = IntegrityPolicy::ParseHeaders(headerValue, headerROValue,
   4078                                     getter_AddRefs(integrityPolicy));
   4079  NS_ENSURE_SUCCESS(rv, rv);
   4080 
   4081  mPolicyContainer->SetIntegrityPolicy(integrityPolicy);
   4082  return NS_OK;
   4083 }
   4084 
   4085 nsresult Document::InitTLSCertificateBinding(nsIChannel* aChannel) {
   4086  mTLSCertificateBindingURI = nullptr;
   4087  nsCOMPtr<nsIHttpChannel> httpChannel;
   4088  nsresult rv = GetHttpChannelHelper(aChannel, getter_AddRefs(httpChannel));
   4089  if (NS_WARN_IF(NS_FAILED(rv))) {
   4090    return rv;
   4091  }
   4092 
   4093  if (!httpChannel) {
   4094    return NS_OK;
   4095  }
   4096 
   4097  nsAutoCString linkHeader;
   4098  rv = httpChannel->GetResponseHeader("link"_ns, linkHeader);
   4099  if (NS_FAILED(rv) || linkHeader.IsEmpty()) {
   4100    return NS_OK;
   4101  }
   4102  nsTArray<LinkHeader> linkHeaders(
   4103      ParseLinkHeader(NS_ConvertUTF8toUTF16(linkHeader)));
   4104  for (const auto& linkHeader : linkHeaders) {
   4105    // According to ETSI TS 119 411-5 V2.1.1 Section 5.2, "When using a 2-QWAC,
   4106    // website operators shall... Configure their website to serve... an HTTP
   4107    // 'Link' response header (as defined in IETF RFC 8288 [6]) with a relative
   4108    // reference to the TLS Certificate Binding, and a rel value of
   4109    // tls-certificate-binding".
   4110    if (linkHeader.mRel.EqualsIgnoreCase("tls-certificate-binding") &&
   4111        !net_IsAbsoluteURL(NS_ConvertUTF16toUTF8(linkHeader.mHref)) &&
   4112        !net_IsAbsoluteURL(NS_ConvertUTF16toUTF8(linkHeader.mAnchor))) {
   4113      if (NS_SUCCEEDED(linkHeader.NewResolveHref(
   4114              getter_AddRefs(mTLSCertificateBindingURI), mDocumentURI))) {
   4115        break;
   4116      } else {
   4117        mTLSCertificateBindingURI = nullptr;
   4118      }
   4119    }
   4120  }
   4121 
   4122  return NS_OK;
   4123 }
   4124 
   4125 static FeaturePolicy* GetFeaturePolicyFromElement(Element* aElement) {
   4126  if (auto* iframe = HTMLIFrameElement::FromNodeOrNull(aElement)) {
   4127    return iframe->FeaturePolicy();
   4128  }
   4129 
   4130  if (!HTMLObjectElement::FromNodeOrNull(aElement) &&
   4131      !HTMLEmbedElement::FromNodeOrNull(aElement)) {
   4132    return nullptr;
   4133  }
   4134 
   4135  return aElement->OwnerDoc()->FeaturePolicy();
   4136 }
   4137 
   4138 nsresult Document::InitDocPolicy(nsIChannel* aChannel) {
   4139  // We only use document policy to implement the text fragments spec, so leave
   4140  // everything at the default value if it isn't enabled. This includes the
   4141  // behavior for element fragments.
   4142  if (!StaticPrefs::dom_text_fragments_enabled()) {
   4143    return NS_OK;
   4144  }
   4145 
   4146  nsCOMPtr<nsIHttpChannel> httpChannel;
   4147  nsresult rv = GetHttpChannelHelper(aChannel, getter_AddRefs(httpChannel));
   4148  if (NS_WARN_IF(NS_FAILED(rv))) {
   4149    return rv;
   4150  }
   4151 
   4152  nsAutoCString docPolicyString;
   4153  if (httpChannel) {
   4154    (void)httpChannel->GetResponseHeader("Document-Policy"_ns, docPolicyString);
   4155  }
   4156 
   4157  if (docPolicyString.IsEmpty()) {
   4158    return NS_OK;
   4159  }
   4160 
   4161  mForceLoadAtTop = NS_GetForceLoadAtTopFromHeader(docPolicyString);
   4162 
   4163  return NS_OK;
   4164 }
   4165 
   4166 void Document::InitFeaturePolicy(
   4167    const Variant<Nothing, FeaturePolicyInfo, Element*>&
   4168        aContainerFeaturePolicy) {
   4169  MOZ_ASSERT(mFeaturePolicy, "we should have FeaturePolicy created");
   4170 
   4171  mFeaturePolicy->ResetDeclaredPolicy();
   4172 
   4173  mFeaturePolicy->SetDefaultOrigin(NodePrincipal());
   4174 
   4175  RefPtr<dom::FeaturePolicy> featurePolicy = mFeaturePolicy;
   4176  aContainerFeaturePolicy.match(
   4177      [](const Nothing&) {},
   4178      [featurePolicy](const FeaturePolicyInfo& aContainerFeaturePolicy) {
   4179        // Let's inherit the policy from the possibly cross-origin container.
   4180        featurePolicy->InheritPolicy(aContainerFeaturePolicy);
   4181        featurePolicy->SetSrcOrigin(aContainerFeaturePolicy.mSrcOrigin);
   4182      },
   4183      [featurePolicy](Element* aContainer) {
   4184        // Let's inherit the policy from the parent container element if it
   4185        // exists.
   4186        if (RefPtr<dom::FeaturePolicy> containerFeaturePolicy =
   4187                GetFeaturePolicyFromElement(aContainer)) {
   4188          featurePolicy->InheritPolicy(containerFeaturePolicy);
   4189          featurePolicy->SetSrcOrigin(containerFeaturePolicy->GetSrcOrigin());
   4190        }
   4191      });
   4192 }
   4193 
   4194 Element* GetEmbedderElementFrom(BrowsingContext* aBrowsingContext) {
   4195  if (!aBrowsingContext) {
   4196    return nullptr;
   4197  }
   4198  if (!aBrowsingContext->IsContentSubframe()) {
   4199    return nullptr;
   4200  }
   4201 
   4202  return aBrowsingContext->GetEmbedderElement();
   4203 }
   4204 
   4205 nsresult Document::InitFeaturePolicy(nsIChannel* aChannel) {
   4206  nsCOMPtr<nsILoadInfo> loadInfo = aChannel->LoadInfo();
   4207  if (Element* embedderElement = GetEmbedderElementFrom(GetBrowsingContext())) {
   4208    InitFeaturePolicy(AsVariant(embedderElement));
   4209  } else if (Maybe<FeaturePolicyInfo> featurePolicyContainer =
   4210                 loadInfo->GetContainerFeaturePolicyInfo()) {
   4211    InitFeaturePolicy(AsVariant(*featurePolicyContainer));
   4212  } else {
   4213    InitFeaturePolicy(AsVariant(Nothing{}));
   4214  }
   4215 
   4216  // We don't want to parse the http Feature-Policy header if this pref is off.
   4217  if (!StaticPrefs::dom_security_featurePolicy_header_enabled()) {
   4218    return NS_OK;
   4219  }
   4220 
   4221  nsCOMPtr<nsIHttpChannel> httpChannel;
   4222  nsresult rv = GetHttpChannelHelper(aChannel, getter_AddRefs(httpChannel));
   4223  if (NS_WARN_IF(NS_FAILED(rv))) {
   4224    return rv;
   4225  }
   4226 
   4227  if (!httpChannel) {
   4228    return NS_OK;
   4229  }
   4230 
   4231  // query the policy from the header
   4232  nsAutoCString value;
   4233  rv = httpChannel->GetResponseHeader("Feature-Policy"_ns, value);
   4234  if (NS_SUCCEEDED(rv)) {
   4235    mFeaturePolicy->SetDeclaredPolicy(this, NS_ConvertUTF8toUTF16(value),
   4236                                      NodePrincipal(), nullptr);
   4237  }
   4238 
   4239  return NS_OK;
   4240 }
   4241 
   4242 void Document::EnsureNotEnteringAndExitFullscreen() {
   4243  Document::ClearPendingFullscreenRequests(this);
   4244  if (GetFullscreenElement()) {
   4245    Document::AsyncExitFullscreen(this);
   4246  }
   4247 }
   4248 
   4249 // https://html.spec.whatwg.org/#document-state-request-referrer-policy
   4250 ReferrerPolicy Document::ReferrerPolicyUsedToFetchThisDocument() const {
   4251  return mRequestReferrerPolicy;
   4252 }
   4253 
   4254 void Document::SetReferrerInfo(nsIReferrerInfo* aReferrerInfo) {
   4255  mReferrerInfo = aReferrerInfo;
   4256  mCachedReferrerInfoForInternalCSSAndSVGResources = nullptr;
   4257  mCachedURLData = nullptr;
   4258 }
   4259 
   4260 nsresult Document::InitReferrerInfo(nsIChannel* aChannel) {
   4261  MOZ_ASSERT(mReferrerInfo);
   4262  MOZ_ASSERT(mPreloadReferrerInfo);
   4263 
   4264  if (ReferrerInfo::ShouldResponseInheritReferrerInfo(aChannel)) {
   4265    // The channel is loading `about:srcdoc`. Srcdoc loads should respond with
   4266    // their parent's ReferrerInfo when asked for their ReferrerInfo, unless
   4267    // they have an opaque origin.
   4268    // https://w3c.github.io/webappsec-referrer-policy/#determine-requests-referrer
   4269    if (BrowsingContext* bc = GetBrowsingContext()) {
   4270      // At this point the document is not fully created and mParentDocument has
   4271      // not been set yet,
   4272      Document* parentDoc = bc->GetEmbedderElement()
   4273                                ? bc->GetEmbedderElement()->OwnerDoc()
   4274                                : nullptr;
   4275      if (parentDoc) {
   4276        SetReferrerInfo(parentDoc->GetReferrerInfo());
   4277        mPreloadReferrerInfo = mReferrerInfo;
   4278        return NS_OK;
   4279      }
   4280 
   4281      MOZ_ASSERT(bc->IsInProcess() || NodePrincipal()->GetIsNullPrincipal(),
   4282                 "srcdoc without null principal as toplevel!");
   4283    }
   4284  }
   4285 
   4286  nsCOMPtr<nsIHttpChannel> httpChannel;
   4287  nsresult rv = GetHttpChannelHelper(aChannel, getter_AddRefs(httpChannel));
   4288  if (NS_WARN_IF(NS_FAILED(rv))) {
   4289    return rv;
   4290  }
   4291 
   4292  if (!httpChannel) {
   4293    return NS_OK;
   4294  }
   4295 
   4296  if (nsCOMPtr<nsIReferrerInfo> referrerInfo = httpChannel->GetReferrerInfo()) {
   4297    SetReferrerInfo(referrerInfo);
   4298    mRequestReferrerPolicy = referrerInfo->ReferrerPolicy();
   4299  }
   4300 
   4301  // Override policy if we get one from Referrerr-Policy header
   4302  mozilla::dom::ReferrerPolicy policy =
   4303      nsContentUtils::GetReferrerPolicyFromChannel(aChannel);
   4304  nsCOMPtr<nsIReferrerInfo> clone =
   4305      static_cast<dom::ReferrerInfo*>(mReferrerInfo.get())
   4306          ->CloneWithNewPolicy(policy);
   4307  SetReferrerInfo(clone);
   4308  mPreloadReferrerInfo = mReferrerInfo;
   4309  return NS_OK;
   4310 }
   4311 
   4312 nsresult Document::InitCOEP(nsIChannel* aChannel) {
   4313  nsCOMPtr<nsIHttpChannel> httpChannel;
   4314  nsresult rv = GetHttpChannelHelper(aChannel, getter_AddRefs(httpChannel));
   4315  if (NS_FAILED(rv)) {
   4316    return NS_OK;
   4317  }
   4318 
   4319  nsCOMPtr<nsIHttpChannelInternal> intChannel = do_QueryInterface(httpChannel);
   4320 
   4321  if (!intChannel) {
   4322    return NS_OK;
   4323  }
   4324 
   4325  nsILoadInfo::CrossOriginEmbedderPolicy policy =
   4326      nsILoadInfo::EMBEDDER_POLICY_NULL;
   4327  if (NS_SUCCEEDED(intChannel->GetResponseEmbedderPolicy(
   4328          mTrials.IsEnabled(OriginTrial::CoepCredentialless), &policy))) {
   4329    mEmbedderPolicy = Some(policy);
   4330  }
   4331 
   4332  return NS_OK;
   4333 }
   4334 
   4335 void Document::StopDocumentLoad() {
   4336  if (mParser) {
   4337    mParserAborted = true;
   4338    mParser->Terminate();
   4339  }
   4340 }
   4341 
   4342 void Document::SetDocumentURI(nsIURI* aURI) {
   4343  nsCOMPtr<nsIURI> oldBase = GetDocBaseURI();
   4344  mDocumentURI = aURI;
   4345  // This loosely implements §3.4.1 of Text Fragments
   4346  // https://wicg.github.io/scroll-to-text-fragment/#invoking-text-directives
   4347  // Unlike specified in the spec, the fragment directive is not stripped from
   4348  // the URL in the session history entry. Instead it is removed when the URL is
   4349  // set in the `Document`. Also, instead of storing the `uninvokedDirective` in
   4350  // `Document` as mentioned in the spec, the extracted directives are moved to
   4351  // the `FragmentDirective` object which deals with finding the ranges to
   4352  // highlight in `ScrollToRef()`.
   4353  // XXX(:jjaschke): This is only a temporary solution.
   4354  // https://bugzil.la/1881429 is filed for revisiting this.
   4355  nsTArray<TextDirective> textDirectives;
   4356  FragmentDirective::ParseAndRemoveFragmentDirectiveFromFragment(
   4357      mDocumentURI, &textDirectives);
   4358  if (!textDirectives.IsEmpty()) {
   4359    SetUseCounter(eUseCounter_custom_TextDirectivePages);
   4360  }
   4361  FragmentDirective()->SetTextDirectives(std::move(textDirectives));
   4362 
   4363  nsIURI* newBase = GetDocBaseURI();
   4364 
   4365  mChromeRulesEnabled = URLExtraData::ChromeRulesEnabled(aURI);
   4366 
   4367  bool equalBases = false;
   4368  // Changing just the ref of a URI does not change how relative URIs would
   4369  // resolve wrt to it, so we can treat the bases as equal as long as they're
   4370  // equal ignoring the ref.
   4371  if (oldBase && newBase) {
   4372    oldBase->EqualsExceptRef(newBase, &equalBases);
   4373  } else {
   4374    equalBases = !oldBase && !newBase;
   4375  }
   4376 
   4377  // If this is the first time we're setting the document's URI, set the
   4378  // document's original URI.
   4379  if (!mOriginalURI) mOriginalURI = mDocumentURI;
   4380 
   4381  // If changing the document's URI changed the base URI of the document, we
   4382  // need to refresh the hrefs of all the links on the page.
   4383  if (!equalBases) {
   4384    mCachedURLData = nullptr;
   4385    RefreshLinkHrefs();
   4386  }
   4387 
   4388  // Tell our WindowGlobalParent that the document's URI has been changed.
   4389  if (WindowGlobalChild* wgc = GetWindowGlobalChild()) {
   4390    wgc->SetDocumentURI(mDocumentURI);
   4391  }
   4392 }
   4393 
   4394 static void GetFormattedTimeString(PRTime aTime, bool aUniversal,
   4395                                   nsAString& aFormattedTimeString) {
   4396  PRExplodedTime prtime;
   4397  PR_ExplodeTime(aTime, aUniversal ? PR_GMTParameters : PR_LocalTimeParameters,
   4398                 &prtime);
   4399  // "MM/DD/YYYY hh:mm:ss"
   4400  char formatedTime[24];
   4401  if (SprintfLiteral(formatedTime, "%02d/%02d/%04d %02d:%02d:%02d",
   4402                     prtime.tm_month + 1, prtime.tm_mday, int(prtime.tm_year),
   4403                     prtime.tm_hour, prtime.tm_min, prtime.tm_sec)) {
   4404    CopyASCIItoUTF16(nsDependentCString(formatedTime), aFormattedTimeString);
   4405  } else {
   4406    // If we for whatever reason failed to find the last modified time
   4407    // (or even the current time), fall back to what NS4.x returned.
   4408    aFormattedTimeString.AssignLiteral(u"01/01/1970 00:00:00");
   4409  }
   4410 }
   4411 
   4412 void Document::GetLastModified(nsAString& aLastModified) const {
   4413  if (!mLastModified.IsEmpty()) {
   4414    aLastModified.Assign(mLastModified);
   4415  } else {
   4416    GetFormattedTimeString(PR_Now(),
   4417                           ShouldResistFingerprinting(RFPTarget::JSDateTimeUTC),
   4418                           aLastModified);
   4419  }
   4420 }
   4421 
   4422 static void IncrementExpandoGeneration(Document& aDoc) {
   4423  ++aDoc.mExpandoAndGeneration.generation;
   4424 }
   4425 
   4426 void Document::AddToNameTable(Element* aElement, nsAtom* aName) {
   4427  MOZ_ASSERT(nsGenericHTMLElement::ShouldExposeNameAsWindowProperty(aElement),
   4428             "Only put elements that need to be exposed as window['name'] in "
   4429             "the named table.");
   4430 
   4431  IdentifierMapEntry* entry = mIdentifierMap.PutEntry(aName);
   4432 
   4433  // Null for out-of-memory
   4434  if (entry) {
   4435    if (!entry->HasNameElement() &&
   4436        !entry->HasIdElementExposedAsHTMLDocumentProperty()) {
   4437      IncrementExpandoGeneration(*this);
   4438    }
   4439    entry->AddNameElement(this, aElement);
   4440  }
   4441 }
   4442 
   4443 void Document::RemoveFromNameTable(Element* aElement, nsAtom* aName) {
   4444  // Speed up document teardown
   4445  if (mIdentifierMap.Count() == 0) return;
   4446 
   4447  IdentifierMapEntry* entry = mIdentifierMap.GetEntry(aName);
   4448  if (!entry)  // Could be false if the element was anonymous, hence never added
   4449    return;
   4450 
   4451  entry->RemoveNameElement(aElement);
   4452  if (!entry->HasNameElement() &&
   4453      !entry->HasIdElementExposedAsHTMLDocumentProperty()) {
   4454    IncrementExpandoGeneration(*this);
   4455  }
   4456 }
   4457 
   4458 void Document::AddToDocumentNameTable(nsGenericHTMLElement* aElement,
   4459                                      nsAtom* aName) {
   4460  MOZ_ASSERT(
   4461      nsGenericHTMLElement::ShouldExposeIdAsHTMLDocumentProperty(aElement) ||
   4462          nsGenericHTMLElement::ShouldExposeNameAsHTMLDocumentProperty(
   4463              aElement),
   4464      "Only put elements that need to be exposed as document['name'] in "
   4465      "the document named table.");
   4466 
   4467  if (IdentifierMapEntry* entry = mIdentifierMap.PutEntry(aName)) {
   4468    entry->AddDocumentNameElement(this, aElement);
   4469  }
   4470 }
   4471 
   4472 void Document::RemoveFromDocumentNameTable(nsGenericHTMLElement* aElement,
   4473                                           nsAtom* aName) {
   4474  if (mIdentifierMap.Count() == 0) {
   4475    return;
   4476  }
   4477 
   4478  if (IdentifierMapEntry* entry = mIdentifierMap.GetEntry(aName)) {
   4479    entry->RemoveDocumentNameElement(aElement);
   4480    nsBaseContentList* list = entry->GetDocumentNameContentList();
   4481    if (!list || list->Length() == 0) {
   4482      IncrementExpandoGeneration(*this);
   4483    }
   4484  }
   4485 }
   4486 
   4487 void Document::AddToIdTable(Element* aElement, nsAtom* aId) {
   4488  IdentifierMapEntry* entry = mIdentifierMap.PutEntry(aId);
   4489 
   4490  if (entry) { /* True except on OOM */
   4491    if (nsGenericHTMLElement::ShouldExposeIdAsHTMLDocumentProperty(aElement) &&
   4492        !entry->HasNameElement() &&
   4493        !entry->HasIdElementExposedAsHTMLDocumentProperty()) {
   4494      IncrementExpandoGeneration(*this);
   4495    }
   4496    entry->AddIdElement(aElement);
   4497  }
   4498 }
   4499 
   4500 void Document::RemoveFromIdTable(Element* aElement, nsAtom* aId) {
   4501  NS_ASSERTION(aId, "huhwhatnow?");
   4502 
   4503  // Speed up document teardown
   4504  if (mIdentifierMap.Count() == 0) {
   4505    return;
   4506  }
   4507 
   4508  IdentifierMapEntry* entry = mIdentifierMap.GetEntry(aId);
   4509  if (!entry)  // Can be null for XML elements with changing ids.
   4510    return;
   4511 
   4512  entry->RemoveIdElement(aElement);
   4513  if (nsGenericHTMLElement::ShouldExposeIdAsHTMLDocumentProperty(aElement) &&
   4514      !entry->HasNameElement() &&
   4515      !entry->HasIdElementExposedAsHTMLDocumentProperty()) {
   4516    IncrementExpandoGeneration(*this);
   4517  }
   4518  if (entry->IsEmpty()) {
   4519    mIdentifierMap.RemoveEntry(entry);
   4520  }
   4521 }
   4522 
   4523 void Document::UpdateReferrerInfoFromMeta(const nsAString& aMetaReferrer,
   4524                                          bool aPreload) {
   4525  ReferrerPolicyEnum policy =
   4526      ReferrerInfo::ReferrerPolicyFromMetaString(aMetaReferrer);
   4527  // The empty string "" corresponds to no referrer policy, causing a fallback
   4528  // to a referrer policy defined elsewhere.
   4529  if (policy == ReferrerPolicy::_empty) {
   4530    return;
   4531  }
   4532 
   4533  MOZ_ASSERT(mReferrerInfo);
   4534  MOZ_ASSERT(mPreloadReferrerInfo);
   4535 
   4536  if (aPreload) {
   4537    mPreloadReferrerInfo =
   4538        static_cast<mozilla::dom::ReferrerInfo*>((mPreloadReferrerInfo).get())
   4539            ->CloneWithNewPolicy(policy);
   4540  } else {
   4541    nsCOMPtr<nsIReferrerInfo> clone =
   4542        static_cast<mozilla::dom::ReferrerInfo*>((mReferrerInfo).get())
   4543            ->CloneWithNewPolicy(policy);
   4544    SetReferrerInfo(clone);
   4545  }
   4546 }
   4547 
   4548 void Document::SetPrincipals(nsIPrincipal* aNewPrincipal,
   4549                             nsIPrincipal* aNewPartitionedPrincipal) {
   4550  MOZ_ASSERT(!!aNewPrincipal == !!aNewPartitionedPrincipal);
   4551  if (aNewPrincipal && mAllowDNSPrefetch &&
   4552      StaticPrefs::network_dns_disablePrefetchFromHTTPS()) {
   4553    if (aNewPrincipal->SchemeIs("https")) {
   4554      mAllowDNSPrefetch = false;
   4555    }
   4556  }
   4557 
   4558  if (mScriptLoader) {
   4559    mScriptLoader->DeregisterFromCache();
   4560  }
   4561  if (mCSSLoader) {
   4562    mCSSLoader->DeregisterFromSheetCache();
   4563  }
   4564 
   4565  mNodeInfoManager->SetDocumentPrincipal(aNewPrincipal);
   4566  mPartitionedPrincipal = aNewPartitionedPrincipal;
   4567 
   4568  mCachedURLData = nullptr;
   4569 
   4570  if (mCSSLoader) {
   4571    mCSSLoader->RegisterInSheetCache();
   4572  }
   4573  if (mScriptLoader) {
   4574    mScriptLoader->RegisterToCache();
   4575  }
   4576 
   4577  RecomputeResistFingerprinting();
   4578 
   4579 #ifdef DEBUG
   4580  // Validate that the docgroup is set correctly.
   4581  //
   4582  // If we're setting the principal to null, we don't want to perform the check,
   4583  // as the document is entering an intermediate state where it does not have a
   4584  // principal. It will be given another real principal shortly which we will
   4585  // check. It's not unsafe to have a document which has a null principal in the
   4586  // same docgroup as another document, so this should not be a problem.
   4587  if (aNewPrincipal) {
   4588    AssertDocGroupMatchesKey();
   4589  }
   4590 #endif
   4591 }
   4592 
   4593 #ifdef DEBUG
   4594 void Document::AssertDocGroupMatchesKey() const {
   4595  // Sanity check that we have an up-to-date and accurate docgroup
   4596  // We only check if the principal when we can get the browsing context, as
   4597  // documents without a BrowsingContext do not need to have a matching
   4598  // principal to their DocGroup.
   4599 
   4600  // Note that we can be invoked during cycle collection, so we need to handle
   4601  // the browsingcontext being partially unlinked - normally you shouldn't
   4602  // null-check `Group()` as it shouldn't return nullptr.
   4603  if (!GetBrowsingContext() || !GetBrowsingContext()->Group()) {
   4604    return;
   4605  }
   4606 
   4607  if (mDocGroup && mDocGroup->GetBrowsingContextGroup()) {
   4608    MOZ_ASSERT(mDocGroup->GetBrowsingContextGroup() ==
   4609               GetBrowsingContext()->Group());
   4610    mDocGroup->AssertMatches(this);
   4611  }
   4612 }
   4613 #endif
   4614 
   4615 nsresult Document::Dispatch(already_AddRefed<nsIRunnable>&& aRunnable) const {
   4616  return SchedulerGroup::Dispatch(std::move(aRunnable));
   4617 }
   4618 
   4619 void Document::NoteScriptTrackingStatus(const nsACString& aURL,
   4620                                        net::ClassificationFlags& aFlags) {
   4621  // If the script is not tracking, we don't need to do anything.
   4622  if (aFlags.firstPartyFlags || aFlags.thirdPartyFlags) {
   4623    mTrackingScripts.InsertOrUpdate(aURL, aFlags);
   4624  }
   4625  // Ideally, whether a given script is tracking or not should be consistent,
   4626  // but there is a race so that it is not, when loading real sites in debug
   4627  // builds. See bug 1925286.
   4628  // MOZ_ASSERT_IF(!aIsTracking, !mTrackingScripts.Contains(aURL));
   4629 }
   4630 
   4631 bool Document::IsScriptTracking(JSContext* aCx) const {
   4632  JS::AutoFilename filename;
   4633  if (!JS::DescribeScriptedCaller(&filename, aCx)) {
   4634    return false;
   4635  }
   4636 
   4637  auto entry = mTrackingScripts.Lookup(nsDependentCString(filename.get()));
   4638  if (!entry) {
   4639    return false;
   4640  }
   4641 
   4642  return net::UrlClassifierCommon::IsTrackingClassificationFlag(
   4643      entry.Data().thirdPartyFlags, IsInPrivateBrowsing());
   4644 }
   4645 
   4646 net::ClassificationFlags Document::GetScriptTrackingFlags() const {
   4647  if (auto loc = JSCallingLocation::Get()) {
   4648    if (auto entry = mTrackingScripts.Lookup(loc.FileName())) {
   4649      return entry.Data();
   4650    }
   4651  }
   4652 
   4653  // If the currently executing script is not a tracker, return the
   4654  // classification flags of the document.
   4655 
   4656  return mClassificationFlags;
   4657 }
   4658 
   4659 void Document::GetContentType(nsAString& aContentType) {
   4660  CopyUTF8toUTF16(GetContentTypeInternal(), aContentType);
   4661 }
   4662 
   4663 void Document::SetContentType(const nsACString& aContentType) {
   4664  if (!IsHTMLOrXHTML() && mDefaultElementType == kNameSpaceID_None &&
   4665      aContentType.EqualsLiteral("application/xhtml+xml")) {
   4666    mDefaultElementType = kNameSpaceID_XHTML;
   4667  }
   4668 
   4669  mCachedEncoder = nullptr;
   4670  mContentType = aContentType;
   4671 }
   4672 
   4673 bool Document::HasPendingInitialTranslation() {
   4674  return mDocumentL10n && mDocumentL10n->GetState() != DocumentL10nState::Ready;
   4675 }
   4676 
   4677 bool Document::HasPendingL10nMutations() const {
   4678  return mDocumentL10n && mDocumentL10n->HasPendingMutations();
   4679 }
   4680 
   4681 bool Document::DocumentSupportsL10n(JSContext* aCx, JSObject* aObject) {
   4682  JS::Rooted<JSObject*> object(aCx, aObject);
   4683  nsCOMPtr<nsIPrincipal> callerPrincipal =
   4684      nsContentUtils::SubjectPrincipal(aCx);
   4685  nsGlobalWindowInner* win = xpc::WindowOrNull(object);
   4686  bool allowed = false;
   4687  callerPrincipal->IsL10nAllowed(win ? win->GetDocumentURI() : nullptr,
   4688                                 &allowed);
   4689  return allowed;
   4690 }
   4691 
   4692 void Document::LocalizationLinkAdded(Element* aLinkElement) {
   4693  if (!AllowsL10n()) {
   4694    return;
   4695  }
   4696 
   4697  nsAutoString href;
   4698  aLinkElement->GetAttr(nsGkAtoms::href, href);
   4699 
   4700  if (!mDocumentL10n) {
   4701    Element* elem = GetDocumentElement();
   4702    MOZ_DIAGNOSTIC_ASSERT(elem);
   4703 
   4704    bool isSync = elem->HasAttr(nsGkAtoms::datal10nsync);
   4705    mDocumentL10n = DocumentL10n::Create(this, isSync);
   4706    if (NS_WARN_IF(!mDocumentL10n)) {
   4707      return;
   4708    }
   4709  }
   4710 
   4711  mDocumentL10n->AddResourceId(NS_ConvertUTF16toUTF8(href));
   4712 
   4713  if (mReadyState >= READYSTATE_INTERACTIVE) {
   4714    nsContentUtils::AddScriptRunner(NewRunnableMethod(
   4715        "DocumentL10n::TriggerInitialTranslation()", mDocumentL10n,
   4716        &DocumentL10n::TriggerInitialTranslation));
   4717  } else {
   4718    if (!mDocumentL10n->mBlockingLayout) {
   4719      // Our initial translation is going to block layout start.  Make sure
   4720      // we don't fire the load event until after that stops happening and
   4721      // layout has a chance to start.
   4722      BlockOnload();
   4723      mDocumentL10n->mBlockingLayout = true;
   4724    }
   4725  }
   4726 }
   4727 
   4728 void Document::LocalizationLinkRemoved(Element* aLinkElement) {
   4729  if (!AllowsL10n()) {
   4730    return;
   4731  }
   4732 
   4733  if (mDocumentL10n) {
   4734    nsAutoString href;
   4735    aLinkElement->GetAttr(nsGkAtoms::href, href);
   4736    uint32_t remaining =
   4737        mDocumentL10n->RemoveResourceId(NS_ConvertUTF16toUTF8(href));
   4738    if (remaining == 0) {
   4739      if (mDocumentL10n->mBlockingLayout) {
   4740        mDocumentL10n->mBlockingLayout = false;
   4741        UnblockOnload(/* aFireSync = */ false);
   4742      }
   4743      mDocumentL10n = nullptr;
   4744    }
   4745  }
   4746 }
   4747 
   4748 /**
   4749 * This method should be called once the end of the l10n
   4750 * resource container has been parsed.
   4751 *
   4752 * In XUL this is the end of the first </linkset>,
   4753 * In XHTML/HTML this is the end of </head>.
   4754 *
   4755 * This milestone is used to allow for batch
   4756 * localization context I/O and building done
   4757 * once when all resources in the document have been
   4758 * collected.
   4759 */
   4760 void Document::OnL10nResourceContainerParsed() {
   4761  // XXX: This is a scaffolding for where we might inject prefetch
   4762  // in bug 1717241.
   4763 }
   4764 
   4765 void Document::OnParsingCompleted() {
   4766  // Let's call it again, in case the resource
   4767  // container has not been closed, and only
   4768  // now we're closing the document.
   4769  OnL10nResourceContainerParsed();
   4770 
   4771  if (mDocumentL10n) {
   4772    RefPtr<DocumentL10n> l10n = mDocumentL10n;
   4773    l10n->TriggerInitialTranslation();
   4774  }
   4775 }
   4776 
   4777 void Document::InitialTranslationCompleted(bool aL10nCached) {
   4778  if (mDocumentL10n && mDocumentL10n->mBlockingLayout) {
   4779    // This means we blocked the load event in LocalizationLinkAdded.  It's
   4780    // important that the load blocker removal here be async, because our caller
   4781    // will notify the content sink after us, and we want the content sync's
   4782    // work to happen before the load event fires.
   4783    mDocumentL10n->mBlockingLayout = false;
   4784    UnblockOnload(/* aFireSync = */ false);
   4785  }
   4786 
   4787  mL10nProtoElements.Clear();
   4788 
   4789  nsXULPrototypeDocument* proto = GetPrototype();
   4790  if (proto) {
   4791    proto->SetIsL10nCached(aL10nCached);
   4792  }
   4793 }
   4794 
   4795 bool Document::AllowsL10n() const {
   4796  if (IsStaticDocument()) {
   4797    // We don't allow l10n on static documents, because the nodes are already
   4798    // cloned translated, and static docs don't get parsed so we never
   4799    // TriggerInitialTranslation, etc, so a load blocker would keep hanging
   4800    // forever.
   4801    return false;
   4802  }
   4803  bool allowed = false;
   4804  NodePrincipal()->IsL10nAllowed(GetDocumentURI(), &allowed);
   4805  return allowed;
   4806 }
   4807 
   4808 DocumentTimeline* Document::Timeline() {
   4809  if (!mDocumentTimeline) {
   4810    mDocumentTimeline = new DocumentTimeline(this, TimeDuration(0));
   4811  }
   4812 
   4813  return mDocumentTimeline;
   4814 }
   4815 
   4816 SVGSVGElement* Document::GetSVGRootElement() const {
   4817  Element* root = GetRootElement();
   4818  if (!root || !root->IsSVGElement(nsGkAtoms::svg)) {
   4819    return nullptr;
   4820  }
   4821  return static_cast<SVGSVGElement*>(root);
   4822 }
   4823 
   4824 /* Return true if the document is in the focused top-level window, and is an
   4825 * ancestor of the focused DOMWindow. */
   4826 bool Document::HasFocus(ErrorResult& rv) const {
   4827  nsFocusManager* fm = nsFocusManager::GetFocusManager();
   4828  if (!fm) {
   4829    rv.Throw(NS_ERROR_NOT_AVAILABLE);
   4830    return false;
   4831  }
   4832 
   4833  BrowsingContext* bc = GetBrowsingContext();
   4834  if (!bc) {
   4835    return false;
   4836  }
   4837 
   4838  if (!fm->IsInActiveWindow(bc)) {
   4839    return false;
   4840  }
   4841 
   4842  return fm->IsSameOrAncestor(bc, fm->GetFocusedBrowsingContext());
   4843 }
   4844 
   4845 bool Document::ThisDocumentHasFocus() const {
   4846  nsFocusManager* fm = nsFocusManager::GetFocusManager();
   4847  return fm && fm->GetFocusedWindow() &&
   4848         fm->GetFocusedWindow()->GetExtantDoc() == this;
   4849 }
   4850 
   4851 void Document::GetDesignMode(nsAString& aDesignMode) {
   4852  if (IsInDesignMode()) {
   4853    aDesignMode.AssignLiteral("on");
   4854  } else {
   4855    aDesignMode.AssignLiteral("off");
   4856  }
   4857 }
   4858 
   4859 void Document::SetDesignMode(const nsAString& aDesignMode,
   4860                             nsIPrincipal& aSubjectPrincipal, ErrorResult& rv) {
   4861  SetDesignMode(aDesignMode, Some(&aSubjectPrincipal), rv);
   4862 }
   4863 
   4864 static void NotifyEditableStateChange(Document& aDoc) {
   4865 #ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
   4866  nsMutationGuard g;
   4867 #endif
   4868  for (nsIContent* node = aDoc.GetNextNode(&aDoc); node;
   4869       node = node->GetNextNode(&aDoc)) {
   4870    if (auto* element = Element::FromNode(node)) {
   4871      element->UpdateEditableState(true);
   4872    }
   4873  }
   4874  MOZ_DIAGNOSTIC_ASSERT(!g.Mutated(0));
   4875 }
   4876 
   4877 void Document::SetDocumentEditableFlag(bool aEditable) {
   4878  if (HasFlag(NODE_IS_EDITABLE) == aEditable) {
   4879    return;
   4880  }
   4881  SetEditableFlag(aEditable);
   4882  // Changing the NODE_IS_EDITABLE flags on document changes the intrinsic
   4883  // state of all descendant elements of it. Update that now.
   4884  NotifyEditableStateChange(*this);
   4885 }
   4886 
   4887 void Document::SetDesignMode(const nsAString& aDesignMode,
   4888                             const Maybe<nsIPrincipal*>& aSubjectPrincipal,
   4889                             ErrorResult& rv) {
   4890  if (aSubjectPrincipal.isSome() &&
   4891      !aSubjectPrincipal.value()->Subsumes(NodePrincipal())) {
   4892    rv.Throw(NS_ERROR_DOM_PROP_ACCESS_DENIED);
   4893    return;
   4894  }
   4895  const bool editableMode = IsInDesignMode();
   4896  if (aDesignMode.LowerCaseEqualsASCII(editableMode ? "off" : "on")) {
   4897    SetDocumentEditableFlag(!editableMode);
   4898    rv = EditingStateChanged();
   4899  }
   4900 }
   4901 
   4902 nsCommandManager* Document::GetMidasCommandManager() {
   4903  // check if we have it cached
   4904  if (mMidasCommandManager) {
   4905    return mMidasCommandManager;
   4906  }
   4907 
   4908  nsPIDOMWindowOuter* window = GetWindow();
   4909  if (!window) {
   4910    return nullptr;
   4911  }
   4912 
   4913  nsIDocShell* docshell = window->GetDocShell();
   4914  if (!docshell) {
   4915    return nullptr;
   4916  }
   4917 
   4918  mMidasCommandManager = docshell->GetCommandManager();
   4919  return mMidasCommandManager;
   4920 }
   4921 
   4922 // static
   4923 void Document::EnsureInitializeInternalCommandDataHashtable() {
   4924  if (sInternalCommandDataHashtable) {
   4925    return;
   4926  }
   4927  using CommandOnTextEditor = InternalCommandData::CommandOnTextEditor;
   4928  sInternalCommandDataHashtable = new InternalCommandDataHashtable();
   4929  // clang-format off
   4930  sInternalCommandDataHashtable->InsertOrUpdate(
   4931      u"bold"_ns,
   4932      InternalCommandData(
   4933          "cmd_bold",
   4934          Command::FormatBold,
   4935          ExecCommandParam::Ignore,
   4936          StyleUpdatingCommand::GetInstance,
   4937          CommandOnTextEditor::Disabled));
   4938  sInternalCommandDataHashtable->InsertOrUpdate(
   4939      u"italic"_ns,
   4940      InternalCommandData(
   4941          "cmd_italic",
   4942          Command::FormatItalic,
   4943          ExecCommandParam::Ignore,
   4944          StyleUpdatingCommand::GetInstance,
   4945          CommandOnTextEditor::Disabled));
   4946  sInternalCommandDataHashtable->InsertOrUpdate(
   4947      u"underline"_ns,
   4948      InternalCommandData(
   4949          "cmd_underline",
   4950          Command::FormatUnderline,
   4951          ExecCommandParam::Ignore,
   4952          StyleUpdatingCommand::GetInstance,
   4953          CommandOnTextEditor::Disabled));
   4954  sInternalCommandDataHashtable->InsertOrUpdate(
   4955      u"strikethrough"_ns,
   4956      InternalCommandData(
   4957          "cmd_strikethrough",
   4958          Command::FormatStrikeThrough,
   4959          ExecCommandParam::Ignore,
   4960          StyleUpdatingCommand::GetInstance,
   4961          CommandOnTextEditor::Disabled));
   4962  sInternalCommandDataHashtable->InsertOrUpdate(
   4963      u"subscript"_ns,
   4964      InternalCommandData(
   4965          "cmd_subscript",
   4966          Command::FormatSubscript,
   4967          ExecCommandParam::Ignore,
   4968          StyleUpdatingCommand::GetInstance,
   4969          CommandOnTextEditor::Disabled));
   4970  sInternalCommandDataHashtable->InsertOrUpdate(
   4971      u"superscript"_ns,
   4972      InternalCommandData(
   4973          "cmd_superscript",
   4974          Command::FormatSuperscript,
   4975          ExecCommandParam::Ignore,
   4976          StyleUpdatingCommand::GetInstance,
   4977          CommandOnTextEditor::Disabled));
   4978  sInternalCommandDataHashtable->InsertOrUpdate(
   4979      u"cut"_ns,
   4980      InternalCommandData(
   4981          "cmd_cut",
   4982          Command::Cut,
   4983          ExecCommandParam::Ignore,
   4984          CutCommand::GetInstance,
   4985          CommandOnTextEditor::Enabled));
   4986  sInternalCommandDataHashtable->InsertOrUpdate(
   4987      u"copy"_ns,
   4988      InternalCommandData(
   4989          "cmd_copy",
   4990          Command::Copy,
   4991          ExecCommandParam::Ignore,
   4992          CopyCommand::GetInstance,
   4993          CommandOnTextEditor::Enabled));
   4994  sInternalCommandDataHashtable->InsertOrUpdate(
   4995      u"paste"_ns,
   4996      InternalCommandData(
   4997          "cmd_paste",
   4998          Command::Paste,
   4999          ExecCommandParam::Ignore,
   5000          PasteCommand::GetInstance,
   5001          CommandOnTextEditor::Enabled));
   5002  sInternalCommandDataHashtable->InsertOrUpdate(
   5003      u"delete"_ns,
   5004      InternalCommandData(
   5005          "cmd_deleteCharBackward",
   5006          Command::DeleteCharBackward,
   5007          ExecCommandParam::Ignore,
   5008          DeleteCommand::GetInstance,
   5009          CommandOnTextEditor::Enabled));
   5010  sInternalCommandDataHashtable->InsertOrUpdate(
   5011      u"forwarddelete"_ns,
   5012      InternalCommandData(
   5013          "cmd_deleteCharForward",
   5014          Command::DeleteCharForward,
   5015          ExecCommandParam::Ignore,
   5016          DeleteCommand::GetInstance,
   5017          CommandOnTextEditor::Enabled));
   5018  sInternalCommandDataHashtable->InsertOrUpdate(
   5019      u"selectall"_ns,
   5020      InternalCommandData(
   5021          "cmd_selectAll",
   5022          Command::SelectAll,
   5023          ExecCommandParam::Ignore,
   5024          SelectAllCommand::GetInstance,
   5025          CommandOnTextEditor::Enabled));
   5026  sInternalCommandDataHashtable->InsertOrUpdate(
   5027      u"undo"_ns,
   5028      InternalCommandData(
   5029          "cmd_undo",
   5030          Command::HistoryUndo,
   5031          ExecCommandParam::Ignore,
   5032          UndoCommand::GetInstance,
   5033          CommandOnTextEditor::Enabled));
   5034  sInternalCommandDataHashtable->InsertOrUpdate(
   5035      u"redo"_ns,
   5036      InternalCommandData(
   5037          "cmd_redo",
   5038          Command::HistoryRedo,
   5039          ExecCommandParam::Ignore,
   5040          RedoCommand::GetInstance,
   5041          CommandOnTextEditor::Enabled));
   5042  sInternalCommandDataHashtable->InsertOrUpdate(
   5043      u"indent"_ns,
   5044      InternalCommandData("cmd_indent",
   5045          Command::FormatIndent,
   5046          ExecCommandParam::Ignore,
   5047          IndentCommand::GetInstance,
   5048          CommandOnTextEditor::Disabled));
   5049  sInternalCommandDataHashtable->InsertOrUpdate(
   5050      u"outdent"_ns,
   5051      InternalCommandData(
   5052          "cmd_outdent",
   5053          Command::FormatOutdent,
   5054          ExecCommandParam::Ignore,
   5055          OutdentCommand::GetInstance,
   5056          CommandOnTextEditor::Disabled));
   5057  sInternalCommandDataHashtable->InsertOrUpdate(
   5058      u"backcolor"_ns,
   5059      InternalCommandData(
   5060          "cmd_highlight",
   5061          Command::FormatBackColor,
   5062          ExecCommandParam::String,
   5063          HighlightColorStateCommand::GetInstance,
   5064          CommandOnTextEditor::Disabled));
   5065  sInternalCommandDataHashtable->InsertOrUpdate(
   5066      u"hilitecolor"_ns,
   5067      InternalCommandData(
   5068          "cmd_highlight",
   5069          Command::FormatBackColor,
   5070          ExecCommandParam::String,
   5071          HighlightColorStateCommand::GetInstance,
   5072          CommandOnTextEditor::Disabled));
   5073  sInternalCommandDataHashtable->InsertOrUpdate(
   5074      u"forecolor"_ns,
   5075      InternalCommandData(
   5076          "cmd_fontColor",
   5077          Command::FormatFontColor,
   5078          ExecCommandParam::String,
   5079          FontColorStateCommand::GetInstance,
   5080          CommandOnTextEditor::Disabled));
   5081  sInternalCommandDataHashtable->InsertOrUpdate(
   5082      u"fontname"_ns,
   5083      InternalCommandData(
   5084          "cmd_fontFace",
   5085          Command::FormatFontName,
   5086          ExecCommandParam::String,
   5087          FontFaceStateCommand::GetInstance,
   5088          CommandOnTextEditor::Disabled));
   5089  sInternalCommandDataHashtable->InsertOrUpdate(
   5090      u"fontsize"_ns,
   5091      InternalCommandData(
   5092          "cmd_fontSize",
   5093          Command::FormatFontSize,
   5094          ExecCommandParam::String,
   5095          FontSizeStateCommand::GetInstance,
   5096          CommandOnTextEditor::Disabled));
   5097  sInternalCommandDataHashtable->InsertOrUpdate(
   5098      u"inserthorizontalrule"_ns,
   5099      InternalCommandData(
   5100          "cmd_insertHR",
   5101          Command::InsertHorizontalRule,
   5102          ExecCommandParam::Ignore,
   5103          InsertTagCommand::GetInstance,
   5104          CommandOnTextEditor::Disabled));
   5105  sInternalCommandDataHashtable->InsertOrUpdate(
   5106      u"createlink"_ns,
   5107      InternalCommandData(
   5108          "cmd_insertLinkNoUI",
   5109          Command::InsertLink,
   5110          ExecCommandParam::String,
   5111          InsertTagCommand::GetInstance,
   5112          CommandOnTextEditor::Disabled));
   5113  sInternalCommandDataHashtable->InsertOrUpdate(
   5114      u"insertimage"_ns,
   5115      InternalCommandData(
   5116          "cmd_insertImageNoUI",
   5117          Command::InsertImage,
   5118          ExecCommandParam::String,
   5119          InsertTagCommand::GetInstance,
   5120          CommandOnTextEditor::Disabled));
   5121  sInternalCommandDataHashtable->InsertOrUpdate(
   5122      u"inserthtml"_ns,
   5123      InternalCommandData(
   5124          "cmd_insertHTML",
   5125          Command::InsertHTML,
   5126          ExecCommandParam::String,
   5127          InsertHTMLCommand::GetInstance,
   5128          // TODO: Chromium inserts text content of the document fragment
   5129          //       created from the param.
   5130          //       https://source.chromium.org/chromium/chromium/src/+/master:third_party/blink/renderer/core/editing/commands/insert_commands.cc;l=105;drc=a4708b724062f17824815b896c3aaa43825128f8
   5131          CommandOnTextEditor::Disabled));
   5132  sInternalCommandDataHashtable->InsertOrUpdate(
   5133      u"inserttext"_ns,
   5134      InternalCommandData(
   5135          "cmd_insertText",
   5136          Command::InsertText,
   5137          ExecCommandParam::String,
   5138          InsertPlaintextCommand::GetInstance,
   5139          CommandOnTextEditor::Enabled));
   5140  sInternalCommandDataHashtable->InsertOrUpdate(
   5141      u"justifyleft"_ns,
   5142      InternalCommandData(
   5143          "cmd_align",
   5144          Command::FormatJustifyLeft,
   5145          ExecCommandParam::Ignore,  // Will be set to "left"
   5146          AlignCommand::GetInstance,
   5147          CommandOnTextEditor::Disabled));
   5148  sInternalCommandDataHashtable->InsertOrUpdate(
   5149      u"justifyright"_ns,
   5150      InternalCommandData(
   5151          "cmd_align",
   5152          Command::FormatJustifyRight,
   5153          ExecCommandParam::Ignore,  // Will be set to "right"
   5154          AlignCommand::GetInstance,
   5155          CommandOnTextEditor::Disabled));
   5156  sInternalCommandDataHashtable->InsertOrUpdate(
   5157      u"justifycenter"_ns,
   5158      InternalCommandData(
   5159          "cmd_align",
   5160          Command::FormatJustifyCenter,
   5161          ExecCommandParam::Ignore,  // Will be set to "center"
   5162          AlignCommand::GetInstance,
   5163          CommandOnTextEditor::Disabled));
   5164  sInternalCommandDataHashtable->InsertOrUpdate(
   5165      u"justifyfull"_ns,
   5166      InternalCommandData(
   5167          "cmd_align",
   5168          Command::FormatJustifyFull,
   5169          ExecCommandParam::Ignore,  // Will be set to "justify"
   5170          AlignCommand::GetInstance,
   5171          CommandOnTextEditor::Disabled));
   5172  sInternalCommandDataHashtable->InsertOrUpdate(
   5173      u"removeformat"_ns,
   5174      InternalCommandData(
   5175          "cmd_removeStyles",
   5176          Command::FormatRemove,
   5177          ExecCommandParam::Ignore,
   5178          RemoveStylesCommand::GetInstance,
   5179          CommandOnTextEditor::Disabled));
   5180  sInternalCommandDataHashtable->InsertOrUpdate(
   5181      u"unlink"_ns,
   5182      InternalCommandData(
   5183          "cmd_removeLinks",
   5184          Command::FormatRemoveLink,
   5185          ExecCommandParam::Ignore,
   5186          StyleUpdatingCommand::GetInstance,
   5187          CommandOnTextEditor::Disabled));
   5188  sInternalCommandDataHashtable->InsertOrUpdate(
   5189      u"insertorderedlist"_ns,
   5190      InternalCommandData(
   5191          "cmd_ol",
   5192          Command::InsertOrderedList,
   5193          ExecCommandParam::Ignore,
   5194          ListCommand::GetInstance,
   5195          CommandOnTextEditor::Disabled));
   5196  sInternalCommandDataHashtable->InsertOrUpdate(
   5197      u"insertunorderedlist"_ns,
   5198      InternalCommandData(
   5199          "cmd_ul",
   5200          Command::InsertUnorderedList,
   5201          ExecCommandParam::Ignore,
   5202          ListCommand::GetInstance,
   5203          CommandOnTextEditor::Disabled));
   5204  sInternalCommandDataHashtable->InsertOrUpdate(
   5205      u"insertparagraph"_ns,
   5206      InternalCommandData(
   5207          "cmd_insertParagraph",
   5208          Command::InsertParagraph,
   5209          ExecCommandParam::Ignore,
   5210          InsertParagraphCommand::GetInstance,
   5211          CommandOnTextEditor::Enabled));
   5212  sInternalCommandDataHashtable->InsertOrUpdate(
   5213      u"insertlinebreak"_ns,
   5214      InternalCommandData(
   5215          "cmd_insertLineBreak",
   5216          Command::InsertLineBreak,
   5217          ExecCommandParam::Ignore,
   5218          InsertLineBreakCommand::GetInstance,
   5219          CommandOnTextEditor::Enabled));
   5220  sInternalCommandDataHashtable->InsertOrUpdate(
   5221      u"formatblock"_ns,
   5222      InternalCommandData(
   5223          "cmd_formatBlock",
   5224          Command::FormatBlock,
   5225          ExecCommandParam::String,
   5226          FormatBlockStateCommand::GetInstance,
   5227          CommandOnTextEditor::Disabled));
   5228  sInternalCommandDataHashtable->InsertOrUpdate(
   5229      u"styleWithCSS"_ns,
   5230      InternalCommandData(
   5231          "cmd_setDocumentUseCSS",
   5232          Command::SetDocumentUseCSS,
   5233          ExecCommandParam::Boolean,
   5234          SetDocumentStateCommand::GetInstance,
   5235          CommandOnTextEditor::FallThrough));
   5236  sInternalCommandDataHashtable->InsertOrUpdate(
   5237      u"usecss"_ns,  // Legacy command
   5238      InternalCommandData(
   5239          "cmd_setDocumentUseCSS",
   5240          Command::SetDocumentUseCSS,
   5241          ExecCommandParam::InvertedBoolean,
   5242          SetDocumentStateCommand::GetInstance,
   5243          CommandOnTextEditor::FallThrough));
   5244  sInternalCommandDataHashtable->InsertOrUpdate(
   5245      u"contentReadOnly"_ns,
   5246      InternalCommandData(
   5247          "cmd_setDocumentReadOnly",
   5248          Command::SetDocumentReadOnly,
   5249          ExecCommandParam::Boolean,
   5250          SetDocumentStateCommand::GetInstance,
   5251          CommandOnTextEditor::Enabled));
   5252  sInternalCommandDataHashtable->InsertOrUpdate(
   5253      u"insertBrOnReturn"_ns,
   5254      InternalCommandData(
   5255          "cmd_insertBrOnReturn",
   5256          Command::SetDocumentInsertBROnEnterKeyPress,
   5257          ExecCommandParam::Boolean,
   5258          SetDocumentStateCommand::GetInstance,
   5259          CommandOnTextEditor::FallThrough));
   5260  sInternalCommandDataHashtable->InsertOrUpdate(
   5261      u"defaultParagraphSeparator"_ns,
   5262      InternalCommandData(
   5263          "cmd_defaultParagraphSeparator",
   5264          Command::SetDocumentDefaultParagraphSeparator,
   5265          ExecCommandParam::String,
   5266          SetDocumentStateCommand::GetInstance,
   5267          CommandOnTextEditor::FallThrough));
   5268  sInternalCommandDataHashtable->InsertOrUpdate(
   5269      u"enableObjectResizing"_ns,
   5270      InternalCommandData(
   5271          "cmd_enableObjectResizing",
   5272          Command::ToggleObjectResizers,
   5273          ExecCommandParam::Boolean,
   5274          SetDocumentStateCommand::GetInstance,
   5275          CommandOnTextEditor::FallThrough));
   5276  sInternalCommandDataHashtable->InsertOrUpdate(
   5277      u"enableInlineTableEditing"_ns,
   5278      InternalCommandData(
   5279          "cmd_enableInlineTableEditing",
   5280          Command::ToggleInlineTableEditor,
   5281          ExecCommandParam::Boolean,
   5282          SetDocumentStateCommand::GetInstance,
   5283          CommandOnTextEditor::FallThrough));
   5284  sInternalCommandDataHashtable->InsertOrUpdate(
   5285      u"enableAbsolutePositionEditing"_ns,
   5286      InternalCommandData(
   5287          "cmd_enableAbsolutePositionEditing",
   5288          Command::ToggleAbsolutePositionEditor,
   5289          ExecCommandParam::Boolean,
   5290          SetDocumentStateCommand::GetInstance,
   5291          CommandOnTextEditor::FallThrough));
   5292  sInternalCommandDataHashtable->InsertOrUpdate(
   5293      u"enableCompatibleJoinSplitDirection"_ns,
   5294      InternalCommandData("cmd_enableCompatibleJoinSplitNodeDirection",
   5295          Command::EnableCompatibleJoinSplitNodeDirection,
   5296          ExecCommandParam::Boolean,
   5297          SetDocumentStateCommand::GetInstance,
   5298          CommandOnTextEditor::FallThrough));
   5299 #if 0
   5300  // with empty string
   5301  sInternalCommandDataHashtable->InsertOrUpdate(
   5302      u"justifynone"_ns,
   5303      InternalCommandData(
   5304          "cmd_align",
   5305          Command::Undefined,
   5306          ExecCommandParam::Ignore,
   5307          nullptr,
   5308          CommandOnTextEditor::Disabled));  // Not implemented yet.
   5309  // REQUIRED SPECIAL REVIEW special review
   5310  sInternalCommandDataHashtable->InsertOrUpdate(
   5311      u"saveas"_ns,
   5312      InternalCommandData(
   5313          "cmd_saveAs",
   5314          Command::Undefined,
   5315          ExecCommandParam::Boolean,
   5316          nullptr,
   5317          CommandOnTextEditor::FallThrough));  // Not implemented yet.
   5318  // REQUIRED SPECIAL REVIEW special review
   5319  sInternalCommandDataHashtable->InsertOrUpdate(
   5320      u"print"_ns,
   5321      InternalCommandData(
   5322          "cmd_print",
   5323          Command::Undefined,
   5324          ExecCommandParam::Boolean,
   5325          nullptr,
   5326          CommandOnTextEditor::FallThrough));  // Not implemented yet.
   5327 #endif  // #if 0
   5328  // clang-format on
   5329 }
   5330 
   5331 Document::InternalCommandData Document::ConvertToInternalCommand(
   5332    const nsAString& aHTMLCommandName,
   5333    const TrustedHTMLOrString* aValue /* = nullptr */,
   5334    nsIPrincipal* aSubjectPrincipal /* = nullptr */,
   5335    ErrorResult* aRv /* = nullptr */,
   5336    nsAString* aAdjustedValue /* = nullptr */) {
   5337  MOZ_ASSERT(!aAdjustedValue || aAdjustedValue->IsEmpty());
   5338  EnsureInitializeInternalCommandDataHashtable();
   5339  InternalCommandData commandData;
   5340  if (!sInternalCommandDataHashtable->Get(aHTMLCommandName, &commandData)) {
   5341    return InternalCommandData();
   5342  }
   5343  // Ignore if the command is disabled by a corresponding pref due to Gecko
   5344  // specific.
   5345  switch (commandData.mCommand) {
   5346    case Command::SetDocumentReadOnly:
   5347      if (!StaticPrefs::dom_document_edit_command_contentReadOnly_enabled() &&
   5348          aHTMLCommandName.LowerCaseEqualsLiteral("contentreadonly")) {
   5349        return InternalCommandData();
   5350      }
   5351      break;
   5352    case Command::SetDocumentInsertBROnEnterKeyPress:
   5353      MOZ_DIAGNOSTIC_ASSERT(
   5354          aHTMLCommandName.LowerCaseEqualsLiteral("insertbronreturn"));
   5355      if (!StaticPrefs::dom_document_edit_command_insertBrOnReturn_enabled()) {
   5356        return InternalCommandData();
   5357      }
   5358      break;
   5359    default:
   5360      break;
   5361  }
   5362  if (!aAdjustedValue) {
   5363    // No further work to do
   5364    return commandData;
   5365  }
   5366  MOZ_ASSERT(aValue);
   5367  MOZ_ASSERT(aRv);
   5368  Maybe<nsAutoString> compliantStringHolder;
   5369  const nsAString* compliantString = nullptr;
   5370  if (commandData.mCommand == Command::InsertHTML) {
   5371    constexpr nsLiteralString sink = u"Document execCommand"_ns;
   5372    compliantString = TrustedTypeUtils::GetTrustedTypesCompliantString(
   5373        *aValue, sink, kTrustedTypesOnlySinkGroup, *this, aSubjectPrincipal,
   5374        compliantStringHolder, *aRv);
   5375    if (aRv->Failed()) {
   5376      return InternalCommandData();
   5377    }
   5378  } else {
   5379    compliantString = aValue->IsString() ? &aValue->GetAsString()
   5380                                         : &aValue->GetAsTrustedHTML().mData;
   5381  }
   5382 
   5383  switch (commandData.mExecCommandParam) {
   5384    case ExecCommandParam::Ignore:
   5385      // Just have to copy it, no checking
   5386      switch (commandData.mCommand) {
   5387        case Command::FormatJustifyLeft:
   5388          aAdjustedValue->AssignLiteral("left");
   5389          break;
   5390        case Command::FormatJustifyRight:
   5391          aAdjustedValue->AssignLiteral("right");
   5392          break;
   5393        case Command::FormatJustifyCenter:
   5394          aAdjustedValue->AssignLiteral("center");
   5395          break;
   5396        case Command::FormatJustifyFull:
   5397          aAdjustedValue->AssignLiteral("justify");
   5398          break;
   5399        default:
   5400          MOZ_ASSERT(EditorCommand::GetParamType(commandData.mCommand) ==
   5401                     EditorCommandParamType::None);
   5402          break;
   5403      }
   5404      return commandData;
   5405 
   5406    case ExecCommandParam::Boolean:
   5407      MOZ_ASSERT(!!(EditorCommand::GetParamType(commandData.mCommand) &
   5408                    EditorCommandParamType::Bool));
   5409      // If this is a boolean value and it's not explicitly false (e.g. no
   5410      // value).  We default to "true" (see bug 301490).
   5411      if (!compliantString->LowerCaseEqualsLiteral("false")) {
   5412        aAdjustedValue->AssignLiteral("true");
   5413      } else {
   5414        aAdjustedValue->AssignLiteral("false");
   5415      }
   5416      return commandData;
   5417 
   5418    case ExecCommandParam::InvertedBoolean:
   5419      MOZ_ASSERT(!!(EditorCommand::GetParamType(commandData.mCommand) &
   5420                    EditorCommandParamType::Bool));
   5421      // For old backwards commands we invert the check.
   5422      if (compliantString->LowerCaseEqualsLiteral("false")) {
   5423        aAdjustedValue->AssignLiteral("true");
   5424      } else {
   5425        aAdjustedValue->AssignLiteral("false");
   5426      }
   5427      return commandData;
   5428 
   5429    case ExecCommandParam::String:
   5430      MOZ_ASSERT(!!(
   5431          EditorCommand::GetParamType(commandData.mCommand) &
   5432          (EditorCommandParamType::String | EditorCommandParamType::CString)));
   5433      switch (commandData.mCommand) {
   5434        case Command::FormatBlock: {
   5435          const char16_t* start = compliantString->BeginReading();
   5436          const char16_t* end = compliantString->EndReading();
   5437          if (start != end && *start == '<' && *(end - 1) == '>') {
   5438            ++start;
   5439            --end;
   5440          }
   5441          // XXX Should we reorder this array with actual usage?
   5442          static const nsStaticAtom* kFormattableBlockTags[] = {
   5443              // clang-format off
   5444            nsGkAtoms::address,
   5445            nsGkAtoms::article,
   5446            nsGkAtoms::aside,
   5447            nsGkAtoms::blockquote,
   5448            nsGkAtoms::dd,
   5449            nsGkAtoms::div,
   5450            nsGkAtoms::dl,
   5451            nsGkAtoms::dt,
   5452            nsGkAtoms::footer,
   5453            nsGkAtoms::h1,
   5454            nsGkAtoms::h2,
   5455            nsGkAtoms::h3,
   5456            nsGkAtoms::h4,
   5457            nsGkAtoms::h5,
   5458            nsGkAtoms::h6,
   5459            nsGkAtoms::header,
   5460            nsGkAtoms::hgroup,
   5461            nsGkAtoms::main,
   5462            nsGkAtoms::nav,
   5463            nsGkAtoms::p,
   5464            nsGkAtoms::pre,
   5465            nsGkAtoms::section,
   5466              // clang-format on
   5467          };
   5468          nsAutoString value(nsDependentSubstring(start, end));
   5469          ToLowerCase(value);
   5470          const nsStaticAtom* valueAtom = NS_GetStaticAtom(value);
   5471          for (const nsStaticAtom* kTag : kFormattableBlockTags) {
   5472            if (valueAtom == kTag) {
   5473              kTag->ToString(*aAdjustedValue);
   5474              return commandData;
   5475            }
   5476          }
   5477          return InternalCommandData();
   5478        }
   5479        case Command::FormatFontSize: {
   5480          // Per editing spec as of April 23, 2012, we need to reject the value
   5481          // if it's not a valid floating-point number surrounded by optional
   5482          // whitespace.  Otherwise, we parse it as a legacy font size.  For
   5483          // now, we just parse as a legacy font size regardless (matching
   5484          // WebKit) -- bug 747879.
   5485          int32_t size = nsContentUtils::ParseLegacyFontSize(*compliantString);
   5486          if (!size) {
   5487            return InternalCommandData();
   5488          }
   5489          MOZ_ASSERT(aAdjustedValue->IsEmpty());
   5490          aAdjustedValue->AppendInt(size);
   5491          return commandData;
   5492        }
   5493        case Command::InsertImage:
   5494        case Command::InsertLink:
   5495          if (compliantString->IsEmpty()) {
   5496            // Invalid value, return false
   5497            return InternalCommandData();
   5498          }
   5499          aAdjustedValue->Assign(*compliantString);
   5500          return commandData;
   5501        case Command::SetDocumentDefaultParagraphSeparator:
   5502          if (!compliantString->LowerCaseEqualsLiteral("div") &&
   5503              !compliantString->LowerCaseEqualsLiteral("p") &&
   5504              !compliantString->LowerCaseEqualsLiteral("br")) {
   5505            // Invalid value
   5506            return InternalCommandData();
   5507          }
   5508          aAdjustedValue->Assign(*compliantString);
   5509          return commandData;
   5510        default:
   5511          aAdjustedValue->Assign(*compliantString);
   5512          return commandData;
   5513      }
   5514 
   5515    default:
   5516      MOZ_ASSERT_UNREACHABLE("New ExecCommandParam value hasn't been handled");
   5517      return InternalCommandData();
   5518  }
   5519 }
   5520 
   5521 Document::AutoEditorCommandTarget::AutoEditorCommandTarget(
   5522    Document& aDocument, const InternalCommandData& aCommandData)
   5523    : mCommandData(aCommandData) {
   5524  // We'll retrieve an editor with current DOM tree and layout information.
   5525  // However, JS may have already hidden or remove exposed root content of
   5526  // the editor.  Therefore, we need the latest layout information here.
   5527  aDocument.FlushPendingNotifications(FlushType::Layout);
   5528  if (!aDocument.GetPresShell() || aDocument.GetPresShell()->IsDestroying()) {
   5529    mDoNothing = true;
   5530    return;
   5531  }
   5532 
   5533  if (nsPresContext* presContext = aDocument.GetPresContext()) {
   5534    // Consider context of command handling which is automatically resolved
   5535    // by order of controllers in `nsCommandManager::GetControllerForCommand()`.
   5536    // The order is:
   5537    //   1. TextEditor if there is an active element and it has TextEditor like
   5538    //      <input type="text"> or <textarea>.
   5539    //   2. HTMLEditor for the document, if there is.
   5540    //   3. Retarget to the DocShell or nsCommandManager as what we've done.
   5541    if (aCommandData.IsCutOrCopyCommand()) {
   5542      // Note that we used to use DocShell to handle `cut` and `copy` command
   5543      // for dispatching corresponding events for making possible web apps to
   5544      // implement their own editor without editable elements but supports
   5545      // standard shortcut keys, etc.  In this case, we prefer to use active
   5546      // element's editor to keep same behavior.
   5547      mActiveEditor = nsContentUtils::GetActiveEditor(presContext);
   5548    } else {
   5549      mActiveEditor = nsContentUtils::GetActiveEditor(presContext);
   5550      mHTMLEditor = nsContentUtils::GetHTMLEditor(presContext);
   5551      if (!mActiveEditor) {
   5552        mActiveEditor = mHTMLEditor;
   5553      }
   5554    }
   5555  }
   5556 
   5557  // Then, retrieve editor command class instance which should handle it
   5558  // and can handle it now.
   5559  if (!mActiveEditor) {
   5560    // If the command is available without editor, we should redirect the
   5561    // command to focused descendant with DocShell.
   5562    if (aCommandData.IsAvailableOnlyWhenEditable()) {
   5563      mDoNothing = true;
   5564      return;
   5565    }
   5566    return;
   5567  }
   5568 
   5569  // Otherwise, we should use EditorCommand instance (which is singleton
   5570  // instance) when it's enabled.
   5571  mEditorCommand = aCommandData.mGetEditorCommandFunc
   5572                       ? aCommandData.mGetEditorCommandFunc()
   5573                       : nullptr;
   5574  if (!mEditorCommand) {
   5575    mDoNothing = true;
   5576    mActiveEditor = nullptr;
   5577    mHTMLEditor = nullptr;
   5578    return;
   5579  }
   5580 
   5581  if (IsCommandEnabled()) {
   5582    return;
   5583  }
   5584 
   5585  // If the EditorCommand instance is disabled, we should do nothing if
   5586  // the command requires an editor.
   5587  if (aCommandData.IsAvailableOnlyWhenEditable()) {
   5588    // Do nothing if editor specific commands is disabled (bug 760052).
   5589    mDoNothing = true;
   5590    return;
   5591  }
   5592 
   5593  // Otherwise, we should redirect it to focused descendant with DocShell.
   5594  mEditorCommand = nullptr;
   5595  mActiveEditor = nullptr;
   5596  mHTMLEditor = nullptr;
   5597 }
   5598 
   5599 EditorBase* Document::AutoEditorCommandTarget::GetTargetEditor() const {
   5600  using CommandOnTextEditor = InternalCommandData::CommandOnTextEditor;
   5601  switch (mCommandData.mCommandOnTextEditor) {
   5602    case CommandOnTextEditor::Enabled:
   5603      return mActiveEditor;
   5604    case CommandOnTextEditor::Disabled:
   5605      return mActiveEditor && mActiveEditor->IsTextEditor()
   5606                 ? nullptr
   5607                 : mActiveEditor.get();
   5608    case CommandOnTextEditor::FallThrough:
   5609      return mHTMLEditor;
   5610  }
   5611  return nullptr;
   5612 }
   5613 
   5614 bool Document::AutoEditorCommandTarget::IsEditable(Document* aDocument) const {
   5615  if (RefPtr<Document> doc = aDocument->GetInProcessParentDocument()) {
   5616    // Make sure frames are up to date, since that can affect whether
   5617    // we're editable.
   5618    doc->FlushPendingNotifications(FlushType::Frames);
   5619  }
   5620  EditorBase* targetEditor = GetTargetEditor();
   5621  if (targetEditor && targetEditor->IsTextEditor()) {
   5622    // FYI: When `disabled` attribute is set, `TextEditor` treats it as
   5623    //      "readonly" too.
   5624    return !targetEditor->IsReadonly();
   5625  }
   5626  return aDocument->IsEditingOn();
   5627 }
   5628 
   5629 bool Document::AutoEditorCommandTarget::IsCommandEnabled() const {
   5630  EditorBase* targetEditor = GetTargetEditor();
   5631  if (!targetEditor) {
   5632    return false;
   5633  }
   5634  MOZ_ASSERT(targetEditor == mActiveEditor || targetEditor == mHTMLEditor);
   5635  return MOZ_KnownLive(mEditorCommand)
   5636      ->IsCommandEnabled(mCommandData.mCommand, MOZ_KnownLive(targetEditor));
   5637 }
   5638 
   5639 nsresult Document::AutoEditorCommandTarget::DoCommand(
   5640    nsIPrincipal* aPrincipal) const {
   5641  MOZ_ASSERT(!DoNothing());
   5642  MOZ_ASSERT(mEditorCommand);
   5643  EditorBase* targetEditor = GetTargetEditor();
   5644  if (!targetEditor) {
   5645    return NS_SUCCESS_DOM_NO_OPERATION;
   5646  }
   5647  MOZ_ASSERT(targetEditor == mActiveEditor || targetEditor == mHTMLEditor);
   5648  return MOZ_KnownLive(mEditorCommand)
   5649      ->DoCommand(mCommandData.mCommand, MOZ_KnownLive(*targetEditor),
   5650                  aPrincipal);
   5651 }
   5652 
   5653 template <typename ParamType>
   5654 nsresult Document::AutoEditorCommandTarget::DoCommandParam(
   5655    const ParamType& aParam, nsIPrincipal* aPrincipal) const {
   5656  MOZ_ASSERT(!DoNothing());
   5657  MOZ_ASSERT(mEditorCommand);
   5658  EditorBase* targetEditor = GetTargetEditor();
   5659  if (!targetEditor) {
   5660    return NS_SUCCESS_DOM_NO_OPERATION;
   5661  }
   5662  MOZ_ASSERT(targetEditor == mActiveEditor || targetEditor == mHTMLEditor);
   5663  return MOZ_KnownLive(mEditorCommand)
   5664      ->DoCommandParam(mCommandData.mCommand, aParam,
   5665                       MOZ_KnownLive(*targetEditor), aPrincipal);
   5666 }
   5667 
   5668 nsresult Document::AutoEditorCommandTarget::GetCommandStateParams(
   5669    nsCommandParams& aParams) const {
   5670  MOZ_ASSERT(mEditorCommand);
   5671  EditorBase* targetEditor = GetTargetEditor();
   5672  if (!targetEditor) {
   5673    return NS_OK;
   5674  }
   5675  MOZ_ASSERT(targetEditor == mActiveEditor || targetEditor == mHTMLEditor);
   5676  return MOZ_KnownLive(mEditorCommand)
   5677      ->GetCommandStateParams(mCommandData.mCommand, MOZ_KnownLive(aParams),
   5678                              MOZ_KnownLive(targetEditor), nullptr);
   5679 }
   5680 
   5681 Document::AutoRunningExecCommandMarker::AutoRunningExecCommandMarker(
   5682    Document& aDocument, nsIPrincipal* aPrincipal)
   5683    : mDocument(aDocument),
   5684      mTreatAsUserInput(EditorBase::TreatAsUserInput(aPrincipal)),
   5685      mHasBeenRunningByContent(aDocument.mIsRunningExecCommandByContent),
   5686      mHasBeenRunningByChromeOrAddon(
   5687          aDocument.mIsRunningExecCommandByChromeOrAddon) {
   5688  if (mTreatAsUserInput) {
   5689    aDocument.mIsRunningExecCommandByChromeOrAddon = true;
   5690  } else {
   5691    aDocument.mIsRunningExecCommandByContent = true;
   5692  }
   5693 }
   5694 
   5695 /**
   5696 * Returns true if calling execCommand with 'paste' arguments is allowed for the
   5697 * given subject principal. These are only allowed if the user initiated them
   5698 * (like with a mouse-click or key press).
   5699 */
   5700 static bool IsExecCommandPasteAllowed(Document* aDocument,
   5701                                      nsIPrincipal& aSubjectPrincipal) {
   5702  if (StaticPrefs::dom_execCommand_paste_enabled() && aDocument &&
   5703      aDocument->HasValidTransientUserGestureActivation()) {
   5704    return true;
   5705  }
   5706 
   5707  return nsContentUtils::PrincipalHasPermission(aSubjectPrincipal,
   5708                                                nsGkAtoms::clipboardRead);
   5709 }
   5710 
   5711 bool Document::ExecCommand(const nsAString& aHTMLCommandName, bool aShowUI,
   5712                           const TrustedHTMLOrString& aValue,
   5713                           nsIPrincipal& aSubjectPrincipal, ErrorResult& aRv) {
   5714  // Only allow on HTML documents.
   5715  if (!IsHTMLOrXHTML()) {
   5716    aRv.ThrowInvalidStateError(
   5717        "execCommand is only supported on HTML documents");
   5718    return false;
   5719  }
   5720  // Otherwise, don't throw exception for compatibility with Chrome.
   5721 
   5722  // if they are requesting UI from us, let's fail since we have no UI
   5723  if (aShowUI) {
   5724    return false;
   5725  }
   5726 
   5727  //  for optional parameters see dom/src/base/nsHistory.cpp: HistoryImpl::Go()
   5728  //  this might add some ugly JS dependencies?
   5729 
   5730  nsAutoString adjustedValue;
   5731  InternalCommandData commandData = ConvertToInternalCommand(
   5732      aHTMLCommandName, &aValue, &aSubjectPrincipal, &aRv, &adjustedValue);
   5733  switch (commandData.mCommand) {
   5734    case Command::DoNothing:
   5735      return false;
   5736    case Command::SetDocumentReadOnly:
   5737      SetUseCounter(eUseCounter_custom_DocumentExecCommandContentReadOnly);
   5738      break;
   5739    case Command::EnableCompatibleJoinSplitNodeDirection:
   5740      // We didn't allow to enable the legacy behavior once we've enabled the
   5741      // new behavior by default.  For keeping the behavior at supporting both
   5742      // mode, we should keep returning `false` if the web app to enable the
   5743      // legacy mode.  Additionally, we don't support the legacy direction
   5744      // anymore.  Therefore, we can return `false` here even if the caller is
   5745      // an addon or chrome script.
   5746      if (!adjustedValue.EqualsLiteral("true")) {
   5747        return false;
   5748      }
   5749      break;
   5750    default:
   5751      break;
   5752  }
   5753 
   5754  AutoRunningExecCommandMarker markRunningExecCommand(*this,
   5755                                                      &aSubjectPrincipal);
   5756 
   5757  // If we're running an execCommand, we should just return false.
   5758  // https://github.com/w3c/editing/issues/200#issuecomment-575241816
   5759  if (!markRunningExecCommand.IsSafeToRun()) {
   5760    return false;
   5761  }
   5762 
   5763  // Do security check first.
   5764  if (commandData.IsCutOrCopyCommand()) {
   5765    if (!nsContentUtils::IsCutCopyAllowed(this, aSubjectPrincipal)) {
   5766      // We have rejected the event due to it not being performed in an
   5767      // input-driven context therefore, we report the error to the console.
   5768      nsContentUtils::ReportToConsole(nsIScriptError::warningFlag, "DOM"_ns,
   5769                                      this, nsContentUtils::eDOM_PROPERTIES,
   5770                                      "ExecCommandCutCopyDeniedNotInputDriven");
   5771      return false;
   5772    }
   5773  } else if (commandData.IsPasteCommand()) {
   5774    if (!IsExecCommandPasteAllowed(this, aSubjectPrincipal)) {
   5775      if (StaticPrefs::dom_execCommand_paste_enabled()) {
   5776        // We rejected the command because it was not performed with a valid
   5777        // user activation; therefore, we report the error to the console.
   5778        nsContentUtils::ReportToConsole(nsIScriptError::warningFlag, "DOM"_ns,
   5779                                        this, nsContentUtils::eDOM_PROPERTIES,
   5780                                        "ExecCommandPasteDeniedNotInputDriven");
   5781      }
   5782      return false;
   5783    }
   5784  }
   5785 
   5786  // Next, consider context of command handling which is automatically resolved
   5787  // by order of controllers in `nsCommandManager::GetControllerForCommand()`.
   5788  AutoEditorCommandTarget editCommandTarget(*this, commandData);
   5789  if (commandData.IsAvailableOnlyWhenEditable()) {
   5790    if (!editCommandTarget.IsEditable(this)) {
   5791      return false;
   5792    }
   5793    // If currently the editor cannot dispatch `input` events, it means that the
   5794    // editor value is being set and that caused unexpected composition events.
   5795    // In this case, the value will be updated to the setting value soon and
   5796    // Chromium does not dispatch any events during the sequence but we dispatch
   5797    // `compositionupdate` and `compositionend` events to conform to the UI
   5798    // Events spec.  Therefore, this execCommand must be called accidentally.
   5799    EditorBase* targetEditor = editCommandTarget.GetTargetEditor();
   5800    if (targetEditor && targetEditor->IsSuppressingDispatchingInputEvent()) {
   5801      return false;
   5802    }
   5803  }
   5804 
   5805  if (editCommandTarget.DoNothing()) {
   5806    return false;
   5807  }
   5808 
   5809  // If we cannot use EditorCommand instance directly, we need to handle the
   5810  // command with traditional path (i.e., with DocShell or nsCommandManager).
   5811  if (!editCommandTarget.IsEditor()) {
   5812    MOZ_ASSERT(!commandData.IsAvailableOnlyWhenEditable());
   5813 
   5814    // Special case clipboard write commands like Command::Cut and
   5815    // Command::Copy.  For such commands, we need the behaviour from
   5816    // nsWindowRoot::GetControllers() which is to look at the focused element,
   5817    // and defer to a focused textbox's controller.  The code past taken by
   5818    // other commands in ExecCommand() always uses the window directly, rather
   5819    // than deferring to the textbox, which is desireable for most editor
   5820    // commands, but not these commands (as those should allow copying out of
   5821    // embedded editors). This behaviour is invoked if we call DoCommand()
   5822    // directly on the docShell.
   5823    // XXX This means that we allow web app to pick up selected content in
   5824    //     descendant document and write it into the clipboard when a
   5825    //     descendant document has focus.  However, Chromium does not allow
   5826    //     this and this seems that it's not good behavior from point of view
   5827    //     of security.  We should treat this issue in another bug.
   5828    if (commandData.IsCutOrCopyCommand()) {
   5829      nsCOMPtr<nsIDocShell> docShell(mDocumentContainer);
   5830      if (!docShell) {
   5831        return false;
   5832      }
   5833      nsresult rv = docShell->DoCommand(commandData.mXULCommandName);
   5834      if (rv == NS_SUCCESS_DOM_NO_OPERATION) {
   5835        return false;
   5836      }
   5837      return NS_SUCCEEDED(rv);
   5838    }
   5839 
   5840    // Otherwise (currently, only clipboard read commands like Command::Paste),
   5841    // we don't need to redirect the command to focused subdocument.
   5842    // Therefore, we should handle it with nsCommandManager as used to be.
   5843    // It may dispatch only preceding event of editing on non-editable element
   5844    // to make web apps possible to handle standard shortcut key, etc in
   5845    // their own editor.
   5846    RefPtr<nsCommandManager> commandManager = GetMidasCommandManager();
   5847    if (!commandManager) {
   5848      return false;
   5849    }
   5850 
   5851    nsCOMPtr<nsPIDOMWindowOuter> window = GetWindow();
   5852    if (!window) {
   5853      return false;
   5854    }
   5855 
   5856    // Return false for disabled commands (bug 760052)
   5857    if (!commandManager->IsCommandEnabled(
   5858            nsDependentCString(commandData.mXULCommandName), window)) {
   5859      return false;
   5860    }
   5861 
   5862    MOZ_ASSERT(commandData.IsPasteCommand() ||
   5863               commandData.mCommand == Command::SelectAll);
   5864    nsresult rv =
   5865        commandManager->DoCommand(commandData.mXULCommandName, nullptr, window);
   5866    return NS_SUCCEEDED(rv) && rv != NS_SUCCESS_DOM_NO_OPERATION;
   5867  }
   5868 
   5869  // Now, our target is fixed to the editor.  So, we can use EditorCommand
   5870  // in EditorCommandTarget directly.
   5871 
   5872  EditorCommandParamType paramType =
   5873      EditorCommand::GetParamType(commandData.mCommand);
   5874 
   5875  // If we don't have meaningful parameter or the EditorCommand does not
   5876  // require additional parameter, we can use `DoCommand()`.
   5877  if (adjustedValue.IsEmpty() || paramType == EditorCommandParamType::None) {
   5878    MOZ_ASSERT(!(paramType & EditorCommandParamType::Bool));
   5879    nsresult rv = editCommandTarget.DoCommand(&aSubjectPrincipal);
   5880    return NS_SUCCEEDED(rv) && rv != NS_SUCCESS_DOM_NO_OPERATION;
   5881  }
   5882 
   5883  // If the EditorCommand requires `bool` parameter, `adjustedValue` must be
   5884  // "true" or "false" here.  So, we can use `DoCommandParam()` which takes
   5885  // a `bool` value.
   5886  if (!!(paramType & EditorCommandParamType::Bool)) {
   5887    MOZ_ASSERT(adjustedValue.EqualsLiteral("true") ||
   5888               adjustedValue.EqualsLiteral("false"));
   5889    nsresult rv = editCommandTarget.DoCommandParam(
   5890        Some(adjustedValue.EqualsLiteral("true")), &aSubjectPrincipal);
   5891    return NS_SUCCEEDED(rv) && rv != NS_SUCCESS_DOM_NO_OPERATION;
   5892  }
   5893 
   5894  // Now, the EditorCommand requires `nsAString` or `nsACString` parameter
   5895  // in this case.  However, `paramType` may contain both `String` and
   5896  // `CString` but in such case, we should use `DoCommandParam()` which
   5897  // takes `nsAString`.  So, we should check whether `paramType` contains
   5898  // `String` or not first.
   5899  if (!!(paramType & EditorCommandParamType::String)) {
   5900    MOZ_ASSERT(!adjustedValue.IsVoid());
   5901    nsresult rv =
   5902        editCommandTarget.DoCommandParam(adjustedValue, &aSubjectPrincipal);
   5903    return NS_SUCCEEDED(rv) && rv != NS_SUCCESS_DOM_NO_OPERATION;
   5904  }
   5905 
   5906  // Finally, `paramType` should have `CString`.  We should use
   5907  // `DoCommandParam()` which takes `nsACString`.
   5908  if (!!(paramType & EditorCommandParamType::CString)) {
   5909    NS_ConvertUTF16toUTF8 utf8Value(adjustedValue);
   5910    MOZ_ASSERT(!utf8Value.IsVoid());
   5911    nsresult rv =
   5912        editCommandTarget.DoCommandParam(utf8Value, &aSubjectPrincipal);
   5913    return NS_SUCCEEDED(rv) && rv != NS_SUCCESS_DOM_NO_OPERATION;
   5914  }
   5915 
   5916  MOZ_ASSERT_UNREACHABLE(
   5917      "Not yet implemented to handle new EditorCommandParamType");
   5918  return false;
   5919 }
   5920 
   5921 bool Document::QueryCommandEnabled(const nsAString& aHTMLCommandName,
   5922                                   nsIPrincipal& aSubjectPrincipal,
   5923                                   ErrorResult& aRv) {
   5924  // Only allow on HTML documents.
   5925  if (!IsHTMLOrXHTML()) {
   5926    aRv.ThrowInvalidStateError(
   5927        "queryCommandEnabled is only supported on HTML documents");
   5928    return false;
   5929  }
   5930  // Otherwise, don't throw exception for compatibility with Chrome.
   5931 
   5932  InternalCommandData commandData = ConvertToInternalCommand(aHTMLCommandName);
   5933  switch (commandData.mCommand) {
   5934    case Command::DoNothing:
   5935      return false;
   5936    case Command::SetDocumentReadOnly:
   5937      SetUseCounter(
   5938          eUseCounter_custom_DocumentQueryCommandSupportedOrEnabledContentReadOnly);
   5939      break;
   5940    case Command::SetDocumentInsertBROnEnterKeyPress:
   5941      SetUseCounter(
   5942          eUseCounter_custom_DocumentQueryCommandSupportedOrEnabledInsertBrOnReturn);
   5943      break;
   5944    default:
   5945      break;
   5946  }
   5947 
   5948  // Report false for restricted commands
   5949  if (commandData.IsCutOrCopyCommand()) {
   5950    // XXX: should we report "disabled" when the target is not editable for cut
   5951    // command?
   5952    return nsContentUtils::IsCutCopyAllowed(this, aSubjectPrincipal);
   5953  }
   5954 
   5955  if (commandData.IsPasteCommand() &&
   5956      !IsExecCommandPasteAllowed(this, aSubjectPrincipal)) {
   5957    return false;
   5958  }
   5959 
   5960  AutoEditorCommandTarget editCommandTarget(*this, commandData);
   5961  if (commandData.IsAvailableOnlyWhenEditable() &&
   5962      !editCommandTarget.IsEditable(this)) {
   5963    return false;
   5964  }
   5965 
   5966  if (editCommandTarget.IsEditor()) {
   5967    return editCommandTarget.IsCommandEnabled();
   5968  }
   5969 
   5970  // get command manager and dispatch command to our window if it's acceptable
   5971  RefPtr<nsCommandManager> commandManager = GetMidasCommandManager();
   5972  if (!commandManager) {
   5973    return false;
   5974  }
   5975 
   5976  nsPIDOMWindowOuter* window = GetWindow();
   5977  if (!window) {
   5978    return false;
   5979  }
   5980 
   5981  return commandManager->IsCommandEnabled(
   5982      nsDependentCString(commandData.mXULCommandName), window);
   5983 }
   5984 
   5985 bool Document::QueryCommandIndeterm(const nsAString& aHTMLCommandName,
   5986                                    ErrorResult& aRv) {
   5987  // Only allow on HTML documents.
   5988  if (!IsHTMLOrXHTML()) {
   5989    aRv.ThrowInvalidStateError(
   5990        "queryCommandIndeterm is only supported on HTML documents");
   5991    return false;
   5992  }
   5993  // Otherwise, don't throw exception for compatibility with Chrome.
   5994 
   5995  InternalCommandData commandData = ConvertToInternalCommand(aHTMLCommandName);
   5996  if (commandData.mCommand == Command::DoNothing) {
   5997    return false;
   5998  }
   5999 
   6000  AutoEditorCommandTarget editCommandTarget(*this, commandData);
   6001  if (commandData.IsAvailableOnlyWhenEditable() &&
   6002      !editCommandTarget.IsEditable(this)) {
   6003    return false;
   6004  }
   6005  RefPtr<nsCommandParams> params = new nsCommandParams();
   6006  if (editCommandTarget.IsEditor()) {
   6007    if (NS_FAILED(editCommandTarget.GetCommandStateParams(*params))) {
   6008      return false;
   6009    }
   6010  } else {
   6011    // get command manager and dispatch command to our window if it's acceptable
   6012    RefPtr<nsCommandManager> commandManager = GetMidasCommandManager();
   6013    if (!commandManager) {
   6014      return false;
   6015    }
   6016 
   6017    nsPIDOMWindowOuter* window = GetWindow();
   6018    if (!window) {
   6019      return false;
   6020    }
   6021 
   6022    if (NS_FAILED(commandManager->GetCommandState(commandData.mXULCommandName,
   6023                                                  window, params))) {
   6024      return false;
   6025    }
   6026  }
   6027 
   6028  // If command does not have a state_mixed value, this call fails and sets
   6029  // retval to false.  This is fine -- we want to return false in that case
   6030  // anyway (bug 738385), so we just don't throw regardless.
   6031  return params->GetBool("state_mixed");
   6032 }
   6033 
   6034 bool Document::QueryCommandState(const nsAString& aHTMLCommandName,
   6035                                 ErrorResult& aRv) {
   6036  // Only allow on HTML documents.
   6037  if (!IsHTMLOrXHTML()) {
   6038    aRv.ThrowInvalidStateError(
   6039        "queryCommandState is only supported on HTML documents");
   6040    return false;
   6041  }
   6042  // Otherwise, don't throw exception for compatibility with Chrome.
   6043 
   6044  InternalCommandData commandData = ConvertToInternalCommand(aHTMLCommandName);
   6045  switch (commandData.mCommand) {
   6046    case Command::DoNothing:
   6047      return false;
   6048    case Command::SetDocumentReadOnly:
   6049      SetUseCounter(
   6050          eUseCounter_custom_DocumentQueryCommandStateOrValueContentReadOnly);
   6051      break;
   6052    case Command::SetDocumentInsertBROnEnterKeyPress:
   6053      SetUseCounter(
   6054          eUseCounter_custom_DocumentQueryCommandStateOrValueInsertBrOnReturn);
   6055      break;
   6056    default:
   6057      break;
   6058  }
   6059 
   6060  if (aHTMLCommandName.LowerCaseEqualsLiteral("usecss")) {
   6061    // Per spec, state is supported for styleWithCSS but not useCSS, so we just
   6062    // return false always.
   6063    return false;
   6064  }
   6065 
   6066  AutoEditorCommandTarget editCommandTarget(*this, commandData);
   6067  if (commandData.IsAvailableOnlyWhenEditable() &&
   6068      !editCommandTarget.IsEditable(this)) {
   6069    return false;
   6070  }
   6071  RefPtr<nsCommandParams> params = new nsCommandParams();
   6072  if (editCommandTarget.IsEditor()) {
   6073    if (NS_FAILED(editCommandTarget.GetCommandStateParams(*params))) {
   6074      return false;
   6075    }
   6076  } else {
   6077    // get command manager and dispatch command to our window if it's acceptable
   6078    RefPtr<nsCommandManager> commandManager = GetMidasCommandManager();
   6079    if (!commandManager) {
   6080      return false;
   6081    }
   6082 
   6083    nsPIDOMWindowOuter* window = GetWindow();
   6084    if (!window) {
   6085      return false;
   6086    }
   6087 
   6088    if (NS_FAILED(commandManager->GetCommandState(commandData.mXULCommandName,
   6089                                                  window, params))) {
   6090      return false;
   6091    }
   6092  }
   6093 
   6094  // handle alignment as a special case (possibly other commands too?)
   6095  // Alignment is special because the external api is individual
   6096  // commands but internally we use cmd_align with different
   6097  // parameters.  When getting the state of this command, we need to
   6098  // return the boolean for this particular alignment rather than the
   6099  // string of 'which alignment is this?'
   6100  switch (commandData.mCommand) {
   6101    case Command::FormatJustifyLeft: {
   6102      nsAutoCString currentValue;
   6103      nsresult rv = params->GetCString("state_attribute", currentValue);
   6104      if (NS_FAILED(rv)) {
   6105        return false;
   6106      }
   6107      return currentValue.EqualsLiteral("left");
   6108    }
   6109    case Command::FormatJustifyRight: {
   6110      nsAutoCString currentValue;
   6111      nsresult rv = params->GetCString("state_attribute", currentValue);
   6112      if (NS_FAILED(rv)) {
   6113        return false;
   6114      }
   6115      return currentValue.EqualsLiteral("right");
   6116    }
   6117    case Command::FormatJustifyCenter: {
   6118      nsAutoCString currentValue;
   6119      nsresult rv = params->GetCString("state_attribute", currentValue);
   6120      if (NS_FAILED(rv)) {
   6121        return false;
   6122      }
   6123      return currentValue.EqualsLiteral("center");
   6124    }
   6125    case Command::FormatJustifyFull: {
   6126      nsAutoCString currentValue;
   6127      nsresult rv = params->GetCString("state_attribute", currentValue);
   6128      if (NS_FAILED(rv)) {
   6129        return false;
   6130      }
   6131      return currentValue.EqualsLiteral("justify");
   6132    }
   6133    default:
   6134      break;
   6135  }
   6136 
   6137  // If command does not have a state_all value, this call fails and sets
   6138  // retval to false.  This is fine -- we want to return false in that case
   6139  // anyway (bug 738385), so we just succeed and return false regardless.
   6140  return params->GetBool("state_all");
   6141 }
   6142 
   6143 bool Document::QueryCommandSupported(const nsAString& aHTMLCommandName,
   6144                                     nsIPrincipal& aSubjectPrincipal,
   6145                                     ErrorResult& aRv) {
   6146  // Only allow on HTML documents.
   6147  if (!IsHTMLOrXHTML()) {
   6148    aRv.ThrowInvalidStateError(
   6149        "queryCommandSupported is only supported on HTML documents");
   6150    return false;
   6151  }
   6152  // Otherwise, don't throw exception for compatibility with Chrome.
   6153 
   6154  InternalCommandData commandData = ConvertToInternalCommand(aHTMLCommandName);
   6155  switch (commandData.mCommand) {
   6156    case Command::DoNothing:
   6157      return false;
   6158    case Command::SetDocumentReadOnly:
   6159      SetUseCounter(
   6160          eUseCounter_custom_DocumentQueryCommandSupportedOrEnabledContentReadOnly);
   6161      break;
   6162    case Command::SetDocumentInsertBROnEnterKeyPress:
   6163      SetUseCounter(
   6164          eUseCounter_custom_DocumentQueryCommandSupportedOrEnabledInsertBrOnReturn);
   6165      break;
   6166    default:
   6167      break;
   6168  }
   6169 
   6170  // Gecko technically supports all the clipboard commands including
   6171  // cut/copy/paste, and depending on the pref "dom.allow_cut_copy", cut and
   6172  // copy may also be disallowed to be called from non-privileged content. For
   6173  // that reason, we report the support status of corresponding command
   6174  // accordingly.
   6175  if (commandData.IsPasteCommand() &&
   6176      !StaticPrefs::dom_execCommand_paste_enabled() &&
   6177      !nsContentUtils::PrincipalHasPermission(aSubjectPrincipal,
   6178                                              nsGkAtoms::clipboardRead)) {
   6179    return false;
   6180  }
   6181  if (commandData.IsCutOrCopyCommand() && !StaticPrefs::dom_allow_cut_copy() &&
   6182      !nsContentUtils::PrincipalHasPermission(aSubjectPrincipal,
   6183                                              nsGkAtoms::clipboardWrite)) {
   6184    return false;
   6185  }
   6186 
   6187  // aHTMLCommandName is supported if it can be converted to a Midas command
   6188  return true;
   6189 }
   6190 
   6191 void Document::QueryCommandValue(const nsAString& aHTMLCommandName,
   6192                                 nsAString& aValue, ErrorResult& aRv) {
   6193  aValue.Truncate();
   6194 
   6195  // Only allow on HTML documents.
   6196  if (!IsHTMLOrXHTML()) {
   6197    aRv.ThrowInvalidStateError(
   6198        "queryCommandValue is only supported on HTML documents");
   6199    return;
   6200  }
   6201  // Otherwise, don't throw exception for compatibility with Chrome.
   6202 
   6203  InternalCommandData commandData = ConvertToInternalCommand(aHTMLCommandName);
   6204  switch (commandData.mCommand) {
   6205    case Command::DoNothing:
   6206      // Return empty string
   6207      return;
   6208    case Command::SetDocumentReadOnly:
   6209      SetUseCounter(
   6210          eUseCounter_custom_DocumentQueryCommandStateOrValueContentReadOnly);
   6211      break;
   6212    case Command::SetDocumentInsertBROnEnterKeyPress:
   6213      SetUseCounter(
   6214          eUseCounter_custom_DocumentQueryCommandStateOrValueInsertBrOnReturn);
   6215      break;
   6216    default:
   6217      break;
   6218  }
   6219 
   6220  AutoEditorCommandTarget editCommandTarget(*this, commandData);
   6221  if (commandData.IsAvailableOnlyWhenEditable() &&
   6222      !editCommandTarget.IsEditable(this)) {
   6223    return;
   6224  }
   6225  RefPtr<nsCommandParams> params = new nsCommandParams();
   6226  if (editCommandTarget.IsEditor()) {
   6227    if (NS_FAILED(params->SetCString("state_attribute", ""_ns))) {
   6228      return;
   6229    }
   6230 
   6231    if (NS_FAILED(editCommandTarget.GetCommandStateParams(*params))) {
   6232      return;
   6233    }
   6234  } else {
   6235    // get command manager and dispatch command to our window if it's acceptable
   6236    RefPtr<nsCommandManager> commandManager = GetMidasCommandManager();
   6237    if (!commandManager) {
   6238      return;
   6239    }
   6240 
   6241    nsCOMPtr<nsPIDOMWindowOuter> window = GetWindow();
   6242    if (!window) {
   6243      return;
   6244    }
   6245 
   6246    if (NS_FAILED(params->SetCString("state_attribute", ""_ns))) {
   6247      return;
   6248    }
   6249 
   6250    if (NS_FAILED(commandManager->GetCommandState(commandData.mXULCommandName,
   6251                                                  window, params))) {
   6252      return;
   6253    }
   6254  }
   6255 
   6256  // If command does not have a state_attribute value, this call fails, and
   6257  // aValue will wind up being the empty string.  This is fine -- we want to
   6258  // return "" in that case anyway (bug 738385), so we just return NS_OK
   6259  // regardless.
   6260  nsAutoCString result;
   6261  params->GetCString("state_attribute", result);
   6262  CopyUTF8toUTF16(result, aValue);
   6263 }
   6264 
   6265 void Document::MaybeEditingStateChanged() {
   6266  if (!mPendingMaybeEditingStateChanged && mMayStartLayout &&
   6267      mUpdateNestLevel == 0 && (mContentEditableCount > 0) != IsEditingOn()) {
   6268    if (nsContentUtils::IsSafeToRunScript()) {
   6269      EditingStateChanged();
   6270    } else if (!mInDestructor) {
   6271      nsContentUtils::AddScriptRunner(
   6272          NewRunnableMethod("Document::MaybeEditingStateChanged", this,
   6273                            &Document::MaybeEditingStateChanged));
   6274    }
   6275  }
   6276 }
   6277 
   6278 void Document::NotifyFetchOrXHRSuccess() {
   6279  if (mShouldNotifyFetchSuccess) {
   6280    nsContentUtils::DispatchEventOnlyToChrome(
   6281        this, this, u"DOMDocFetchSuccess"_ns, CanBubble::eNo, Cancelable::eNo,
   6282        /* DefaultAction */ nullptr);
   6283  }
   6284 }
   6285 
   6286 void Document::SetNotifyFetchSuccess(bool aShouldNotify) {
   6287  mShouldNotifyFetchSuccess = aShouldNotify;
   6288 }
   6289 
   6290 void Document::SetNotifyFormOrPasswordRemoved(bool aShouldNotify) {
   6291  mShouldNotifyFormOrPasswordRemoved = aShouldNotify;
   6292 }
   6293 
   6294 void Document::TearingDownEditor() {
   6295  if (IsEditingOn()) {
   6296    mEditingState = EditingState::eTearingDown;
   6297  }
   6298 }
   6299 
   6300 nsresult Document::TurnEditingOff() {
   6301  NS_ASSERTION(mEditingState != EditingState::eOff, "Editing is already off.");
   6302 
   6303  nsPIDOMWindowOuter* window = GetWindow();
   6304  if (!window) {
   6305    return NS_ERROR_FAILURE;
   6306  }
   6307 
   6308  nsIDocShell* docshell = GetDocShell();
   6309  if (!docshell || docshell->IsBeingDestroyed()) {
   6310    return NS_ERROR_FAILURE;
   6311  }
   6312 
   6313  nsCOMPtr<nsIEditingSession> editSession;
   6314  MOZ_TRY(docshell->GetEditingSession(getter_AddRefs(editSession)));
   6315 
   6316  // turn editing off
   6317  MOZ_TRY(editSession->TearDownEditorOnWindow(window));
   6318 
   6319  mEditingState = EditingState::eOff;
   6320 
   6321  // Editor resets selection since it is being destroyed.  But if focus is
   6322  // still into editable control, we have to initialize selection again.
   6323  if (RefPtr<TextControlElement> textControlElement =
   6324          TextControlElement::FromNodeOrNull(
   6325              nsFocusManager::GetFocusedElementStatic())) {
   6326    if (RefPtr<TextEditor> textEditor = textControlElement->GetTextEditor()) {
   6327      textEditor->ReinitializeSelection(*textControlElement);
   6328    }
   6329  }
   6330 
   6331  return NS_OK;
   6332 }
   6333 
   6334 HTMLEditor* Document::GetHTMLEditor() const {
   6335  nsPIDOMWindowOuter* window = GetWindow();
   6336  if (!window) {
   6337    return nullptr;
   6338  }
   6339 
   6340  nsIDocShell* docshell = window->GetDocShell();
   6341  if (!docshell) {
   6342    return nullptr;
   6343  }
   6344 
   6345  return docshell->GetHTMLEditor();
   6346 }
   6347 
   6348 nsresult Document::EditingStateChanged() {
   6349  if (mRemovedFromDocShell) {
   6350    return NS_OK;
   6351  }
   6352 
   6353  if (mEditingState == EditingState::eSettingUp ||
   6354      mEditingState == EditingState::eTearingDown) {
   6355    // XXX We shouldn't recurse
   6356    return NS_OK;
   6357  }
   6358 
   6359  const bool designMode = IsInDesignMode();
   6360  const EditingState newState =
   6361      designMode ? EditingState::eDesignMode
   6362                 : (mContentEditableCount > 0 ? EditingState::eContentEditable
   6363                                              : EditingState::eOff);
   6364  if (mEditingState == newState) {
   6365    // No changes in editing mode.
   6366    return NS_OK;
   6367  }
   6368 
   6369  const bool thisDocumentHasFocus = ThisDocumentHasFocus();
   6370  if (newState == EditingState::eOff) {
   6371    // Editing is being turned off.
   6372    nsAutoScriptBlocker scriptBlocker;
   6373    RefPtr<HTMLEditor> htmlEditor = GetHTMLEditor();
   6374    nsresult rv = TurnEditingOff();
   6375    // If this document has focus and the editing state of this document
   6376    // becomes "off", it means that HTMLEditor won't handle any inputs nor
   6377    // modify the DOM tree.  However, HTMLEditor may not receive `blur`
   6378    // event for this state change since this may occur without focus change.
   6379    // Therefore, let's notify HTMLEditor of this editing state change.
   6380    // Note that even if focusedElement is an editable text control element,
   6381    // it becomes not editable from HTMLEditor point of view since text
   6382    // control elements are manged by TextEditor.
   6383    RefPtr<Element> focusedElement = nsFocusManager::GetFocusedElementStatic();
   6384    DebugOnly<nsresult> rvIgnored =
   6385        HTMLEditor::FocusedElementOrDocumentBecomesNotEditable(
   6386            htmlEditor, *this, focusedElement);
   6387    NS_WARNING_ASSERTION(
   6388        NS_SUCCEEDED(rvIgnored),
   6389        "HTMLEditor::FocusedElementOrDocumentBecomesNotEditable() failed, but "
   6390        "ignored");
   6391    return rv;
   6392  }
   6393 
   6394  const EditingState oldState = mEditingState;
   6395  MOZ_ASSERT(newState == EditingState::eDesignMode ||
   6396             newState == EditingState::eContentEditable);
   6397  MOZ_ASSERT_IF(newState == EditingState::eDesignMode,
   6398                oldState == EditingState::eContentEditable ||
   6399                    oldState == EditingState::eOff);
   6400  MOZ_ASSERT_IF(
   6401      newState == EditingState::eContentEditable,
   6402      oldState == EditingState::eDesignMode || oldState == EditingState::eOff);
   6403 
   6404  // Flush out style changes on our _parent_ document, if any, so that
   6405  // our check for a presshell won't get stale information.
   6406  if (mParentDocument) {
   6407    mParentDocument->FlushPendingNotifications(FlushType::Style);
   6408  }
   6409 
   6410  // get editing session, make sure this is a strong reference so the
   6411  // window can't get deleted during the rest of this call.
   6412  const nsCOMPtr<nsPIDOMWindowOuter> window = GetWindow();
   6413  if (!window) {
   6414    return NS_ERROR_FAILURE;
   6415  }
   6416 
   6417  nsIDocShell* docshell = GetDocShell();
   6418  if (!docshell || docshell->IsBeingDestroyed()) {
   6419    return NS_ERROR_FAILURE;
   6420  }
   6421 
   6422  nsCOMPtr<nsIEditingSession> editSession;
   6423  MOZ_TRY(docshell->GetEditingSession(getter_AddRefs(editSession)));
   6424 
   6425  if (RefPtr<HTMLEditor> htmlEditor = docshell->GetHTMLEditor()) {
   6426    // We might already have an editor if it was set up for mail, let's see
   6427    // if this is actually the case.
   6428    uint32_t flags = 0;
   6429    htmlEditor->GetFlags(&flags);
   6430    if (flags & nsIEditor::eEditorMailMask) {
   6431      // We already have a mail editor, then we should not attempt to create
   6432      // another one.
   6433      return NS_OK;
   6434    }
   6435  }
   6436 
   6437  RefPtr<PresShell> presShell = GetPresShell();
   6438  if (!presShell) {
   6439    // We should not make the window editable or setup its editor.
   6440    // It's probably style=display:none.
   6441    return NS_OK;
   6442  }
   6443 
   6444  bool makeWindowEditable = mEditingState == EditingState::eOff;
   6445  bool spellRecheckAll = false;
   6446  bool putOffToRemoveScriptBlockerUntilModifyingEditingState = false;
   6447 
   6448  RefPtr<HTMLEditor> htmlEditor;
   6449  {
   6450    nsAutoEditingState push(this, EditingState::eSettingUp);
   6451 
   6452    // If we're entering the design mode from non-editable state, put the
   6453    // selection at the beginning of the document for compatibility reasons.
   6454    bool collapseSelectionAtBeginningOfDocument =
   6455        designMode && oldState == EditingState::eOff;
   6456    // However, mEditingState may be eOff even if there is some
   6457    // `contenteditable` area and selection has been initialized for it because
   6458    // mEditingState for `contenteditable` may have been scheduled to modify
   6459    // when safe.  In such case, we should not reinitialize selection.
   6460    if (collapseSelectionAtBeginningOfDocument && mContentEditableCount) {
   6461      Selection* selection =
   6462          presShell->GetSelection(nsISelectionController::SELECTION_NORMAL);
   6463      NS_WARNING_ASSERTION(selection, "Why don't we have Selection?");
   6464      if (selection && selection->RangeCount()) {
   6465        // Perhaps, we don't need to check whether the selection is in
   6466        // an editing host or not because all contents will be editable
   6467        // in designMode. (And we don't want to make this code so complicated
   6468        // because of legacy API.)
   6469        collapseSelectionAtBeginningOfDocument = false;
   6470      }
   6471    }
   6472 
   6473    MOZ_ASSERT(mStyleSetFilled);
   6474 
   6475    if (designMode) {
   6476      // designMode is being turned on (overrides contentEditable).
   6477      spellRecheckAll = oldState == EditingState::eContentEditable;
   6478    }
   6479 
   6480    // Adjust focused element with new style but blur event shouldn't be fired
   6481    // until mEditingState is modified with newState.
   6482    nsAutoScriptBlocker scriptBlocker;
   6483    if (designMode) {
   6484      nsCOMPtr<nsPIDOMWindowOuter> focusedWindow;
   6485      nsIContent* focusedContent = nsFocusManager::GetFocusedDescendant(
   6486          window, nsFocusManager::eOnlyCurrentWindow,
   6487          getter_AddRefs(focusedWindow));
   6488      if (focusedContent) {
   6489        nsIFrame* focusedFrame = focusedContent->GetPrimaryFrame();
   6490        bool clearFocus = focusedFrame
   6491                              ? !focusedFrame->IsFocusable()
   6492                              : !focusedContent->IsFocusableWithoutStyle();
   6493        if (clearFocus) {
   6494          if (RefPtr<nsFocusManager> fm = nsFocusManager::GetFocusManager()) {
   6495            fm->ClearFocus(window);
   6496            // If we need to dispatch blur event, we should put off after
   6497            // modifying mEditingState since blur event handler may change
   6498            // designMode state again.
   6499            putOffToRemoveScriptBlockerUntilModifyingEditingState = true;
   6500          }
   6501        }
   6502      }
   6503    }
   6504 
   6505    if (makeWindowEditable) {
   6506      // Editing is being turned on (through designMode or contentEditable)
   6507      // Turn on editor.
   6508      // XXX This can cause flushing which can change the editing state, so make
   6509      //     sure to avoid recursing.
   6510      MOZ_TRY(
   6511          editSession->MakeWindowEditable(window, "html", false, false, true));
   6512    }
   6513 
   6514    // XXX Need to call TearDownEditorOnWindow for all failures.
   6515    htmlEditor = docshell->GetHTMLEditor();
   6516    if (!htmlEditor) {
   6517      // Return NS_OK even though we've failed to create an editor here.  This
   6518      // is so that the setter of designMode on non-HTML documents does not
   6519      // fail.
   6520      // This is OK to do because in nsEditingSession::SetupEditorOnWindow() we
   6521      // would detect that we can't support the mimetype if appropriate and
   6522      // would fall onto the eEditorErrorCantEditMimeType path.
   6523      return NS_OK;
   6524    }
   6525 
   6526    if (collapseSelectionAtBeginningOfDocument) {
   6527      htmlEditor->BeginningOfDocument();
   6528    }
   6529 
   6530    if (putOffToRemoveScriptBlockerUntilModifyingEditingState) {
   6531      nsContentUtils::AddScriptBlocker();
   6532    }
   6533  }
   6534 
   6535  mEditingState = newState;
   6536  if (putOffToRemoveScriptBlockerUntilModifyingEditingState) {
   6537    nsContentUtils::RemoveScriptBlocker();
   6538    // If mEditingState is overwritten by another call and already disabled
   6539    // the editing, we shouldn't keep making window editable.
   6540    if (mEditingState == EditingState::eOff) {
   6541      return NS_OK;
   6542    }
   6543  }
   6544 
   6545  if (makeWindowEditable) {
   6546    // TODO: We should do this earlier in this method.
   6547    //       Previously, we called `ExecCommand` with `insertBrOnReturn` command
   6548    //       whose argument is false here.  Then, if it returns error, we
   6549    //       stopped making it editable.  However, after bug 1697078 fixed,
   6550    //       `ExecCommand` returns error only when the document is not XHTML's
   6551    //       nor HTML's.  Therefore, we use same error handling for now.
   6552    if (MOZ_UNLIKELY(NS_WARN_IF(!IsHTMLOrXHTML()))) {
   6553      // Editor setup failed. Editing is not on after all.
   6554      // XXX Should we reset the editable flag on nodes?
   6555      editSession->TearDownEditorOnWindow(window);
   6556      mEditingState = EditingState::eOff;
   6557      return NS_ERROR_DOM_INVALID_STATE_ERR;
   6558    }
   6559    // Set the editor to not insert <br> elements on return when in <p> elements
   6560    // by default.
   6561    htmlEditor->SetReturnInParagraphCreatesNewParagraph(true);
   6562  }
   6563 
   6564  // Resync the editor's spellcheck state.
   6565  if (spellRecheckAll) {
   6566    nsCOMPtr<nsISelectionController> selectionController =
   6567        htmlEditor->GetSelectionController();
   6568    if (NS_WARN_IF(!selectionController)) {
   6569      return NS_ERROR_FAILURE;
   6570    }
   6571 
   6572    RefPtr<Selection> spellCheckSelection = selectionController->GetSelection(
   6573        nsISelectionController::SELECTION_SPELLCHECK);
   6574    if (spellCheckSelection) {
   6575      spellCheckSelection->RemoveAllRanges(IgnoreErrors());
   6576    }
   6577  }
   6578  htmlEditor->SyncRealTimeSpell();
   6579 
   6580  MaybeDispatchCheckKeyPressEventModelEvent();
   6581 
   6582  // If this document keeps having focus, the HTMLEditor may not receive `focus`
   6583  // event for this editing state change since this may occur without a focus
   6584  // change.  Therefore, let's notify HTMLEditor of this editing state change.
   6585  if (thisDocumentHasFocus && ThisDocumentHasFocus()) {
   6586    RefPtr<Element> focusedElement = nsFocusManager::GetFocusedElementStatic();
   6587    MOZ_ASSERT_IF(focusedElement, focusedElement->GetComposedDoc() == this);
   6588    if ((focusedElement && focusedElement->IsEditable() &&
   6589         (!focusedElement->IsTextControlElement() ||
   6590          !TextControlElement::FromNode(focusedElement)
   6591               ->IsSingleLineTextControlOrTextArea())) ||
   6592        (!focusedElement && IsInDesignMode())) {
   6593      DebugOnly<nsresult> rvIgnored =
   6594          htmlEditor->FocusedElementOrDocumentBecomesEditable(*this,
   6595                                                              focusedElement);
   6596      NS_WARNING_ASSERTION(
   6597          NS_SUCCEEDED(rvIgnored),
   6598          "HTMLEditor::FocusedElementOrDocumentBecomesEditable() failed, but "
   6599          "ignored");
   6600    } else if (htmlEditor->HasFocus()) {
   6601      DebugOnly<nsresult> rvIgnored =
   6602          HTMLEditor::FocusedElementOrDocumentBecomesNotEditable(
   6603              htmlEditor, *this, focusedElement);
   6604      NS_WARNING_ASSERTION(
   6605          NS_SUCCEEDED(rvIgnored),
   6606          "HTMLEditor::FocusedElementOrDocumentBecomesNotEditable() failed, "
   6607          "but ignored");
   6608    }
   6609  }
   6610 
   6611  return NS_OK;
   6612 }
   6613 
   6614 // Helper class, used below in ChangeContentEditableCount().
   6615 class DeferredContentEditableCountChangeEvent : public Runnable {
   6616 public:
   6617  DeferredContentEditableCountChangeEvent(Document* aDoc, Element* aElement)
   6618      : mozilla::Runnable("DeferredContentEditableCountChangeEvent"),
   6619        mDoc(aDoc),
   6620        mElement(aElement) {}
   6621 
   6622  MOZ_CAN_RUN_SCRIPT_BOUNDARY NS_IMETHOD Run() override {
   6623    if (mElement && mElement->OwnerDoc() == mDoc) {
   6624      RefPtr<Document> doc = std::move(mDoc);
   6625      RefPtr<Element> element = std::move(mElement);
   6626      doc->DeferredContentEditableCountChange(element);
   6627    }
   6628    return NS_OK;
   6629  }
   6630 
   6631 private:
   6632  RefPtr<Document> mDoc;
   6633  RefPtr<Element> mElement;
   6634 };
   6635 
   6636 void Document::ChangeContentEditableCount(Element* aElement, int32_t aChange) {
   6637  NS_ASSERTION(int32_t(mContentEditableCount) + aChange >= 0,
   6638               "Trying to decrement too much.");
   6639 
   6640  mContentEditableCount += aChange;
   6641 
   6642  if (aElement) {
   6643    nsContentUtils::AddScriptRunner(
   6644        new DeferredContentEditableCountChangeEvent(this, aElement));
   6645  }
   6646 }
   6647 
   6648 void Document::DeferredContentEditableCountChange(Element* aElement) {
   6649  const bool elementHasFocus =
   6650      aElement && nsFocusManager::GetFocusedElementStatic() == aElement;
   6651  if (elementHasFocus) {
   6652    MOZ_ASSERT(nsContentUtils::IsSafeToRunScript());
   6653    // When contenteditable of aElement is changed and HTMLEditor works with it
   6654    // or needs to start working with it, HTMLEditor may not receive `focus`
   6655    // event nor `blur` event because this may occur without a focus change.
   6656    // Therefore, we need to notify HTMLEditor of this contenteditable attribute
   6657    // change.
   6658    RefPtr<HTMLEditor> htmlEditor = GetHTMLEditor();
   6659    if (aElement->HasFlag(NODE_IS_EDITABLE)) {
   6660      if (htmlEditor) {
   6661        DebugOnly<nsresult> rvIgnored =
   6662            htmlEditor->FocusedElementOrDocumentBecomesEditable(*this,
   6663                                                                aElement);
   6664        NS_WARNING_ASSERTION(
   6665            NS_SUCCEEDED(rvIgnored),
   6666            "HTMLEditor::FocusedElementOrDocumentBecomesEditable() failed, but "
   6667            "ignored");
   6668      }
   6669    } else {
   6670      DebugOnly<nsresult> rvIgnored =
   6671          HTMLEditor::FocusedElementOrDocumentBecomesNotEditable(
   6672              htmlEditor, *this, aElement);
   6673      NS_WARNING_ASSERTION(
   6674          NS_SUCCEEDED(rvIgnored),
   6675          "HTMLEditor::FocusedElementOrDocumentBecomesNotEditable() failed, "
   6676          "but ignored");
   6677    }
   6678  }
   6679 
   6680  if (mParser ||
   6681      (mUpdateNestLevel > 0 && (mContentEditableCount > 0) != IsEditingOn())) {
   6682    return;
   6683  }
   6684 
   6685  EditingState oldState = mEditingState;
   6686 
   6687  nsresult rv = EditingStateChanged();
   6688  NS_ENSURE_SUCCESS_VOID(rv);
   6689 
   6690  if (oldState == mEditingState &&
   6691      mEditingState == EditingState::eContentEditable) {
   6692    // We just changed the contentEditable state of a node, we need to reset
   6693    // the spellchecking state of that node.
   6694    if (aElement) {
   6695      if (RefPtr<HTMLEditor> htmlEditor = GetHTMLEditor()) {
   6696        nsCOMPtr<nsIInlineSpellChecker> spellChecker;
   6697        DebugOnly<nsresult> rvIgnored = htmlEditor->GetInlineSpellChecker(
   6698            false, getter_AddRefs(spellChecker));
   6699        NS_WARNING_ASSERTION(
   6700            NS_SUCCEEDED(rvIgnored),
   6701            "EditorBase::GetInlineSpellChecker() failed, but ignored");
   6702 
   6703        if (spellChecker &&
   6704            aElement->InclusiveDescendantMayNeedSpellchecking(htmlEditor)) {
   6705          RefPtr<nsRange> range = nsRange::Create(aElement);
   6706          IgnoredErrorResult res;
   6707          range->SelectNodeContents(*aElement, res);
   6708          if (res.Failed()) {
   6709            // The node might be detached from the document at this point,
   6710            // which would cause this call to fail.  In this case, we can
   6711            // safely ignore the contenteditable count change.
   6712            return;
   6713          }
   6714 
   6715          rv = spellChecker->SpellCheckRange(range);
   6716          NS_ENSURE_SUCCESS_VOID(rv);
   6717        }
   6718      }
   6719    }
   6720  }
   6721 
   6722  // aElement causes creating new HTMLEditor and the element had and keep
   6723  // having focus, the HTMLEditor won't receive `focus` event.  Therefore, we
   6724  // need to notify HTMLEditor of it becomes editable.
   6725  if (elementHasFocus && aElement->HasFlag(NODE_IS_EDITABLE) &&
   6726      nsFocusManager::GetFocusedElementStatic() == aElement) {
   6727    if (RefPtr<HTMLEditor> htmlEditor = GetHTMLEditor()) {
   6728      DebugOnly<nsresult> rvIgnored =
   6729          htmlEditor->FocusedElementOrDocumentBecomesEditable(*this, aElement);
   6730      NS_WARNING_ASSERTION(
   6731          NS_SUCCEEDED(rvIgnored),
   6732          "HTMLEditor::FocusedElementOrDocumentBecomesEditable() failed, but "
   6733          "ignored");
   6734    }
   6735  }
   6736 }
   6737 
   6738 void Document::MaybeDispatchCheckKeyPressEventModelEvent() {
   6739  // Currently, we need to check only when we're becoming editable for
   6740  // contenteditable.
   6741  if (mEditingState != EditingState::eContentEditable) {
   6742    return;
   6743  }
   6744 
   6745  if (mHasBeenEditable) {
   6746    return;
   6747  }
   6748  mHasBeenEditable = true;
   6749 
   6750  // Dispatch "CheckKeyPressEventModel" event.  That is handled only by
   6751  // KeyPressEventModelCheckerChild.  Then, it calls SetKeyPressEventModel()
   6752  // with proper keypress event for the active web app.
   6753  WidgetEvent checkEvent(true, eUnidentifiedEvent);
   6754  checkEvent.mSpecifiedEventType = nsGkAtoms::onCheckKeyPressEventModel;
   6755  checkEvent.mFlags.mCancelable = false;
   6756  checkEvent.mFlags.mBubbles = false;
   6757  checkEvent.mFlags.mOnlySystemGroupDispatch = true;
   6758  // Post the event rather than dispatching it synchronously because we need
   6759  // a call of SetKeyPressEventModel() before first key input.  Therefore, we
   6760  // can avoid paying unnecessary runtime cost for most web apps.
   6761  (new AsyncEventDispatcher(this, checkEvent))->PostDOMEvent();
   6762 }
   6763 
   6764 void Document::SetKeyPressEventModel(uint16_t aKeyPressEventModel) {
   6765  PresShell* presShell = GetPresShell();
   6766  if (!presShell) {
   6767    return;
   6768  }
   6769  presShell->SetKeyPressEventModel(aKeyPressEventModel);
   6770 }
   6771 
   6772 TimeStamp Document::LastFocusTime() const { return mLastFocusTime; }
   6773 
   6774 void Document::SetLastFocusTime(const TimeStamp& aFocusTime) {
   6775  MOZ_DIAGNOSTIC_ASSERT(!aFocusTime.IsNull());
   6776  MOZ_DIAGNOSTIC_ASSERT(mLastFocusTime.IsNull() ||
   6777                        aFocusTime >= mLastFocusTime);
   6778  mLastFocusTime = aFocusTime;
   6779 }
   6780 
   6781 void Document::GetReferrer(nsACString& aReferrer) const {
   6782  aReferrer.Truncate();
   6783  if (!mReferrerInfo) {
   6784    return;
   6785  }
   6786 
   6787  nsCOMPtr<nsIURI> referrer = mReferrerInfo->GetComputedReferrer();
   6788  if (!referrer) {
   6789    return;
   6790  }
   6791 
   6792  URLDecorationStripper::StripTrackingIdentifiers(referrer, aReferrer);
   6793 }
   6794 
   6795 void Document::GetCookie(nsAString& aCookie, ErrorResult& aRv) {
   6796  aCookie.Truncate();  // clear current cookie in case service fails;
   6797                       // no cookie isn't an error condition.
   6798 
   6799  nsCOMPtr<nsIPrincipal> cookiePrincipal;
   6800  nsCOMPtr<nsIPrincipal> cookiePartitionedPrincipal;
   6801 
   6802  CookieCommons::SecurityChecksResult checkResult =
   6803      CookieCommons::CheckGlobalAndRetrieveCookiePrincipals(
   6804          this, getter_AddRefs(cookiePrincipal),
   6805          getter_AddRefs(cookiePartitionedPrincipal));
   6806  switch (checkResult) {
   6807    case CookieCommons::SecurityChecksResult::eSandboxedError:
   6808      aRv.ThrowSecurityError(
   6809          "Forbidden in a sandboxed document without the 'allow-same-origin' "
   6810          "flag.");
   6811      return;
   6812 
   6813    case CookieCommons::SecurityChecksResult::eSecurityError:
   6814      [[fallthrough]];
   6815 
   6816    case CookieCommons::SecurityChecksResult::eDoNotContinue:
   6817      return;
   6818 
   6819    case CookieCommons::SecurityChecksResult::eContinue:
   6820      break;
   6821  }
   6822 
   6823  bool thirdParty = true;
   6824  nsPIDOMWindowInner* innerWindow = GetInnerWindow();
   6825  // in gtests we don't have a window, let's consider those requests as 3rd
   6826  // party.
   6827  if (innerWindow) {
   6828    ThirdPartyUtil* thirdPartyUtil = ThirdPartyUtil::GetInstance();
   6829 
   6830    if (thirdPartyUtil) {
   6831      (void)thirdPartyUtil->IsThirdPartyWindow(innerWindow->GetOuterWindow(),
   6832                                               nullptr, &thirdParty);
   6833    }
   6834  }
   6835 
   6836  nsTArray<nsCOMPtr<nsIPrincipal>> principals;
   6837 
   6838  MOZ_ASSERT(cookiePrincipal);
   6839  principals.AppendElement(cookiePrincipal);
   6840 
   6841  if (cookiePartitionedPrincipal) {
   6842    principals.AppendElement(cookiePartitionedPrincipal);
   6843  }
   6844 
   6845  nsTArray<RefPtr<Cookie>> cookieList;
   6846  bool stale = false;
   6847  int64_t currentTimeInUsec = PR_Now();
   6848  int64_t currentTimeInMSec = currentTimeInUsec / PR_USEC_PER_MSEC;
   6849 
   6850  // not having a cookie service isn't an error
   6851  nsCOMPtr<nsICookieService> service =
   6852      do_GetService(NS_COOKIESERVICE_CONTRACTID);
   6853  if (!service) {
   6854    return;
   6855  }
   6856 
   6857  nsCOMPtr<nsILoadInfo> loadInfo =
   6858      GetChannel() ? GetChannel()->LoadInfo() : nullptr;
   6859  bool on3pcbException = loadInfo && loadInfo->GetIsOn3PCBExceptionList();
   6860 
   6861  for (auto& principal : principals) {
   6862    nsAutoCString baseDomain;
   6863    nsresult rv = CookieCommons::GetBaseDomain(principal, baseDomain);
   6864    if (NS_WARN_IF(NS_FAILED(rv))) {
   6865      return;
   6866    }
   6867 
   6868    nsAutoCString hostFromURI;
   6869    rv = nsContentUtils::GetHostOrIPv6WithBrackets(principal, hostFromURI);
   6870    if (NS_WARN_IF(NS_FAILED(rv))) {
   6871      return;
   6872    }
   6873 
   6874    nsAutoCString pathFromURI;
   6875    rv = principal->GetFilePath(pathFromURI);
   6876    if (NS_WARN_IF(NS_FAILED(rv))) {
   6877      return;
   6878    }
   6879 
   6880    nsTArray<RefPtr<Cookie>> cookies;
   6881    service->GetCookiesFromHost(baseDomain, principal->OriginAttributesRef(),
   6882                                cookies);
   6883    if (cookies.IsEmpty()) {
   6884      continue;
   6885    }
   6886 
   6887    // check if the nsIPrincipal is using an https secure protocol.
   6888    // if it isn't, then we can't send a secure cookie over the connection.
   6889    bool potentiallyTrustworthy =
   6890        principal->GetIsOriginPotentiallyTrustworthy();
   6891 
   6892    // iterate the cookies!
   6893    for (Cookie* cookie : cookies) {
   6894      // check the host, since the base domain lookup is conservative.
   6895      if (!CookieCommons::DomainMatches(cookie, hostFromURI)) {
   6896        continue;
   6897      }
   6898 
   6899      // if the cookie is httpOnly and it's not going directly to the HTTP
   6900      // connection, don't send it
   6901      if (cookie->IsHttpOnly()) {
   6902        continue;
   6903      }
   6904 
   6905      nsCOMPtr<nsIURI> cookieURI = cookiePrincipal->GetURI();
   6906 
   6907      if (thirdParty &&
   6908          !CookieCommons::ShouldIncludeCrossSiteCookie(
   6909              cookie, cookieURI, CookieJarSettings()->GetPartitionForeign(),
   6910              IsInPrivateBrowsing(), UsingStorageAccess(), on3pcbException)) {
   6911        continue;
   6912      }
   6913 
   6914      // if the cookie is secure and the host scheme isn't, we can't send it
   6915      if (cookie->IsSecure() && !potentiallyTrustworthy) {
   6916        continue;
   6917      }
   6918 
   6919      // if the nsIURI path doesn't match the cookie path, don't send it back
   6920      if (!CookieCommons::PathMatches(cookie, pathFromURI)) {
   6921        continue;
   6922      }
   6923 
   6924      // check if the cookie has expired
   6925      if (cookie->ExpiryInMSec() <= currentTimeInMSec) {
   6926        continue;
   6927      }
   6928 
   6929      // all checks passed - add to list and check if lastAccessed stamp needs
   6930      // updating
   6931      cookieList.AppendElement(cookie);
   6932      if (cookie->IsStale()) {
   6933        stale = true;
   6934      }
   6935    }
   6936  }
   6937 
   6938  if (cookieList.IsEmpty()) {
   6939    return;
   6940  }
   6941 
   6942  // update lastAccessed timestamps. we only do this if the timestamp is stale
   6943  // by a certain amount, to avoid thrashing the db during pageload.
   6944  if (stale) {
   6945    service->StaleCookies(cookieList, currentTimeInUsec);
   6946  }
   6947 
   6948  // return cookies in order of path length; longest to shortest.
   6949  // this is required per RFC2109.  if cookies match in length,
   6950  // then sort by creation time (see bug 236772).
   6951  cookieList.Sort(CompareCookiesForSending());
   6952 
   6953  nsAutoCString cookieString;
   6954  CookieCommons::ComposeCookieString(cookieList, cookieString);
   6955 
   6956  // CopyUTF8toUTF16 doesn't handle error
   6957  // because it assumes that the input is valid.
   6958  UTF_8_ENCODING->DecodeWithoutBOMHandling(cookieString, aCookie);
   6959 }
   6960 
   6961 void Document::SetCookie(const nsAString& aCookieString, ErrorResult& aRv) {
   6962  nsCOMPtr<nsIPrincipal> cookiePrincipal;
   6963 
   6964  CookieCommons::SecurityChecksResult checkResult =
   6965      CookieCommons::CheckGlobalAndRetrieveCookiePrincipals(
   6966          this, getter_AddRefs(cookiePrincipal), nullptr);
   6967  switch (checkResult) {
   6968    case CookieCommons::SecurityChecksResult::eSandboxedError:
   6969      aRv.ThrowSecurityError(
   6970          "Forbidden in a sandboxed document without the 'allow-same-origin' "
   6971          "flag.");
   6972      return;
   6973 
   6974    case CookieCommons::SecurityChecksResult::eSecurityError:
   6975      [[fallthrough]];
   6976 
   6977    case CookieCommons::SecurityChecksResult::eDoNotContinue:
   6978      return;
   6979 
   6980    case CookieCommons::SecurityChecksResult::eContinue:
   6981      break;
   6982  }
   6983 
   6984  if (!mDocumentURI) {
   6985    return;
   6986  }
   6987 
   6988  // not having a cookie service isn't an error
   6989  nsCOMPtr<nsICookieService> service =
   6990      do_GetService(NS_COOKIESERVICE_CONTRACTID);
   6991  if (!service) {
   6992    return;
   6993  }
   6994 
   6995  NS_ConvertUTF16toUTF8 cookieString(aCookieString);
   6996 
   6997  nsCOMPtr<nsIURI> documentURI;
   6998  nsAutoCString baseDomain;
   6999  OriginAttributes attrs;
   7000 
   7001  int64_t currentTimeInUsec = PR_Now();
   7002 
   7003  auto* basePrincipal = BasePrincipal::Cast(NodePrincipal());
   7004  basePrincipal->GetURI(getter_AddRefs(documentURI));
   7005  if (NS_WARN_IF(!documentURI)) {
   7006    // Document's principal is not a content or null (may be system), so
   7007    // can't set cookies
   7008    return;
   7009  }
   7010 
   7011  // Console report takes care of the correct reporting at the exit of this
   7012  // method.
   7013  RefPtr<ConsoleReportCollector> crc = new ConsoleReportCollector();
   7014  auto scopeExit = MakeScopeExit([&] { crc->FlushConsoleReports(this); });
   7015 
   7016  CookieParser cookieParser(crc, documentURI);
   7017 
   7018  ThirdPartyUtil* thirdPartyUtil = ThirdPartyUtil::GetInstance();
   7019  if (!thirdPartyUtil) {
   7020    return;
   7021  }
   7022 
   7023  nsCOMPtr<nsIEffectiveTLDService> tldService =
   7024      mozilla::components::EffectiveTLD::Service();
   7025  if (!tldService) {
   7026    return;
   7027  }
   7028 
   7029  RefPtr<Cookie> cookie = CookieCommons::CreateCookieFromDocument(
   7030      cookieParser, this, cookieString, currentTimeInUsec, tldService,
   7031      thirdPartyUtil, baseDomain, attrs);
   7032  if (!cookie) {
   7033    return;
   7034  }
   7035 
   7036  bool thirdParty = true;
   7037  nsPIDOMWindowInner* innerWindow = GetInnerWindow();
   7038  // in gtests we don't have a window, let's consider those requests as 3rd
   7039  // party.
   7040  if (innerWindow) {
   7041    (void)thirdPartyUtil->IsThirdPartyWindow(innerWindow->GetOuterWindow(),
   7042                                             nullptr, &thirdParty);
   7043  }
   7044 
   7045  nsCOMPtr<nsILoadInfo> loadInfo =
   7046      GetChannel() ? GetChannel()->LoadInfo() : nullptr;
   7047  bool on3pcbException = loadInfo && loadInfo->GetIsOn3PCBExceptionList();
   7048 
   7049  if (thirdParty &&
   7050      !CookieCommons::ShouldIncludeCrossSiteCookie(
   7051          cookie, documentURI, CookieJarSettings()->GetPartitionForeign(),
   7052          IsInPrivateBrowsing(), UsingStorageAccess(), on3pcbException)) {
   7053    return;
   7054  }
   7055 
   7056  // add the cookie to the list. AddCookieFromDocument() takes care of logging.
   7057  service->AddCookieFromDocument(cookieParser, baseDomain, attrs, *cookie,
   7058                                 currentTimeInUsec, documentURI, thirdParty,
   7059                                 this);
   7060 
   7061  nsCOMPtr<nsIObserverService> observerService =
   7062      mozilla::services::GetObserverService();
   7063  if (observerService) {
   7064    observerService->NotifyObservers(ToSupports(this), "document-set-cookie",
   7065                                     nsString(aCookieString).get());
   7066  }
   7067 }
   7068 
   7069 ReferrerPolicy Document::GetReferrerPolicy() const {
   7070  return mReferrerInfo ? mReferrerInfo->ReferrerPolicy()
   7071                       : ReferrerPolicy::_empty;
   7072 }
   7073 
   7074 void Document::GetAlinkColor(nsAString& aAlinkColor) {
   7075  aAlinkColor.Truncate();
   7076 
   7077  HTMLBodyElement* body = GetBodyElement();
   7078  if (body) {
   7079    body->GetALink(aAlinkColor);
   7080  }
   7081 }
   7082 
   7083 void Document::SetAlinkColor(const nsAString& aAlinkColor) {
   7084  HTMLBodyElement* body = GetBodyElement();
   7085  if (body) {
   7086    body->SetALink(aAlinkColor);
   7087  }
   7088 }
   7089 
   7090 void Document::GetLinkColor(nsAString& aLinkColor) {
   7091  aLinkColor.Truncate();
   7092 
   7093  HTMLBodyElement* body = GetBodyElement();
   7094  if (body) {
   7095    body->GetLink(aLinkColor);
   7096  }
   7097 }
   7098 
   7099 void Document::SetLinkColor(const nsAString& aLinkColor) {
   7100  HTMLBodyElement* body = GetBodyElement();
   7101  if (body) {
   7102    body->SetLink(aLinkColor);
   7103  }
   7104 }
   7105 
   7106 void Document::GetVlinkColor(nsAString& aVlinkColor) {
   7107  aVlinkColor.Truncate();
   7108 
   7109  HTMLBodyElement* body = GetBodyElement();
   7110  if (body) {
   7111    body->GetVLink(aVlinkColor);
   7112  }
   7113 }
   7114 
   7115 void Document::SetVlinkColor(const nsAString& aVlinkColor) {
   7116  HTMLBodyElement* body = GetBodyElement();
   7117  if (body) {
   7118    body->SetVLink(aVlinkColor);
   7119  }
   7120 }
   7121 
   7122 void Document::GetBgColor(nsAString& aBgColor) {
   7123  aBgColor.Truncate();
   7124 
   7125  HTMLBodyElement* body = GetBodyElement();
   7126  if (body) {
   7127    body->GetBgColor(aBgColor);
   7128  }
   7129 }
   7130 
   7131 void Document::SetBgColor(const nsAString& aBgColor) {
   7132  HTMLBodyElement* body = GetBodyElement();
   7133  if (body) {
   7134    body->SetBgColor(aBgColor);
   7135  }
   7136 }
   7137 
   7138 void Document::GetFgColor(nsAString& aFgColor) {
   7139  aFgColor.Truncate();
   7140 
   7141  HTMLBodyElement* body = GetBodyElement();
   7142  if (body) {
   7143    body->GetText(aFgColor);
   7144  }
   7145 }
   7146 
   7147 void Document::SetFgColor(const nsAString& aFgColor) {
   7148  HTMLBodyElement* body = GetBodyElement();
   7149  if (body) {
   7150    body->SetText(aFgColor);
   7151  }
   7152 }
   7153 
   7154 void Document::CaptureEvents() {
   7155  WarnOnceAbout(DeprecatedOperations::eUseOfCaptureEvents);
   7156 }
   7157 
   7158 void Document::ReleaseEvents() {
   7159  WarnOnceAbout(DeprecatedOperations::eUseOfReleaseEvents);
   7160 }
   7161 
   7162 HTMLAllCollection* Document::All() {
   7163  if (!mAll) {
   7164    mAll = new HTMLAllCollection(this);
   7165  }
   7166  return mAll;
   7167 }
   7168 
   7169 nsresult Document::GetSrcdocData(nsAString& aSrcdocData) {
   7170  if (mIsSrcdocDocument) {
   7171    nsCOMPtr<nsIInputStreamChannel> inStrmChan = do_QueryInterface(mChannel);
   7172    if (inStrmChan) {
   7173      return inStrmChan->GetSrcdocData(aSrcdocData);
   7174    }
   7175  }
   7176  aSrcdocData = VoidString();
   7177  return NS_OK;
   7178 }
   7179 
   7180 Nullable<WindowProxyHolder> Document::GetDefaultView() const {
   7181  nsPIDOMWindowOuter* win = GetWindow();
   7182  if (!win) {
   7183    return nullptr;
   7184  }
   7185  return WindowProxyHolder(win->GetBrowsingContext());
   7186 }
   7187 
   7188 nsIContent* Document::GetUnretargetedFocusedContent(
   7189    IncludeChromeOnly aIncludeChromeOnly) const {
   7190  nsCOMPtr<nsPIDOMWindowOuter> window = GetWindow();
   7191  if (!window) {
   7192    return nullptr;
   7193  }
   7194  nsCOMPtr<nsPIDOMWindowOuter> focusedWindow;
   7195  nsIContent* focusedContent = nsFocusManager::GetFocusedDescendant(
   7196      window, nsFocusManager::eOnlyCurrentWindow,
   7197      getter_AddRefs(focusedWindow));
   7198  if (!focusedContent) {
   7199    return nullptr;
   7200  }
   7201  // be safe and make sure the element is from this document
   7202  if (focusedContent->OwnerDoc() != this) {
   7203    return nullptr;
   7204  }
   7205  if (focusedContent->ChromeOnlyAccess() &&
   7206      aIncludeChromeOnly == IncludeChromeOnly::No) {
   7207    return focusedContent->FindFirstNonChromeOnlyAccessContent();
   7208  }
   7209  return focusedContent;
   7210 }
   7211 
   7212 Element* Document::GetActiveElement() {
   7213  // Get the focused element.
   7214  Element* focusedElement = GetRetargetedFocusedElement();
   7215  if (focusedElement) {
   7216    return focusedElement;
   7217  }
   7218 
   7219  // No focused element anywhere in this document.  Try to get the BODY.
   7220  if (IsHTMLOrXHTML()) {
   7221    Element* bodyElement = AsHTMLDocument()->GetBody();
   7222    if (bodyElement) {
   7223      return bodyElement;
   7224    }
   7225    // Special case to handle the transition to XHTML from XUL documents
   7226    // where there currently isn't a body element, but we need to match the
   7227    // XUL behavior. This should be removed when bug 1540278 is resolved.
   7228    if (nsContentUtils::IsChromeDoc(this)) {
   7229      Element* docElement = GetDocumentElement();
   7230      if (docElement && docElement->IsXULElement()) {
   7231        return docElement;
   7232      }
   7233    }
   7234    // Because of IE compatibility, return null when html document doesn't have
   7235    // a body.
   7236    return nullptr;
   7237  }
   7238 
   7239  // If we couldn't get a BODY, return the root element.
   7240  return GetDocumentElement();
   7241 }
   7242 
   7243 Element* Document::GetCurrentScript() {
   7244  if (!mScriptLoader) {
   7245    return nullptr;
   7246  }
   7247  nsCOMPtr<Element> el(do_QueryInterface(mScriptLoader->GetCurrentScript()));
   7248  return el;
   7249 }
   7250 
   7251 void Document::ReleaseCapture() const {
   7252  // only release the capture if the caller can access it. This prevents a
   7253  // page from stopping a scrollbar grab for example.
   7254  nsCOMPtr<nsINode> node = PresShell::GetCapturingContent();
   7255  if (node && nsContentUtils::CanCallerAccess(node)) {
   7256    PresShell::ReleaseCapturingContent();
   7257  }
   7258 }
   7259 
   7260 nsIURI* Document::GetBaseURI(bool aTryUseXHRDocBaseURI) const {
   7261  if (aTryUseXHRDocBaseURI && mChromeXHRDocBaseURI) {
   7262    return mChromeXHRDocBaseURI;
   7263  }
   7264 
   7265  return GetDocBaseURI();
   7266 }
   7267 
   7268 void Document::SetBaseURI(nsIURI* aURI) {
   7269  if (!aURI && !mDocumentBaseURI) {
   7270    return;
   7271  }
   7272 
   7273  // Don't do anything if the URI wasn't actually changed.
   7274  if (aURI && mDocumentBaseURI) {
   7275    bool equalBases = false;
   7276    mDocumentBaseURI->Equals(aURI, &equalBases);
   7277    if (equalBases) {
   7278      return;
   7279    }
   7280  }
   7281 
   7282  mDocumentBaseURI = aURI;
   7283  mCachedURLData = nullptr;
   7284  RefreshLinkHrefs();
   7285 }
   7286 
   7287 Result<OwningNonNull<nsIURI>, nsresult> Document::ResolveWithBaseURI(
   7288    const nsAString& aURI) {
   7289  RefPtr<nsIURI> resolvedURI;
   7290  MOZ_TRY(
   7291      NS_NewURI(getter_AddRefs(resolvedURI), aURI, nullptr, GetDocBaseURI()));
   7292  return OwningNonNull<nsIURI>(std::move(resolvedURI));
   7293 }
   7294 
   7295 nsIReferrerInfo* Document::ReferrerInfoForInternalCSSAndSVGResources() {
   7296  if (!mCachedReferrerInfoForInternalCSSAndSVGResources) {
   7297    mCachedReferrerInfoForInternalCSSAndSVGResources =
   7298        ReferrerInfo::CreateForInternalCSSAndSVGResources(this);
   7299  }
   7300  return mCachedReferrerInfoForInternalCSSAndSVGResources;
   7301 }
   7302 
   7303 URLExtraData* Document::DefaultStyleAttrURLData() {
   7304  MOZ_ASSERT(NS_IsMainThread());
   7305  if (!mCachedURLData) {
   7306    mCachedURLData = new URLExtraData(
   7307        GetDocBaseURI(), ReferrerInfoForInternalCSSAndSVGResources(),
   7308        NodePrincipal());
   7309  }
   7310  return mCachedURLData;
   7311 }
   7312 
   7313 void Document::SetDocumentCharacterSet(NotNull<const Encoding*> aEncoding) {
   7314  if (mCharacterSet != aEncoding) {
   7315    mCharacterSet = aEncoding;
   7316    mEncodingMenuDisabled = aEncoding == UTF_8_ENCODING;
   7317    RecomputeLanguageFromCharset();
   7318 
   7319    if (nsPresContext* context = GetPresContext()) {
   7320      context->DocumentCharSetChanged(aEncoding);
   7321    }
   7322  }
   7323 }
   7324 
   7325 void Document::GetSandboxFlagsAsString(nsAString& aFlags) {
   7326  nsContentUtils::SandboxFlagsToString(mSandboxFlags, aFlags);
   7327 }
   7328 
   7329 void Document::GetHeaderData(nsAtom* aHeaderField, nsAString& aData) const {
   7330  aData.Truncate();
   7331  const HeaderData* data = mHeaderData.get();
   7332  while (data) {
   7333    if (data->mField == aHeaderField) {
   7334      aData = data->mData;
   7335      break;
   7336    }
   7337    data = data->mNext.get();
   7338  }
   7339 }
   7340 
   7341 static bool IsValidOnionLocation(nsIURI* aDocumentURI,
   7342                                 nsIURI* aOnionLocationURI) {
   7343  if (!aDocumentURI || !aOnionLocationURI) {
   7344    return false;
   7345  }
   7346 
   7347  // Current URI
   7348  nsAutoCString host;
   7349  if (!aDocumentURI->SchemeIs("https")) {
   7350    return false;
   7351  }
   7352  NS_ENSURE_SUCCESS(aDocumentURI->GetAsciiHost(host), false);
   7353  if (StringEndsWith(host, ".onion"_ns)) {
   7354    // Already in the .onion site
   7355    return false;
   7356  }
   7357 
   7358  // Target URI
   7359  if (!aOnionLocationURI->SchemeIs("http") &&
   7360      !aOnionLocationURI->SchemeIs("https")) {
   7361    return false;
   7362  }
   7363  nsCOMPtr<nsIEffectiveTLDService> eTLDService =
   7364      do_GetService(NS_EFFECTIVETLDSERVICE_CONTRACTID);
   7365  if (!eTLDService) {
   7366    NS_ENSURE_SUCCESS(aOnionLocationURI->GetAsciiHost(host), false);
   7367    // This should not happen, but in the unlikely case, still check if it is a
   7368    // .onion and in case allow it.
   7369    return StringEndsWith(host, ".onion"_ns);
   7370  }
   7371  NS_ENSURE_SUCCESS(eTLDService->GetBaseDomain(aOnionLocationURI, 0, host),
   7372                    false);
   7373  if (!StringEndsWith(host, ".onion"_ns)) {
   7374    return false;
   7375  }
   7376 
   7377  // Ignore v2
   7378  if (host.Length() == 22) {
   7379    const char* cur = host.BeginWriting();
   7380    // We have already checked that it ends by ".onion"
   7381    const char* end = host.EndWriting() - 6;
   7382    bool base32 = true;
   7383    for (; cur < end && base32; ++cur) {
   7384      base32 = isalpha(*cur) || ('2' <= *cur && *cur <= '7');
   7385    }
   7386    return !base32;
   7387  }
   7388 
   7389  return true;
   7390 }
   7391 
   7392 void Document::SetHeaderData(nsAtom* aHeaderField, const nsAString& aData) {
   7393  if (!aHeaderField) {
   7394    NS_ERROR("null headerField");
   7395    return;
   7396  }
   7397 
   7398  if (!mHeaderData) {
   7399    if (!aData.IsEmpty()) {  // don't bother storing empty string
   7400      mHeaderData = MakeUnique<HeaderData>(aHeaderField, aData);
   7401    }
   7402  } else {
   7403    HeaderData* data = mHeaderData.get();
   7404    UniquePtr<HeaderData>* lastPtr = &mHeaderData;
   7405    bool found = false;
   7406    do {  // look for existing and replace
   7407      if (data->mField == aHeaderField) {
   7408        if (!aData.IsEmpty()) {
   7409          data->mData.Assign(aData);
   7410        } else {  // don't store empty string
   7411          // Note that data->mNext is moved to a temporary before the old value
   7412          // of *lastPtr is deleted.
   7413          *lastPtr = std::move(data->mNext);
   7414        }
   7415        found = true;
   7416 
   7417        break;
   7418      }
   7419      lastPtr = &data->mNext;
   7420      data = lastPtr->get();
   7421    } while (data);
   7422 
   7423    if (!aData.IsEmpty() && !found) {
   7424      // didn't find, append
   7425      *lastPtr = MakeUnique<HeaderData>(aHeaderField, aData);
   7426    }
   7427  }
   7428 
   7429  if (aHeaderField == nsGkAtoms::headerContentLanguage) {
   7430    if (aData.IsEmpty()) {
   7431      mContentLanguage = nullptr;
   7432    } else {
   7433      mContentLanguage = NS_AtomizeMainThread(aData);
   7434    }
   7435    mMayNeedFontPrefsUpdate = true;
   7436    if (auto* presContext = GetPresContext()) {
   7437      presContext->ContentLanguageChanged();
   7438    }
   7439  }
   7440 
   7441  if (aHeaderField == nsGkAtoms::origin_trial) {
   7442    mTrials.UpdateFromToken(aData, NodePrincipal());
   7443    if (mTrials.IsEnabled(OriginTrial::CoepCredentialless)) {
   7444      InitCOEP(mChannel);
   7445 
   7446      // If we still don't have a WindowContext, WindowContext::OnNewDocument
   7447      // will take care of this.
   7448      if (WindowContext* ctx = GetWindowContext()) {
   7449        if (mEmbedderPolicy) {
   7450          (void)ctx->SetEmbedderPolicy(mEmbedderPolicy.value());
   7451        }
   7452      }
   7453    }
   7454  }
   7455 
   7456  if (aHeaderField == nsGkAtoms::headerDefaultStyle) {
   7457    SetPreferredStyleSheetSet(aData);
   7458  }
   7459 
   7460  if (aHeaderField == nsGkAtoms::refresh && !IsStaticDocument()) {
   7461    // We get into this code before we have a script global yet, so get to our
   7462    // container via mDocumentContainer.
   7463    if (mDocumentContainer) {
   7464      // Note: using mDocumentURI instead of mBaseURI here, for consistency
   7465      // (used to just use the current URI of our webnavigation, but that
   7466      // should really be the same thing).  Note that this code can run
   7467      // before the current URI of the webnavigation has been updated, so we
   7468      // can't assert equality here.
   7469      mDocumentContainer->SetupRefreshURIFromHeader(this, aData);
   7470    }
   7471  }
   7472 
   7473  if (aHeaderField == nsGkAtoms::headerDNSPrefetchControl &&
   7474      mAllowDNSPrefetch) {
   7475    // Chromium treats any value other than 'on' (case insensitive) as 'off'.
   7476    mAllowDNSPrefetch = aData.IsEmpty() || aData.LowerCaseEqualsLiteral("on");
   7477  }
   7478 
   7479  if (aHeaderField == nsGkAtoms::handheldFriendly) {
   7480    mViewportType = Unknown;
   7481  }
   7482 
   7483  if (aHeaderField == nsGkAtoms::headerOnionLocation && !aData.IsEmpty()) {
   7484    nsCOMPtr<nsIURI> onionURI;
   7485    if (NS_SUCCEEDED(NS_NewURI(getter_AddRefs(onionURI), aData)) &&
   7486        IsValidOnionLocation(Document::GetDocumentURI(), onionURI)) {
   7487      mOnionLocationURI = onionURI;
   7488    }
   7489  }
   7490 }
   7491 
   7492 void Document::SetEarlyHints(
   7493    nsTArray<net::EarlyHintConnectArgs>&& aEarlyHints) {
   7494  mEarlyHints = std::move(aEarlyHints);
   7495 }
   7496 
   7497 void Document::TryChannelCharset(nsIChannel* aChannel, int32_t& aCharsetSource,
   7498                                 NotNull<const Encoding*>& aEncoding,
   7499                                 nsHtml5TreeOpExecutor* aExecutor) {
   7500  if (aChannel) {
   7501    nsAutoCString charsetVal;
   7502    nsresult rv = aChannel->GetContentCharset(charsetVal);
   7503    if (NS_SUCCEEDED(rv)) {
   7504      const Encoding* preferred = Encoding::ForLabel(charsetVal);
   7505      if (preferred) {
   7506        if (aExecutor && preferred == REPLACEMENT_ENCODING) {
   7507          aExecutor->ComplainAboutBogusProtocolCharset(this, false);
   7508        }
   7509        aEncoding = WrapNotNull(preferred);
   7510        aCharsetSource = kCharsetFromChannel;
   7511        return;
   7512      } else if (aExecutor && !charsetVal.IsEmpty()) {
   7513        aExecutor->ComplainAboutBogusProtocolCharset(this, true);
   7514      }
   7515    }
   7516  }
   7517 }
   7518 
   7519 static inline void AssertNoStaleServoDataIn(nsINode& aSubtreeRoot) {
   7520 #ifdef DEBUG
   7521  for (nsINode* node : ShadowIncludingTreeIterator(aSubtreeRoot)) {
   7522    const Element* element = Element::FromNode(node);
   7523    if (!element) {
   7524      continue;
   7525    }
   7526    MOZ_ASSERT(!element->HasServoData());
   7527  }
   7528 #endif
   7529 }
   7530 
   7531 already_AddRefed<PresShell> Document::CreatePresShell(
   7532    nsPresContext* aContext, nsSubDocumentFrame* aEmbedderFrame) {
   7533  MOZ_DIAGNOSTIC_ASSERT(!mPresShell, "We have a presshell already!");
   7534 
   7535  NS_ENSURE_FALSE(GetBFCacheEntry(), nullptr);
   7536 
   7537  AssertNoStaleServoDataIn(*this);
   7538 
   7539  RefPtr<PresShell> presShell = new PresShell(this);
   7540  // Note: we don't hold a ref to the shell (it holds a ref to us)
   7541  mPresShell = presShell;
   7542 
   7543  if (aEmbedderFrame) {
   7544    // It's important to do this as soon as possible so that
   7545    // GetRootPresContext() and so on do the right thing from the get go.
   7546    aEmbedderFrame->AddEmbeddingPresShell(presShell);
   7547  }
   7548 
   7549  if (!mStyleSetFilled) {
   7550    FillStyleSet();
   7551  }
   7552 
   7553  presShell->Init(aContext);
   7554  if (RefPtr<class HighlightRegistry> highlightRegistry = mHighlightRegistry) {
   7555    highlightRegistry->AddHighlightSelectionsToFrameSelection();
   7556  }
   7557  // Gaining a shell causes changes in how media queries are evaluated, so
   7558  // invalidate that.
   7559  aContext->MediaFeatureValuesChanged(
   7560      {MediaFeatureChange::kAllChanges},
   7561      MediaFeatureChangePropagation::JustThisDocument);
   7562 
   7563  // Make sure to never paint if we belong to an invisible DocShell.
   7564  nsCOMPtr<nsIDocShell> docShell(mDocumentContainer);
   7565  if (docShell && docShell->IsInvisible()) {
   7566    presShell->SetNeverPainting(true);
   7567  }
   7568 
   7569  MOZ_LOG(gDocumentLeakPRLog, LogLevel::Debug,
   7570          ("DOCUMENT %p with PressShell %p and DocShell %p", this,
   7571           presShell.get(), docShell.get()));
   7572 
   7573  mExternalResourceMap.ShowViewers();
   7574 
   7575  if (mDocumentL10n) {
   7576    // In case we already accumulated mutations,
   7577    // we'll trigger the refresh driver now.
   7578    mDocumentL10n->OnCreatePresShell();
   7579  }
   7580 
   7581  // Now that we have a shell, we might have @font-face rules (the presence of a
   7582  // shell may change which rules apply to us). We don't need to do anything
   7583  // like EnsureStyleFlush or such, there's nothing to update yet and when stuff
   7584  // is ready to update we'll flush the font set.
   7585  MarkUserFontSetDirty();
   7586 
   7587  // Take the author style disabled state from the top browsing cvontext.
   7588  // (PageStyleChild.sys.mjs ensures this is up to date.)
   7589  if (BrowsingContext* bc = GetBrowsingContext()) {
   7590    presShell->SetAuthorStyleDisabled(bc->Top()->AuthorStyleDisabledDefault());
   7591  }
   7592 
   7593  // We may need to set up the editor now if we haven't earlier, since we avoid
   7594  // setting up the editor without a pres shell.
   7595  MaybeEditingStateChanged();
   7596  return presShell.forget();
   7597 }
   7598 
   7599 // This roughly matches https://html.spec.whatwg.org/#update-the-rendering
   7600 // step 3:
   7601 //
   7602 //     Remove from docs any Document object doc for which any of the
   7603 //     following are true.
   7604 //
   7605 // If this function changes make sure to call MaybeScheduleRendering at the
   7606 // right places.
   7607 bool Document::IsRenderingSuppressed() const {
   7608  // TODO(emilio): Per spec we should suppress when doc's visibility state is
   7609  // "hidden", but tests rely on throttling at least (otherwise webdriver tests
   7610  // that test minimized windows and so on time out). Maybe we can do it only in
   7611  // content or something along those lines?
   7612  // if (Hidden()) {
   7613  //   return true;
   7614  // }
   7615 
   7616  // doc's rendering is suppressed for view transitions
   7617  if (mRenderingSuppressedForViewTransitions) {
   7618    return true;
   7619  }
   7620  // The user agent believes that updating the rendering of doc's node navigable
   7621  // would have no visible effect.
   7622  if (!IsEventHandlingEnabled() && !IsBeingUsedAsImage() && !mDisplayDocument &&
   7623      !mPausedByDevTools) {
   7624    return true;
   7625  }
   7626  if (!mPresShell || !mPresShell->DidInitialize()) {
   7627    return true;
   7628  }
   7629  return false;
   7630 }
   7631 
   7632 void Document::MaybeScheduleRenderingPhases(RenderingPhases aPhases) {
   7633  if (IsRenderingSuppressed()) {
   7634    return;
   7635  }
   7636  MOZ_ASSERT(mPresShell);
   7637  nsRefreshDriver* rd = mPresShell->GetPresContext()->RefreshDriver();
   7638  rd->ScheduleRenderingPhases(aPhases);
   7639 }
   7640 
   7641 void Document::TakeVideoFrameRequestCallbacks(
   7642    nsTArray<RefPtr<HTMLVideoElement>>& aVideoCallbacks) {
   7643  MOZ_ASSERT(aVideoCallbacks.IsEmpty());
   7644  mFrameRequestManager.Take(aVideoCallbacks);
   7645 }
   7646 
   7647 bool Document::ShouldThrottleFrameRequests() const {
   7648  if (mStaticCloneCount > 0) {
   7649    // Even if we're not visible, a static clone may be, so run at full speed.
   7650    return false;
   7651  }
   7652 
   7653  if (Hidden() && !StaticPrefs::layout_testing_top_level_always_active()) {
   7654    // We're not visible (probably in a background tab or the bf cache).
   7655    return true;
   7656  }
   7657 
   7658  if (!mPresShell) {
   7659    // Can't do anything smarter. We don't run frame requests in documents
   7660    // without a pres shell anyways.
   7661    return false;
   7662  }
   7663 
   7664  if (!mPresShell->IsActive()) {
   7665    // The pres shell is not active (we're an invisible OOP iframe or such), so
   7666    // throttle.
   7667    return true;
   7668  }
   7669 
   7670  if (mPresShell->IsPaintingSuppressed()) {
   7671    // Historically we have throttled frame requests until we've painted at
   7672    // least once, so keep doing that.
   7673    return true;
   7674  }
   7675 
   7676  if (mPresShell->IsUnderHiddenEmbedderElement()) {
   7677    // For display: none and visibility: hidden we always throttle, for
   7678    // consistency with OOP iframes.
   7679    return true;
   7680  }
   7681 
   7682  Element* el = GetEmbedderElement();
   7683  if (!el) {
   7684    // If we're not in-process, our refresh driver is throttled separately (via
   7685    // PresShell::SetIsActive, so not much more we can do here.
   7686    return false;
   7687  }
   7688 
   7689  if (!StaticPrefs::layout_throttle_in_process_iframes()) {
   7690    return false;
   7691  }
   7692 
   7693  // Note that because we have to scroll this document into view at least once
   7694  // to un-throttle it, we will drop one requestAnimationFrame frame when a
   7695  // document that previously wasn't visible scrolls into view. This is
   7696  // acceptable / unlikely to be human-perceivable, though we could improve on
   7697  // it if needed by adding an intersection margin or something of that sort.
   7698  const IntersectionInput input =
   7699      DOMIntersectionObserver::ComputeInputForIframeThrottling(*el->OwnerDoc());
   7700  const IntersectionOutput output = DOMIntersectionObserver::Intersect(
   7701      input, *el, DOMIntersectionObserver::BoxToUse::Content);
   7702  return !output.Intersects();
   7703 }
   7704 
   7705 void Document::DeletePresShell() {
   7706  mExternalResourceMap.HideViewers();
   7707  mPendingFullscreenEvents.Clear();
   7708 
   7709  // When our shell goes away, request that all our images be immediately
   7710  // discarded, so we don't carry around decoded image data for a document we
   7711  // no longer intend to paint.
   7712  for (imgIRequest* image : mTrackedImages.Keys()) {
   7713    image->RequestDiscard();
   7714  }
   7715 
   7716  // Now that we no longer have a shell, we need to forget about any FontFace
   7717  // objects for @font-face rules that came from the style set. There's no need
   7718  // to call EnsureStyleFlush either, the shell is going away anyway, so there's
   7719  // no point on it.
   7720  mFontFaceSetDirty = true;
   7721 
   7722  if (IsEditingOn()) {
   7723    TurnEditingOff();
   7724  }
   7725 
   7726  mPresShell = nullptr;
   7727 
   7728  ClearStaleServoData();
   7729  AssertNoStaleServoDataIn(*this);
   7730 
   7731  mStyleSet->ShellDetachedFromDocument();
   7732  mStyleSetFilled = false;
   7733  mQuirkSheetAdded = false;
   7734 }
   7735 
   7736 void Document::DisallowBFCaching(uint32_t aStatus) {
   7737  NS_ASSERTION(!mBFCacheEntry, "We're already in the bfcache!");
   7738  if (!mBFCacheDisallowed) {
   7739    if (WindowGlobalChild* wgc = GetWindowGlobalChild()) {
   7740      wgc->SendUpdateBFCacheStatus(aStatus, 0);
   7741    }
   7742  }
   7743  mBFCacheDisallowed = true;
   7744 }
   7745 
   7746 void Document::SetBFCacheEntry(nsIBFCacheEntry* aEntry) {
   7747  MOZ_ASSERT(IsBFCachingAllowed() || !aEntry, "You should have checked!");
   7748 
   7749  if (mPresShell) {
   7750    if (!aEntry && mBFCacheEntry) {
   7751      mPresShell->StartObservingRefreshDriver();
   7752    }
   7753  }
   7754  mBFCacheEntry = aEntry;
   7755 }
   7756 
   7757 bool Document::RemoveFromBFCacheSync() {
   7758  bool removed = false;
   7759  if (nsCOMPtr<nsIBFCacheEntry> entry = GetBFCacheEntry()) {
   7760    entry->RemoveFromBFCacheSync();
   7761    removed = true;
   7762  } else if (!IsCurrentActiveDocument()) {
   7763    // In the old bfcache implementation while the new page is loading, but
   7764    // before nsIDocumentViewer.show() has been called, the previous page
   7765    // doesn't yet have nsIBFCacheEntry. However, the previous page isn't the
   7766    // current active document anymore.
   7767    DisallowBFCaching();
   7768    removed = true;
   7769  }
   7770 
   7771  if (mozilla::SessionHistoryInParent() && XRE_IsContentProcess()) {
   7772    if (BrowsingContext* bc = GetBrowsingContext()) {
   7773      if (bc->IsInBFCache()) {
   7774        ContentChild* cc = ContentChild::GetSingleton();
   7775        // IPC is asynchronous but the caller is supposed to check the return
   7776        // value. The reason for 'Sync' in the method name is that the old
   7777        // implementation may run scripts. There is Async variant in
   7778        // the old session history implementation for the cases where
   7779        // synchronous operation isn't safe.
   7780        cc->SendRemoveFromBFCache(bc->Top());
   7781        removed = true;
   7782      }
   7783    }
   7784  }
   7785  return removed;
   7786 }
   7787 
   7788 static void SubDocClearEntry(PLDHashTable* table, PLDHashEntryHdr* entry) {
   7789  SubDocMapEntry* e = static_cast<SubDocMapEntry*>(entry);
   7790 
   7791  NS_RELEASE(e->mKey);
   7792  if (e->mSubDocument) {
   7793    e->mSubDocument->SetParentDocument(nullptr);
   7794    NS_RELEASE(e->mSubDocument);
   7795  }
   7796 }
   7797 
   7798 static void SubDocInitEntry(PLDHashEntryHdr* entry, const void* key) {
   7799  SubDocMapEntry* e =
   7800      const_cast<SubDocMapEntry*>(static_cast<const SubDocMapEntry*>(entry));
   7801 
   7802  e->mKey = const_cast<Element*>(static_cast<const Element*>(key));
   7803  NS_ADDREF(e->mKey);
   7804 
   7805  e->mSubDocument = nullptr;
   7806 }
   7807 
   7808 nsresult Document::SetSubDocumentFor(Element* aElement, Document* aSubDoc) {
   7809  NS_ENSURE_TRUE(aElement, NS_ERROR_UNEXPECTED);
   7810 
   7811  if (!aSubDoc) {
   7812    // aSubDoc is nullptr, remove the mapping
   7813 
   7814    if (mSubDocuments) {
   7815      mSubDocuments->Remove(aElement);
   7816    }
   7817  } else {
   7818    if (!mSubDocuments) {
   7819      // Create a new hashtable
   7820 
   7821      static const PLDHashTableOps hash_table_ops = {
   7822          PLDHashTable::HashVoidPtrKeyStub, PLDHashTable::MatchEntryStub,
   7823          PLDHashTable::MoveEntryStub, SubDocClearEntry, SubDocInitEntry};
   7824 
   7825      mSubDocuments =
   7826          MakeUnique<PLDHashTable>(&hash_table_ops, sizeof(SubDocMapEntry));
   7827    }
   7828 
   7829    // Add a mapping to the hash table
   7830    auto entry =
   7831        static_cast<SubDocMapEntry*>(mSubDocuments->Add(aElement, fallible));
   7832 
   7833    if (!entry) {
   7834      return NS_ERROR_OUT_OF_MEMORY;
   7835    }
   7836 
   7837    if (entry->mSubDocument) {
   7838      entry->mSubDocument->SetParentDocument(nullptr);
   7839 
   7840      // Release the old sub document
   7841      NS_RELEASE(entry->mSubDocument);
   7842    }
   7843 
   7844    entry->mSubDocument = aSubDoc;
   7845    NS_ADDREF(entry->mSubDocument);
   7846 
   7847    aSubDoc->SetParentDocument(this);
   7848  }
   7849 
   7850  return NS_OK;
   7851 }
   7852 
   7853 Document* Document::GetSubDocumentFor(nsIContent* aContent) const {
   7854  if (mSubDocuments && aContent->IsElement()) {
   7855    auto entry = static_cast<SubDocMapEntry*>(
   7856        mSubDocuments->Search(aContent->AsElement()));
   7857 
   7858    if (entry) {
   7859      return entry->mSubDocument;
   7860    }
   7861  }
   7862 
   7863  return nullptr;
   7864 }
   7865 
   7866 Element* Document::GetEmbedderElement() const {
   7867  // We check if we're the active document in our BrowsingContext
   7868  // by comparing against its document, rather than checking if the
   7869  // WindowContext is cached, since mWindow may be null when we're
   7870  // called (such as in nsPresContext::Init).
   7871  if (BrowsingContext* bc = GetBrowsingContext()) {
   7872    return bc->GetExtantDocument() == this ? bc->GetEmbedderElement() : nullptr;
   7873  }
   7874 
   7875  return nullptr;
   7876 }
   7877 
   7878 Element* Document::GetRootElement() const {
   7879  return (mCachedRootElement && mCachedRootElement->GetParentNode() == this)
   7880             ? mCachedRootElement
   7881             : GetRootElementInternal();
   7882 }
   7883 
   7884 Element* Document::GetUnfocusedKeyEventTarget() { return GetRootElement(); }
   7885 
   7886 Element* Document::GetRootElementInternal() const {
   7887  // We invoke GetRootElement() immediately before the servo traversal, so we
   7888  // should always have a cache hit from Servo.
   7889  MOZ_ASSERT(NS_IsMainThread());
   7890 
   7891  // Loop backwards because any non-elements, such as doctypes and PIs
   7892  // are likely to appear before the root element.
   7893  for (nsIContent* child = GetLastChild(); child;
   7894       child = child->GetPreviousSibling()) {
   7895    if (Element* element = Element::FromNode(child)) {
   7896      const_cast<Document*>(this)->mCachedRootElement = element;
   7897      return element;
   7898    }
   7899  }
   7900 
   7901  const_cast<Document*>(this)->mCachedRootElement = nullptr;
   7902  return nullptr;
   7903 }
   7904 
   7905 void Document::InsertChildBefore(
   7906    nsIContent* aKid, nsIContent* aBeforeThis, bool aNotify, ErrorResult& aRv,
   7907    nsINode* aOldParent, MutationEffectOnScript aMutationEffectOnScript) {
   7908  const bool isElementInsertion = aKid->IsElement();
   7909  if (isElementInsertion && GetRootElement()) {
   7910    NS_WARNING("Inserting root element when we already have one");
   7911    aRv.ThrowHierarchyRequestError("There is already a root element.");
   7912    return;
   7913  }
   7914 
   7915  nsINode::InsertChildBefore(aKid, aBeforeThis, aNotify, aRv, aOldParent,
   7916                             aMutationEffectOnScript);
   7917  if (isElementInsertion && !aRv.Failed()) {
   7918    CreateCustomContentContainerIfNeeded();
   7919  }
   7920 }
   7921 
   7922 void Document::RemoveChildNode(nsIContent* aKid, bool aNotify,
   7923                               const BatchRemovalState* aState,
   7924                               nsINode* aNewParent,
   7925                               MutationEffectOnScript aMutationEffectOnScript) {
   7926  Maybe<mozAutoDocUpdate> updateBatch;
   7927  const bool removingRoot = aKid->IsElement();
   7928  if (removingRoot) {
   7929    updateBatch.emplace(this, aNotify);
   7930 
   7931    WillRemoveRoot();
   7932 
   7933    // Notify early so that we can clear the cached element after notifying,
   7934    // without having to slow down nsINode::RemoveChildNode.
   7935    if (aNotify) {
   7936      ContentRemoveInfo info;
   7937      info.mBatchRemovalState = aState;
   7938      info.mNewParent = aNewParent;
   7939      MutationObservers::NotifyContentWillBeRemoved(this, aKid, info);
   7940      aNotify = false;
   7941    }
   7942 
   7943    // Preemptively clear mCachedRootElement, since we are about to remove it
   7944    // from our child list, and we don't want to return this maybe-obsolete
   7945    // value from any GetRootElement() calls that happen inside of
   7946    // RemoveChildNode().
   7947    // (NOTE: for this to be useful, RemoveChildNode() must NOT trigger any
   7948    // GetRootElement() calls until after it's removed the child from mChildren.
   7949    // Any call before that point would restore this soon-to-be-obsolete cached
   7950    // answer, and our clearing here would be fruitless.)
   7951    mCachedRootElement = nullptr;
   7952  }
   7953 
   7954  nsINode::RemoveChildNode(aKid, aNotify, nullptr, aNewParent,
   7955                           aMutationEffectOnScript);
   7956  MOZ_ASSERT(mCachedRootElement != aKid,
   7957             "Stale pointer in mCachedRootElement, after we tried to clear it "
   7958             "(maybe somebody called GetRootElement() too early?)");
   7959 }
   7960 
   7961 void Document::AddStyleSheetToStyleSets(StyleSheet& aSheet) {
   7962  if (mStyleSetFilled) {
   7963    EnsureStyleSet().AddDocStyleSheet(aSheet);
   7964    ApplicableStylesChanged();
   7965  }
   7966 }
   7967 
   7968 void Document::RecordShadowStyleChange(ShadowRoot& aShadowRoot) {
   7969  EnsureStyleSet().RecordShadowStyleChange(aShadowRoot);
   7970  ApplicableStylesChanged(/* aKnownInShadowTree= */ true);
   7971 }
   7972 
   7973 void Document::ApplicableStylesChanged(bool aKnownInShadowTree) {
   7974  // TODO(emilio): if we decide to resolve style in display: none iframes, then
   7975  // we need to always track style changes and remove the mStyleSetFilled.
   7976  if (!mStyleSetFilled) {
   7977    return;
   7978  }
   7979  if (!aKnownInShadowTree) {
   7980    MarkUserFontSetDirty();
   7981  }
   7982  PresShell* ps = GetPresShell();
   7983  if (!ps) {
   7984    return;
   7985  }
   7986 
   7987  ps->EnsureStyleFlush();
   7988  nsPresContext* pc = ps->GetPresContext();
   7989  if (!pc) {
   7990    return;
   7991  }
   7992 
   7993  if (!aKnownInShadowTree) {
   7994    pc->MarkCounterStylesDirty();
   7995    pc->MarkFontFeatureValuesDirty();
   7996    pc->MarkFontPaletteValuesDirty();
   7997  }
   7998  pc->RestyleManager()->NextRestyleIsForCSSRuleChanges();
   7999 }
   8000 
   8001 void Document::RemoveStyleSheetFromStyleSets(StyleSheet& aSheet) {
   8002  if (mStyleSetFilled) {
   8003    mStyleSet->RemoveStyleSheet(aSheet);
   8004    ApplicableStylesChanged();
   8005  }
   8006 }
   8007 
   8008 void Document::InsertSheetAt(size_t aIndex, StyleSheet& aSheet) {
   8009  DocumentOrShadowRoot::InsertSheetAt(aIndex, aSheet);
   8010 
   8011  if (aSheet.IsApplicable()) {
   8012    AddStyleSheetToStyleSets(aSheet);
   8013  }
   8014 }
   8015 
   8016 void Document::StyleSheetApplicableStateChanged(StyleSheet& aSheet) {
   8017  if (!aSheet.IsDirectlyAssociatedTo(*this)) {
   8018    return;
   8019  }
   8020  if (aSheet.IsApplicable()) {
   8021    AddStyleSheetToStyleSets(aSheet);
   8022  } else {
   8023    RemoveStyleSheetFromStyleSets(aSheet);
   8024  }
   8025 }
   8026 
   8027 void Document::PostStyleSheetApplicableStateChangeEvent(StyleSheet& aSheet) {
   8028  if (!StyleSheetChangeEventsEnabled()) {
   8029    return;
   8030  }
   8031 
   8032  StyleSheetApplicableStateChangeEventInit init;
   8033  init.mBubbles = true;
   8034  init.mCancelable = true;
   8035  init.mStylesheet = &aSheet;
   8036  init.mApplicable = aSheet.IsApplicable();
   8037 
   8038  RefPtr<StyleSheetApplicableStateChangeEvent> event =
   8039      StyleSheetApplicableStateChangeEvent::Constructor(
   8040          this, u"StyleSheetApplicableStateChanged"_ns, init);
   8041  event->SetTrusted(true);
   8042  event->SetTarget(this);
   8043  RefPtr<AsyncEventDispatcher> asyncDispatcher =
   8044      new AsyncEventDispatcher(this, event.forget(), ChromeOnlyDispatch::eYes);
   8045  asyncDispatcher->PostDOMEvent();
   8046 }
   8047 
   8048 void Document::PostStyleSheetRemovedEvent(StyleSheet& aSheet) {
   8049  if (!StyleSheetChangeEventsEnabled()) {
   8050    return;
   8051  }
   8052 
   8053  StyleSheetRemovedEventInit init;
   8054  init.mBubbles = true;
   8055  init.mCancelable = false;
   8056  init.mStylesheet = &aSheet;
   8057 
   8058  RefPtr<StyleSheetRemovedEvent> event =
   8059      StyleSheetRemovedEvent::Constructor(this, u"StyleSheetRemoved"_ns, init);
   8060  event->SetTrusted(true);
   8061  event->SetTarget(this);
   8062  RefPtr<AsyncEventDispatcher> asyncDispatcher =
   8063      new AsyncEventDispatcher(this, event.forget(), ChromeOnlyDispatch::eYes);
   8064  asyncDispatcher->PostDOMEvent();
   8065 }
   8066 
   8067 void Document::PostCustomPropertyRegistered(
   8068    const PropertyDefinition& aDefinition) {
   8069  if (!StyleSheetChangeEventsEnabled()) {
   8070    return;
   8071  }
   8072 
   8073  CSSCustomPropertyRegisteredEventInit init;
   8074  init.mBubbles = true;
   8075  init.mCancelable = false;
   8076 
   8077  InspectorCSSPropertyDefinition property;
   8078 
   8079  property.mName.Append(aDefinition.mName);
   8080  property.mSyntax.Append(aDefinition.mSyntax);
   8081  property.mInherits = aDefinition.mInherits;
   8082  if (aDefinition.mInitialValue.WasPassed()) {
   8083    property.mInitialValue.Append(aDefinition.mInitialValue.Value());
   8084  } else {
   8085    property.mInitialValue.SetIsVoid(true);
   8086  }
   8087  property.mFromJS = true;
   8088  init.mPropertyDefinition = property;
   8089 
   8090  RefPtr<CSSCustomPropertyRegisteredEvent> event =
   8091      CSSCustomPropertyRegisteredEvent::Constructor(
   8092          this, u"csscustompropertyregistered"_ns, init);
   8093  event->SetTrusted(true);
   8094  event->SetTarget(this);
   8095  RefPtr<AsyncEventDispatcher> asyncDispatcher =
   8096      new AsyncEventDispatcher(this, event.forget(), ChromeOnlyDispatch::eYes);
   8097  asyncDispatcher->PostDOMEvent();
   8098 }
   8099 
   8100 static int32_t FindSheet(const nsTArray<RefPtr<StyleSheet>>& aSheets,
   8101                         nsIURI* aSheetURI) {
   8102  for (int32_t i = aSheets.Length() - 1; i >= 0; i--) {
   8103    bool bEqual;
   8104    nsIURI* uri = aSheets[i]->GetOriginalURI();
   8105    if (uri && NS_SUCCEEDED(uri->Equals(aSheetURI, &bEqual)) && bEqual) {
   8106      return i;
   8107    }
   8108  }
   8109 
   8110  return -1;
   8111 }
   8112 
   8113 nsresult Document::LoadAdditionalStyleSheet(additionalSheetType aType,
   8114                                            nsIURI* aSheetURI) {
   8115  MOZ_ASSERT(aSheetURI, "null arg");
   8116 
   8117  // Checking if we have loaded this one already.
   8118  if (FindSheet(mAdditionalSheets[aType], aSheetURI) >= 0) {
   8119    return NS_ERROR_INVALID_ARG;
   8120  }
   8121 
   8122  // Loading the sheet sync.
   8123  RefPtr<css::Loader> loader = new css::Loader(GetDocGroup());
   8124 
   8125  css::SheetParsingMode parsingMode;
   8126  switch (aType) {
   8127    case Document::eAgentSheet:
   8128      parsingMode = css::eAgentSheetFeatures;
   8129      break;
   8130 
   8131    case Document::eUserSheet:
   8132      parsingMode = css::eUserSheetFeatures;
   8133      break;
   8134 
   8135    case Document::eAuthorSheet:
   8136      parsingMode = css::eAuthorSheetFeatures;
   8137      break;
   8138 
   8139    default:
   8140      MOZ_CRASH("impossible value for aType");
   8141  }
   8142 
   8143  auto result = loader->LoadSheetSync(aSheetURI, parsingMode,
   8144                                      css::Loader::UseSystemPrincipal::Yes);
   8145  if (result.isErr()) {
   8146    return result.unwrapErr();
   8147  }
   8148 
   8149  RefPtr<StyleSheet> sheet = result.unwrap();
   8150 
   8151  MOZ_ASSERT(sheet->IsApplicable());
   8152 
   8153  return AddAdditionalStyleSheet(aType, sheet);
   8154 }
   8155 
   8156 nsresult Document::AddAdditionalStyleSheet(additionalSheetType aType,
   8157                                           StyleSheet* aSheet) {
   8158  if (mAdditionalSheets[aType].Contains(aSheet)) {
   8159    return NS_ERROR_INVALID_ARG;
   8160  }
   8161 
   8162  if (!aSheet->IsApplicable()) {
   8163    return NS_ERROR_INVALID_ARG;
   8164  }
   8165 
   8166  if (NS_WARN_IF(aSheet->GetAssociatedDocumentOrShadowRoot())) {
   8167    return NS_ERROR_INVALID_ARG;
   8168  }
   8169 
   8170  mAdditionalSheets[aType].AppendElement(aSheet);
   8171 
   8172  if (mStyleSetFilled) {
   8173    EnsureStyleSet().AppendStyleSheet(*aSheet);
   8174    ApplicableStylesChanged();
   8175  }
   8176  return NS_OK;
   8177 }
   8178 
   8179 void Document::RemoveAdditionalStyleSheet(additionalSheetType aType,
   8180                                          nsIURI* aSheetURI) {
   8181  MOZ_ASSERT(aSheetURI);
   8182 
   8183  nsTArray<RefPtr<StyleSheet>>& sheets = mAdditionalSheets[aType];
   8184 
   8185  int32_t i = FindSheet(mAdditionalSheets[aType], aSheetURI);
   8186  if (i >= 0) {
   8187    RefPtr<StyleSheet> sheetRef = std::move(sheets[i]);
   8188    sheets.RemoveElementAt(i);
   8189 
   8190    if (!mIsGoingAway) {
   8191      MOZ_ASSERT(sheetRef->IsApplicable());
   8192      if (mStyleSetFilled) {
   8193        EnsureStyleSet().RemoveStyleSheet(*sheetRef);
   8194        ApplicableStylesChanged();
   8195      }
   8196    }
   8197    sheetRef->ClearAssociatedDocumentOrShadowRoot();
   8198  }
   8199 }
   8200 
   8201 void Document::CreateCSSAndStyleImageLoaders(bool aLazy) {
   8202  if (aLazy) {
   8203    PROFILER_MARKER_UNTYPED("LazyCreateCSSAndStyleImageLoaders", DOM,
   8204                            MarkerStack::Capture());
   8205  }
   8206  mStyleImageLoader = new css::ImageLoader(this);
   8207  mCSSLoader = new css::Loader(this);
   8208  mCSSLoader->SetCompatibilityMode(mCompatMode);
   8209 }
   8210 
   8211 nsIGlobalObject* Document::GetScopeObject() const {
   8212  nsCOMPtr<nsIGlobalObject> scope(do_QueryReferent(mScopeObject));
   8213  return scope;
   8214 }
   8215 
   8216 DocGroup* Document::GetDocGroupOrCreate() {
   8217  if (!mDocGroup && GetBrowsingContext()) {
   8218    BrowsingContextGroup* group = GetBrowsingContext()->Group();
   8219    MOZ_ASSERT(group);
   8220 
   8221    mDocGroup = group->AddDocument(this);
   8222  }
   8223  return mDocGroup;
   8224 }
   8225 
   8226 void Document::SetScopeObject(nsIGlobalObject* aGlobal) {
   8227  mScopeObject = do_GetWeakReference(aGlobal);
   8228  if (aGlobal) {
   8229    mHasHadScriptHandlingObject = true;
   8230 
   8231    nsPIDOMWindowInner* window = aGlobal->GetAsInnerWindow();
   8232    if (!window) {
   8233      return;
   8234    }
   8235 
   8236    // Attempt to join a DocGroup based on our global and container now that our
   8237    // principal is locked in (due to being added to a window).
   8238    DocGroup* docGroup = GetDocGroupOrCreate();
   8239    if (!docGroup) {
   8240      // If we failed to join a DocGroup, inherit from our parent window.
   8241      //
   8242      // NOTE: It is possible for Document to be cross-origin to window while
   8243      // loading a cross-origin data document over XHR with CORS. In that case,
   8244      // the DocGroup can have a key which does not match NodePrincipal().
   8245      MOZ_ASSERT(!mDocumentContainer,
   8246                 "Must have DocGroup if loaded in a DocShell");
   8247      mDocGroup = window->GetDocGroup();
   8248      mDocGroup->AddDocument(this);
   8249    }
   8250 
   8251 #ifdef DEBUG
   8252    AssertDocGroupMatchesKey();
   8253 #endif
   8254    MOZ_ASSERT_IF(
   8255        mNodeInfoManager->GetArenaAllocator(),
   8256        mNodeInfoManager->GetArenaAllocator() == mDocGroup->ArenaAllocator());
   8257  }
   8258 }
   8259 
   8260 bool Document::ContainsEMEContent() {
   8261  nsPIDOMWindowInner* win = GetInnerWindow();
   8262  // Note this case is different from checking just media elements in that
   8263  // it covers when we've created MediaKeys but not associated them with a
   8264  // media element.
   8265  return win && win->HasActiveMediaKeysInstance();
   8266 }
   8267 
   8268 bool Document::ContainsMSEContent() {
   8269  bool containsMSE = false;
   8270  EnumerateActivityObservers([&containsMSE](nsISupports* aSupports) {
   8271    nsCOMPtr<nsIContent> content(do_QueryInterface(aSupports));
   8272    if (auto* mediaElem = HTMLMediaElement::FromNodeOrNull(content)) {
   8273      RefPtr<MediaSource> ms = mediaElem->GetMozMediaSourceObject();
   8274      if (ms) {
   8275        containsMSE = true;
   8276      }
   8277    }
   8278  });
   8279  return containsMSE;
   8280 }
   8281 
   8282 static void NotifyActivityChangedCallback(nsISupports* aSupports) {
   8283  nsCOMPtr<nsIContent> content(do_QueryInterface(aSupports));
   8284  if (auto* mediaElem = HTMLMediaElement::FromNodeOrNull(content)) {
   8285    mediaElem->NotifyOwnerDocumentActivityChanged();
   8286  }
   8287  nsCOMPtr<nsIDocumentActivity> objectDocumentActivity(
   8288      do_QueryInterface(aSupports));
   8289  if (objectDocumentActivity) {
   8290    objectDocumentActivity->NotifyOwnerDocumentActivityChanged();
   8291  } else {
   8292    nsCOMPtr<nsIImageLoadingContent> imageLoadingContent(
   8293        do_QueryInterface(aSupports));
   8294    if (imageLoadingContent) {
   8295      auto* ilc =
   8296          static_cast<nsImageLoadingContent*>(imageLoadingContent.get());
   8297      ilc->NotifyOwnerDocumentActivityChanged();
   8298    }
   8299  }
   8300 }
   8301 
   8302 void Document::NotifyActivityChanged() {
   8303  EnumerateActivityObservers(NotifyActivityChangedCallback);
   8304  // https://w3c.github.io/screen-wake-lock/#handling-document-loss-of-full-activity
   8305  if (!IsActive()) {
   8306    UnlockAllWakeLocks(WakeLockType::Screen);
   8307  }
   8308 }
   8309 
   8310 void Document::SetContainer(nsDocShell* aContainer) {
   8311  if (aContainer) {
   8312    mDocumentContainer = aContainer;
   8313  } else {
   8314    mDocumentContainer = WeakPtr<nsDocShell>();
   8315  }
   8316 
   8317  mInChromeDocShell =
   8318      aContainer && aContainer->GetBrowsingContext()->IsChrome();
   8319 
   8320  NotifyActivityChanged();
   8321 
   8322  // IsTopLevelWindowInactive depends on the docshell, so
   8323  // update the cached value now that it's available.
   8324  UpdateDocumentStates(DocumentState::WINDOW_INACTIVE, false);
   8325  if (!aContainer) {
   8326    return;
   8327  }
   8328 
   8329  BrowsingContext* context = aContainer->GetBrowsingContext();
   8330  MOZ_ASSERT_IF(context && mDocGroup,
   8331                context->Group() == mDocGroup->GetBrowsingContextGroup());
   8332  if (context && context->IsContent()) {
   8333    SetIsTopLevelContentDocument(context->IsTopContent());
   8334    SetIsContentDocument(true);
   8335  } else {
   8336    SetIsTopLevelContentDocument(false);
   8337    SetIsContentDocument(false);
   8338  }
   8339 }
   8340 
   8341 nsISupports* Document::GetContainer() const {
   8342  return static_cast<nsIDocShell*>(mDocumentContainer);
   8343 }
   8344 
   8345 void Document::SetScriptGlobalObject(
   8346    nsIScriptGlobalObject* aScriptGlobalObject) {
   8347  MOZ_ASSERT(aScriptGlobalObject || !mAnimationController ||
   8348                 mAnimationController->IsPausedByType(
   8349                     SMILTimeContainer::PAUSE_PAGEHIDE |
   8350                     SMILTimeContainer::PAUSE_BEGIN),
   8351             "Clearing window pointer while animations are unpaused");
   8352 
   8353  if (mScriptGlobalObject && !aScriptGlobalObject) {
   8354    // We're detaching from the window.  We need to grab a pointer to
   8355    // our layout history state now.
   8356    mLayoutHistoryState = GetLayoutHistoryState();
   8357 
   8358    // Also make sure to remove our onload blocker now if we haven't done it yet
   8359    if (mOnloadBlockCount != 0) {
   8360      nsCOMPtr<nsILoadGroup> loadGroup = GetDocumentLoadGroup();
   8361      if (loadGroup) {
   8362        loadGroup->RemoveRequest(mOnloadBlocker, nullptr, NS_OK);
   8363      }
   8364    }
   8365 
   8366    if (GetController().isSome()) {
   8367      if (imgLoader* loader = nsContentUtils::GetImgLoaderForDocument(this)) {
   8368        loader->ClearCacheForControlledDocument(this);
   8369      }
   8370 
   8371      // We may become controlled again if this document comes back out
   8372      // of bfcache.  Clear our state to allow that to happen.  Only
   8373      // clear this flag if we are actually controlled, though, so pages
   8374      // that were force reloaded don't become controlled when they
   8375      // come out of bfcache.
   8376      mMaybeServiceWorkerControlled = false;
   8377    }
   8378 
   8379    if (GetWindowContext()) {
   8380      // The document is about to lose its window, so this is a good time to
   8381      // send our page use counters, while we still have access to our
   8382      // WindowContext.
   8383      //
   8384      // (We also do this in nsGlobalWindowInner::FreeInnerObjects(), which
   8385      // catches some cases of documents losing their window that don't
   8386      // get in here.)
   8387      SendPageUseCounters();
   8388    }
   8389  }
   8390 
   8391  // BlockOnload() might be called before mScriptGlobalObject is set.
   8392  // We may need to add the blocker once mScriptGlobalObject is set.
   8393  bool needOnloadBlocker = !mScriptGlobalObject && aScriptGlobalObject;
   8394 
   8395  mScriptGlobalObject = aScriptGlobalObject;
   8396 
   8397  if (needOnloadBlocker) {
   8398    EnsureOnloadBlocker();
   8399  }
   8400 
   8401  // FIXME(emilio): is this really needed?
   8402  MaybeScheduleFrameRequestCallbacks();
   8403 
   8404  if (aScriptGlobalObject) {
   8405    // Go back to using the docshell for the layout history state
   8406    mLayoutHistoryState = nullptr;
   8407    SetScopeObject(aScriptGlobalObject);
   8408    mHasHadDefaultView = true;
   8409 
   8410    if (mAllowDNSPrefetch) {
   8411      nsCOMPtr<nsIDocShell> docShell(mDocumentContainer);
   8412      if (docShell) {
   8413 #ifdef DEBUG
   8414        nsCOMPtr<nsIWebNavigation> webNav =
   8415            do_GetInterface(aScriptGlobalObject);
   8416        NS_ASSERTION(SameCOMIdentity(webNav, docShell),
   8417                     "Unexpected container or script global?");
   8418 #endif
   8419        bool allowDNSPrefetch;
   8420        docShell->GetAllowDNSPrefetch(&allowDNSPrefetch);
   8421        mAllowDNSPrefetch = allowDNSPrefetch;
   8422      }
   8423    }
   8424 
   8425    // If we are set in a window that is already focused we should remember this
   8426    // as the time the document gained focus.
   8427    if (HasFocus(IgnoreErrors())) {
   8428      SetLastFocusTime(TimeStamp::Now());
   8429    }
   8430  }
   8431 
   8432  // Remember the pointer to our window (or lack there of), to avoid
   8433  // having to QI every time it's asked for.
   8434  nsCOMPtr<nsPIDOMWindowInner> window = do_QueryInterface(mScriptGlobalObject);
   8435  mWindow = window;
   8436 
   8437  if (mReadyState != READYSTATE_COMPLETE) {
   8438    if (auto* wgc = GetWindowGlobalChild()) {
   8439      // This gets unset on OnPageShow.
   8440      wgc->BlockBFCacheFor(BFCacheStatus::PAGE_LOADING);
   8441    }
   8442  }
   8443 
   8444  // Now that we know what our window is, we can flush the CSP errors to the
   8445  // Web Console. We are flushing all messages that occurred and were stored in
   8446  // the queue prior to this point.
   8447  if (nsIContentSecurityPolicy* csp =
   8448          PolicyContainer::GetCSP(mPolicyContainer)) {
   8449    nsCSPContext::Cast(csp)->flushConsoleMessages();
   8450  }
   8451 
   8452  nsCOMPtr<nsIHttpChannelInternal> internalChannel =
   8453      do_QueryInterface(GetChannel());
   8454  if (internalChannel) {
   8455    nsCOMArray<nsISecurityConsoleMessage> messages;
   8456    DebugOnly<nsresult> rv = internalChannel->TakeAllSecurityMessages(messages);
   8457    MOZ_ASSERT(NS_SUCCEEDED(rv));
   8458    SendToConsole(messages);
   8459  }
   8460 
   8461  // Set our visibility state, but do not fire the event.  This is correct
   8462  // because either we're coming out of bfcache (in which case IsVisible() will
   8463  // still test false at this point and no state change will happen) or we're
   8464  // doing the initial document load and don't want to fire the event for this
   8465  // change.
   8466  //
   8467  // When the visibility is changed, notify it to observers.
   8468  // Some observers need the notification, for example HTMLMediaElement uses
   8469  // it to update internal media resource allocation.
   8470  // When video is loaded via VideoDocument, HTMLMediaElement and MediaDecoder
   8471  // creation are already done before Document::SetScriptGlobalObject() call.
   8472  // MediaDecoder decides whether starting decoding is decided based on
   8473  // document's visibility. When the MediaDecoder is created,
   8474  // Document::SetScriptGlobalObject() is not yet called and document is
   8475  // hidden state. Therefore the MediaDecoder decides that decoding is
   8476  // not yet necessary. But soon after Document::SetScriptGlobalObject()
   8477  // call, the document becomes not hidden. At the time, MediaDecoder needs
   8478  // to know it and needs to start updating decoding.
   8479  UpdateVisibilityState(DispatchVisibilityChange::No);
   8480 
   8481  // The global in the template contents owner document should be the same.
   8482  if (mTemplateContentsOwner && mTemplateContentsOwner != this) {
   8483    mTemplateContentsOwner->SetScriptGlobalObject(aScriptGlobalObject);
   8484  }
   8485 
   8486  // Tell the script loader about the new global object.
   8487  if (mScriptLoader && !IsTemplateContentsOwner()) {
   8488    mScriptLoader->SetGlobalObject(mScriptGlobalObject);
   8489  }
   8490 
   8491  if (!mMaybeServiceWorkerControlled && mDocumentContainer &&
   8492      mScriptGlobalObject && GetChannel()) {
   8493    // If we are shift-reloaded, don't associate with a ServiceWorker.
   8494    if (mDocumentContainer->IsForceReloading()) {
   8495      NS_WARNING("Page was shift reloaded, skipping ServiceWorker control");
   8496      return;
   8497    }
   8498 
   8499    mMaybeServiceWorkerControlled = true;
   8500  }
   8501 }
   8502 
   8503 nsIScriptGlobalObject* Document::GetScriptHandlingObjectInternal() const {
   8504  MOZ_ASSERT(!mScriptGlobalObject,
   8505             "Do not call this when mScriptGlobalObject is set!");
   8506  if (mHasHadDefaultView) {
   8507    return nullptr;
   8508  }
   8509 
   8510  nsCOMPtr<nsIScriptGlobalObject> scriptHandlingObject =
   8511      do_QueryReferent(mScopeObject);
   8512  nsCOMPtr<nsPIDOMWindowInner> win = do_QueryInterface(scriptHandlingObject);
   8513  if (win) {
   8514    nsPIDOMWindowOuter* outer = win->GetOuterWindow();
   8515    if (!outer || outer->GetCurrentInnerWindow() != win) {
   8516      NS_WARNING("Wrong inner/outer window combination!");
   8517      return nullptr;
   8518    }
   8519  }
   8520  return scriptHandlingObject;
   8521 }
   8522 void Document::SetScriptHandlingObject(nsIScriptGlobalObject* aScriptObject) {
   8523  NS_ASSERTION(!mScriptGlobalObject || mScriptGlobalObject == aScriptObject,
   8524               "Wrong script object!");
   8525  if (aScriptObject) {
   8526    SetScopeObject(aScriptObject);
   8527    mHasHadDefaultView = false;
   8528  }
   8529 }
   8530 
   8531 nsPIDOMWindowOuter* Document::GetWindowInternal() const {
   8532  MOZ_ASSERT(!mWindow, "This should not be called when mWindow is not null!");
   8533  // Let's use mScriptGlobalObject. Even if the document is already removed from
   8534  // the docshell, the outer window might be still obtainable from the it.
   8535  nsCOMPtr<nsPIDOMWindowOuter> win;
   8536  if (mRemovedFromDocShell) {
   8537    // The docshell returns the outer window we are done.
   8538    nsCOMPtr<nsIDocShell> kungFuDeathGrip(mDocumentContainer);
   8539    if (kungFuDeathGrip) {
   8540      win = kungFuDeathGrip->GetWindow();
   8541    }
   8542  } else {
   8543    if (nsCOMPtr<nsPIDOMWindowInner> inner =
   8544            do_QueryInterface(mScriptGlobalObject)) {
   8545      // mScriptGlobalObject is always the inner window, let's get the outer.
   8546      win = inner->GetOuterWindow();
   8547    }
   8548  }
   8549 
   8550  return win;
   8551 }
   8552 
   8553 bool Document::InternalAllowXULXBL() {
   8554  if (nsContentUtils::AllowXULXBLForPrincipal(NodePrincipal())) {
   8555    mAllowXULXBL = eTriTrue;
   8556    return true;
   8557  }
   8558 
   8559  mAllowXULXBL = eTriFalse;
   8560  return false;
   8561 }
   8562 
   8563 // Note: We don't hold a reference to the document observer; we assume
   8564 // that it has a live reference to the document.
   8565 void Document::AddObserver(nsIDocumentObserver* aObserver) {
   8566  NS_ASSERTION(mObservers.IndexOf(aObserver) == nsTArray<int>::NoIndex,
   8567               "Observer already in the list");
   8568  mObservers.AppendElement(aObserver);
   8569  AddMutationObserver(aObserver);
   8570 }
   8571 
   8572 bool Document::RemoveObserver(nsIDocumentObserver* aObserver) {
   8573  // If we're in the process of destroying the document (and we're
   8574  // informing the observers of the destruction), don't remove the
   8575  // observers from the list. This is not a big deal, since we
   8576  // don't hold a live reference to the observers.
   8577  if (!mInDestructor) {
   8578    RemoveMutationObserver(aObserver);
   8579    return mObservers.RemoveElement(aObserver);
   8580  }
   8581 
   8582  return mObservers.Contains(aObserver);
   8583 }
   8584 
   8585 void Document::BeginUpdate() {
   8586  ++mUpdateNestLevel;
   8587  nsContentUtils::AddScriptBlocker();
   8588  NS_DOCUMENT_NOTIFY_OBSERVERS(BeginUpdate, (this));
   8589 }
   8590 
   8591 void Document::EndUpdate() {
   8592  const bool reset = !mPendingMaybeEditingStateChanged;
   8593  mPendingMaybeEditingStateChanged = true;
   8594 
   8595  NS_DOCUMENT_NOTIFY_OBSERVERS(EndUpdate, (this));
   8596 
   8597  --mUpdateNestLevel;
   8598 
   8599  nsContentUtils::RemoveScriptBlocker();
   8600 
   8601  if (mXULBroadcastManager) {
   8602    mXULBroadcastManager->MaybeBroadcast();
   8603  }
   8604 
   8605  if (reset) {
   8606    mPendingMaybeEditingStateChanged = false;
   8607  }
   8608  MaybeEditingStateChanged();
   8609 }
   8610 
   8611 void Document::BeginLoad() {
   8612  if (IsEditingOn()) {
   8613    // Reset() blows away all event listeners in the document, and our
   8614    // editor relies heavily on those. Midas is turned on, to make it
   8615    // work, re-initialize it to give it a chance to add its event
   8616    // listeners again.
   8617 
   8618    TurnEditingOff();
   8619    EditingStateChanged();
   8620  }
   8621 
   8622  MOZ_ASSERT(!mDidCallBeginLoad);
   8623  mDidCallBeginLoad = true;
   8624 
   8625  // Block onload here to prevent having to deal with blocking and
   8626  // unblocking it while we know the document is loading.
   8627  BlockOnload();
   8628  mDidFireDOMContentLoaded = false;
   8629  BlockDOMContentLoaded();
   8630 
   8631  if (mScriptLoader && !IsInitialDocument()) {
   8632    mScriptLoader->BeginDeferringScripts();
   8633  }
   8634 
   8635  NS_DOCUMENT_NOTIFY_OBSERVERS(BeginLoad, (this));
   8636 }
   8637 
   8638 void Document::MozSetImageElement(const nsAString& aImageElementId,
   8639                                  Element* aElement) {
   8640  if (aImageElementId.IsEmpty()) return;
   8641 
   8642  // Hold a script blocker while calling SetImageElement since that can call
   8643  // out to id-observers
   8644  nsAutoScriptBlocker scriptBlocker;
   8645 
   8646  IdentifierMapEntry* entry = mIdentifierMap.PutEntry(aImageElementId);
   8647  if (entry) {
   8648    entry->SetImageElement(aElement);
   8649    if (entry->IsEmpty()) {
   8650      mIdentifierMap.RemoveEntry(entry);
   8651    }
   8652  }
   8653 }
   8654 
   8655 void Document::DispatchContentLoadedEvents() {
   8656  // If you add early returns from this method, make sure you're
   8657  // calling UnblockOnload properly.
   8658 
   8659  // Unpin references to preloaded images
   8660  mPreloadingImages.Clear();
   8661 
   8662  // DOM manipulation after content loaded should not care if the element
   8663  // came from the preloader.
   8664  mPreloadedPreconnects.Clear();
   8665 
   8666  if (mTiming) {
   8667    mTiming->NotifyDOMContentLoadedStart(Document::GetDocumentURI());
   8668  }
   8669 
   8670  // Dispatch observer notification to notify observers document is interactive.
   8671  nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
   8672  if (os) {
   8673    nsIPrincipal* principal = NodePrincipal();
   8674    os->NotifyObservers(ToSupports(this),
   8675                        principal->IsSystemPrincipal()
   8676                            ? "chrome-document-interactive"
   8677                            : "content-document-interactive",
   8678                        nullptr);
   8679  }
   8680 
   8681  // Fire a DOM event notifying listeners that this document has been
   8682  // loaded (excluding images and other loads initiated by this
   8683  // document).
   8684  nsContentUtils::DispatchTrustedEvent(this, this, u"DOMContentLoaded"_ns,
   8685                                       CanBubble::eYes, Cancelable::eNo);
   8686 
   8687  if (auto* const window = GetInnerWindow()) {
   8688    const RefPtr<ServiceWorkerContainer> serviceWorker =
   8689        window->Navigator()->ServiceWorker();
   8690 
   8691    // This could cause queued messages from a service worker to get
   8692    // dispatched on serviceWorker.
   8693    serviceWorker->StartMessages();
   8694  }
   8695 
   8696  if (MayStartLayout()) {
   8697    MaybeResolveReadyForIdle();
   8698  }
   8699 
   8700  if (mTiming) {
   8701    mTiming->NotifyDOMContentLoadedEnd(Document::GetDocumentURI());
   8702  }
   8703 
   8704  // If this document is a [i]frame, fire a DOMFrameContentLoaded
   8705  // event on all parent documents notifying that the HTML (excluding
   8706  // other external files such as images and stylesheets) in a frame
   8707  // has finished loading.
   8708 
   8709  // target_frame is the [i]frame element that will be used as the
   8710  // target for the event. It's the [i]frame whose content is done
   8711  // loading.
   8712  nsCOMPtr<Element> target_frame = GetEmbedderElement();
   8713 
   8714  if (target_frame && target_frame->IsInComposedDoc()) {
   8715    nsCOMPtr<Document> parent = target_frame->OwnerDoc();
   8716    while (parent) {
   8717      RefPtr<Event> event;
   8718      if (parent) {
   8719        IgnoredErrorResult ignored;
   8720        event = parent->CreateEvent(u"Events"_ns, CallerType::System, ignored);
   8721      }
   8722 
   8723      if (event) {
   8724        event->InitEvent(u"DOMFrameContentLoaded"_ns, true, true);
   8725 
   8726        event->SetTarget(target_frame);
   8727        event->SetTrusted(true);
   8728 
   8729        // To dispatch this event we must manually call
   8730        // EventDispatcher::Dispatch() on the ancestor document since the
   8731        // target is not in the same document, so the event would never reach
   8732        // the ancestor document if we used the normal event
   8733        // dispatching code.
   8734 
   8735        WidgetEvent* innerEvent = event->WidgetEventPtr();
   8736        if (innerEvent) {
   8737          nsEventStatus status = nsEventStatus_eIgnore;
   8738 
   8739          if (RefPtr<nsPresContext> context = parent->GetPresContext()) {
   8740            EventDispatcher::Dispatch(parent, context, innerEvent, event,
   8741                                      &status);
   8742          }
   8743        }
   8744      }
   8745 
   8746      parent = parent->GetInProcessParentDocument();
   8747    }
   8748  }
   8749 
   8750  nsPIDOMWindowInner* inner = GetInnerWindow();
   8751  if (inner) {
   8752    inner->NoteDOMContentLoaded();
   8753  }
   8754 
   8755  // TODO
   8756  if (mMaybeServiceWorkerControlled) {
   8757    using mozilla::dom::ServiceWorkerManager;
   8758    RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
   8759    if (swm) {
   8760      Maybe<ClientInfo> clientInfo = GetClientInfo();
   8761      if (clientInfo.isSome()) {
   8762        swm->MaybeCheckNavigationUpdate(clientInfo.ref());
   8763      }
   8764    }
   8765  }
   8766 
   8767  if (mSetCompleteAfterDOMContentLoaded) {
   8768    SetReadyStateInternal(ReadyState::READYSTATE_COMPLETE);
   8769    mSetCompleteAfterDOMContentLoaded = false;
   8770  }
   8771 
   8772  UnblockOnload(true);
   8773 }
   8774 
   8775 void Document::EndLoad() {
   8776  bool turnOnEditing =
   8777      mParser && (IsInDesignMode() || mContentEditableCount > 0);
   8778 
   8779 #if defined(DEBUG)
   8780  // only assert if nothing stopped the load on purpose
   8781  if (!mParserAborted) {
   8782    nsContentSecurityUtils::AssertAboutPageHasCSP(this);
   8783    nsContentSecurityUtils::AssertChromePageHasCSP(this);
   8784  }
   8785 #endif
   8786 
   8787  // EndLoad may have been called without a matching call to BeginLoad, in the
   8788  // case of a failed parse (for example, due to timeout). In such a case, we
   8789  // still want to execute part of this code to do appropriate cleanup, but we
   8790  // gate part of it because it is intended to match 1-for-1 with calls to
   8791  // BeginLoad. We have an explicit flag bit for this purpose, since it's
   8792  // complicated and error prone to derive this condition from other related
   8793  // flags that can be manipulated outside of a BeginLoad/EndLoad pair.
   8794 
   8795  // Part 1: Code that always executes to cleanup end of parsing, whether
   8796  // that parsing was successful or not.
   8797 
   8798  // Drop the ref to our parser, if any, but keep hold of the sink so that we
   8799  // can flush it from FlushPendingNotifications as needed.  We might have to
   8800  // do that to get a StartLayout() to happen.
   8801  if (mParser) {
   8802    mWeakSink = do_GetWeakReference(mParser->GetContentSink());
   8803    mParser = nullptr;
   8804  }
   8805 
   8806  // Update the attributes on the PerformanceNavigationTiming before notifying
   8807  // the onload observers.
   8808  if (nsPIDOMWindowInner* window = GetInnerWindow()) {
   8809    if (RefPtr<Performance> performance = window->GetPerformance()) {
   8810      performance->UpdateNavigationTimingEntry();
   8811    }
   8812  }
   8813 
   8814  NS_DOCUMENT_NOTIFY_OBSERVERS(EndLoad, (this));
   8815 
   8816  // Part 2: Code that only executes when this EndLoad matches a BeginLoad.
   8817 
   8818  if (!mDidCallBeginLoad) {
   8819    return;
   8820  }
   8821  mDidCallBeginLoad = false;
   8822 
   8823  UnblockDOMContentLoaded();
   8824 
   8825  if (turnOnEditing) {
   8826    EditingStateChanged();
   8827  }
   8828 
   8829  if (!GetWindow()) {
   8830    // This is a document that's not in a window.  For example, this could be an
   8831    // XMLHttpRequest responseXML document, or a document created via DOMParser
   8832    // or DOMImplementation.  We don't reach this code normally for such
   8833    // documents (which is not obviously correct), but can reach it via
   8834    // document.open()/document.close().
   8835    //
   8836    // Such documents don't fire load events, but per spec should set their
   8837    // readyState to "complete" when parsing and all loading of subresources is
   8838    // done.  Parsing is done now, and documents not in a window don't load
   8839    // subresources, so just go ahead and mark ourselves as complete.
   8840    SetReadyStateInternal(Document::READYSTATE_COMPLETE,
   8841                          /* updateTimingInformation = */ false);
   8842 
   8843    // Reset mSkipLoadEventAfterClose just in case.
   8844    mSkipLoadEventAfterClose = false;
   8845  }
   8846 }
   8847 
   8848 void Document::UnblockDOMContentLoaded() {
   8849  MOZ_ASSERT(mBlockDOMContentLoaded);
   8850  if (--mBlockDOMContentLoaded != 0 || mDidFireDOMContentLoaded) {
   8851    return;
   8852  }
   8853 
   8854  MOZ_LOG(gDocumentLeakPRLog, LogLevel::Debug,
   8855          ("DOCUMENT %p UnblockDOMContentLoaded", this));
   8856 
   8857  mDidFireDOMContentLoaded = true;
   8858 
   8859  MOZ_ASSERT(IsInitialDocument() || mReadyState == READYSTATE_INTERACTIVE);
   8860  if (!mSynchronousDOMContentLoaded) {
   8861    MOZ_RELEASE_ASSERT(NS_IsMainThread());
   8862    MOZ_ASSERT(!IsInitialDocument());
   8863    nsCOMPtr<nsIRunnable> ev =
   8864        NewRunnableMethod("Document::DispatchContentLoadedEvents", this,
   8865                          &Document::DispatchContentLoadedEvents);
   8866    Dispatch(ev.forget());
   8867  } else {
   8868    DispatchContentLoadedEvents();
   8869  }
   8870 }
   8871 
   8872 void Document::ElementStateChanged(Element* aElement, ElementState aStateMask) {
   8873  MOZ_ASSERT(!nsContentUtils::IsSafeToRunScript(),
   8874             "Someone forgot a scriptblocker");
   8875  NS_DOCUMENT_NOTIFY_OBSERVERS(ElementStateChanged,
   8876                               (this, aElement, aStateMask));
   8877 }
   8878 
   8879 void Document::RuleChanged(StyleSheet& aSheet, css::Rule*,
   8880                           const StyleRuleChange&) {
   8881  if (aSheet.IsApplicable()) {
   8882    ApplicableStylesChanged();
   8883  }
   8884 }
   8885 
   8886 void Document::RuleAdded(StyleSheet& aSheet, css::Rule& aRule) {
   8887  if (aRule.IsIncompleteImportRule()) {
   8888    return;
   8889  }
   8890 
   8891  if (aSheet.IsApplicable()) {
   8892    ApplicableStylesChanged();
   8893  }
   8894 }
   8895 
   8896 void Document::ImportRuleLoaded(StyleSheet& aSheet) {
   8897  if (aSheet.IsApplicable()) {
   8898    ApplicableStylesChanged();
   8899  }
   8900 }
   8901 
   8902 void Document::RuleRemoved(StyleSheet& aSheet, css::Rule& aRule) {
   8903  if (aSheet.IsApplicable()) {
   8904    ApplicableStylesChanged();
   8905  }
   8906 }
   8907 
   8908 static void UnbindAnonymousContent(AnonymousContent& aAnonContent) {
   8909  nsCOMPtr<nsINode> parent = aAnonContent.Host()->GetParentNode();
   8910  if (!parent) {
   8911    return;
   8912  }
   8913  MOZ_ASSERT(parent->IsElement());
   8914  MOZ_ASSERT(parent->AsElement()->IsRootOfNativeAnonymousSubtree());
   8915  parent->RemoveChildNode(aAnonContent.Host(), true);
   8916 }
   8917 
   8918 static void BindAnonymousContent(AnonymousContent& aAnonContent,
   8919                                 Element& aContainer) {
   8920  UnbindAnonymousContent(aAnonContent);
   8921  aContainer.AppendChildTo(aAnonContent.Host(), true, IgnoreErrors());
   8922 }
   8923 
   8924 void Document::RemoveCustomContentContainer() {
   8925  RefPtr container = std::move(mCustomContentContainer);
   8926  if (!container) {
   8927    return;
   8928  }
   8929  nsAutoScriptBlocker scriptBlocker;
   8930  if (DevToolsAnonymousAndShadowEventsEnabled()) {
   8931    container->QueueDevtoolsAnonymousEvent(/* aIsRemove = */ true);
   8932  }
   8933  if (PresShell* ps = GetPresShell()) {
   8934    ps->ContentWillBeRemoved(container, {});
   8935  }
   8936  container->UnbindFromTree();
   8937 }
   8938 
   8939 void Document::CreateCustomContentContainerIfNeeded() {
   8940  if (mAnonymousContents.IsEmpty()) {
   8941    MOZ_ASSERT(!mCustomContentContainer);
   8942    return;
   8943  }
   8944  if (mCustomContentContainer) {
   8945    return;
   8946  }
   8947  RefPtr root = GetRootElement();
   8948  if (!root) {
   8949    // We'll deal with it when we get a root element, if needed.
   8950    return;
   8951  }
   8952  // Create the custom content container.
   8953  RefPtr container = CreateHTMLElement(nsGkAtoms::div);
   8954 #ifdef DEBUG
   8955  // We restyle our mCustomContentContainer, even though it's root anonymous
   8956  // content.  Normally that's not OK because the frame constructor doesn't know
   8957  // how to order the frame tree in such cases, but we make this work for this
   8958  // particular case, so it's OK.
   8959  container->SetProperty(nsGkAtoms::restylableAnonymousNode,
   8960                         reinterpret_cast<void*>(true));
   8961 #endif  // DEBUG
   8962  container->SetProperty(nsGkAtoms::docLevelNativeAnonymousContent,
   8963                         reinterpret_cast<void*>(true));
   8964  container->SetIsNativeAnonymousRoot();
   8965  // Do not create an accessible object for the container.
   8966  container->SetAttr(kNameSpaceID_None, nsGkAtoms::role, u"presentation"_ns,
   8967                     false);
   8968  container->SetAttr(kNameSpaceID_None, nsGkAtoms::_class,
   8969                     u"moz-custom-content-container"_ns, false);
   8970  nsAutoScriptBlocker scriptBlocker;
   8971  BindContext context(*root, BindContext::ForNativeAnonymous);
   8972  if (NS_WARN_IF(NS_FAILED(container->BindToTree(context, *root)))) {
   8973    container->UnbindFromTree();
   8974    return;
   8975  }
   8976  mCustomContentContainer = container;
   8977  if (DevToolsAnonymousAndShadowEventsEnabled()) {
   8978    container->QueueDevtoolsAnonymousEvent(/* aIsRemove = */ false);
   8979  }
   8980  if (PresShell* ps = GetPresShell()) {
   8981    ps->ContentAppended(container, {});
   8982  }
   8983  for (auto& anonContent : mAnonymousContents) {
   8984    BindAnonymousContent(*anonContent, *container);
   8985  }
   8986 }
   8987 
   8988 already_AddRefed<AnonymousContent> Document::InsertAnonymousContent(
   8989    ErrorResult& aRv) {
   8990  RefPtr<AnonymousContent> anonContent = AnonymousContent::Create(*this);
   8991  if (!anonContent) {
   8992    aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
   8993    return nullptr;
   8994  }
   8995  mAnonymousContents.AppendElement(anonContent);
   8996  if (RefPtr container = mCustomContentContainer) {
   8997    BindAnonymousContent(*anonContent, *container);
   8998  } else {
   8999    CreateCustomContentContainerIfNeeded();
   9000  }
   9001  return anonContent.forget();
   9002 }
   9003 
   9004 void Document::RemoveAnonymousContent(AnonymousContent& aContent) {
   9005  nsAutoScriptBlocker scriptBlocker;
   9006 
   9007  auto index = mAnonymousContents.IndexOf(&aContent);
   9008  if (index == mAnonymousContents.NoIndex) {
   9009    return;
   9010  }
   9011 
   9012  mAnonymousContents.RemoveElementAt(index);
   9013  UnbindAnonymousContent(aContent);
   9014 
   9015  if (mAnonymousContents.IsEmpty()) {
   9016    RemoveCustomContentContainer();
   9017  }
   9018 }
   9019 
   9020 Maybe<ClientInfo> Document::GetClientInfo() const {
   9021  if (const Document* orig = GetOriginalDocument()) {
   9022    if (Maybe<ClientInfo> info = orig->GetClientInfo()) {
   9023      return info;
   9024    }
   9025  }
   9026 
   9027  if (nsPIDOMWindowInner* inner = GetInnerWindow()) {
   9028    return inner->GetClientInfo();
   9029  }
   9030 
   9031  return Maybe<ClientInfo>();
   9032 }
   9033 
   9034 Maybe<ClientState> Document::GetClientState() const {
   9035  if (const Document* orig = GetOriginalDocument()) {
   9036    if (Maybe<ClientState> state = orig->GetClientState()) {
   9037      return state;
   9038    }
   9039  }
   9040 
   9041  if (nsPIDOMWindowInner* inner = GetInnerWindow()) {
   9042    return inner->GetClientState();
   9043  }
   9044 
   9045  return Maybe<ClientState>();
   9046 }
   9047 
   9048 Maybe<ServiceWorkerDescriptor> Document::GetController() const {
   9049  if (const Document* orig = GetOriginalDocument()) {
   9050    if (Maybe<ServiceWorkerDescriptor> controller = orig->GetController()) {
   9051      return controller;
   9052    }
   9053  }
   9054 
   9055  if (nsPIDOMWindowInner* inner = GetInnerWindow()) {
   9056    return inner->GetController();
   9057  }
   9058 
   9059  return Maybe<ServiceWorkerDescriptor>();
   9060 }
   9061 
   9062 //
   9063 // Document interface
   9064 //
   9065 DocumentType* Document::GetDoctype() const {
   9066  for (nsIContent* child = GetFirstChild(); child;
   9067       child = child->GetNextSibling()) {
   9068    if (child->NodeType() == DOCUMENT_TYPE_NODE) {
   9069      return static_cast<DocumentType*>(child);
   9070    }
   9071  }
   9072  return nullptr;
   9073 }
   9074 
   9075 DOMImplementation* Document::GetImplementation(ErrorResult& rv) {
   9076  if (!mDOMImplementation) {
   9077    nsCOMPtr<nsIURI> uri;
   9078    NS_NewURI(getter_AddRefs(uri), "about:blank");
   9079    if (!uri) {
   9080      rv.Throw(NS_ERROR_OUT_OF_MEMORY);
   9081      return nullptr;
   9082    }
   9083    bool hasHadScriptObject = true;
   9084    nsIScriptGlobalObject* scriptObject =
   9085        GetScriptHandlingObject(hasHadScriptObject);
   9086    if (!scriptObject && hasHadScriptObject) {
   9087      rv.Throw(NS_ERROR_UNEXPECTED);
   9088      return nullptr;
   9089    }
   9090    mDOMImplementation = new DOMImplementation(
   9091        this, scriptObject ? scriptObject : GetScopeObject(), uri, uri);
   9092  }
   9093 
   9094  return mDOMImplementation;
   9095 }
   9096 
   9097 bool IsLowercaseASCII(const nsAString& aValue) {
   9098  int32_t len = aValue.Length();
   9099  for (int32_t i = 0; i < len; ++i) {
   9100    char16_t c = aValue[i];
   9101    if (!(0x0061 <= (c) && ((c) <= 0x007a))) {
   9102      return false;
   9103    }
   9104  }
   9105  return true;
   9106 }
   9107 
   9108 already_AddRefed<Element> Document::CreateElement(
   9109    const nsAString& aTagName, const ElementCreationOptionsOrString& aOptions,
   9110    ErrorResult& rv) {
   9111  rv = nsContentUtils::CheckQName(aTagName, false);
   9112  if (rv.Failed()) {
   9113    return nullptr;
   9114  }
   9115 
   9116  bool needsLowercase = IsHTMLDocument() && !IsLowercaseASCII(aTagName);
   9117  nsAutoString lcTagName;
   9118  if (needsLowercase) {
   9119    nsContentUtils::ASCIIToLower(aTagName, lcTagName);
   9120  }
   9121 
   9122  const nsString* is = nullptr;
   9123  PseudoStyleType pseudoType = PseudoStyleType::NotPseudo;
   9124  if (aOptions.IsElementCreationOptions()) {
   9125    const ElementCreationOptions& options =
   9126        aOptions.GetAsElementCreationOptions();
   9127 
   9128    if (options.mIs.WasPassed()) {
   9129      is = &options.mIs.Value();
   9130    }
   9131 
   9132    // Check 'pseudo' and throw an exception if it's not one allowed
   9133    // with CSS_PSEUDO_ELEMENT_IS_JS_CREATED_NAC.
   9134    if (options.mPseudo.WasPassed()) {
   9135      Maybe<PseudoStyleRequest> request =
   9136          nsCSSPseudoElements::ParsePseudoElement(options.mPseudo.Value());
   9137      if (!request || request->IsNotPseudo() ||
   9138          !nsCSSPseudoElements::PseudoElementIsJSCreatedNAC(request->mType)) {
   9139        rv.ThrowNotSupportedError("Invalid pseudo-element");
   9140        return nullptr;
   9141      }
   9142      pseudoType = request->mType;
   9143    }
   9144  }
   9145 
   9146  RefPtr<Element> elem = CreateElem(needsLowercase ? lcTagName : aTagName,
   9147                                    nullptr, mDefaultElementType, is);
   9148 
   9149  if (pseudoType != PseudoStyleType::NotPseudo) {
   9150    elem->SetPseudoElementType(pseudoType);
   9151  }
   9152 
   9153  return elem.forget();
   9154 }
   9155 
   9156 already_AddRefed<Element> Document::CreateElementNS(
   9157    const nsAString& aNamespaceURI, const nsAString& aQualifiedName,
   9158    const ElementCreationOptionsOrString& aOptions, ErrorResult& rv) {
   9159  RefPtr<mozilla::dom::NodeInfo> nodeInfo;
   9160  rv = nsContentUtils::GetNodeInfoFromQName(aNamespaceURI, aQualifiedName,
   9161                                            mNodeInfoManager, ELEMENT_NODE,
   9162                                            getter_AddRefs(nodeInfo));
   9163  if (rv.Failed()) {
   9164    return nullptr;
   9165  }
   9166 
   9167  const nsString* is = nullptr;
   9168  if (aOptions.IsElementCreationOptions()) {
   9169    const ElementCreationOptions& options =
   9170        aOptions.GetAsElementCreationOptions();
   9171    if (options.mIs.WasPassed()) {
   9172      is = &options.mIs.Value();
   9173    }
   9174  }
   9175 
   9176  nsCOMPtr<Element> element;
   9177  rv = NS_NewElement(getter_AddRefs(element), nodeInfo.forget(),
   9178                     NOT_FROM_PARSER, is);
   9179  if (rv.Failed()) {
   9180    return nullptr;
   9181  }
   9182 
   9183  return element.forget();
   9184 }
   9185 
   9186 already_AddRefed<Element> Document::CreateXULElement(
   9187    const nsAString& aTagName, const ElementCreationOptionsOrString& aOptions,
   9188    ErrorResult& aRv) {
   9189  aRv = nsContentUtils::CheckQName(aTagName, false);
   9190  if (aRv.Failed()) {
   9191    return nullptr;
   9192  }
   9193 
   9194  const nsString* is = nullptr;
   9195  if (aOptions.IsElementCreationOptions()) {
   9196    const ElementCreationOptions& options =
   9197        aOptions.GetAsElementCreationOptions();
   9198    if (options.mIs.WasPassed()) {
   9199      is = &options.mIs.Value();
   9200    }
   9201  }
   9202 
   9203  RefPtr<Element> elem = CreateElem(aTagName, nullptr, kNameSpaceID_XUL, is);
   9204  if (!elem) {
   9205    aRv.Throw(NS_ERROR_NOT_AVAILABLE);
   9206    return nullptr;
   9207  }
   9208  return elem.forget();
   9209 }
   9210 
   9211 already_AddRefed<nsTextNode> Document::CreateEmptyTextNode() const {
   9212  RefPtr<nsTextNode> text = new (mNodeInfoManager) nsTextNode(mNodeInfoManager);
   9213  return text.forget();
   9214 }
   9215 
   9216 already_AddRefed<nsTextNode> Document::CreateTextNode(
   9217    const nsAString& aData) const {
   9218  RefPtr<nsTextNode> text = new (mNodeInfoManager) nsTextNode(mNodeInfoManager);
   9219  // Don't notify; this node is still being created.
   9220  text->SetText(aData, false);
   9221  return text.forget();
   9222 }
   9223 
   9224 already_AddRefed<DocumentFragment> Document::CreateDocumentFragment() const {
   9225  RefPtr<DocumentFragment> frag =
   9226      new (mNodeInfoManager) DocumentFragment(mNodeInfoManager);
   9227  return frag.forget();
   9228 }
   9229 
   9230 // Unfortunately, bareword "Comment" is ambiguous with some Mac system headers.
   9231 already_AddRefed<dom::Comment> Document::CreateComment(
   9232    const nsAString& aData) const {
   9233  RefPtr<dom::Comment> comment =
   9234      new (mNodeInfoManager) dom::Comment(mNodeInfoManager);
   9235 
   9236  // Don't notify; this node is still being created.
   9237  comment->SetText(aData, false);
   9238  return comment.forget();
   9239 }
   9240 
   9241 already_AddRefed<CDATASection> Document::CreateCDATASection(
   9242    const nsAString& aData, ErrorResult& rv) {
   9243  if (IsHTMLDocument()) {
   9244    rv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
   9245    return nullptr;
   9246  }
   9247 
   9248  if (FindInReadable(u"]]>"_ns, aData)) {
   9249    rv.Throw(NS_ERROR_DOM_INVALID_CHARACTER_ERR);
   9250    return nullptr;
   9251  }
   9252 
   9253  RefPtr<CDATASection> cdata =
   9254      new (mNodeInfoManager) CDATASection(mNodeInfoManager);
   9255 
   9256  // Don't notify; this node is still being created.
   9257  cdata->SetText(aData, false);
   9258 
   9259  return cdata.forget();
   9260 }
   9261 
   9262 already_AddRefed<ProcessingInstruction> Document::CreateProcessingInstruction(
   9263    const nsAString& aTarget, const nsAString& aData, ErrorResult& rv) const {
   9264  nsresult res = nsContentUtils::CheckQName(aTarget, false);
   9265  if (NS_FAILED(res)) {
   9266    rv.Throw(res);
   9267    return nullptr;
   9268  }
   9269 
   9270  if (FindInReadable(u"?>"_ns, aData)) {
   9271    rv.Throw(NS_ERROR_DOM_INVALID_CHARACTER_ERR);
   9272    return nullptr;
   9273  }
   9274 
   9275  RefPtr<ProcessingInstruction> pi =
   9276      NS_NewXMLProcessingInstruction(mNodeInfoManager, aTarget, aData);
   9277 
   9278  return pi.forget();
   9279 }
   9280 
   9281 already_AddRefed<Attr> Document::CreateAttribute(const nsAString& aName,
   9282                                                 ErrorResult& rv) {
   9283  if (!mNodeInfoManager) {
   9284    rv.Throw(NS_ERROR_NOT_INITIALIZED);
   9285    return nullptr;
   9286  }
   9287 
   9288  nsresult res = nsContentUtils::CheckQName(aName, false);
   9289  if (NS_FAILED(res)) {
   9290    rv.Throw(res);
   9291    return nullptr;
   9292  }
   9293 
   9294  nsAutoString name;
   9295  if (IsHTMLDocument()) {
   9296    nsContentUtils::ASCIIToLower(aName, name);
   9297  } else {
   9298    name = aName;
   9299  }
   9300 
   9301  RefPtr<mozilla::dom::NodeInfo> nodeInfo;
   9302  res = mNodeInfoManager->GetNodeInfo(name, nullptr, kNameSpaceID_None,
   9303                                      ATTRIBUTE_NODE, getter_AddRefs(nodeInfo));
   9304  if (NS_FAILED(res)) {
   9305    rv.Throw(res);
   9306    return nullptr;
   9307  }
   9308 
   9309  RefPtr<Attr> attribute =
   9310      new (mNodeInfoManager) Attr(nullptr, nodeInfo.forget(), u""_ns);
   9311  return attribute.forget();
   9312 }
   9313 
   9314 already_AddRefed<Attr> Document::CreateAttributeNS(
   9315    const nsAString& aNamespaceURI, const nsAString& aQualifiedName,
   9316    ErrorResult& rv) {
   9317  RefPtr<mozilla::dom::NodeInfo> nodeInfo;
   9318  rv = nsContentUtils::GetNodeInfoFromQName(aNamespaceURI, aQualifiedName,
   9319                                            mNodeInfoManager, ATTRIBUTE_NODE,
   9320                                            getter_AddRefs(nodeInfo));
   9321  if (rv.Failed()) {
   9322    return nullptr;
   9323  }
   9324 
   9325  RefPtr<Attr> attribute =
   9326      new (mNodeInfoManager) Attr(nullptr, nodeInfo.forget(), u""_ns);
   9327  return attribute.forget();
   9328 }
   9329 
   9330 void Document::ScheduleForPresAttrEvaluation(Element* aElement) {
   9331  MOZ_ASSERT(aElement->IsInComposedDoc());
   9332  DebugOnly<bool> inserted = mLazyPresElements.EnsureInserted(aElement);
   9333  MOZ_ASSERT(inserted);
   9334  if (aElement->HasServoData()) {
   9335    // TODO(emilio): RESTYLE_SELF is too strong, there should be no need to
   9336    // re-selector-match, but right now this is needed to pick up the new mapped
   9337    // attributes. We need something like RESTYLE_STYLE_ATTRIBUTE but for mapped
   9338    // attributes.
   9339    nsLayoutUtils::PostRestyleEvent(aElement, RestyleHint::RESTYLE_SELF,
   9340                                    nsChangeHint(0));
   9341  } else if (auto* presContext = GetPresContext()) {
   9342    presContext->RestyleManager()->IncrementUndisplayedRestyleGeneration();
   9343  }
   9344 }
   9345 
   9346 void Document::UnscheduleForPresAttrEvaluation(Element* aElement) {
   9347  mLazyPresElements.Remove(aElement);
   9348 }
   9349 
   9350 void Document::DoResolveScheduledPresAttrs() {
   9351  MOZ_ASSERT(!mLazyPresElements.IsEmpty());
   9352  for (Element* el : mLazyPresElements) {
   9353    MOZ_ASSERT(el->IsInComposedDoc(),
   9354               "Un-schedule when removing from the document");
   9355    MOZ_ASSERT(el->IsPendingMappedAttributeEvaluation());
   9356    if (auto* svg = SVGElement::FromNode(el)) {
   9357      // SVG does its own (very similar) thing, for now at least.
   9358      svg->UpdateMappedDeclarationBlock();
   9359    } else {
   9360      MappedDeclarationsBuilder builder(*el, *this,
   9361                                        el->GetMappedAttributeStyle());
   9362      auto function = el->GetAttributeMappingFunction();
   9363      function(builder);
   9364      el->SetMappedDeclarationBlock(builder.TakeDeclarationBlock());
   9365    }
   9366    MOZ_ASSERT(!el->IsPendingMappedAttributeEvaluation());
   9367  }
   9368  mLazyPresElements.Clear();
   9369 }
   9370 
   9371 already_AddRefed<nsSimpleContentList> Document::BlockedNodesByClassifier()
   9372    const {
   9373  RefPtr<nsSimpleContentList> list = new nsSimpleContentList(nullptr);
   9374 
   9375  for (const nsWeakPtr& weakNode : mBlockedNodesByClassifier) {
   9376    if (nsCOMPtr<nsIContent> node = do_QueryReferent(weakNode)) {
   9377      // Consider only nodes to which we have managed to get strong references.
   9378      // Coping with nullptrs since it's expected for nodes to disappear when
   9379      // nobody else is referring to them.
   9380      list->AppendElement(node);
   9381    }
   9382  }
   9383 
   9384  return list.forget();
   9385 }
   9386 
   9387 void Document::GetSelectedStyleSheetSet(nsAString& aSheetSet) {
   9388  aSheetSet.Truncate();
   9389 
   9390  // Look through our sheets, find the selected set title
   9391  size_t count = SheetCount();
   9392  nsAutoString title;
   9393  for (size_t index = 0; index < count; index++) {
   9394    StyleSheet* sheet = SheetAt(index);
   9395    NS_ASSERTION(sheet, "Null sheet in sheet list!");
   9396 
   9397    if (sheet->Disabled()) {
   9398      // Disabled sheets don't affect the currently selected set
   9399      continue;
   9400    }
   9401 
   9402    sheet->GetTitle(title);
   9403 
   9404    if (aSheetSet.IsEmpty()) {
   9405      aSheetSet = title;
   9406    } else if (!title.IsEmpty() && !aSheetSet.Equals(title)) {
   9407      // Sheets from multiple sets enabled; return null string, per spec.
   9408      SetDOMStringToNull(aSheetSet);
   9409      return;
   9410    }
   9411  }
   9412 }
   9413 
   9414 void Document::SetSelectedStyleSheetSet(const nsAString& aSheetSet) {
   9415  if (DOMStringIsNull(aSheetSet)) {
   9416    return;
   9417  }
   9418 
   9419  // Must update mLastStyleSheetSet before doing anything else with stylesheets
   9420  // or CSSLoaders.
   9421  mLastStyleSheetSet = aSheetSet;
   9422  EnableStyleSheetsForSetInternal(aSheetSet, true);
   9423 }
   9424 
   9425 void Document::SetPreferredStyleSheetSet(const nsAString& aSheetSet) {
   9426  mPreferredStyleSheetSet = aSheetSet;
   9427  // Only mess with our stylesheets if we don't have a lastStyleSheetSet, per
   9428  // spec.
   9429  if (DOMStringIsNull(mLastStyleSheetSet)) {
   9430    // Calling EnableStyleSheetsForSetInternal, not SetSelectedStyleSheetSet,
   9431    // per spec.  The idea here is that we're changing our preferred set and
   9432    // that shouldn't change the value of lastStyleSheetSet.  Also, we're
   9433    // using the Internal version so we can update the CSSLoader and not have
   9434    // to worry about null strings.
   9435    EnableStyleSheetsForSetInternal(aSheetSet, true);
   9436  }
   9437 }
   9438 
   9439 DOMStringList* Document::StyleSheetSets() {
   9440  if (!mStyleSheetSetList) {
   9441    mStyleSheetSetList = new DOMStyleSheetSetList(this);
   9442  }
   9443  return mStyleSheetSetList;
   9444 }
   9445 
   9446 void Document::EnableStyleSheetsForSet(const nsAString& aSheetSet) {
   9447  // Per spec, passing in null is a no-op.
   9448  if (!DOMStringIsNull(aSheetSet)) {
   9449    // Note: must make sure to not change the CSSLoader's preferred sheet --
   9450    // that value should be equal to either our lastStyleSheetSet (if that's
   9451    // non-null) or to our preferredStyleSheetSet.  And this method doesn't
   9452    // change either of those.
   9453    EnableStyleSheetsForSetInternal(aSheetSet, false);
   9454  }
   9455 }
   9456 
   9457 void Document::EnableStyleSheetsForSetInternal(const nsAString& aSheetSet,
   9458                                               bool aUpdateCSSLoader) {
   9459  size_t count = SheetCount();
   9460  nsAutoString title;
   9461  for (size_t index = 0; index < count; index++) {
   9462    StyleSheet* sheet = SheetAt(index);
   9463    NS_ASSERTION(sheet, "Null sheet in sheet list!");
   9464 
   9465    sheet->GetTitle(title);
   9466    if (!title.IsEmpty()) {
   9467      sheet->SetEnabled(title.Equals(aSheetSet));
   9468    }
   9469  }
   9470  if (aUpdateCSSLoader) {
   9471    EnsureCSSLoader().DocumentStyleSheetSetChanged();
   9472  }
   9473  if (EnsureStyleSet().StyleSheetsHaveChanged()) {
   9474    ApplicableStylesChanged();
   9475  }
   9476 }
   9477 
   9478 void Document::GetCharacterSet(nsAString& aCharacterSet) const {
   9479  nsAutoCString charset;
   9480  GetDocumentCharacterSet()->Name(charset);
   9481  CopyASCIItoUTF16(charset, aCharacterSet);
   9482 }
   9483 
   9484 already_AddRefed<nsINode> Document::ImportNode(nsINode& aNode, bool aDeep,
   9485                                               ErrorResult& rv) const {
   9486  nsINode* imported = &aNode;
   9487 
   9488  switch (imported->NodeType()) {
   9489    case DOCUMENT_NODE: {
   9490      break;
   9491    }
   9492    case DOCUMENT_FRAGMENT_NODE:
   9493    case ATTRIBUTE_NODE:
   9494    case ELEMENT_NODE:
   9495    case PROCESSING_INSTRUCTION_NODE:
   9496    case TEXT_NODE:
   9497    case CDATA_SECTION_NODE:
   9498    case COMMENT_NODE:
   9499    case DOCUMENT_TYPE_NODE: {
   9500      return imported->Clone(aDeep, mNodeInfoManager, rv);
   9501    }
   9502    default: {
   9503      NS_WARNING("Don't know how to clone this nodetype for importNode.");
   9504    }
   9505  }
   9506 
   9507  rv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
   9508  return nullptr;
   9509 }
   9510 
   9511 already_AddRefed<nsRange> Document::CreateRange(ErrorResult& rv) {
   9512  return nsRange::Create(this, 0, this, 0, rv);
   9513 }
   9514 
   9515 already_AddRefed<NodeIterator> Document::CreateNodeIterator(
   9516    nsINode& aRoot, uint32_t aWhatToShow, NodeFilter* aFilter,
   9517    ErrorResult& rv) const {
   9518  RefPtr<NodeIterator> iterator =
   9519      new NodeIterator(&aRoot, aWhatToShow, aFilter);
   9520  return iterator.forget();
   9521 }
   9522 
   9523 already_AddRefed<TreeWalker> Document::CreateTreeWalker(nsINode& aRoot,
   9524                                                        uint32_t aWhatToShow,
   9525                                                        NodeFilter* aFilter,
   9526                                                        ErrorResult& rv) const {
   9527  RefPtr<TreeWalker> walker = new TreeWalker(&aRoot, aWhatToShow, aFilter);
   9528  return walker.forget();
   9529 }
   9530 
   9531 already_AddRefed<Location> Document::GetLocation() const {
   9532  nsCOMPtr<nsPIDOMWindowInner> w = do_QueryInterface(mScriptGlobalObject);
   9533 
   9534  if (!w) {
   9535    return nullptr;
   9536  }
   9537 
   9538  return do_AddRef(w->Location());
   9539 }
   9540 
   9541 already_AddRefed<nsIURI> Document::GetDomainURI() {
   9542  nsIPrincipal* principal = NodePrincipal();
   9543 
   9544  nsCOMPtr<nsIURI> uri;
   9545  principal->GetDomain(getter_AddRefs(uri));
   9546  if (uri) {
   9547    return uri.forget();
   9548  }
   9549  auto* basePrin = BasePrincipal::Cast(principal);
   9550  basePrin->GetURI(getter_AddRefs(uri));
   9551  return uri.forget();
   9552 }
   9553 
   9554 void Document::GetDomain(nsAString& aDomain) {
   9555  nsCOMPtr<nsIURI> uri = GetDomainURI();
   9556 
   9557  if (!uri) {
   9558    aDomain.Truncate();
   9559    return;
   9560  }
   9561 
   9562  nsAutoCString hostName;
   9563  nsresult rv = nsContentUtils::GetHostOrIPv6WithBrackets(uri, hostName);
   9564  if (NS_SUCCEEDED(rv)) {
   9565    CopyUTF8toUTF16(hostName, aDomain);
   9566  } else {
   9567    // If we can't get the host from the URI (e.g. about:, javascript:,
   9568    // etc), just return an empty string.
   9569    aDomain.Truncate();
   9570  }
   9571 }
   9572 
   9573 void Document::SetDomain(const nsAString& aDomain, ErrorResult& rv) {
   9574  if (!GetBrowsingContext()) {
   9575    // If our browsing context is null; disallow setting domain
   9576    rv.Throw(NS_ERROR_DOM_SECURITY_ERR);
   9577    return;
   9578  }
   9579 
   9580  if (mSandboxFlags & SANDBOXED_DOMAIN) {
   9581    // We're sandboxed; disallow setting domain
   9582    rv.Throw(NS_ERROR_DOM_SECURITY_ERR);
   9583    return;
   9584  }
   9585 
   9586  if (!FeaturePolicyUtils::IsFeatureAllowed(this, u"document-domain"_ns)) {
   9587    rv.Throw(NS_ERROR_DOM_SECURITY_ERR);
   9588    return;
   9589  }
   9590 
   9591  if (aDomain.IsEmpty()) {
   9592    rv.Throw(NS_ERROR_DOM_SECURITY_ERR);
   9593    return;
   9594  }
   9595 
   9596  nsCOMPtr<nsIURI> uri = GetDomainURI();
   9597  if (!uri) {
   9598    rv.Throw(NS_ERROR_FAILURE);
   9599    return;
   9600  }
   9601 
   9602  // Check new domain - must be a superdomain of the current host
   9603  // For example, a page from foo.bar.com may set domain to bar.com,
   9604  // but not to ar.com, baz.com, or fi.foo.bar.com.
   9605 
   9606  nsCOMPtr<nsIURI> newURI = RegistrableDomainSuffixOfInternal(aDomain, uri);
   9607  if (!newURI) {
   9608    // Error: illegal domain
   9609    rv.Throw(NS_ERROR_DOM_SECURITY_ERR);
   9610    return;
   9611  }
   9612 
   9613  if (!GetDocGroup() || GetDocGroup()->IsOriginKeyed()) {
   9614    WarnOnceAbout(Document::eDocumentSetDomainIgnored);
   9615    return;
   9616  }
   9617 
   9618  MOZ_ALWAYS_SUCCEEDS(NodePrincipal()->SetDomain(newURI));
   9619  MOZ_ALWAYS_SUCCEEDS(PartitionedPrincipal()->SetDomain(newURI));
   9620  if (WindowGlobalChild* wgc = GetWindowGlobalChild()) {
   9621    wgc->SendSetDocumentDomain(WrapNotNull(newURI));
   9622  }
   9623 }
   9624 
   9625 already_AddRefed<nsIURI> Document::CreateInheritingURIForHost(
   9626    const nsACString& aHostString) {
   9627  if (aHostString.IsEmpty()) {
   9628    return nullptr;
   9629  }
   9630 
   9631  // Create new URI
   9632  nsCOMPtr<nsIURI> uri = GetDomainURI();
   9633  if (!uri) {
   9634    return nullptr;
   9635  }
   9636 
   9637  nsresult rv;
   9638  rv = NS_MutateURI(uri)
   9639           .SetUserPass(""_ns)
   9640           .SetPort(-1)  // we want to reset the port number if needed.
   9641           .SetHostPort(aHostString)
   9642           .Finalize(uri);
   9643  if (NS_FAILED(rv)) {
   9644    return nullptr;
   9645  }
   9646 
   9647  return uri.forget();
   9648 }
   9649 
   9650 already_AddRefed<nsIURI> Document::RegistrableDomainSuffixOfInternal(
   9651    const nsAString& aNewDomain, nsIURI* aOrigHost) {
   9652  if (NS_WARN_IF(!aOrigHost)) {
   9653    return nullptr;
   9654  }
   9655 
   9656  nsCOMPtr<nsIURI> newURI =
   9657      CreateInheritingURIForHost(NS_ConvertUTF16toUTF8(aNewDomain));
   9658  if (!newURI) {
   9659    // Error: failed to parse input domain
   9660    return nullptr;
   9661  }
   9662 
   9663  if (!IsValidDomain(aOrigHost, newURI)) {
   9664    // Error: illegal domain
   9665    return nullptr;
   9666  }
   9667 
   9668  nsAutoCString domain;
   9669  if (NS_FAILED(newURI->GetAsciiHost(domain))) {
   9670    return nullptr;
   9671  }
   9672 
   9673  return CreateInheritingURIForHost(domain);
   9674 }
   9675 
   9676 /* static */
   9677 bool Document::IsValidDomain(nsIURI* aOrigHost, nsIURI* aNewURI) {
   9678  // Check new domain - must be a superdomain of the current host
   9679  // For example, a page from foo.bar.com may set domain to bar.com,
   9680  // but not to ar.com, baz.com, or fi.foo.bar.com.
   9681  nsAutoCString current;
   9682  nsAutoCString domain;
   9683  if (NS_FAILED(aOrigHost->GetAsciiHost(current))) {
   9684    current.Truncate();
   9685  }
   9686  if (NS_FAILED(aNewURI->GetAsciiHost(domain))) {
   9687    domain.Truncate();
   9688  }
   9689 
   9690  bool ok = current.Equals(domain);
   9691  if (current.Length() > domain.Length() && StringEndsWith(current, domain) &&
   9692      current.CharAt(current.Length() - domain.Length() - 1) == '.') {
   9693    // We're golden if the new domain is the current page's base domain or a
   9694    // subdomain of it.
   9695    nsCOMPtr<nsIEffectiveTLDService> tldService =
   9696        mozilla::components::EffectiveTLD::Service();
   9697    if (!tldService) {
   9698      return false;
   9699    }
   9700 
   9701    nsAutoCString currentBaseDomain;
   9702    ok = NS_SUCCEEDED(
   9703        tldService->GetBaseDomain(aOrigHost, 0, currentBaseDomain));
   9704    NS_ASSERTION(StringEndsWith(domain, currentBaseDomain) ==
   9705                     (domain.Length() >= currentBaseDomain.Length()),
   9706                 "uh-oh!  slight optimization wasn't valid somehow!");
   9707    ok = ok && domain.Length() >= currentBaseDomain.Length();
   9708  }
   9709 
   9710  return ok;
   9711 }
   9712 
   9713 Element* Document::GetHtmlElement() const {
   9714  Element* rootElement = GetRootElement();
   9715  if (rootElement && rootElement->IsHTMLElement(nsGkAtoms::html)) {
   9716    return rootElement;
   9717  }
   9718  return nullptr;
   9719 }
   9720 
   9721 Element* Document::GetHtmlChildElement(
   9722    nsAtom* aTag, const nsIContent* aContentToIgnore) const {
   9723  Element* html = GetHtmlElement();
   9724  if (!html) {
   9725    return nullptr;
   9726  }
   9727 
   9728  // Look for the element with aTag inside html. This needs to run
   9729  // forwards to find the first such element.
   9730  for (nsIContent* child = html->GetFirstChild(); child;
   9731       child = child->GetNextSibling()) {
   9732    if (child->IsHTMLElement(aTag) && MOZ_LIKELY(child != aContentToIgnore)) {
   9733      return child->AsElement();
   9734    }
   9735  }
   9736  return nullptr;
   9737 }
   9738 
   9739 nsGenericHTMLElement* Document::GetBody() const {
   9740  Element* html = GetHtmlElement();
   9741  if (!html) {
   9742    return nullptr;
   9743  }
   9744 
   9745  for (nsIContent* child = html->GetFirstChild(); child;
   9746       child = child->GetNextSibling()) {
   9747    if (child->IsAnyOfHTMLElements(nsGkAtoms::body, nsGkAtoms::frameset)) {
   9748      return static_cast<nsGenericHTMLElement*>(child);
   9749    }
   9750  }
   9751 
   9752  return nullptr;
   9753 }
   9754 
   9755 void Document::SetBody(nsGenericHTMLElement* newBody, ErrorResult& rv) {
   9756  nsCOMPtr<Element> root = GetRootElement();
   9757 
   9758  // The body element must be either a body tag or a frameset tag. And we must
   9759  // have a root element to be able to add kids to it.
   9760  if (!newBody ||
   9761      !newBody->IsAnyOfHTMLElements(nsGkAtoms::body, nsGkAtoms::frameset)) {
   9762    rv.ThrowHierarchyRequestError(
   9763        "The new body must be either a body tag or frameset tag.");
   9764    return;
   9765  }
   9766 
   9767  if (!root) {
   9768    rv.ThrowHierarchyRequestError("No root element.");
   9769    return;
   9770  }
   9771 
   9772  // Use DOM methods so that we pass through the appropriate security checks.
   9773  nsCOMPtr<Element> currentBody = GetBody();
   9774  if (currentBody) {
   9775    root->ReplaceChild(*newBody, *currentBody, rv);
   9776  } else {
   9777    root->AppendChild(*newBody, rv);
   9778  }
   9779 }
   9780 
   9781 HTMLSharedElement* Document::GetHead() const {
   9782  return static_cast<HTMLSharedElement*>(GetHeadElement());
   9783 }
   9784 
   9785 Element* Document::GetTitleElement() {
   9786  // mMayHaveTitleElement will have been set to true if any HTML or SVG
   9787  // <title> element has been bound to this document. So if it's false,
   9788  // we know there is nothing to do here. This avoids us having to search
   9789  // the whole DOM if someone calls document.title on a large document
   9790  // without a title.
   9791  if (!mMayHaveTitleElement) {
   9792    return nullptr;
   9793  }
   9794 
   9795  if (Element* root = GetSVGRootElement()) {
   9796    // In SVG, the document's title must be a child
   9797    for (nsIContent* child = root->GetFirstChild(); child;
   9798         child = child->GetNextSibling()) {
   9799      if (child->IsSVGElement(nsGkAtoms::title)) {
   9800        return child->AsElement();
   9801      }
   9802    }
   9803    return nullptr;
   9804  }
   9805 
   9806  // We check the HTML namespace even for non-HTML documents, except SVG.  This
   9807  // matches the spec and the behavior of all tested browsers.
   9808  for (nsINode* node = GetFirstChild(); node; node = node->GetNextNode(this)) {
   9809    if (node->IsHTMLElement(nsGkAtoms::title)) {
   9810      return node->AsElement();
   9811    }
   9812  }
   9813  return nullptr;
   9814 }
   9815 
   9816 void Document::GetTitle(nsAString& aTitle) {
   9817  aTitle.Truncate();
   9818 
   9819  Element* rootElement = GetRootElement();
   9820  if (!rootElement) {
   9821    return;
   9822  }
   9823 
   9824  if (rootElement->IsXULElement()) {
   9825    rootElement->GetAttr(nsGkAtoms::title, aTitle);
   9826  } else if (Element* title = GetTitleElement()) {
   9827    nsContentUtils::GetNodeTextContent(title, false, aTitle);
   9828  } else {
   9829    return;
   9830  }
   9831 
   9832  aTitle.CompressWhitespace();
   9833 }
   9834 
   9835 void Document::SetTitle(const nsAString& aTitle, ErrorResult& aRv) {
   9836  Element* rootElement = GetRootElement();
   9837  if (!rootElement) {
   9838    return;
   9839  }
   9840 
   9841  if (rootElement->IsXULElement()) {
   9842    aRv =
   9843        rootElement->SetAttr(kNameSpaceID_None, nsGkAtoms::title, aTitle, true);
   9844    return;
   9845  }
   9846 
   9847  Maybe<mozAutoDocUpdate> updateBatch;
   9848  nsCOMPtr<Element> title = GetTitleElement();
   9849  if (rootElement->IsSVGElement(nsGkAtoms::svg)) {
   9850    if (!title) {
   9851      // Batch updates so that mutation events don't change "the title
   9852      // element" under us
   9853      updateBatch.emplace(this, true);
   9854      RefPtr<mozilla::dom::NodeInfo> titleInfo = mNodeInfoManager->GetNodeInfo(
   9855          nsGkAtoms::title, nullptr, kNameSpaceID_SVG, ELEMENT_NODE);
   9856      NS_NewSVGElement(getter_AddRefs(title), titleInfo.forget(),
   9857                       NOT_FROM_PARSER);
   9858      if (!title) {
   9859        return;
   9860      }
   9861      rootElement->InsertChildBefore(title, rootElement->GetFirstChild(), true,
   9862                                     IgnoreErrors());
   9863    }
   9864  } else if (rootElement->IsHTMLElement()) {
   9865    if (!title) {
   9866      // Batch updates so that mutation events don't change "the title
   9867      // element" under us
   9868      updateBatch.emplace(this, true);
   9869      Element* head = GetHeadElement();
   9870      if (!head) {
   9871        return;
   9872      }
   9873 
   9874      RefPtr<mozilla::dom::NodeInfo> titleInfo;
   9875      titleInfo = mNodeInfoManager->GetNodeInfo(
   9876          nsGkAtoms::title, nullptr, kNameSpaceID_XHTML, ELEMENT_NODE);
   9877      title = NS_NewHTMLTitleElement(titleInfo.forget());
   9878      if (!title) {
   9879        return;
   9880      }
   9881 
   9882      head->AppendChildTo(title, true, IgnoreErrors());
   9883    }
   9884  } else {
   9885    return;
   9886  }
   9887 
   9888  aRv = nsContentUtils::SetNodeTextContent(title, aTitle, false);
   9889 }
   9890 
   9891 class Document::TitleChangeEvent final : public Runnable {
   9892 public:
   9893  explicit TitleChangeEvent(Document* aDoc)
   9894      : Runnable("Document::TitleChangeEvent"),
   9895        mDoc(aDoc),
   9896        mBlockOnload(aDoc->IsInChromeDocShell()) {
   9897    if (mBlockOnload) {
   9898      mDoc->BlockOnload();
   9899    }
   9900  }
   9901 
   9902  NS_IMETHOD Run() final {
   9903    if (!mDoc) {
   9904      return NS_OK;
   9905    }
   9906    const RefPtr<Document> doc = mDoc;
   9907    const bool blockOnload = mBlockOnload;
   9908    mDoc = nullptr;
   9909    doc->DoNotifyPossibleTitleChange();
   9910    if (blockOnload) {
   9911      doc->UnblockOnload(/* aFireSync = */ true);
   9912    }
   9913    return NS_OK;
   9914  }
   9915 
   9916  void Revoke() {
   9917    if (mDoc) {
   9918      if (mBlockOnload) {
   9919        mDoc->UnblockOnload(/* aFireSync = */ false);
   9920      }
   9921      mDoc = nullptr;
   9922    }
   9923  }
   9924 
   9925 private:
   9926  // Weak, caller is responsible for calling Revoke() when needed.
   9927  Document* mDoc;
   9928  // title changes should block the load event on chrome docshells, so that the
   9929  // window title is consistently set by the time the top window is displayed.
   9930  // Otherwise, some window manager integrations don't work properly,
   9931  // see bug 1874766.
   9932  const bool mBlockOnload = false;
   9933 };
   9934 
   9935 void Document::NotifyPossibleTitleChange(bool aBoundTitleElement) {
   9936  NS_ASSERTION(!mInUnlinkOrDeletion || !aBoundTitleElement,
   9937               "Setting a title while unlinking or destroying the element?");
   9938  if (mInUnlinkOrDeletion) {
   9939    return;
   9940  }
   9941 
   9942  if (aBoundTitleElement) {
   9943    mMayHaveTitleElement = true;
   9944  }
   9945 
   9946  if (mPendingTitleChangeEvent.IsPending()) {
   9947    return;
   9948  }
   9949 
   9950  MOZ_RELEASE_ASSERT(NS_IsMainThread());
   9951  RefPtr<TitleChangeEvent> event = new TitleChangeEvent(this);
   9952  if (NS_WARN_IF(NS_FAILED(Dispatch(do_AddRef(event))))) {
   9953    event->Revoke();
   9954    return;
   9955  }
   9956  mPendingTitleChangeEvent = std::move(event);
   9957 }
   9958 
   9959 void Document::DoNotifyPossibleTitleChange() {
   9960  if (!mPendingTitleChangeEvent.IsPending()) {
   9961    return;
   9962  }
   9963  // Make sure the pending runnable method is cleared.
   9964  mPendingTitleChangeEvent.Revoke();
   9965  mHaveFiredTitleChange = true;
   9966 
   9967  nsAutoString title;
   9968  GetTitle(title);
   9969 
   9970  if (RefPtr<PresShell> presShell = GetPresShell()) {
   9971    nsCOMPtr<nsISupports> container =
   9972        presShell->GetPresContext()->GetContainerWeak();
   9973    if (container) {
   9974      if (nsCOMPtr<nsIBaseWindow> docShellWin = do_QueryInterface(container)) {
   9975        docShellWin->SetTitle(title);
   9976      }
   9977    }
   9978  }
   9979 
   9980  if (WindowGlobalChild* child = GetWindowGlobalChild()) {
   9981    child->SendUpdateDocumentTitle(title);
   9982  }
   9983 
   9984  // Fire a DOM event for the title change.
   9985  nsContentUtils::DispatchChromeEvent(this, this, u"DOMTitleChanged"_ns,
   9986                                      CanBubble::eYes, Cancelable::eYes);
   9987 
   9988  nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
   9989  if (obs) {
   9990    obs->NotifyObservers(ToSupports(this), "document-title-changed", nullptr);
   9991  }
   9992 }
   9993 
   9994 already_AddRefed<MediaQueryList> Document::MatchMedia(
   9995    const nsACString& aMediaQueryList, CallerType aCallerType) {
   9996  RefPtr<MediaQueryList> result =
   9997      new MediaQueryList(this, aMediaQueryList, aCallerType);
   9998 
   9999  mDOMMediaQueryLists.insertBack(result);
  10000 
  10001  return result.forget();
  10002 }
  10003 
  10004 void Document::SetMayStartLayout(bool aMayStartLayout) {
  10005  mMayStartLayout = aMayStartLayout;
  10006  if (MayStartLayout()) {
  10007    // Before starting layout, check whether we're a toplevel chrome
  10008    // window.  If we are, setup some state so that we don't have to restyle
  10009    // the whole tree after StartLayout.
  10010    if (nsCOMPtr<nsIAppWindow> win = GetAppWindowIfToplevelChrome()) {
  10011      // We're the chrome document!
  10012      win->BeforeStartLayout();
  10013    }
  10014    ReadyState state = GetReadyStateEnum();
  10015    if (state >= READYSTATE_INTERACTIVE) {
  10016      // DOMContentLoaded has fired already.
  10017      MaybeResolveReadyForIdle();
  10018    }
  10019  }
  10020 
  10021  MaybeEditingStateChanged();
  10022 }
  10023 
  10024 nsresult Document::InitializeFrameLoader(nsFrameLoader* aLoader) {
  10025  mInitializableFrameLoaders.RemoveElement(aLoader);
  10026  // Don't even try to initialize.
  10027  if (mInDestructor) {
  10028    NS_WARNING(
  10029        "Trying to initialize a frame loader while"
  10030        "document is being deleted");
  10031    return NS_ERROR_FAILURE;
  10032  }
  10033 
  10034  mInitializableFrameLoaders.AppendElement(aLoader);
  10035  if (!mFrameLoaderRunner) {
  10036    mFrameLoaderRunner =
  10037        NewRunnableMethod("Document::MaybeInitializeFinalizeFrameLoaders", this,
  10038                          &Document::MaybeInitializeFinalizeFrameLoaders);
  10039    NS_ENSURE_TRUE(mFrameLoaderRunner, NS_ERROR_OUT_OF_MEMORY);
  10040    nsContentUtils::AddScriptRunner(mFrameLoaderRunner);
  10041  }
  10042  return NS_OK;
  10043 }
  10044 
  10045 nsresult Document::FinalizeFrameLoader(nsFrameLoader* aLoader,
  10046                                       nsIRunnable* aFinalizer) {
  10047  mInitializableFrameLoaders.RemoveElement(aLoader);
  10048  if (mInDestructor) {
  10049    return NS_ERROR_FAILURE;
  10050  }
  10051 
  10052  LogRunnable::LogDispatch(aFinalizer);
  10053  mFrameLoaderFinalizers.AppendElement(aFinalizer);
  10054  if (!mFrameLoaderRunner) {
  10055    mFrameLoaderRunner =
  10056        NewRunnableMethod("Document::MaybeInitializeFinalizeFrameLoaders", this,
  10057                          &Document::MaybeInitializeFinalizeFrameLoaders);
  10058    NS_ENSURE_TRUE(mFrameLoaderRunner, NS_ERROR_OUT_OF_MEMORY);
  10059    nsContentUtils::AddScriptRunner(mFrameLoaderRunner);
  10060  }
  10061  return NS_OK;
  10062 }
  10063 
  10064 void Document::MaybeInitializeFinalizeFrameLoaders() {
  10065  if (mDelayFrameLoaderInitialization) {
  10066    // This method will be recalled when !mDelayFrameLoaderInitialization.
  10067    mFrameLoaderRunner = nullptr;
  10068    return;
  10069  }
  10070 
  10071  // We're not in an update, but it is not safe to run scripts, so
  10072  // postpone frameloader initialization and finalization.
  10073  if (!nsContentUtils::IsSafeToRunScript()) {
  10074    if (!mInDestructor && !mFrameLoaderRunner &&
  10075        (mInitializableFrameLoaders.Length() ||
  10076         mFrameLoaderFinalizers.Length())) {
  10077      mFrameLoaderRunner = NewRunnableMethod(
  10078          "Document::MaybeInitializeFinalizeFrameLoaders", this,
  10079          &Document::MaybeInitializeFinalizeFrameLoaders);
  10080      nsContentUtils::AddScriptRunner(mFrameLoaderRunner);
  10081    }
  10082    return;
  10083  }
  10084  mFrameLoaderRunner = nullptr;
  10085 
  10086  // Don't use a temporary array for mInitializableFrameLoaders, because
  10087  // loading a frame may cause some other frameloader to be removed from the
  10088  // array. But be careful to keep the loader alive when starting the load!
  10089  while (mInitializableFrameLoaders.Length()) {
  10090    RefPtr<nsFrameLoader> loader = mInitializableFrameLoaders[0];
  10091    mInitializableFrameLoaders.RemoveElementAt(0);
  10092    NS_ASSERTION(loader, "null frameloader in the array?");
  10093    loader->ReallyStartLoading();
  10094  }
  10095 
  10096  uint32_t length = mFrameLoaderFinalizers.Length();
  10097  if (length > 0) {
  10098    nsTArray<nsCOMPtr<nsIRunnable>> finalizers =
  10099        std::move(mFrameLoaderFinalizers);
  10100    for (uint32_t i = 0; i < length; ++i) {
  10101      LogRunnable::Run run(finalizers[i]);
  10102      finalizers[i]->Run();
  10103    }
  10104  }
  10105 }
  10106 
  10107 void Document::TryCancelFrameLoaderInitialization(nsIDocShell* aShell) {
  10108  uint32_t length = mInitializableFrameLoaders.Length();
  10109  for (uint32_t i = 0; i < length; ++i) {
  10110    if (mInitializableFrameLoaders[i]->GetExistingDocShell() == aShell) {
  10111      mInitializableFrameLoaders.RemoveElementAt(i);
  10112      return;
  10113    }
  10114  }
  10115 }
  10116 
  10117 void Document::SetPrototypeDocument(nsXULPrototypeDocument* aPrototype) {
  10118  mPrototypeDocument = aPrototype;
  10119  mSynchronousDOMContentLoaded = true;
  10120 }
  10121 
  10122 nsIPermissionDelegateHandler* Document::PermDelegateHandler() {
  10123  return GetPermissionDelegateHandler();
  10124 }
  10125 
  10126 Document* Document::RequestExternalResource(
  10127    nsIURI* aURI, nsIReferrerInfo* aReferrerInfo, nsINode* aRequestingNode,
  10128    ExternalResourceLoad** aPendingLoad) {
  10129  MOZ_ASSERT(aURI, "Must have a URI");
  10130  MOZ_ASSERT(aRequestingNode, "Must have a node");
  10131  MOZ_ASSERT(aReferrerInfo, "Must have a referrerInfo");
  10132  if (mDisplayDocument) {
  10133    return mDisplayDocument->RequestExternalResource(
  10134        aURI, aReferrerInfo, aRequestingNode, aPendingLoad);
  10135  }
  10136 
  10137  return mExternalResourceMap.RequestResource(
  10138      aURI, aReferrerInfo, aRequestingNode, this, aPendingLoad);
  10139 }
  10140 
  10141 void Document::EnumerateExternalResources(SubDocEnumFunc aCallback) const {
  10142  mExternalResourceMap.EnumerateResources(aCallback);
  10143 }
  10144 
  10145 SMILAnimationController* Document::GetAnimationController() {
  10146  // We create the animation controller lazily because most documents won't want
  10147  // one and only SVG documents and the like will call this
  10148  if (mAnimationController) return mAnimationController;
  10149  // Refuse to create an Animation Controller for data documents.
  10150  if (mLoadedAsData) {
  10151    return nullptr;
  10152  }
  10153 
  10154  mAnimationController = new SMILAnimationController(this);
  10155 
  10156  // If there's a presContext then check the animation mode and pause if
  10157  // necessary.
  10158  nsPresContext* context = GetPresContext();
  10159  if (mAnimationController && context &&
  10160      context->ImageAnimationMode() == imgIContainer::kDontAnimMode) {
  10161    mAnimationController->Pause(SMILTimeContainer::PAUSE_USERPREF);
  10162  }
  10163 
  10164  // If we're hidden (or being hidden), notify the newly-created animation
  10165  // controller. (Skip this check for SVG-as-an-image documents, though,
  10166  // because they don't get OnPageShow / OnPageHide calls).
  10167  if (!mIsShowing && !mIsBeingUsedAsImage) {
  10168    mAnimationController->OnPageHide();
  10169  }
  10170 
  10171  return mAnimationController;
  10172 }
  10173 
  10174 ScrollTimelineAnimationTracker*
  10175 Document::GetOrCreateScrollTimelineAnimationTracker() {
  10176  if (!mScrollTimelineAnimationTracker) {
  10177    mScrollTimelineAnimationTracker = new ScrollTimelineAnimationTracker(this);
  10178  }
  10179 
  10180  return mScrollTimelineAnimationTracker;
  10181 }
  10182 
  10183 /**
  10184 * Retrieve the "direction" property of the document.
  10185 *
  10186 * @lina 01/09/2001
  10187 */
  10188 void Document::GetDir(nsAString& aDirection) const {
  10189  aDirection.Truncate();
  10190  Element* rootElement = GetHtmlElement();
  10191  if (rootElement) {
  10192    static_cast<nsGenericHTMLElement*>(rootElement)->GetDir(aDirection);
  10193  }
  10194 }
  10195 
  10196 /**
  10197 * Set the "direction" property of the document.
  10198 *
  10199 * @lina 01/09/2001
  10200 */
  10201 void Document::SetDir(const nsAString& aDirection) {
  10202  Element* rootElement = GetHtmlElement();
  10203  if (rootElement) {
  10204    rootElement->SetAttr(kNameSpaceID_None, nsGkAtoms::dir, aDirection, true);
  10205  }
  10206 }
  10207 
  10208 nsIHTMLCollection* Document::Images() {
  10209  if (!mImages) {
  10210    mImages = new nsContentList(this, kNameSpaceID_XHTML, nsGkAtoms::img,
  10211                                nsGkAtoms::img);
  10212  }
  10213  return mImages;
  10214 }
  10215 
  10216 nsIHTMLCollection* Document::Embeds() {
  10217  if (!mEmbeds) {
  10218    mEmbeds = new nsContentList(this, kNameSpaceID_XHTML, nsGkAtoms::embed,
  10219                                nsGkAtoms::embed);
  10220  }
  10221  return mEmbeds;
  10222 }
  10223 
  10224 static bool MatchLinks(Element* aElement, int32_t aNamespaceID, nsAtom* aAtom,
  10225                       void* aData) {
  10226  return aElement->IsAnyOfHTMLElements(nsGkAtoms::a, nsGkAtoms::area) &&
  10227         aElement->HasAttr(nsGkAtoms::href);
  10228 }
  10229 
  10230 nsIHTMLCollection* Document::Links() {
  10231  if (!mLinks) {
  10232    mLinks = new nsContentList(this, MatchLinks, nullptr, nullptr);
  10233  }
  10234  return mLinks;
  10235 }
  10236 
  10237 nsIHTMLCollection* Document::Forms() {
  10238  if (!mForms) {
  10239    // Please keep this in sync with nsHTMLDocument::GetFormsAndFormControls.
  10240    mForms = new nsContentList(this, kNameSpaceID_XHTML, nsGkAtoms::form,
  10241                               nsGkAtoms::form);
  10242  }
  10243 
  10244  return mForms;
  10245 }
  10246 
  10247 nsIHTMLCollection* Document::Scripts() {
  10248  if (!mScripts) {
  10249    mScripts = new nsContentList(this, kNameSpaceID_XHTML, nsGkAtoms::script,
  10250                                 nsGkAtoms::script);
  10251  }
  10252  return mScripts;
  10253 }
  10254 
  10255 nsIHTMLCollection* Document::Applets() {
  10256  if (!mApplets) {
  10257    mApplets = new nsEmptyContentList(this);
  10258  }
  10259  return mApplets;
  10260 }
  10261 
  10262 static bool MatchAnchors(Element* aElement, int32_t aNamespaceID, nsAtom* aAtom,
  10263                         void* aData) {
  10264  return aElement->IsHTMLElement(nsGkAtoms::a) &&
  10265         aElement->HasAttr(nsGkAtoms::name);
  10266 }
  10267 
  10268 nsIHTMLCollection* Document::Anchors() {
  10269  if (!mAnchors) {
  10270    mAnchors = new nsContentList(this, MatchAnchors, nullptr, nullptr);
  10271  }
  10272  return mAnchors;
  10273 }
  10274 
  10275 mozilla::dom::Nullable<mozilla::dom::WindowProxyHolder> Document::Open(
  10276    const nsACString& aURL, const nsAString& aName, const nsAString& aFeatures,
  10277    ErrorResult& rv) {
  10278  MOZ_ASSERT(nsContentUtils::CanCallerAccess(this),
  10279             "XOW should have caught this!");
  10280 
  10281  nsCOMPtr<nsPIDOMWindowInner> window = GetInnerWindow();
  10282  if (!window) {
  10283    rv.Throw(NS_ERROR_DOM_INVALID_ACCESS_ERR);
  10284    return nullptr;
  10285  }
  10286  nsCOMPtr<nsPIDOMWindowOuter> outer =
  10287      nsPIDOMWindowOuter::GetFromCurrentInner(window);
  10288  if (!outer) {
  10289    rv.Throw(NS_ERROR_NOT_INITIALIZED);
  10290    return nullptr;
  10291  }
  10292  RefPtr<nsGlobalWindowOuter> win = nsGlobalWindowOuter::Cast(outer);
  10293  RefPtr<BrowsingContext> newBC;
  10294  rv = win->OpenJS(aURL, aName, aFeatures, getter_AddRefs(newBC));
  10295  if (!newBC) {
  10296    return nullptr;
  10297  }
  10298  return WindowProxyHolder(std::move(newBC));
  10299 }
  10300 
  10301 Document* Document::Open(const Optional<nsAString>& /* unused */,
  10302                         const Optional<nsAString>& /* unused */,
  10303                         ErrorResult& aError) {
  10304  // Implements
  10305  // <https://html.spec.whatwg.org/multipage/dynamic-markup-insertion.html#document-open-steps>
  10306 
  10307  MOZ_ASSERT(nsContentUtils::CanCallerAccess(this),
  10308             "XOW should have caught this!");
  10309 
  10310  // Step 1 -- throw if we're an XML document.
  10311  if (!IsHTMLDocument() || mDisableDocWrite) {
  10312    aError.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
  10313    return nullptr;
  10314  }
  10315 
  10316  // Step 2 -- throw if dynamic markup insertion should throw.
  10317  if (ShouldThrowOnDynamicMarkupInsertion()) {
  10318    aError.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
  10319    return nullptr;
  10320  }
  10321 
  10322  // Step 3 -- get the entry document, so we can use it for security checks.
  10323  nsCOMPtr<Document> callerDoc = GetEntryDocument();
  10324  if (!callerDoc) {
  10325    if (nsIGlobalObject* callerGlobal = GetEntryGlobal()) {
  10326      if (callerGlobal->IsXPCSandbox()) {
  10327        if (nsIPrincipal* principal = callerGlobal->PrincipalOrNull()) {
  10328          if (principal->Equals(NodePrincipal())) {
  10329            // In case we're being called from some JS sandbox scope,
  10330            // pretend that the caller is the document itself.
  10331            callerDoc = this;
  10332          }
  10333        }
  10334      }
  10335    }
  10336 
  10337    if (!callerDoc) {
  10338      // If we're called from C++ or in some other way without an originating
  10339      // document we can't do a document.open w/o changing the principal of the
  10340      // document to something like about:blank (as that's the only sane thing
  10341      // to do when we don't know the origin of this call), and since we can't
  10342      // change the principals of a document for security reasons we'll have to
  10343      // refuse to go ahead with this call.
  10344 
  10345      aError.Throw(NS_ERROR_DOM_SECURITY_ERR);
  10346      return nullptr;
  10347    }
  10348  }
  10349 
  10350  // Step 4 -- make sure we're same-origin (not just same origin-domain) with
  10351  // the entry document.
  10352  if (!callerDoc->NodePrincipal()->Equals(NodePrincipal())) {
  10353    aError.Throw(NS_ERROR_DOM_SECURITY_ERR);
  10354    return nullptr;
  10355  }
  10356 
  10357  // Step 5 -- if we have an active parser with a nonzero script nesting level,
  10358  // just no-op.
  10359  if ((mParser && mParser->HasNonzeroScriptNestingLevel()) || mParserAborted) {
  10360    return this;
  10361  }
  10362 
  10363  // Step 6 -- check for open() during unload.  Per spec, this is just a check
  10364  // of the ignore-opens-during-unload counter, but our unload event code
  10365  // doesn't affect that counter yet (unlike pagehide and beforeunload, which
  10366  // do), so we check for unload directly.
  10367  if (ShouldIgnoreOpens()) {
  10368    return this;
  10369  }
  10370 
  10371  RefPtr<nsDocShell> shell(mDocumentContainer);
  10372  if (shell) {
  10373    bool inUnload;
  10374    shell->GetIsInUnload(&inUnload);
  10375    if (inUnload) {
  10376      return this;
  10377    }
  10378  }
  10379 
  10380  // At this point we know this is a valid-enough document.open() call
  10381  // and not a no-op.  Increment our use counter.
  10382  SetUseCounter(eUseCounter_custom_DocumentOpen);
  10383 
  10384  // XXX The spec has changed. There is a new step 7 and step 8 has changed
  10385  // a bit.
  10386 
  10387  // Step 8 -- stop existing navigation of our browsing context (and all other
  10388  // loads it's doing) if we're the active document of our browsing context.
  10389  // Note that we do not want to stop anything if there is no existing
  10390  // navigation.
  10391  if (shell && IsCurrentActiveDocument() &&
  10392      shell->GetIsAttemptingToNavigate()) {
  10393    shell->Stop(nsIWebNavigation::STOP_NETWORK);
  10394 
  10395    // The Stop call may have cancelled the onload blocker request or
  10396    // prevented it from getting added, so we need to make sure it gets added
  10397    // to the document again otherwise the document could have a non-zero
  10398    // onload block count without the onload blocker request being in the
  10399    // loadgroup.
  10400    EnsureOnloadBlocker();
  10401  }
  10402 
  10403  // Step 9 -- clear event listeners out of our DOM tree
  10404  for (nsINode* node : ShadowIncludingTreeIterator(*this)) {
  10405    if (EventListenerManager* elm = node->GetExistingListenerManager()) {
  10406      elm->RemoveAllListeners();
  10407    }
  10408  }
  10409 
  10410  // Step 10 -- clear event listeners from our window, if we have one.
  10411  //
  10412  // Note that we explicitly want the inner window, and only if we're its
  10413  // document.  We want to do this (per spec) even when we're not the "active
  10414  // document", so we can't go through GetWindow(), because it might forward to
  10415  // the wrong inner.
  10416  if (nsPIDOMWindowInner* win = GetInnerWindow()) {
  10417    if (win->GetExtantDoc() == this) {
  10418      if (EventListenerManager* elm =
  10419              nsGlobalWindowInner::Cast(win)->GetExistingListenerManager()) {
  10420        elm->RemoveAllListeners();
  10421      }
  10422    }
  10423  }
  10424 
  10425  // If we have a parser that has a zero script nesting level, we need to
  10426  // properly terminate it.  We do that after we've removed all the event
  10427  // listeners (so termination won't trigger event listeners if it does
  10428  // something to the DOM), but before we remove all elements from the document
  10429  // (so if termination does modify the DOM in some way we will just blow it
  10430  // away immediately.  See the similar code in WriteCommon that handles the
  10431  // !IsInsertionPointDefined() case and should stay in sync with this code.
  10432  if (mParser) {
  10433    MOZ_ASSERT(!mParser->HasNonzeroScriptNestingLevel(),
  10434               "Why didn't we take the early return?");
  10435    // Make sure we don't re-enter.
  10436    IgnoreOpensDuringUnload ignoreOpenGuard(this);
  10437    mParser->Terminate();
  10438    MOZ_RELEASE_ASSERT(!mParser, "mParser should have been null'd out");
  10439  }
  10440 
  10441  // Steps 11, 12, 13, 14 --
  10442  // remove all our DOM kids without notifying DevTools of the node removals.
  10443  {
  10444    // XXX I don't know we should keep hiding the node removals from DevTools.
  10445    // If it's safe even if the user updates the DOM tree from Inspector or
  10446    // Console, we can stop suppressing this.
  10447    AutoSuppressNotifyingDevToolsOfNodeRemovals suppressNotifyingDevTools(
  10448        *this);
  10449 
  10450    // We want to ignore any recursive calls to Open() that happen while
  10451    // disconnecting the node tree.  The spec doesn't say to do this, but the
  10452    // spec also doesn't envision unload events on subframes firing while we do
  10453    // this, while all browsers fire them in practice.  See
  10454    // <https://github.com/whatwg/html/issues/4611>.
  10455    IgnoreOpensDuringUnload ignoreOpenGuard(this);
  10456    DisconnectNodeTree();
  10457  }
  10458 
  10459  // Step 15 -- if we're the current document in our docshell, do the
  10460  // equivalent of pushState() with the new URL we should have.
  10461  if (shell && IsCurrentActiveDocument()) {
  10462    nsCOMPtr<nsIURI> newURI = callerDoc->GetDocumentURI();
  10463    if (callerDoc != this) {
  10464      nsCOMPtr<nsIURI> noFragmentURI;
  10465      nsresult rv = NS_GetURIWithoutRef(newURI, getter_AddRefs(noFragmentURI));
  10466      if (NS_WARN_IF(NS_FAILED(rv))) {
  10467        aError.Throw(rv);
  10468        return nullptr;
  10469      }
  10470      newURI = std::move(noFragmentURI);
  10471    }
  10472 
  10473    // UpdateURLAndHistory might do various member-setting, so make sure we're
  10474    // holding strong refs to all the refcounted args on the stack.  We can
  10475    // assume that our caller is holding on to "this" already.
  10476    nsCOMPtr<nsIURI> currentURI = GetDocumentURI();
  10477    bool equalURIs;
  10478    nsresult rv = currentURI->Equals(newURI, &equalURIs);
  10479    if (NS_WARN_IF(NS_FAILED(rv))) {
  10480      aError.Throw(rv);
  10481      return nullptr;
  10482    }
  10483    nsCOMPtr<nsIStructuredCloneContainer> stateContainer(mStateObjectContainer);
  10484    rv = shell->UpdateURLAndHistory(this, newURI, stateContainer,
  10485                                    NavigationHistoryBehavior::Replace,
  10486                                    currentURI, equalURIs);
  10487    if (NS_WARN_IF(NS_FAILED(rv))) {
  10488      aError.Throw(rv);
  10489      return nullptr;
  10490    }
  10491 
  10492    // And use the security info of the caller document as well, since
  10493    // it's the thing providing our data.
  10494    mSecurityInfo = callerDoc->GetSecurityInfo();
  10495 
  10496    // Step 16
  10497    if (IsInitialDocument()) {
  10498      SetInitialStatus(Document::InitialStatus::IsInitialButExplicitlyOpened);
  10499    }
  10500 
  10501    // And let our docloader know that it will need to track our load event.
  10502    nsDocShell::Cast(shell)->SetDocumentOpenedButNotLoaded();
  10503  }
  10504 
  10505  // Per spec nothing happens with our URI in other cases, though note
  10506  // <https://github.com/whatwg/html/issues/4286>.
  10507 
  10508  // Note that we don't need to do anything here with base URIs per spec.
  10509  // That said, this might be assuming that we implement
  10510  // https://html.spec.whatwg.org/multipage/urls-and-fetching.html#fallback-base-url
  10511  // correctly, which we don't right now for the about:blank case.
  10512 
  10513  // Step 17, but note <https://github.com/whatwg/html/issues/4292>.
  10514  mSkipLoadEventAfterClose = mLoadEventFiring;
  10515 
  10516  // Preliminary to steps 18-21.  Set our ready state to uninitialized before
  10517  // we do anything else, so we can then proceed to later ready state levels.
  10518  SetReadyStateInternal(READYSTATE_UNINITIALIZED,
  10519                        /* updateTimingInformation = */ false);
  10520  // Reset a flag that affects readyState behavior.
  10521  mSetCompleteAfterDOMContentLoaded = false;
  10522 
  10523  // Step 18 -- set our compat mode to standards.
  10524  SetCompatibilityMode(eCompatibility_FullStandards);
  10525 
  10526  // Step 19 -- create a new parser associated with document.  This also does
  10527  // step 20 implicitly.
  10528  mParserAborted = false;
  10529  RefPtr<nsHtml5Parser> parser = nsHtml5Module::NewHtml5Parser();
  10530  mParser = parser;
  10531  parser->Initialize(this, GetDocumentURI(), ToSupports(shell), nullptr);
  10532  nsresult rv = parser->StartExecutor();
  10533  if (NS_WARN_IF(NS_FAILED(rv))) {
  10534    aError.Throw(rv);
  10535    return nullptr;
  10536  }
  10537 
  10538  // Clear out our form control state, because the state of controls
  10539  // in the pre-open() document should not affect the state of
  10540  // controls that are now going to be written.
  10541  mLayoutHistoryState = nullptr;
  10542 
  10543  if (shell) {
  10544    // Prepare the docshell and the document viewer for the impending
  10545    // out-of-band document.write()
  10546    shell->PrepareForNewContentModel();
  10547 
  10548    nsCOMPtr<nsIDocumentViewer> viewer;
  10549    shell->GetDocViewer(getter_AddRefs(viewer));
  10550    if (viewer) {
  10551      viewer->LoadStart(this);
  10552    }
  10553  }
  10554 
  10555  // Step 21.
  10556  SetReadyStateInternal(Document::READYSTATE_LOADING,
  10557                        /* updateTimingInformation = */ false);
  10558 
  10559  // Step 22.
  10560  return this;
  10561 }
  10562 
  10563 void Document::Close(ErrorResult& rv) {
  10564  if (!IsHTMLDocument()) {
  10565    // No calling document.close() on XHTML!
  10566 
  10567    rv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
  10568    return;
  10569  }
  10570 
  10571  if (ShouldThrowOnDynamicMarkupInsertion()) {
  10572    rv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
  10573    return;
  10574  }
  10575 
  10576  if (!mParser || !mParser->IsScriptCreated()) {
  10577    return;
  10578  }
  10579 
  10580  ++mWriteLevel;
  10581  rv = (static_cast<nsHtml5Parser*>(mParser.get()))
  10582           ->Parse(u""_ns, nullptr, true);
  10583  --mWriteLevel;
  10584 }
  10585 
  10586 void Document::WriteCommon(const Sequence<OwningTrustedHTMLOrString>& aText,
  10587                           bool aNewlineTerminate,
  10588                           nsIPrincipal* aSubjectPrincipal,
  10589                           mozilla::ErrorResult& rv) {
  10590  bool isTrusted = true;
  10591  auto getAsString =
  10592      [&isTrusted](const OwningTrustedHTMLOrString& aTrustedHTMLOrString) {
  10593        if (aTrustedHTMLOrString.IsString()) {
  10594          isTrusted = false;
  10595          return &aTrustedHTMLOrString.GetAsString();
  10596        }
  10597        return &aTrustedHTMLOrString.GetAsTrustedHTML()->mData;
  10598      };
  10599 
  10600  // Fast path the common case
  10601  if (aText.Length() == 1) {
  10602    WriteCommon(*getAsString(aText[0]), aNewlineTerminate,
  10603                aText[0].IsTrustedHTML(), aSubjectPrincipal, rv);
  10604  } else {
  10605    // XXXbz it would be nice if we could pass all the strings to the parser
  10606    // without having to do all this copying and then ask it to start
  10607    // parsing....
  10608    nsString text;
  10609    for (size_t i = 0; i < aText.Length(); ++i) {
  10610      text.Append(*getAsString(aText[i]));
  10611    }
  10612    WriteCommon(text, aNewlineTerminate, isTrusted, aSubjectPrincipal, rv);
  10613  }
  10614 }
  10615 
  10616 void Document::WriteCommon(const nsAString& aText, bool aNewlineTerminate,
  10617                           bool aIsTrusted, nsIPrincipal* aSubjectPrincipal,
  10618                           ErrorResult& aRv) {
  10619 #ifdef DEBUG
  10620  {
  10621    // Assert that we do not use or accidentally introduce doc.write()
  10622    // in system privileged context or in any of our about: pages.
  10623    nsCOMPtr<nsIPrincipal> principal = NodePrincipal();
  10624    bool isAboutOrPrivContext = principal->IsSystemPrincipal();
  10625    if (!isAboutOrPrivContext) {
  10626      if (principal->SchemeIs("about")) {
  10627        // about:blank inherits the security contetext and this assertion
  10628        // is only meant for actual about: pages.
  10629        nsAutoCString host;
  10630        principal->GetHost(host);
  10631        isAboutOrPrivContext = !host.EqualsLiteral("blank");
  10632      }
  10633    }
  10634    // Some automated tests use an empty string to kick off some parsing
  10635    // mechansims, but they do not do any harm since they use an empty string.
  10636    MOZ_ASSERT(!isAboutOrPrivContext || aText.IsEmpty(),
  10637               "do not use doc.write in privileged context!");
  10638  }
  10639 #endif
  10640 
  10641  mTooDeepWriteRecursion =
  10642      (mWriteLevel > NS_MAX_DOCUMENT_WRITE_DEPTH || mTooDeepWriteRecursion);
  10643  if (NS_WARN_IF(mTooDeepWriteRecursion)) {
  10644    aRv.Throw(NS_ERROR_UNEXPECTED);
  10645    return;
  10646  }
  10647 
  10648  Maybe<nsAutoString> compliantStringHolder;
  10649  const nsAString* compliantString = &aText;
  10650  if (!aIsTrusted) {
  10651    constexpr nsLiteralString sinkWrite = u"Document write"_ns;
  10652    constexpr nsLiteralString sinkWriteLn = u"Document writeln"_ns;
  10653    compliantString =
  10654        TrustedTypeUtils::GetTrustedTypesCompliantStringForTrustedHTML(
  10655            aText, aNewlineTerminate ? sinkWriteLn : sinkWrite,
  10656            kTrustedTypesOnlySinkGroup, *this, aSubjectPrincipal,
  10657            compliantStringHolder, aRv);
  10658    if (aRv.Failed()) {
  10659      return;
  10660    }
  10661  }
  10662 
  10663  if (!IsHTMLDocument() || mDisableDocWrite) {
  10664    // No calling document.write*() on XHTML!
  10665 
  10666    aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
  10667    return;
  10668  }
  10669 
  10670  if (ShouldThrowOnDynamicMarkupInsertion()) {
  10671    aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
  10672    return;
  10673  }
  10674 
  10675  if (mParserAborted) {
  10676    // Hixie says aborting the parser doesn't undefine the insertion point.
  10677    // However, since we null out mParser in that case, we track the
  10678    // theoretically defined insertion point using mParserAborted.
  10679    return;
  10680  }
  10681 
  10682  // Implement Step 4.1 of:
  10683  // https://html.spec.whatwg.org/multipage/dynamic-markup-insertion.html#document-write-steps
  10684  if (ShouldIgnoreOpens()) {
  10685    return;
  10686  }
  10687 
  10688  void* key = GenerateParserKey();
  10689  if (mParser && !mParser->IsInsertionPointDefined()) {
  10690    if (mIgnoreDestructiveWritesCounter) {
  10691      // Instead of implying a call to document.open(), ignore the call.
  10692      nsContentUtils::ReportToConsole(
  10693          nsIScriptError::warningFlag, "DOM Events"_ns, this,
  10694          nsContentUtils::eDOM_PROPERTIES, "DocumentWriteIgnored");
  10695      return;
  10696    }
  10697    // The spec doesn't tell us to ignore opens from here, but we need to
  10698    // ensure opens are ignored here.  See similar code in Open() that handles
  10699    // the case of an existing parser which is not currently running script and
  10700    // should stay in sync with this code.
  10701    IgnoreOpensDuringUnload ignoreOpenGuard(this);
  10702    mParser->Terminate();
  10703    MOZ_RELEASE_ASSERT(!mParser, "mParser should have been null'd out");
  10704  }
  10705 
  10706  if (!mParser) {
  10707    if (mIgnoreDestructiveWritesCounter) {
  10708      // Instead of implying a call to document.open(), ignore the call.
  10709      nsContentUtils::ReportToConsole(
  10710          nsIScriptError::warningFlag, "DOM Events"_ns, this,
  10711          nsContentUtils::eDOM_PROPERTIES, "DocumentWriteIgnored");
  10712      return;
  10713    }
  10714 
  10715    Open({}, {}, aRv);
  10716 
  10717    // If Open() fails, or if it didn't create a parser (as it won't
  10718    // if the user chose to not discard the current document through
  10719    // onbeforeunload), don't write anything.
  10720    if (aRv.Failed() || !mParser) {
  10721      return;
  10722    }
  10723  }
  10724 
  10725  static constexpr auto new_line = u"\n"_ns;
  10726 
  10727  ++mWriteLevel;
  10728 
  10729  // This could be done with less code, but for performance reasons it
  10730  // makes sense to have the code for two separate Parse() calls here
  10731  // since the concatenation of strings costs more than we like. And
  10732  // why pay that price when we don't need to?
  10733  if (aNewlineTerminate) {
  10734    aRv = (static_cast<nsHtml5Parser*>(mParser.get()))
  10735              ->Parse(*compliantString + new_line, key, false);
  10736  } else {
  10737    aRv = (static_cast<nsHtml5Parser*>(mParser.get()))
  10738              ->Parse(*compliantString, key, false);
  10739  };
  10740 
  10741  --mWriteLevel;
  10742 
  10743  mTooDeepWriteRecursion = (mWriteLevel != 0 && mTooDeepWriteRecursion);
  10744 }
  10745 
  10746 void Document::Write(const Sequence<OwningTrustedHTMLOrString>& aText,
  10747                     nsIPrincipal* aSubjectPrincipal, ErrorResult& rv) {
  10748  WriteCommon(aText, false, aSubjectPrincipal, rv);
  10749 }
  10750 
  10751 void Document::Writeln(const Sequence<OwningTrustedHTMLOrString>& aText,
  10752                       nsIPrincipal* aSubjectPrincipal, ErrorResult& rv) {
  10753  WriteCommon(aText, true, aSubjectPrincipal, rv);
  10754 }
  10755 
  10756 void* Document::GenerateParserKey(void) {
  10757  if (!mScriptLoader) {
  10758    // If we don't have a script loader, then the parser probably isn't parsing
  10759    // anything anyway, so just return null.
  10760    return nullptr;
  10761  }
  10762 
  10763  // The script loader provides us with the currently executing script element,
  10764  // which is guaranteed to be unique per script.
  10765  nsIScriptElement* script = mScriptLoader->GetCurrentParserInsertedScript();
  10766  if (script && mParser && mParser->IsScriptCreated()) {
  10767    nsCOMPtr<nsIParser> creatorParser = script->GetCreatorParser();
  10768    if (creatorParser != mParser) {
  10769      // Make scripts that aren't inserted by the active parser of this document
  10770      // participate in the context of the script that document.open()ed
  10771      // this document.
  10772      return nullptr;
  10773    }
  10774  }
  10775  return script;
  10776 }
  10777 
  10778 /* static */
  10779 bool Document::MatchNameAttribute(Element* aElement, int32_t aNamespaceID,
  10780                                  nsAtom* aAtom, void* aData) {
  10781  MOZ_ASSERT(aElement, "Must have element to work with!");
  10782 
  10783  if (!aElement->HasName()) {
  10784    return false;
  10785  }
  10786 
  10787  nsString* elementName = static_cast<nsString*>(aData);
  10788  return aElement->GetNameSpaceID() == kNameSpaceID_XHTML &&
  10789         aElement->AttrValueIs(kNameSpaceID_None, nsGkAtoms::name, *elementName,
  10790                               eCaseMatters);
  10791 }
  10792 
  10793 /* static */
  10794 void* Document::UseExistingNameString(nsINode* aRootNode,
  10795                                      const nsString* aName) {
  10796  return const_cast<nsString*>(aName);
  10797 }
  10798 
  10799 nsresult Document::GetDocumentURI(nsString& aDocumentURI) const {
  10800  if (mDocumentURI) {
  10801    nsAutoCString uri;
  10802    nsresult rv = mDocumentURI->GetSpec(uri);
  10803    NS_ENSURE_SUCCESS(rv, rv);
  10804 
  10805    CopyUTF8toUTF16(uri, aDocumentURI);
  10806  } else {
  10807    aDocumentURI.Truncate();
  10808  }
  10809 
  10810  return NS_OK;
  10811 }
  10812 
  10813 // Alias of above
  10814 nsresult Document::GetURL(nsString& aURL) const { return GetDocumentURI(aURL); }
  10815 
  10816 void Document::GetDocumentURIFromJS(nsString& aDocumentURI,
  10817                                    CallerType aCallerType,
  10818                                    ErrorResult& aRv) const {
  10819  if (!mChromeXHRDocURI || aCallerType != CallerType::System) {
  10820    aRv = GetDocumentURI(aDocumentURI);
  10821    return;
  10822  }
  10823 
  10824  nsAutoCString uri;
  10825  nsresult res = mChromeXHRDocURI->GetSpec(uri);
  10826  if (NS_FAILED(res)) {
  10827    aRv.Throw(res);
  10828    return;
  10829  }
  10830  CopyUTF8toUTF16(uri, aDocumentURI);
  10831 }
  10832 
  10833 nsIURI* Document::GetDocumentURIObject() const {
  10834  if (!mChromeXHRDocURI) {
  10835    return GetDocumentURI();
  10836  }
  10837 
  10838  return mChromeXHRDocURI;
  10839 }
  10840 
  10841 void Document::GetCompatMode(nsString& aCompatMode) const {
  10842  NS_ASSERTION(mCompatMode == eCompatibility_NavQuirks ||
  10843                   mCompatMode == eCompatibility_AlmostStandards ||
  10844                   mCompatMode == eCompatibility_FullStandards,
  10845               "mCompatMode is neither quirks nor strict for this document");
  10846 
  10847  if (mCompatMode == eCompatibility_NavQuirks) {
  10848    aCompatMode.AssignLiteral("BackCompat");
  10849  } else {
  10850    aCompatMode.AssignLiteral("CSS1Compat");
  10851  }
  10852 }
  10853 
  10854 }  // namespace dom
  10855 }  // namespace mozilla
  10856 
  10857 void nsDOMAttributeMap::BlastSubtreeToPieces(nsINode* aNode) {
  10858  if (Element* element = Element::FromNode(aNode)) {
  10859    if (const nsDOMAttributeMap* map = element->GetAttributeMap()) {
  10860      while (true) {
  10861        RefPtr<Attr> attr;
  10862        {
  10863          // Use an iterator to get an arbitrary attribute from the
  10864          // cache. The iterator must be destroyed before any other
  10865          // operations on mAttributeCache, to avoid hash table
  10866          // assertions.
  10867          auto iter = map->mAttributeCache.ConstIter();
  10868          if (iter.Done()) {
  10869            break;
  10870          }
  10871          attr = iter.UserData();
  10872        }
  10873 
  10874        BlastSubtreeToPieces(attr);
  10875 
  10876        mozilla::DebugOnly<nsresult> rv =
  10877            element->UnsetAttr(attr->NodeInfo()->NamespaceID(),
  10878                               attr->NodeInfo()->NameAtom(), false);
  10879 
  10880        // XXX Should we abort here?
  10881        NS_ASSERTION(NS_SUCCEEDED(rv), "Uh-oh, UnsetAttr shouldn't fail!");
  10882      }
  10883    }
  10884 
  10885    if (mozilla::dom::ShadowRoot* shadow = element->GetShadowRoot()) {
  10886      BlastSubtreeToPieces(shadow);
  10887      element->UnattachShadow();
  10888    }
  10889  }
  10890 
  10891  while (aNode->HasChildren()) {
  10892    nsIContent* node = aNode->GetFirstChild();
  10893    BlastSubtreeToPieces(node);
  10894    aNode->RemoveChildNode(node, false);
  10895  }
  10896 }
  10897 
  10898 namespace mozilla::dom {
  10899 
  10900 nsINode* Document::AdoptNode(nsINode& aAdoptedNode, ErrorResult& rv,
  10901                             bool aAcceptShadowRoot) {
  10902  OwningNonNull<nsINode> adoptedNode = aAdoptedNode;
  10903  if (adoptedNode->IsShadowRoot() && !aAcceptShadowRoot) {
  10904    rv.ThrowHierarchyRequestError("The adopted node is a shadow root.");
  10905    return nullptr;
  10906  }
  10907 
  10908  if (adoptedNode->GetParentNode()) {
  10909    nsContentUtils::NotifyDevToolsOfNodeRemoval(*adoptedNode);
  10910  }
  10911 
  10912  nsAutoScriptBlocker scriptBlocker;
  10913 
  10914  switch (adoptedNode->NodeType()) {
  10915    case ATTRIBUTE_NODE: {
  10916      // Remove from ownerElement.
  10917      OwningNonNull<Attr> adoptedAttr = static_cast<Attr&>(*adoptedNode);
  10918 
  10919      nsCOMPtr<Element> ownerElement = adoptedAttr->GetOwnerElement();
  10920      if (rv.Failed()) {
  10921        return nullptr;
  10922      }
  10923 
  10924      if (ownerElement) {
  10925        OwningNonNull<Attr> newAttr =
  10926            ownerElement->RemoveAttributeNode(*adoptedAttr, rv);
  10927        if (rv.Failed()) {
  10928          return nullptr;
  10929        }
  10930      }
  10931 
  10932      break;
  10933    }
  10934    case DOCUMENT_FRAGMENT_NODE:
  10935    case ELEMENT_NODE:
  10936    case PROCESSING_INSTRUCTION_NODE:
  10937    case TEXT_NODE:
  10938    case CDATA_SECTION_NODE:
  10939    case COMMENT_NODE:
  10940    case DOCUMENT_TYPE_NODE: {
  10941      // Don't allow adopting a node's anonymous subtree out from under it.
  10942      if (adoptedNode->IsRootOfNativeAnonymousSubtree()) {
  10943        rv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
  10944        return nullptr;
  10945      }
  10946 
  10947      // We don't want to adopt an element into its own contentDocument or into
  10948      // a descendant contentDocument, so we check if the frameElement of this
  10949      // document or any of its parents is the adopted node or one of its
  10950      // descendants.
  10951      RefPtr<BrowsingContext> bc = GetBrowsingContext();
  10952      while (bc) {
  10953        nsCOMPtr<nsINode> node = bc->GetEmbedderElement();
  10954        if (node && node->IsInclusiveDescendantOf(adoptedNode)) {
  10955          rv.ThrowHierarchyRequestError(
  10956              "Trying to adopt a node into its own contentDocument or a "
  10957              "descendant contentDocument.");
  10958          return nullptr;
  10959        }
  10960 
  10961        if (XRE_IsParentProcess()) {
  10962          bc = bc->Canonical()->GetParentCrossChromeBoundary();
  10963        } else {
  10964          bc = bc->GetParent();
  10965        }
  10966      }
  10967 
  10968      // Remove from parent.
  10969      nsCOMPtr<nsINode> parent = adoptedNode->GetParentNode();
  10970      if (parent) {
  10971        parent->RemoveChildNode(adoptedNode->AsContent(), true);
  10972      } else {
  10973        MOZ_ASSERT(!adoptedNode->IsInUncomposedDoc());
  10974      }
  10975 
  10976      break;
  10977    }
  10978    case DOCUMENT_NODE: {
  10979      rv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
  10980      return nullptr;
  10981    }
  10982    default: {
  10983      NS_WARNING("Don't know how to adopt this nodetype for adoptNode.");
  10984 
  10985      rv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
  10986      return nullptr;
  10987    }
  10988  }
  10989 
  10990  nsCOMPtr<Document> oldDocument = adoptedNode->OwnerDoc();
  10991  bool sameDocument = oldDocument == this;
  10992 
  10993  AutoJSContext cx;
  10994  JS::Rooted<JSObject*> newScope(cx, nullptr);
  10995  if (!sameDocument) {
  10996    newScope = GetWrapper();
  10997    if (!newScope && GetScopeObject() && GetScopeObject()->HasJSGlobal()) {
  10998      // Make sure cx is in a semi-sane compartment before we call WrapNative.
  10999      // It's kind of irrelevant, given that we're passing aAllowWrapping =
  11000      // false, and documents should always insist on being wrapped in an
  11001      // canonical scope. But we try to pass something sane anyway.
  11002      JSObject* globalObject = GetScopeObject()->GetGlobalJSObject();
  11003      JSAutoRealm ar(cx, globalObject);
  11004      JS::Rooted<JS::Value> v(cx);
  11005      rv = nsContentUtils::WrapNative(cx, ToSupports(this), this, &v,
  11006                                      /* aAllowWrapping = */ false);
  11007      if (rv.Failed()) return nullptr;
  11008      newScope = &v.toObject();
  11009    }
  11010  }
  11011 
  11012  adoptedNode->Adopt(sameDocument ? nullptr : mNodeInfoManager, newScope, rv);
  11013  if (rv.Failed()) {
  11014    // Disconnect all nodes from their parents, since some have the old document
  11015    // as their ownerDocument and some have this as their ownerDocument.
  11016    nsDOMAttributeMap::BlastSubtreeToPieces(adoptedNode);
  11017    return nullptr;
  11018  }
  11019 
  11020  MOZ_ASSERT(adoptedNode->OwnerDoc() == this,
  11021             "Should still be in the document we just got adopted into");
  11022 
  11023  return adoptedNode;
  11024 }
  11025 
  11026 bool Document::UseWidthDeviceWidthFallbackViewport() const { return false; }
  11027 
  11028 static Maybe<LayoutDeviceToScreenScale> ParseScaleString(
  11029    const nsString& aScaleString) {
  11030  // https://drafts.csswg.org/css-device-adapt/#min-scale-max-scale
  11031  if (aScaleString.EqualsLiteral("device-width") ||
  11032      aScaleString.EqualsLiteral("device-height")) {
  11033    return Some(LayoutDeviceToScreenScale(10.0f));
  11034  } else if (aScaleString.EqualsLiteral("yes")) {
  11035    return Some(LayoutDeviceToScreenScale(1.0f));
  11036  } else if (aScaleString.EqualsLiteral("no")) {
  11037    return Some(LayoutDeviceToScreenScale(ViewportMinScale()));
  11038  } else if (aScaleString.IsEmpty()) {
  11039    return Nothing();
  11040  }
  11041 
  11042  nsresult scaleErrorCode;
  11043  float scale = aScaleString.ToFloatAllowTrailingChars(&scaleErrorCode);
  11044  if (NS_FAILED(scaleErrorCode)) {
  11045    return Some(LayoutDeviceToScreenScale(ViewportMinScale()));
  11046  }
  11047 
  11048  if (scale < 0) {
  11049    return Nothing();
  11050  }
  11051  return Some(std::clamp(LayoutDeviceToScreenScale(scale), ViewportMinScale(),
  11052                         ViewportMaxScale()));
  11053 }
  11054 
  11055 void Document::ParseScalesInViewportMetaData(
  11056    const ViewportMetaData& aViewportMetaData) {
  11057  Maybe<LayoutDeviceToScreenScale> scale;
  11058 
  11059  scale = ParseScaleString(aViewportMetaData.mInitialScale);
  11060  mScaleFloat = scale.valueOr(LayoutDeviceToScreenScale(0.0f));
  11061  mValidScaleFloat = scale.isSome();
  11062 
  11063  scale = ParseScaleString(aViewportMetaData.mMaximumScale);
  11064  // Chrome uses '5' for the fallback value of maximum-scale, we might
  11065  // consider matching it in future.
  11066  // https://cs.chromium.org/chromium/src/third_party/blink/renderer/core/html/html_meta_element.cc?l=452&rcl=65ca4278b42d269ca738fc93ef7ae04a032afeb0
  11067  mScaleMaxFloat = scale.valueOr(ViewportMaxScale());
  11068  mValidMaxScale = scale.isSome();
  11069 
  11070  scale = ParseScaleString(aViewportMetaData.mMinimumScale);
  11071  mScaleMinFloat = scale.valueOr(ViewportMinScale());
  11072  mValidMinScale = scale.isSome();
  11073 
  11074  // Resolve min-zoom and max-zoom values.
  11075  // https://drafts.csswg.org/css-device-adapt/#constraining-min-max-zoom
  11076  if (mValidMaxScale && mValidMinScale) {
  11077    mScaleMaxFloat = std::max(mScaleMinFloat, mScaleMaxFloat);
  11078  }
  11079 }
  11080 
  11081 void Document::ParseWidthAndHeightInMetaViewport(const nsAString& aWidthString,
  11082                                                 const nsAString& aHeightString,
  11083                                                 bool aHasValidScale) {
  11084  // The width and height properties
  11085  // https://drafts.csswg.org/css-device-adapt/#width-and-height-properties
  11086  //
  11087  // The width and height viewport <META> properties are translated into width
  11088  // and height descriptors, setting the min-width/min-height value to
  11089  // extend-to-zoom and the max-width/max-height value to the length from the
  11090  // viewport <META> property as follows:
  11091  //
  11092  // 1. Non-negative number values are translated to pixel lengths, clamped to
  11093  //    the range: [1px, 10000px]
  11094  // 2. Negative number values are dropped
  11095  // 3. device-width and device-height translate to 100vw and 100vh respectively
  11096  // 4. Other keywords and unknown values are also dropped
  11097  mMinWidth = nsViewportInfo::kAuto;
  11098  mMaxWidth = nsViewportInfo::kAuto;
  11099  if (!aWidthString.IsEmpty()) {
  11100    mMinWidth = nsViewportInfo::kExtendToZoom;
  11101    if (aWidthString.EqualsLiteral("device-width")) {
  11102      mMaxWidth = nsViewportInfo::kDeviceSize;
  11103    } else {
  11104      nsresult widthErrorCode;
  11105      mMaxWidth = aWidthString.ToInteger(&widthErrorCode);
  11106      if (NS_FAILED(widthErrorCode)) {
  11107        mMaxWidth = nsViewportInfo::kAuto;
  11108      } else if (mMaxWidth >= 0.0f) {
  11109        mMaxWidth = std::clamp(mMaxWidth, CSSCoord(1.0f), CSSCoord(10000.0f));
  11110      } else {
  11111        mMaxWidth = nsViewportInfo::kAuto;
  11112      }
  11113    }
  11114  } else if (aHasValidScale) {
  11115    if (aHeightString.IsEmpty()) {
  11116      mMinWidth = nsViewportInfo::kExtendToZoom;
  11117      mMaxWidth = nsViewportInfo::kExtendToZoom;
  11118    }
  11119  } else if (aHeightString.IsEmpty() && UseWidthDeviceWidthFallbackViewport()) {
  11120    mMinWidth = nsViewportInfo::kExtendToZoom;
  11121    mMaxWidth = nsViewportInfo::kDeviceSize;
  11122  }
  11123 
  11124  mMinHeight = nsViewportInfo::kAuto;
  11125  mMaxHeight = nsViewportInfo::kAuto;
  11126  if (!aHeightString.IsEmpty()) {
  11127    mMinHeight = nsViewportInfo::kExtendToZoom;
  11128    if (aHeightString.EqualsLiteral("device-height")) {
  11129      mMaxHeight = nsViewportInfo::kDeviceSize;
  11130    } else {
  11131      nsresult heightErrorCode;
  11132      mMaxHeight = aHeightString.ToInteger(&heightErrorCode);
  11133      if (NS_FAILED(heightErrorCode)) {
  11134        mMaxHeight = nsViewportInfo::kAuto;
  11135      } else if (mMaxHeight >= 0.0f) {
  11136        mMaxHeight = std::clamp(mMaxHeight, CSSCoord(1.0f), CSSCoord(10000.0f));
  11137      } else {
  11138        mMaxHeight = nsViewportInfo::kAuto;
  11139      }
  11140    }
  11141  }
  11142 }
  11143 
  11144 nsViewportInfo Document::GetViewportInfo(const ScreenIntSize& aDisplaySize) {
  11145  MOZ_ASSERT(mPresShell);
  11146 
  11147  // Compute the CSS-to-LayoutDevice pixel scale as the product of the
  11148  // widget scale and the full zoom.
  11149  nsPresContext* context = mPresShell->GetPresContext();
  11150  // When querying the full zoom, get it from the device context rather than
  11151  // directly from the pres context, because the device context's value can
  11152  // include an adjustment necessary to keep the number of app units per device
  11153  // pixel an integer, and we want the adjusted value.
  11154  float fullZoom = context ? context->DeviceContext()->GetFullZoom() : 1.0;
  11155  fullZoom = (fullZoom == 0.0) ? 1.0 : fullZoom;
  11156  CSSToLayoutDeviceScale layoutDeviceScale =
  11157      context ? context->CSSToDevPixelScale() : CSSToLayoutDeviceScale(1);
  11158 
  11159  CSSToScreenScale defaultScale =
  11160      layoutDeviceScale * LayoutDeviceToScreenScale(1.0);
  11161 
  11162  auto* bc = GetBrowsingContext();
  11163  const bool inRDM = bc && bc->InRDMPane();
  11164  const bool ignoreMetaTag = [&] {
  11165    if (!nsLayoutUtils::ShouldHandleMetaViewport(this)) {
  11166      return true;
  11167    }
  11168    if (Fullscreen()) {
  11169      // We ignore viewport meta tag etc when in fullscreen, see bug 1696717.
  11170      return true;
  11171    }
  11172    if (inRDM && bc->ForceDesktopViewport()) {
  11173      // We ignore meta viewport when devtools tells us to force desktop
  11174      // viewport on RDM.
  11175      return true;
  11176    }
  11177    return false;
  11178  }();
  11179 
  11180  if (ignoreMetaTag) {
  11181    return nsViewportInfo(aDisplaySize, defaultScale,
  11182                          nsLayoutUtils::AllowZoomingForDocument(this)
  11183                              ? nsViewportInfo::ZoomFlag::AllowZoom
  11184                              : nsViewportInfo::ZoomFlag::DisallowZoom,
  11185                          StaticPrefs::apz_allow_zooming_out()
  11186                              ? nsViewportInfo::ZoomBehaviour::Mobile
  11187                              : nsViewportInfo::ZoomBehaviour::Desktop);
  11188  }
  11189 
  11190  // Special behaviour for desktop mode, provided we are not on an about: page
  11191  // or a PDF.js page.
  11192  if (bc && bc->ForceDesktopViewport() && !IsAboutPage() &&
  11193      !nsContentUtils::IsPDFJS(NodePrincipal())) {
  11194    CSSCoord viewportWidth =
  11195        StaticPrefs::browser_viewport_desktopWidth() / fullZoom;
  11196    // Do not use a desktop viewport size less wide than the display.
  11197    CSSCoord displayWidth = (aDisplaySize / defaultScale).width;
  11198    MOZ_LOG(MobileViewportManager::gLog, LogLevel::Debug,
  11199            ("Desktop-mode viewport size: choosing the larger of display width "
  11200             "(%f) and desktop width (%f)",
  11201             displayWidth.value, viewportWidth.value));
  11202    viewportWidth = nsViewportInfo::Max(displayWidth, viewportWidth);
  11203    CSSToScreenScale scaleToFit(aDisplaySize.width / viewportWidth);
  11204    float aspectRatio = (float)aDisplaySize.height / aDisplaySize.width;
  11205    CSSSize viewportSize(viewportWidth, viewportWidth * aspectRatio);
  11206    ScreenIntSize fakeDesktopSize = RoundedToInt(viewportSize * scaleToFit);
  11207    return nsViewportInfo(fakeDesktopSize, scaleToFit,
  11208                          nsViewportInfo::ZoomFlag::AllowZoom,
  11209                          nsViewportInfo::ZoomBehaviour::Mobile,
  11210                          nsViewportInfo::AutoScaleFlag::AutoScale);
  11211  }
  11212 
  11213  // In cases where the width of the CSS viewport is less than or equal to the
  11214  // width of the display (i.e. width <= device-width) then we disable
  11215  // double-tap-to-zoom behaviour. See bug 941995 for details.
  11216 
  11217  switch (mViewportType) {
  11218    case DisplayWidthHeight:
  11219      return nsViewportInfo(aDisplaySize, defaultScale,
  11220                            nsViewportInfo::ZoomFlag::AllowZoom,
  11221                            nsViewportInfo::ZoomBehaviour::Mobile);
  11222    case Unknown: {
  11223      // We might early exit if the viewport is empty. Even if we don't,
  11224      // at the end of this case we'll note that it was empty. Later, when
  11225      // we're using the cached values, this will trigger alternate code paths.
  11226      if (!mLastModifiedViewportMetaData) {
  11227        // If the docType specifies that we are on a site optimized for mobile,
  11228        // then we want to return specially crafted defaults for the viewport
  11229        // info.
  11230        if (RefPtr<DocumentType> docType = GetDoctype()) {
  11231          nsAutoString docId;
  11232          docType->GetPublicId(docId);
  11233          if ((docId.Find(u"WAP") != -1) || (docId.Find(u"Mobile") != -1) ||
  11234              (docId.Find(u"WML") != -1)) {
  11235            // We're making an assumption that the docType can't change here
  11236            mViewportType = DisplayWidthHeight;
  11237            return nsViewportInfo(aDisplaySize, defaultScale,
  11238                                  nsViewportInfo::ZoomFlag::AllowZoom,
  11239                                  nsViewportInfo::ZoomBehaviour::Mobile);
  11240          }
  11241        }
  11242 
  11243        nsAutoString handheldFriendly;
  11244        GetHeaderData(nsGkAtoms::handheldFriendly, handheldFriendly);
  11245        if (handheldFriendly.EqualsLiteral("true")) {
  11246          mViewportType = DisplayWidthHeight;
  11247          return nsViewportInfo(aDisplaySize, defaultScale,
  11248                                nsViewportInfo::ZoomFlag::AllowZoom,
  11249                                nsViewportInfo::ZoomBehaviour::Mobile);
  11250        }
  11251      }
  11252 
  11253      ViewportMetaData metaData = GetViewportMetaData();
  11254 
  11255      // Parse initial-scale, minimum-scale and maximum-scale.
  11256      ParseScalesInViewportMetaData(metaData);
  11257 
  11258      // Parse width and height properties
  11259      // This function sets m{Min,Max}{Width,Height}.
  11260      ParseWidthAndHeightInMetaViewport(metaData.mWidth, metaData.mHeight,
  11261                                        mValidScaleFloat);
  11262 
  11263      mAllowZoom = true;
  11264      if ((metaData.mUserScalable.EqualsLiteral("0")) ||
  11265          (metaData.mUserScalable.EqualsLiteral("no")) ||
  11266          (metaData.mUserScalable.EqualsLiteral("false"))) {
  11267        mAllowZoom = false;
  11268      }
  11269 
  11270      // Resolve viewport-fit value.
  11271      // https://drafts.csswg.org/css-round-display/#viewport-fit-descriptor
  11272      mViewportFit = ViewportFitType::Auto;
  11273      if (!metaData.mViewportFit.IsEmpty()) {
  11274        if (metaData.mViewportFit.EqualsLiteral("contain")) {
  11275          mViewportFit = ViewportFitType::Contain;
  11276        } else if (metaData.mViewportFit.EqualsLiteral("cover")) {
  11277          mViewportFit = ViewportFitType::Cover;
  11278        }
  11279      }
  11280 
  11281      mWidthStrEmpty = metaData.mWidth.IsEmpty();
  11282 
  11283      mViewportType = Specified;
  11284      [[fallthrough]];
  11285    }
  11286    case Specified:
  11287    default:
  11288      LayoutDeviceToScreenScale effectiveMinScale = mScaleMinFloat;
  11289      LayoutDeviceToScreenScale effectiveMaxScale = mScaleMaxFloat;
  11290      bool effectiveValidMaxScale = mValidMaxScale;
  11291 
  11292      nsViewportInfo::ZoomFlag effectiveZoomFlag =
  11293          mAllowZoom ? nsViewportInfo::ZoomFlag::AllowZoom
  11294                     : nsViewportInfo::ZoomFlag::DisallowZoom;
  11295      if (StaticPrefs::browser_ui_zoom_force_user_scalable()) {
  11296        // If the pref to force user-scalable is enabled, we ignore the values
  11297        // from the meta-viewport tag for these properties and just assume they
  11298        // allow the page to be scalable. Note in particular that this code is
  11299        // in the "Specified" branch of the enclosing switch statement, so that
  11300        // calls to GetViewportInfo always use the latest value of the
  11301        // browser_ui_zoom_force_user_scalable pref. Other codepaths that
  11302        // return nsViewportInfo instances are all consistent with
  11303        // browser_ui_zoom_force_user_scalable() already.
  11304        effectiveMinScale = ViewportMinScale();
  11305        effectiveMaxScale = ViewportMaxScale();
  11306        effectiveValidMaxScale = true;
  11307        effectiveZoomFlag = nsViewportInfo::ZoomFlag::AllowZoom;
  11308      }
  11309 
  11310      // Returns extend-zoom value which is MIN(mScaleFloat, mScaleMaxFloat).
  11311      auto ComputeExtendZoom = [&]() -> float {
  11312        if (mValidScaleFloat && effectiveValidMaxScale) {
  11313          return std::min(mScaleFloat.scale, effectiveMaxScale.scale);
  11314        }
  11315        if (mValidScaleFloat) {
  11316          return mScaleFloat.scale;
  11317        }
  11318        if (effectiveValidMaxScale) {
  11319          return effectiveMaxScale.scale;
  11320        }
  11321        return nsViewportInfo::kAuto;
  11322      };
  11323 
  11324      // Resolving 'extend-to-zoom'
  11325      // https://drafts.csswg.org/css-device-adapt/#resolve-extend-to-zoom
  11326      float extendZoom = ComputeExtendZoom();
  11327 
  11328      CSSCoord minWidth = mMinWidth;
  11329      CSSCoord maxWidth = mMaxWidth;
  11330      CSSCoord minHeight = mMinHeight;
  11331      CSSCoord maxHeight = mMaxHeight;
  11332 
  11333      // aDisplaySize is in screen pixels; convert them to CSS pixels for the
  11334      // viewport size. We need to use this scaled size for any clamping of
  11335      // width or height.
  11336      CSSSize displaySize = ScreenSize(aDisplaySize) / defaultScale;
  11337 
  11338      // Our min and max width and height values are mostly as specified by
  11339      // the viewport declaration, but we make an exception for max width.
  11340      // Max width, if auto, and if there's no initial-scale, will be set
  11341      // to a default size. This is to support legacy site design with no
  11342      // viewport declaration, and to do that using the same scheme as
  11343      // Chrome does, in order to maintain web compatibility. Since the
  11344      // default size has a complicated calculation, we fixup the maxWidth
  11345      // value after setting it, above.
  11346      if (maxWidth == nsViewportInfo::kAuto && !mValidScaleFloat) {
  11347        maxWidth = StaticPrefs::browser_viewport_desktopWidth();
  11348        // Divide by fullZoom to stretch CSS pixel size of viewport in order
  11349        // to keep device pixel size unchanged after full zoom applied.
  11350        // See bug 974242.
  11351        maxWidth /= fullZoom;
  11352 
  11353        // The fallback behaviour of using browser.viewport.desktopWidth
  11354        // was designed with small screens in mind, where we are _expanding_ the
  11355        // viewport width to this value. On wider displays (e.g. most tablets),
  11356        // we would actually be _shrinking_ the viewport width to this value,
  11357        // which we want to avoid because it constrains the viewport width
  11358        // (and often results in a larger initial scale) unnecessarily.
  11359        MOZ_LOG(MobileViewportManager::gLog, LogLevel::Debug,
  11360                ("Fallback viewport size: choosing the larger of display width "
  11361                 "(%f) and desktop width (%f)",
  11362                 displaySize.width, maxWidth.value));
  11363        maxWidth = nsViewportInfo::Max(displaySize.width, maxWidth);
  11364 
  11365        // We set minWidth to ExtendToZoom, which will cause our later width
  11366        // calculation to expand to maxWidth, if scale restrictions allow it.
  11367        minWidth = nsViewportInfo::kExtendToZoom;
  11368      }
  11369 
  11370      // Resolve device-width and device-height first.
  11371      if (maxWidth == nsViewportInfo::kDeviceSize) {
  11372        maxWidth = displaySize.width;
  11373      }
  11374      if (maxHeight == nsViewportInfo::kDeviceSize) {
  11375        maxHeight = displaySize.height;
  11376      }
  11377      if (extendZoom == nsViewportInfo::kAuto) {
  11378        if (maxWidth == nsViewportInfo::kExtendToZoom) {
  11379          maxWidth = nsViewportInfo::kAuto;
  11380        }
  11381        if (maxHeight == nsViewportInfo::kExtendToZoom) {
  11382          maxHeight = nsViewportInfo::kAuto;
  11383        }
  11384        if (minWidth == nsViewportInfo::kExtendToZoom) {
  11385          minWidth = maxWidth;
  11386        }
  11387        if (minHeight == nsViewportInfo::kExtendToZoom) {
  11388          minHeight = maxHeight;
  11389        }
  11390      } else {
  11391        CSSSize extendSize = displaySize / extendZoom;
  11392        if (maxWidth == nsViewportInfo::kExtendToZoom) {
  11393          maxWidth = extendSize.width;
  11394        }
  11395        if (maxHeight == nsViewportInfo::kExtendToZoom) {
  11396          maxHeight = extendSize.height;
  11397        }
  11398        if (minWidth == nsViewportInfo::kExtendToZoom) {
  11399          minWidth = nsViewportInfo::Max(extendSize.width, maxWidth);
  11400        }
  11401        if (minHeight == nsViewportInfo::kExtendToZoom) {
  11402          minHeight = nsViewportInfo::Max(extendSize.height, maxHeight);
  11403        }
  11404      }
  11405 
  11406      // Resolve initial width and height from min/max descriptors
  11407      // https://drafts.csswg.org/css-device-adapt/#resolve-initial-width-height
  11408      CSSCoord width = nsViewportInfo::kAuto;
  11409      if (minWidth != nsViewportInfo::kAuto ||
  11410          maxWidth != nsViewportInfo::kAuto) {
  11411        width = nsViewportInfo::Max(
  11412            minWidth, nsViewportInfo::Min(maxWidth, displaySize.width));
  11413      }
  11414      CSSCoord height = nsViewportInfo::kAuto;
  11415      if (minHeight != nsViewportInfo::kAuto ||
  11416          maxHeight != nsViewportInfo::kAuto) {
  11417        height = nsViewportInfo::Max(
  11418            minHeight, nsViewportInfo::Min(maxHeight, displaySize.height));
  11419      }
  11420 
  11421      // Resolve width value
  11422      // https://drafts.csswg.org/css-device-adapt/#resolve-width
  11423      if (width == nsViewportInfo::kAuto) {
  11424        if (height == nsViewportInfo::kAuto || aDisplaySize.height == 0) {
  11425          width = displaySize.width;
  11426        } else {
  11427          width = height * aDisplaySize.width / aDisplaySize.height;
  11428        }
  11429      }
  11430 
  11431      // Resolve height value
  11432      // https://drafts.csswg.org/css-device-adapt/#resolve-height
  11433      if (height == nsViewportInfo::kAuto) {
  11434        if (aDisplaySize.width == 0) {
  11435          height = displaySize.height;
  11436        } else {
  11437          height = width * aDisplaySize.height / aDisplaySize.width;
  11438        }
  11439      }
  11440      MOZ_ASSERT(width != nsViewportInfo::kAuto &&
  11441                 height != nsViewportInfo::kAuto);
  11442 
  11443      CSSSize size(width, height);
  11444 
  11445      CSSToScreenScale scaleFloat = mScaleFloat * layoutDeviceScale;
  11446      CSSToScreenScale scaleMinFloat = effectiveMinScale * layoutDeviceScale;
  11447      CSSToScreenScale scaleMaxFloat = effectiveMaxScale * layoutDeviceScale;
  11448 
  11449      nsViewportInfo::AutoSizeFlag sizeFlag =
  11450          nsViewportInfo::AutoSizeFlag::FixedSize;
  11451      if (mMaxWidth == nsViewportInfo::kDeviceSize ||
  11452          (mWidthStrEmpty && (mMaxHeight == nsViewportInfo::kDeviceSize ||
  11453                              mScaleFloat.scale == 1.0f)) ||
  11454          (!mWidthStrEmpty && mMaxWidth == nsViewportInfo::kAuto &&
  11455           mMaxHeight < 0)) {
  11456        sizeFlag = nsViewportInfo::AutoSizeFlag::AutoSize;
  11457      }
  11458 
  11459      // FIXME: Resolving width and height should be done above 'Resolve width
  11460      // value' and 'Resolve height value'.
  11461      if (sizeFlag == nsViewportInfo::AutoSizeFlag::AutoSize) {
  11462        size = displaySize;
  11463      }
  11464 
  11465      // The purpose of clamping the viewport width to a minimum size is to
  11466      // prevent page authors from setting it to a ridiculously small value.
  11467      // If the page is actually being rendered in a very small area (as might
  11468      // happen in e.g. Android 8's picture-in-picture mode), we don't want to
  11469      // prevent the viewport from taking on that size.
  11470      CSSSize effectiveMinSize = Min(CSSSize(kViewportMinSize), displaySize);
  11471 
  11472      size.width = std::clamp(size.width, effectiveMinSize.width,
  11473                              float(kViewportMaxSize.width));
  11474 
  11475      // Also recalculate the default zoom, if it wasn't specified in the
  11476      // metadata, and the width is specified.
  11477      if (!mValidScaleFloat && !mWidthStrEmpty) {
  11478        CSSToScreenScale bestFitScale(float(aDisplaySize.width) / size.width);
  11479        scaleFloat = (scaleFloat > bestFitScale) ? scaleFloat : bestFitScale;
  11480      }
  11481 
  11482      size.height = std::clamp(size.height, effectiveMinSize.height,
  11483                               float(kViewportMaxSize.height));
  11484 
  11485      // In cases of user-scalable=no, if we have a positive scale, clamp it to
  11486      // min and max, and then use the clamped value for the scale, the min, and
  11487      // the max. If we don't have a positive scale, assert that we are setting
  11488      // the auto scale flag.
  11489      if (effectiveZoomFlag == nsViewportInfo::ZoomFlag::DisallowZoom &&
  11490          scaleFloat > CSSToScreenScale(0.0f)) {
  11491        scaleFloat = scaleMinFloat = scaleMaxFloat =
  11492            std::clamp(scaleFloat, scaleMinFloat, scaleMaxFloat);
  11493      }
  11494      MOZ_ASSERT(
  11495          scaleFloat > CSSToScreenScale(0.0f) || !mValidScaleFloat,
  11496          "If we don't have a positive scale, we should be using auto scale.");
  11497 
  11498      // We need to perform a conversion, but only if the initial or maximum
  11499      // scale were set explicitly by the user.
  11500      if (mValidScaleFloat && scaleFloat >= scaleMinFloat &&
  11501          scaleFloat <= scaleMaxFloat) {
  11502        CSSSize displaySize = ScreenSize(aDisplaySize) / scaleFloat;
  11503        size.width = std::max(size.width, displaySize.width);
  11504        size.height = std::max(size.height, displaySize.height);
  11505      } else if (effectiveValidMaxScale) {
  11506        CSSSize displaySize = ScreenSize(aDisplaySize) / scaleMaxFloat;
  11507        size.width = std::max(size.width, displaySize.width);
  11508        size.height = std::max(size.height, displaySize.height);
  11509      }
  11510 
  11511      return nsViewportInfo(
  11512          scaleFloat, scaleMinFloat, scaleMaxFloat, size, sizeFlag,
  11513          mValidScaleFloat ? nsViewportInfo::AutoScaleFlag::FixedScale
  11514                           : nsViewportInfo::AutoScaleFlag::AutoScale,
  11515          effectiveZoomFlag, mViewportFit);
  11516  }
  11517 }
  11518 
  11519 ViewportMetaData Document::GetViewportMetaData() const {
  11520  return mLastModifiedViewportMetaData ? *mLastModifiedViewportMetaData
  11521                                       : ViewportMetaData();
  11522 }
  11523 
  11524 static InteractiveWidget ParseInteractiveWidget(
  11525    const ViewportMetaData& aViewportMetaData) {
  11526  if (aViewportMetaData.mInteractiveWidgetMode.IsEmpty()) {
  11527    return InteractiveWidgetUtils::DefaultInteractiveWidgetMode();
  11528  }
  11529 
  11530  if (aViewportMetaData.mInteractiveWidgetMode.EqualsIgnoreCase(
  11531          "resizes-visual")) {
  11532    return InteractiveWidget::ResizesVisual;
  11533  }
  11534  if (aViewportMetaData.mInteractiveWidgetMode.EqualsIgnoreCase(
  11535          "resizes-content")) {
  11536    return InteractiveWidget::ResizesContent;
  11537  }
  11538  if (aViewportMetaData.mInteractiveWidgetMode.EqualsIgnoreCase(
  11539          "overlays-content")) {
  11540    return InteractiveWidget::OverlaysContent;
  11541  }
  11542  return InteractiveWidgetUtils::DefaultInteractiveWidgetMode();
  11543 }
  11544 
  11545 void Document::SetMetaViewportData(UniquePtr<ViewportMetaData> aData) {
  11546  mLastModifiedViewportMetaData = std::move(aData);
  11547  // Trigger recomputation of the nsViewportInfo the next time it's queried.
  11548  mViewportType = Unknown;
  11549 
  11550  // Parse interactive-widget here anyway. Normally we parse any data in the
  11551  // meta viewport tag in GetViewportInfo(), but GetViewportInfo() depends on
  11552  // the document state (e.g. display size, fullscreen, desktop-mode etc.)
  11553  // whereas interactive-widget is independent from the document state, it's
  11554  // necessary whatever the document state is.
  11555  dom::InteractiveWidget interactiveWidget =
  11556      ParseInteractiveWidget(*mLastModifiedViewportMetaData);
  11557  if (mInteractiveWidgetMode != interactiveWidget) {
  11558    mInteractiveWidgetMode = interactiveWidget;
  11559  }
  11560 
  11561  AsyncEventDispatcher::RunDOMEventWhenSafe(
  11562      *this, u"DOMMetaViewportFitChanged"_ns, CanBubble::eYes,
  11563      ChromeOnlyDispatch::eYes);
  11564 }
  11565 
  11566 EventListenerManager* Document::GetOrCreateListenerManager() {
  11567  if (!mListenerManager) {
  11568    mListenerManager =
  11569        new EventListenerManager(static_cast<EventTarget*>(this));
  11570    SetFlags(NODE_HAS_LISTENERMANAGER);
  11571  }
  11572 
  11573  return mListenerManager;
  11574 }
  11575 
  11576 EventListenerManager* Document::GetExistingListenerManager() const {
  11577  return mListenerManager;
  11578 }
  11579 
  11580 void Document::GetEventTargetParent(EventChainPreVisitor& aVisitor) {
  11581  aVisitor.mCanHandle = true;
  11582  // FIXME! This is a hack to make middle mouse paste working also in Editor.
  11583  // Bug 329119
  11584  aVisitor.mForceContentDispatch = true;
  11585 
  11586  // Load events must not propagate to |window| object, see bug 335251.
  11587  if (aVisitor.mEvent->mMessage != eLoad) {
  11588    nsGlobalWindowOuter* window = nsGlobalWindowOuter::Cast(GetWindow());
  11589    aVisitor.SetParentTarget(
  11590        window ? window->GetTargetForEventTargetChain() : nullptr, false);
  11591  }
  11592 }
  11593 
  11594 already_AddRefed<Event> Document::CreateEvent(const nsAString& aEventType,
  11595                                              CallerType aCallerType,
  11596                                              ErrorResult& rv) const {
  11597  nsPresContext* presContext = GetPresContext();
  11598 
  11599  // Create event even without presContext.
  11600  RefPtr<Event> ev =
  11601      EventDispatcher::CreateEvent(const_cast<Document*>(this), presContext,
  11602                                   nullptr, aEventType, aCallerType);
  11603  if (!ev) {
  11604    rv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
  11605    return nullptr;
  11606  }
  11607  WidgetEvent* e = ev->WidgetEventPtr();
  11608  e->mFlags.mBubbles = false;
  11609  e->mFlags.mCancelable = false;
  11610  return ev.forget();
  11611 }
  11612 
  11613 void Document::FlushPendingNotifications(FlushType aType) {
  11614  mozilla::ChangesToFlush flush(aType, aType >= FlushType::Style,
  11615                                aType >= FlushType::Layout);
  11616  FlushPendingNotifications(flush);
  11617 }
  11618 
  11619 void Document::FlushPendingNotifications(mozilla::ChangesToFlush aFlush) {
  11620  FlushType flushType = aFlush.mFlushType;
  11621 
  11622  RefPtr<Document> documentOnStack = this;
  11623 
  11624  // We need to flush the sink for non-HTML documents (because the XML
  11625  // parser still does insertion with deferred notifications).  We
  11626  // also need to flush the sink if this is a layout-related flush, to
  11627  // make sure that layout is started as needed.  But we can skip that
  11628  // part if we have no presshell or if it's already done an initial
  11629  // reflow.
  11630  if ((!IsHTMLDocument() || (flushType > FlushType::ContentAndNotify &&
  11631                             mPresShell && !mPresShell->DidInitialize())) &&
  11632      (mParser || mWeakSink)) {
  11633    nsCOMPtr<nsIContentSink> sink;
  11634    if (mParser) {
  11635      sink = mParser->GetContentSink();
  11636    } else {
  11637      sink = do_QueryReferent(mWeakSink);
  11638      if (!sink) {
  11639        mWeakSink = nullptr;
  11640      }
  11641    }
  11642    // Determine if it is safe to flush the sink notifications
  11643    // by determining if it safe to flush all the presshells.
  11644    if (sink && (flushType == FlushType::Content || IsSafeToFlush())) {
  11645      sink->FlushPendingNotifications(flushType);
  11646    }
  11647  }
  11648 
  11649  // Should we be flushing pending binding constructors in here?
  11650 
  11651  if (flushType <= FlushType::ContentAndNotify) {
  11652    // Nothing to do here
  11653    return;
  11654  }
  11655 
  11656  // If we have a parent we must flush the parent too to ensure that our
  11657  // container is reflowed if its size was changed.
  11658  //
  11659  // We do it only if the subdocument and the parent can observe each other
  11660  // synchronously (that is, if we're not cross-origin), to avoid work that is
  11661  // not observable, and if the parent document has finished loading all its
  11662  // render-blocking stylesheets and may start laying out the document, to avoid
  11663  // unnecessary flashes of unstyled content on the parent document. Note that
  11664  // this last bit means that size-dependent media queries in this document may
  11665  // produce incorrect results temporarily.
  11666  //
  11667  // But if it's not safe to flush ourselves, then don't flush the parent, since
  11668  // that can cause things like resizes of our frame's widget, which we can't
  11669  // handle while flushing is unsafe.
  11670  if (StyleOrLayoutObservablyDependsOnParentDocumentLayout() &&
  11671      mParentDocument->MayStartLayout() && IsSafeToFlush()) {
  11672    ChangesToFlush parentFlush = aFlush;
  11673    if (flushType >= FlushType::Style) {
  11674      // Since media queries mean that a size change of our container can affect
  11675      // style, we need to promote a style flush on ourself to a layout flush on
  11676      // our parent, since we need our container to be the correct size to
  11677      // determine the correct style.
  11678      parentFlush.mFlushType = std::max(FlushType::Layout, flushType);
  11679    }
  11680    mParentDocument->FlushPendingNotifications(parentFlush);
  11681  }
  11682 
  11683  if (RefPtr<PresShell> presShell = GetPresShell()) {
  11684    presShell->FlushPendingNotifications(aFlush);
  11685  }
  11686 }
  11687 
  11688 void Document::FlushExternalResources(FlushType aType) {
  11689  NS_ASSERTION(
  11690      aType >= FlushType::Style,
  11691      "should only need to flush for style or higher in external resources");
  11692  if (GetDisplayDocument()) {
  11693    return;
  11694  }
  11695 
  11696  EnumerateExternalResources([aType](Document& aDoc) {
  11697    aDoc.FlushPendingNotifications(aType);
  11698    return CallState::Continue;
  11699  });
  11700 }
  11701 
  11702 void Document::SetXMLDeclaration(const char16_t* aVersion,
  11703                                 const char16_t* aEncoding,
  11704                                 const int32_t aStandalone) {
  11705  if (!aVersion || *aVersion == '\0') {
  11706    mXMLDeclarationBits = 0;
  11707    return;
  11708  }
  11709 
  11710  mXMLDeclarationBits = XML_DECLARATION_BITS_DECLARATION_EXISTS;
  11711 
  11712  if (aEncoding && *aEncoding != '\0') {
  11713    mXMLDeclarationBits |= XML_DECLARATION_BITS_ENCODING_EXISTS;
  11714  }
  11715 
  11716  if (aStandalone == 1) {
  11717    mXMLDeclarationBits |= XML_DECLARATION_BITS_STANDALONE_EXISTS |
  11718                           XML_DECLARATION_BITS_STANDALONE_YES;
  11719  } else if (aStandalone == 0) {
  11720    mXMLDeclarationBits |= XML_DECLARATION_BITS_STANDALONE_EXISTS;
  11721  }
  11722 }
  11723 
  11724 void Document::GetXMLDeclaration(nsAString& aVersion, nsAString& aEncoding,
  11725                                 nsAString& aStandalone) {
  11726  aVersion.Truncate();
  11727  aEncoding.Truncate();
  11728  aStandalone.Truncate();
  11729 
  11730  if (!(mXMLDeclarationBits & XML_DECLARATION_BITS_DECLARATION_EXISTS)) {
  11731    return;
  11732  }
  11733 
  11734  // always until we start supporting 1.1 etc.
  11735  aVersion.AssignLiteral("1.0");
  11736 
  11737  if (mXMLDeclarationBits & XML_DECLARATION_BITS_ENCODING_EXISTS) {
  11738    // This is what we have stored, not necessarily what was written
  11739    // in the original
  11740    GetCharacterSet(aEncoding);
  11741  }
  11742 
  11743  if (mXMLDeclarationBits & XML_DECLARATION_BITS_STANDALONE_EXISTS) {
  11744    if (mXMLDeclarationBits & XML_DECLARATION_BITS_STANDALONE_YES) {
  11745      aStandalone.AssignLiteral("yes");
  11746    } else {
  11747      aStandalone.AssignLiteral("no");
  11748    }
  11749  }
  11750 }
  11751 
  11752 void Document::AddColorSchemeMeta(HTMLMetaElement& aMeta) {
  11753  mColorSchemeMetaTags.Insert(aMeta);
  11754  RecomputeColorScheme();
  11755 }
  11756 
  11757 void Document::RemoveColorSchemeMeta(HTMLMetaElement& aMeta) {
  11758  mColorSchemeMetaTags.RemoveElement(aMeta);
  11759  RecomputeColorScheme();
  11760 }
  11761 
  11762 void Document::RecomputeColorScheme() {
  11763  auto oldColorScheme = mColorSchemeBits;
  11764  mColorSchemeBits = 0;
  11765  for (const HTMLMetaElement* el : mColorSchemeMetaTags.AsSpan()) {
  11766    nsAutoString content;
  11767    if (!el->GetAttr(nsGkAtoms::content, content)) {
  11768      continue;
  11769    }
  11770 
  11771    NS_ConvertUTF16toUTF8 contentU8(content);
  11772    if (Servo_ColorScheme_Parse(&contentU8, &mColorSchemeBits)) {
  11773      break;
  11774    }
  11775  }
  11776 
  11777  if (mColorSchemeBits == oldColorScheme) {
  11778    return;
  11779  }
  11780 
  11781  if (nsPresContext* pc = GetPresContext()) {
  11782    // This affects system colors, which are inherited, so we need to recascade.
  11783    pc->RebuildAllStyleData(nsChangeHint(0), RestyleHint::RecascadeSubtree());
  11784  }
  11785 }
  11786 
  11787 bool Document::IsScriptEnabled() const {
  11788  // If this document is sandboxed without 'allow-scripts'
  11789  // script is not enabled
  11790  if (HasScriptsBlockedBySandbox()) {
  11791    return false;
  11792  }
  11793 
  11794  nsCOMPtr<nsIScriptGlobalObject> globalObject =
  11795      do_QueryInterface(GetInnerWindow());
  11796  if (!globalObject || !globalObject->HasJSGlobal()) {
  11797    return false;
  11798  }
  11799 
  11800  return xpc::Scriptability::Get(globalObject->GetGlobalJSObjectPreserveColor())
  11801      .Allowed();
  11802 }
  11803 
  11804 void Document::RetrieveRelevantHeaders(nsIChannel* aChannel) {
  11805  PRTime modDate = 0;
  11806  nsresult rv;
  11807 
  11808  nsCOMPtr<nsIHttpChannel> httpChannel;
  11809  rv = GetHttpChannelHelper(aChannel, getter_AddRefs(httpChannel));
  11810  if (NS_WARN_IF(NS_FAILED(rv))) {
  11811    return;
  11812  }
  11813 
  11814  if (httpChannel) {
  11815    nsAutoCString tmp;
  11816    rv = httpChannel->GetResponseHeader("last-modified"_ns, tmp);
  11817 
  11818    if (NS_SUCCEEDED(rv)) {
  11819      PRTime time;
  11820      PRStatus st = PR_ParseTimeString(tmp.get(), true, &time);
  11821      if (st == PR_SUCCESS) {
  11822        modDate = time;
  11823      }
  11824    }
  11825 
  11826    static const char* const headers[] = {
  11827        "default-style", "content-style-type", "content-language",
  11828        "content-disposition", "refresh", "x-dns-prefetch-control",
  11829        "x-frame-options", "origin-trial", "onion-location",
  11830        // add more http headers if you need
  11831        // XXXbz don't add content-location support without reading bug
  11832        // 238654 and its dependencies/dups first.
  11833        0};
  11834 
  11835    nsAutoCString headerVal;
  11836    const char* const* name = headers;
  11837    while (*name) {
  11838      rv = httpChannel->GetResponseHeader(nsDependentCString(*name), headerVal);
  11839      if (NS_SUCCEEDED(rv) && !headerVal.IsEmpty()) {
  11840        RefPtr<nsAtom> key = NS_Atomize(*name);
  11841        SetHeaderData(key, NS_ConvertASCIItoUTF16(headerVal));
  11842      }
  11843      ++name;
  11844    }
  11845  } else {
  11846    nsCOMPtr<nsIFileChannel> fileChannel = do_QueryInterface(aChannel);
  11847    if (fileChannel) {
  11848      nsCOMPtr<nsIFile> file;
  11849      fileChannel->GetFile(getter_AddRefs(file));
  11850      if (file) {
  11851        PRTime msecs;
  11852        rv = file->GetLastModifiedTime(&msecs);
  11853 
  11854        if (NS_SUCCEEDED(rv)) {
  11855          modDate = msecs * int64_t(PR_USEC_PER_MSEC);
  11856        }
  11857      }
  11858    } else {
  11859      nsAutoCString contentDisp;
  11860      rv = aChannel->GetContentDispositionHeader(contentDisp);
  11861      if (NS_SUCCEEDED(rv)) {
  11862        SetHeaderData(nsGkAtoms::headerContentDisposition,
  11863                      NS_ConvertASCIItoUTF16(contentDisp));
  11864      }
  11865    }
  11866  }
  11867 
  11868  mLastModified.Truncate();
  11869  if (modDate != 0) {
  11870    GetFormattedTimeString(modDate,
  11871                           ShouldResistFingerprinting(RFPTarget::JSDateTimeUTC),
  11872                           mLastModified);
  11873  }
  11874 }
  11875 
  11876 void Document::ProcessMETATag(HTMLMetaElement* aMetaElement) {
  11877  // set any HTTP-EQUIV data into document's header data as well as url
  11878  nsAutoString header;
  11879  aMetaElement->GetAttr(nsGkAtoms::httpEquiv, header);
  11880  if (!header.IsEmpty()) {
  11881    // Ignore META REFRESH when document is sandboxed from automatic features.
  11882    nsContentUtils::ASCIIToLower(header);
  11883    if (nsGkAtoms::refresh->Equals(header) &&
  11884        (GetSandboxFlags() & SANDBOXED_AUTOMATIC_FEATURES)) {
  11885      return;
  11886    }
  11887 
  11888    nsAutoString result;
  11889    aMetaElement->GetAttr(nsGkAtoms::content, result);
  11890    if (!result.IsEmpty()) {
  11891      RefPtr<nsAtom> fieldAtom(NS_Atomize(header));
  11892      SetHeaderData(fieldAtom, result);
  11893    }
  11894  }
  11895 
  11896  if (aMetaElement->AttrValueIs(kNameSpaceID_None, nsGkAtoms::name,
  11897                                nsGkAtoms::handheldFriendly, eIgnoreCase)) {
  11898    nsAutoString result;
  11899    aMetaElement->GetAttr(nsGkAtoms::content, result);
  11900    if (!result.IsEmpty()) {
  11901      nsContentUtils::ASCIIToLower(result);
  11902      SetHeaderData(nsGkAtoms::handheldFriendly, result);
  11903    }
  11904  }
  11905  // Check for Restricted To Adults meta tag
  11906  if (aMetaElement->AttrValueIs(kNameSpaceID_None, nsGkAtoms::name,
  11907                                nsGkAtoms::rating, eIgnoreCase)) {
  11908    if (aMetaElement->AttrValueIs(kNameSpaceID_None, nsGkAtoms::content,
  11909                                  nsGkAtoms::adult, eIgnoreCase) ||
  11910        aMetaElement->AttrValueIs(kNameSpaceID_None, nsGkAtoms::content,
  11911                                  nsGkAtoms::restrictToAdults, eIgnoreCase)) {
  11912      BrowsingContext* bc = GetBrowsingContext();
  11913      if (bc && bc->GetParentalControlsEnabled() && GetDocShell()) {
  11914        RefPtr<nsDocShell> docShell = nsDocShell::Cast(GetDocShell());
  11915        nsCOMPtr<nsIRunnable> redirect = NewRunnableMethod(
  11916            "Document::ProcessMETATag::DisplayRestrictedContentError", docShell,
  11917            &nsDocShell::DisplayRestrictedContentError);
  11918        nsContentUtils::AddScriptRunner(redirect.forget());
  11919      }
  11920    }
  11921  }
  11922 }
  11923 
  11924 void Document::TerminateParserAndDisableScripts() {
  11925  if (mParser) {
  11926    (void)mParser->Terminate();
  11927    MOZ_ASSERT(!mParser, "mParser should have been null'd out");
  11928  }
  11929 
  11930  if (WindowContext* wc = GetWindowContext()) {
  11931    (void)wc->SetAllowJavascript(false);
  11932  }
  11933 }
  11934 
  11935 already_AddRefed<Element> Document::CreateElem(const nsAString& aName,
  11936                                               nsAtom* aPrefix,
  11937                                               int32_t aNamespaceID,
  11938                                               const nsAString* aIs) {
  11939 #ifdef DEBUG
  11940  nsAutoString qName;
  11941  if (aPrefix) {
  11942    aPrefix->ToString(qName);
  11943    qName.Append(':');
  11944  }
  11945  qName.Append(aName);
  11946 
  11947  // Note: "a:b:c" is a valid name in non-namespaces XML, and
  11948  // Document::CreateElement can call us with such a name and no prefix,
  11949  // which would cause an error if we just used true here.
  11950  bool nsAware = aPrefix != nullptr || aNamespaceID != GetDefaultNamespaceID();
  11951  NS_ASSERTION(NS_SUCCEEDED(nsContentUtils::CheckQName(qName, nsAware)),
  11952               "Don't pass invalid prefixes to Document::CreateElem, "
  11953               "check caller.");
  11954 #endif
  11955 
  11956  RefPtr<mozilla::dom::NodeInfo> nodeInfo;
  11957  mNodeInfoManager->GetNodeInfo(aName, aPrefix, aNamespaceID, ELEMENT_NODE,
  11958                                getter_AddRefs(nodeInfo));
  11959  NS_ENSURE_TRUE(nodeInfo, nullptr);
  11960 
  11961  nsCOMPtr<Element> element;
  11962  nsresult rv = NS_NewElement(getter_AddRefs(element), nodeInfo.forget(),
  11963                              NOT_FROM_PARSER, aIs);
  11964  return NS_SUCCEEDED(rv) ? element.forget() : nullptr;
  11965 }
  11966 
  11967 bool Document::IsSafeToFlush() const {
  11968  PresShell* presShell = GetPresShell();
  11969  if (!presShell) {
  11970    return true;
  11971  }
  11972  return presShell->IsSafeToFlush();
  11973 }
  11974 
  11975 void Document::Sanitize() {
  11976  // Sanitize the document by resetting all (current and former) password fields
  11977  // and any form fields with autocomplete=off to their default values.  We do
  11978  // this now, instead of when the presentation is restored, to offer some
  11979  // protection in case there is ever an exploit that allows a cached document
  11980  // to be accessed from a different document.
  11981 
  11982  // First locate all input elements, regardless of whether they are
  11983  // in a form, and reset the password and autocomplete=off elements.
  11984 
  11985  RefPtr<nsContentList> nodes = GetElementsByTagName(u"input"_ns);
  11986 
  11987  nsAutoString value;
  11988 
  11989  uint32_t length = nodes->Length(true);
  11990  for (uint32_t i = 0; i < length; ++i) {
  11991    NS_ASSERTION(nodes->Item(i), "null item in node list!");
  11992 
  11993    RefPtr<HTMLInputElement> input =
  11994        HTMLInputElement::FromNodeOrNull(nodes->Item(i));
  11995    if (!input) continue;
  11996 
  11997    input->GetAttr(nsGkAtoms::autocomplete, value);
  11998    if (value.LowerCaseEqualsLiteral("off") || input->HasBeenTypePassword()) {
  11999      input->Reset();
  12000    }
  12001  }
  12002 
  12003  // Now locate all _form_ elements that have autocomplete=off and reset them
  12004  nodes = GetElementsByTagName(u"form"_ns);
  12005 
  12006  length = nodes->Length(true);
  12007  for (uint32_t i = 0; i < length; ++i) {
  12008    // Reset() may change the list dynamically.
  12009    RefPtr<HTMLFormElement> form =
  12010        HTMLFormElement::FromNodeOrNull(nodes->Item(i));
  12011    if (!form) continue;
  12012 
  12013    form->GetAttr(nsGkAtoms::autocomplete, value);
  12014    if (value.LowerCaseEqualsLiteral("off")) form->Reset();
  12015  }
  12016 }
  12017 
  12018 void Document::EnumerateSubDocuments(SubDocEnumFunc aCallback) {
  12019  if (!mSubDocuments) {
  12020    return;
  12021  }
  12022 
  12023  // PLDHashTable::Iterator can't handle modifications while iterating so we
  12024  // copy all entries to an array first before calling any callbacks.
  12025  AutoTArray<RefPtr<Document>, 8> subdocs;
  12026  for (auto iter = mSubDocuments->Iter(); !iter.Done(); iter.Next()) {
  12027    const auto* entry = static_cast<SubDocMapEntry*>(iter.Get());
  12028    if (Document* subdoc = entry->mSubDocument) {
  12029      subdocs.AppendElement(subdoc);
  12030    }
  12031  }
  12032  for (auto& subdoc : subdocs) {
  12033    if (aCallback(*subdoc) == CallState::Stop) {
  12034      break;
  12035    }
  12036  }
  12037 }
  12038 
  12039 void Document::CollectDescendantDocuments(
  12040    nsTArray<RefPtr<Document>>& aDescendants,
  12041    IncludeSubResources aIncludeSubresources, SubDocTestFunc aCallback) const {
  12042  if (mSubDocuments) {
  12043    for (auto iter = mSubDocuments->Iter(); !iter.Done(); iter.Next()) {
  12044      const auto* entry = static_cast<SubDocMapEntry*>(iter.Get());
  12045      if (Document* subdoc = entry->mSubDocument) {
  12046        if (aCallback(subdoc)) {
  12047          aDescendants.AppendElement(subdoc);
  12048        }
  12049        subdoc->CollectDescendantDocuments(aDescendants, aIncludeSubresources,
  12050                                           aCallback);
  12051      }
  12052    }
  12053  }
  12054 
  12055  if (aIncludeSubresources == IncludeSubResources::Yes) {
  12056    mExternalResourceMap.CollectDescendantDocuments(aDescendants, aCallback);
  12057  }
  12058 }
  12059 
  12060 bool Document::CanSavePresentation(nsIRequest* aNewRequest,
  12061                                   uint32_t& aBFCacheCombo,
  12062                                   bool aIncludeSubdocuments,
  12063                                   bool aAllowUnloadListeners) {
  12064  bool ret = true;
  12065 
  12066  if (!IsBFCachingAllowed()) {
  12067    aBFCacheCombo |= BFCacheStatus::NOT_ALLOWED;
  12068    ret = false;
  12069  }
  12070 
  12071  nsAutoCString uri;
  12072  if (MOZ_UNLIKELY(MOZ_LOG_TEST(gPageCacheLog, LogLevel::Verbose))) {
  12073    if (mDocumentURI) {
  12074      mDocumentURI->GetSpec(uri);
  12075    }
  12076  }
  12077 
  12078  if (EventHandlingSuppressed()) {
  12079    MOZ_LOG(gPageCacheLog, mozilla::LogLevel::Verbose,
  12080            ("Save of %s blocked on event handling suppression", uri.get()));
  12081    aBFCacheCombo |= BFCacheStatus::EVENT_HANDLING_SUPPRESSED;
  12082    ret = false;
  12083  }
  12084 
  12085  // Do not allow suspended windows to be placed in the
  12086  // bfcache.  This method is also used to verify a document
  12087  // coming out of the bfcache is ok to restore, though.  So
  12088  // we only want to block suspend windows that aren't also
  12089  // frozen.
  12090  auto* win = nsGlobalWindowInner::Cast(GetInnerWindow());
  12091  if (win && win->IsSuspended() && !win->IsFrozen()) {
  12092    MOZ_LOG(gPageCacheLog, mozilla::LogLevel::Verbose,
  12093            ("Save of %s blocked on suspended Window", uri.get()));
  12094    aBFCacheCombo |= BFCacheStatus::SUSPENDED;
  12095    ret = false;
  12096  }
  12097 
  12098  nsCOMPtr<nsIChannel> channel = do_QueryInterface(aNewRequest);
  12099  bool thirdParty = false;
  12100  // Currently some other mobile browsers seem to bfcache only cross-domain
  12101  // pages, but bfcache those also when there are unload event listeners, so
  12102  // this is trying to match that behavior as much as possible.
  12103  bool allowUnloadListeners =
  12104      aAllowUnloadListeners &&
  12105      StaticPrefs::docshell_shistory_bfcache_allow_unload_listeners() &&
  12106      (!channel || (NS_SUCCEEDED(NodePrincipal()->IsThirdPartyChannel(
  12107                        channel, &thirdParty)) &&
  12108                    thirdParty));
  12109 
  12110  // Check our event listener manager for unload/beforeunload listeners.
  12111  nsCOMPtr<EventTarget> piTarget = do_QueryInterface(mScriptGlobalObject);
  12112  if (!allowUnloadListeners && piTarget) {
  12113    EventListenerManager* manager = piTarget->GetExistingListenerManager();
  12114    if (manager) {
  12115      if (manager->HasUnloadListeners()) {
  12116        MOZ_LOG(gPageCacheLog, mozilla::LogLevel::Verbose,
  12117                ("Save of %s blocked due to unload handlers", uri.get()));
  12118        aBFCacheCombo |= BFCacheStatus::UNLOAD_LISTENER;
  12119        ret = false;
  12120      }
  12121      if (manager->HasBeforeUnloadListeners()) {
  12122        if (!mozilla::SessionHistoryInParent() ||
  12123            !StaticPrefs::
  12124                docshell_shistory_bfcache_ship_allow_beforeunload_listeners()) {
  12125          MOZ_LOG(
  12126              gPageCacheLog, mozilla::LogLevel::Verbose,
  12127              ("Save of %s blocked due to beforeUnload handlers", uri.get()));
  12128          aBFCacheCombo |= BFCacheStatus::BEFOREUNLOAD_LISTENER;
  12129          ret = false;
  12130        }
  12131      }
  12132    }
  12133  }
  12134 
  12135  // Check if we have pending network requests
  12136  nsCOMPtr<nsILoadGroup> loadGroup = GetDocumentLoadGroup();
  12137  if (loadGroup) {
  12138    nsCOMPtr<nsISimpleEnumerator> requests;
  12139    loadGroup->GetRequests(getter_AddRefs(requests));
  12140 
  12141    bool hasMore = false;
  12142 
  12143    // We want to bail out if we have any requests other than aNewRequest (or
  12144    // in the case when aNewRequest is a part of a multipart response the base
  12145    // channel the multipart response is coming in on).
  12146    nsCOMPtr<nsIChannel> baseChannel;
  12147    nsCOMPtr<nsIMultiPartChannel> part(do_QueryInterface(aNewRequest));
  12148    if (part) {
  12149      part->GetBaseChannel(getter_AddRefs(baseChannel));
  12150    }
  12151 
  12152    while (NS_SUCCEEDED(requests->HasMoreElements(&hasMore)) && hasMore) {
  12153      nsCOMPtr<nsISupports> elem;
  12154      requests->GetNext(getter_AddRefs(elem));
  12155 
  12156      nsCOMPtr<nsIRequest> request = do_QueryInterface(elem);
  12157      if (request && request != aNewRequest && request != baseChannel) {
  12158        // Favicon loads don't need to block caching.
  12159        nsCOMPtr<nsIChannel> channel = do_QueryInterface(request);
  12160        if (channel) {
  12161          nsCOMPtr<nsILoadInfo> li = channel->LoadInfo();
  12162          if (li->InternalContentPolicyType() ==
  12163              nsIContentPolicy::TYPE_INTERNAL_IMAGE_FAVICON) {
  12164            continue;
  12165          }
  12166        }
  12167 
  12168        if (MOZ_UNLIKELY(MOZ_LOG_TEST(gPageCacheLog, LogLevel::Verbose))) {
  12169          nsAutoCString requestName;
  12170          request->GetName(requestName);
  12171          MOZ_LOG(gPageCacheLog, LogLevel::Verbose,
  12172                  ("Save of %s blocked because document has request %s",
  12173                   uri.get(), requestName.get()));
  12174        }
  12175        aBFCacheCombo |= BFCacheStatus::REQUEST;
  12176        ret = false;
  12177      }
  12178    }
  12179  }
  12180 
  12181  // Check if we have active GetUserMedia use
  12182  if (MediaManager::Exists() && win &&
  12183      MediaManager::Get()->IsWindowStillActive(win->WindowID())) {
  12184    MOZ_LOG(gPageCacheLog, mozilla::LogLevel::Verbose,
  12185            ("Save of %s blocked due to GetUserMedia", uri.get()));
  12186    aBFCacheCombo |= BFCacheStatus::ACTIVE_GET_USER_MEDIA;
  12187    ret = false;
  12188  }
  12189 
  12190 #ifdef MOZ_WEBRTC
  12191  // Check if we have active PeerConnections
  12192  if (win && win->HasActivePeerConnections()) {
  12193    MOZ_LOG(gPageCacheLog, mozilla::LogLevel::Verbose,
  12194            ("Save of %s blocked due to PeerConnection", uri.get()));
  12195    aBFCacheCombo |= BFCacheStatus::ACTIVE_PEER_CONNECTION;
  12196    ret = false;
  12197  }
  12198 #endif  // MOZ_WEBRTC
  12199 
  12200  // Don't save presentations for documents containing EME content, so that
  12201  // CDMs reliably shutdown upon user navigation.
  12202  if (ContainsEMEContent()) {
  12203    aBFCacheCombo |= BFCacheStatus::CONTAINS_EME_CONTENT;
  12204    ret = false;
  12205  }
  12206 
  12207  // Don't save presentations for documents containing MSE content, to
  12208  // reduce memory usage.
  12209  if (ContainsMSEContent()) {
  12210    MOZ_LOG(gPageCacheLog, mozilla::LogLevel::Verbose,
  12211            ("Save of %s blocked due to MSE use", uri.get()));
  12212    aBFCacheCombo |= BFCacheStatus::CONTAINS_MSE_CONTENT;
  12213    ret = false;
  12214  }
  12215 
  12216  if (aIncludeSubdocuments && mSubDocuments) {
  12217    for (auto iter = mSubDocuments->Iter(); !iter.Done(); iter.Next()) {
  12218      auto entry = static_cast<SubDocMapEntry*>(iter.Get());
  12219      Document* subdoc = entry->mSubDocument;
  12220 
  12221      uint32_t subDocBFCacheCombo = 0;
  12222      // The aIgnoreRequest we were passed is only for us, so don't pass it on.
  12223      bool canCache =
  12224          subdoc ? subdoc->CanSavePresentation(nullptr, subDocBFCacheCombo,
  12225                                               true, allowUnloadListeners)
  12226                 : false;
  12227      if (!canCache) {
  12228        MOZ_LOG(gPageCacheLog, mozilla::LogLevel::Verbose,
  12229                ("Save of %s blocked due to subdocument blocked", uri.get()));
  12230        aBFCacheCombo |= subDocBFCacheCombo;
  12231        ret = false;
  12232      }
  12233    }
  12234  }
  12235 
  12236  if (!mozilla::BFCacheInParent()) {
  12237    // BFCache is currently not compatible with remote subframes (bug 1609324)
  12238    if (RefPtr<BrowsingContext> browsingContext = GetBrowsingContext()) {
  12239      for (auto& child : browsingContext->Children()) {
  12240        if (!child->IsInProcess()) {
  12241          aBFCacheCombo |= BFCacheStatus::CONTAINS_REMOTE_SUBFRAMES;
  12242          ret = false;
  12243          break;
  12244        }
  12245      }
  12246    }
  12247  }
  12248 
  12249  if (win) {
  12250    auto* globalWindow = nsGlobalWindowInner::Cast(win);
  12251 #ifdef MOZ_WEBSPEECH
  12252    if (globalWindow->HasActiveSpeechSynthesis()) {
  12253      MOZ_LOG(gPageCacheLog, mozilla::LogLevel::Verbose,
  12254              ("Save of %s blocked due to Speech use", uri.get()));
  12255      aBFCacheCombo |= BFCacheStatus::HAS_ACTIVE_SPEECH_SYNTHESIS;
  12256      ret = false;
  12257    }
  12258 #endif
  12259    if (globalWindow->HasUsedVR()) {
  12260      MOZ_LOG(gPageCacheLog, mozilla::LogLevel::Verbose,
  12261              ("Save of %s blocked due to having used VR", uri.get()));
  12262      aBFCacheCombo |= BFCacheStatus::HAS_USED_VR;
  12263      ret = false;
  12264    }
  12265 
  12266    if (win->HasActiveLocks()) {
  12267      MOZ_LOG(
  12268          gPageCacheLog, mozilla::LogLevel::Verbose,
  12269          ("Save of %s blocked due to having active lock requests", uri.get()));
  12270      aBFCacheCombo |= BFCacheStatus::ACTIVE_LOCK;
  12271      ret = false;
  12272    }
  12273 
  12274    if (win->HasActiveWebTransports()) {
  12275      MOZ_LOG(gPageCacheLog, mozilla::LogLevel::Verbose,
  12276              ("Save of %s blocked due to WebTransport", uri.get()));
  12277      aBFCacheCombo |= BFCacheStatus::ACTIVE_WEBTRANSPORT;
  12278      ret = false;
  12279    }
  12280  }
  12281 
  12282  return ret;
  12283 }
  12284 
  12285 // https://wicg.github.io/document-picture-in-picture/#close-any-associated-document-picture-in-picture-windows
  12286 void Document::CloseAnyAssociatedDocumentPiPWindows() {
  12287  BrowsingContext* bc = GetBrowsingContext();
  12288  if (!bc || !bc->IsTop()) {
  12289    return;
  12290  }
  12291 
  12292  // 3. Close us if we're a PIP window
  12293  // Note that this method is called when the opener or pip document is
  12294  // destroyed, which might mean the PiP is already closing.
  12295  if (bc->GetIsDocumentPiP() && !bc->GetClosed()) {
  12296    if (IsUncommittedInitialDocument()) {
  12297      // Don't close us if we're just doing the initial about:blank load.
  12298      return;
  12299    }
  12300    return bc->Close(CallerType::System, IgnoreErrors());
  12301  }
  12302 
  12303  // 4,5. Close a PIP window opened by us
  12304  if (nsPIDOMWindowInner* inner = GetInnerWindow()) {
  12305    if (DocumentPictureInPicture* dpip =
  12306            inner->GetExtantDocumentPictureInPicture()) {
  12307      if (RefPtr<nsGlobalWindowInner> pipWindow = dpip->GetWindow()) {
  12308        pipWindow->Close();
  12309      }
  12310    }
  12311  }
  12312 }
  12313 
  12314 void Document::Destroy() {
  12315  // The DocumentViewer wants to release the document now.  So, tell our content
  12316  // to drop any references to the document so that it can be destroyed.
  12317  if (mIsGoingAway) {
  12318    return;
  12319  }
  12320 
  12321  if (RefPtr transition = mActiveViewTransition) {
  12322    transition->SkipTransition(SkipTransitionReason::DocumentHidden);
  12323  }
  12324 
  12325  RemoveCustomContentContainer();
  12326 
  12327  ReportDocumentUseCounters();
  12328  ReportShadowedProperties();
  12329  ReportLCP();
  12330  SetDevToolsWatchingDOMMutations(false);
  12331 
  12332  mIsGoingAway = true;
  12333 
  12334  if (mScriptLoader) {
  12335    mScriptLoader->Destroy();
  12336  }
  12337  SetScriptGlobalObject(nullptr);
  12338  RemovedFromDocShell();
  12339 
  12340  bool oldVal = mInUnlinkOrDeletion;
  12341  mInUnlinkOrDeletion = true;
  12342 
  12343 #ifdef DEBUG
  12344  uint32_t oldChildCount = GetChildCount();
  12345 #endif
  12346 
  12347  for (nsIContent* child = GetFirstChild(); child;
  12348       child = child->GetNextSibling()) {
  12349    child->DestroyContent();
  12350    MOZ_ASSERT(child->GetParentNode() == this);
  12351  }
  12352  MOZ_ASSERT(oldChildCount == GetChildCount());
  12353  MOZ_ASSERT(!mSubDocuments || mSubDocuments->EntryCount() == 0);
  12354 
  12355  mInUnlinkOrDeletion = oldVal;
  12356 
  12357  mLayoutHistoryState = nullptr;
  12358 
  12359  if (mOriginalDocument) {
  12360    mOriginalDocument->mLatestStaticClone = nullptr;
  12361  }
  12362 
  12363  if (IsStaticDocument()) {
  12364    RemoveProperty(nsGkAtoms::printisfocuseddoc);
  12365    RemoveProperty(nsGkAtoms::printselectionranges);
  12366  }
  12367 
  12368  // Shut down our external resource map.  We might not need this for
  12369  // leak-fixing if we fix nsDocumentViewer to do cycle-collection, but
  12370  // tearing down all those frame trees right now is the right thing to do.
  12371  mExternalResourceMap.Shutdown();
  12372 
  12373  // Manually break cycles via promise's global object pointer.
  12374  mReadyForIdle = nullptr;
  12375  mOrientationPendingPromise = nullptr;
  12376 
  12377  // To break cycles.
  12378  mPreloadService.ClearAllPreloads();
  12379 
  12380  if (mDocumentL10n) {
  12381    mDocumentL10n->Destroy();
  12382  }
  12383 
  12384  if (!mPresShell) {
  12385    DropStyleSet();
  12386  }
  12387 }
  12388 
  12389 void Document::RemovedFromDocShell() {
  12390  mEditingState = EditingState::eOff;
  12391 
  12392  if (mRemovedFromDocShell) return;
  12393 
  12394  mRemovedFromDocShell = true;
  12395  NotifyActivityChanged();
  12396 
  12397  for (nsIContent* child = GetFirstChild(); child;
  12398       child = child->GetNextSibling()) {
  12399    child->SaveSubtreeState();
  12400  }
  12401 
  12402  nsIDocShell* docShell = GetDocShell();
  12403  if (docShell) {
  12404    docShell->SynchronizeLayoutHistoryState();
  12405  }
  12406 }
  12407 
  12408 already_AddRefed<nsILayoutHistoryState> Document::GetLayoutHistoryState()
  12409    const {
  12410  nsCOMPtr<nsILayoutHistoryState> state;
  12411  if (!mScriptGlobalObject) {
  12412    state = mLayoutHistoryState;
  12413  } else {
  12414    nsCOMPtr<nsIDocShell> docShell(mDocumentContainer);
  12415    if (docShell) {
  12416      docShell->GetLayoutHistoryState(getter_AddRefs(state));
  12417    }
  12418  }
  12419 
  12420  return state.forget();
  12421 }
  12422 
  12423 void Document::EnsureOnloadBlocker() {
  12424  // If mScriptGlobalObject is null, we shouldn't be messing with the loadgroup
  12425  // -- it's not ours.
  12426  if (mOnloadBlockCount != 0 && mScriptGlobalObject) {
  12427    nsCOMPtr<nsILoadGroup> loadGroup = GetDocumentLoadGroup();
  12428    if (loadGroup) {
  12429      // Check first to see if mOnloadBlocker is in the loadgroup.
  12430      nsCOMPtr<nsISimpleEnumerator> requests;
  12431      loadGroup->GetRequests(getter_AddRefs(requests));
  12432 
  12433      bool hasMore = false;
  12434      while (NS_SUCCEEDED(requests->HasMoreElements(&hasMore)) && hasMore) {
  12435        nsCOMPtr<nsISupports> elem;
  12436        requests->GetNext(getter_AddRefs(elem));
  12437        nsCOMPtr<nsIRequest> request = do_QueryInterface(elem);
  12438        if (request && request == mOnloadBlocker) {
  12439          return;
  12440        }
  12441      }
  12442 
  12443      // Not in the loadgroup, so add it.
  12444      loadGroup->AddRequest(mOnloadBlocker, nullptr);
  12445    }
  12446  }
  12447 }
  12448 
  12449 void Document::BlockOnload() {
  12450  if (mDisplayDocument) {
  12451    mDisplayDocument->BlockOnload();
  12452    return;
  12453  }
  12454 
  12455  // If mScriptGlobalObject is null, we shouldn't be messing with the loadgroup
  12456  // -- it's not ours.
  12457  // If we're already complete there's no need to mess with the loadgroup
  12458  // either, we're not blocking the load event after all.
  12459  // Note that ready state is not reliable for the initial about:blank.
  12460  if (mOnloadBlockCount == 0 && mScriptGlobalObject &&
  12461      (mReadyState != ReadyState::READYSTATE_COMPLETE ||
  12462       mInitialAboutBlankLoadCompleting)) {
  12463    if (nsCOMPtr<nsILoadGroup> loadGroup = GetDocumentLoadGroup()) {
  12464      loadGroup->AddRequest(mOnloadBlocker, nullptr);
  12465    }
  12466  }
  12467  ++mOnloadBlockCount;
  12468 }
  12469 
  12470 void Document::UnblockOnload(bool aFireSync) {
  12471  if (mDisplayDocument) {
  12472    mDisplayDocument->UnblockOnload(aFireSync);
  12473    return;
  12474  }
  12475 
  12476  --mOnloadBlockCount;
  12477 
  12478  if (mOnloadBlockCount != 0) {
  12479    return;
  12480  }
  12481  if (mScriptGlobalObject) {
  12482    // Only manipulate the loadgroup in this case, because if
  12483    // mScriptGlobalObject is null, it's not ours.
  12484    if (aFireSync) {
  12485      // Increment mOnloadBlockCount, since DoUnblockOnload will decrement it
  12486      ++mOnloadBlockCount;
  12487      DoUnblockOnload();
  12488    } else {
  12489      PostUnblockOnloadEvent();
  12490    }
  12491  } else if (mIsBeingUsedAsImage) {
  12492    // To correctly unblock onload for a document that contains an SVG
  12493    // image, we need to know when all of the SVG document's resources are
  12494    // done loading, in a way comparable to |window.onload|. We fire this
  12495    // event to indicate that the SVG should be considered fully loaded.
  12496    // Because scripting is disabled on SVG-as-image documents, this event
  12497    // is not accessible to content authors. (See bug 837315.)
  12498    RefPtr<AsyncEventDispatcher> asyncDispatcher =
  12499        new AsyncEventDispatcher(this, u"MozSVGAsImageDocumentLoad"_ns,
  12500                                 CanBubble::eNo, ChromeOnlyDispatch::eNo);
  12501    asyncDispatcher->PostDOMEvent();
  12502  }
  12503 }
  12504 
  12505 class nsUnblockOnloadEvent : public Runnable {
  12506 public:
  12507  explicit nsUnblockOnloadEvent(Document* aDoc)
  12508      : mozilla::Runnable("nsUnblockOnloadEvent"), mDoc(aDoc) {}
  12509  NS_IMETHOD Run() override {
  12510    mDoc->DoUnblockOnload();
  12511    return NS_OK;
  12512  }
  12513 
  12514 private:
  12515  RefPtr<Document> mDoc;
  12516 };
  12517 
  12518 void Document::PostUnblockOnloadEvent() {
  12519  MOZ_RELEASE_ASSERT(NS_IsMainThread());
  12520  nsCOMPtr<nsIRunnable> evt = new nsUnblockOnloadEvent(this);
  12521  nsresult rv = Dispatch(evt.forget());
  12522  if (NS_SUCCEEDED(rv)) {
  12523    // Stabilize block count so we don't post more events while this one is up
  12524    ++mOnloadBlockCount;
  12525  } else {
  12526    NS_WARNING("failed to dispatch nsUnblockOnloadEvent");
  12527  }
  12528 }
  12529 
  12530 void Document::DoUnblockOnload() {
  12531  MOZ_ASSERT(!mDisplayDocument, "Shouldn't get here for resource document");
  12532  MOZ_ASSERT(mOnloadBlockCount != 0,
  12533             "Shouldn't have a count of zero here, since we stabilized in "
  12534             "PostUnblockOnloadEvent");
  12535 
  12536  --mOnloadBlockCount;
  12537 
  12538  if (mOnloadBlockCount != 0) {
  12539    // We blocked again after the last unblock.  Nothing to do here.  We'll
  12540    // post a new event when we unblock again.
  12541    return;
  12542  }
  12543 
  12544  // If mScriptGlobalObject is null, we shouldn't be messing with the loadgroup
  12545  // -- it's not ours.
  12546  if (mScriptGlobalObject) {
  12547    if (nsCOMPtr<nsILoadGroup> loadGroup = GetDocumentLoadGroup()) {
  12548      loadGroup->RemoveRequest(mOnloadBlocker, nullptr, NS_OK);
  12549    }
  12550  }
  12551 }
  12552 
  12553 nsIContent* Document::GetContentInThisDocument(nsIFrame* aFrame) const {
  12554  for (nsIFrame* f = aFrame; f;
  12555       f = nsLayoutUtils::GetParentOrPlaceholderForCrossDoc(f)) {
  12556    nsIContent* content = f->GetContent();
  12557    if (!content) {
  12558      continue;
  12559    }
  12560 
  12561    if (content->OwnerDoc() == this) {
  12562      return content;
  12563    }
  12564    // We must be in a subdocument so jump directly to the root frame.
  12565    // GetParentOrPlaceholderForCrossDoc gets called immediately to jump up to
  12566    // the containing document.
  12567    f = f->PresContext()->GetPresShell()->GetRootFrame();
  12568  }
  12569 
  12570  return nullptr;
  12571 }
  12572 
  12573 void Document::DispatchPageTransition(EventTarget* aDispatchTarget,
  12574                                      const nsAString& aType, bool aInFrameSwap,
  12575                                      bool aPersisted, bool aOnlySystemGroup) {
  12576  if (!aDispatchTarget) {
  12577    return;
  12578  }
  12579 
  12580  PageTransitionEventInit init;
  12581  init.mBubbles = true;
  12582  init.mCancelable = true;
  12583  init.mPersisted = aPersisted;
  12584  init.mInFrameSwap = aInFrameSwap;
  12585 
  12586  RefPtr<PageTransitionEvent> event =
  12587      PageTransitionEvent::Constructor(this, aType, init);
  12588 
  12589  event->SetTrusted(true);
  12590  event->SetTarget(this);
  12591  if (aOnlySystemGroup) {
  12592    event->WidgetEventPtr()->mFlags.mOnlySystemGroupDispatchInContent = true;
  12593  }
  12594  EventDispatcher::DispatchDOMEvent(aDispatchTarget, nullptr, event, nullptr,
  12595                                    nullptr);
  12596 }
  12597 
  12598 void Document::OnPageShow(bool aPersisted, EventTarget* aDispatchStartTarget,
  12599                          bool aOnlySystemGroup) {
  12600  if (MOZ_LOG_TEST(gSHIPBFCacheLog, LogLevel::Debug)) {
  12601    nsCString uri;
  12602    if (GetDocumentURI()) {
  12603      uri = GetDocumentURI()->GetSpecOrDefault();
  12604    }
  12605    MOZ_LOG(gSHIPBFCacheLog, LogLevel::Debug,
  12606            ("Document::OnPageShow [%s] persisted=%i", uri.get(), aPersisted));
  12607  }
  12608 
  12609  const bool inFrameLoaderSwap = !!aDispatchStartTarget;
  12610  MOZ_DIAGNOSTIC_ASSERT(
  12611      inFrameLoaderSwap ==
  12612      (mDocumentContainer && mDocumentContainer->InFrameSwap()));
  12613 
  12614  Element* root = GetRootElement();
  12615  if (aPersisted && root) {
  12616    // Send out notifications that our <link> elements are attached.
  12617    RefPtr<nsContentList> links =
  12618        NS_GetContentList(root, kNameSpaceID_XHTML, u"link"_ns);
  12619 
  12620    uint32_t linkCount = links->Length(true);
  12621    for (uint32_t i = 0; i < linkCount; ++i) {
  12622      static_cast<HTMLLinkElement*>(links->Item(i, false))->LinkAdded();
  12623    }
  12624  }
  12625 
  12626  // See Document
  12627  if (!inFrameLoaderSwap) {
  12628    if (aPersisted) {
  12629      SetImageAnimationState(true);
  12630    }
  12631 
  12632    // Set mIsShowing before firing events, in case those event handlers
  12633    // move us around.
  12634    mIsShowing = true;
  12635    mVisible = true;
  12636 
  12637    UpdateVisibilityState();
  12638  }
  12639 
  12640  NotifyActivityChanged();
  12641 
  12642  EnumerateExternalResources([aPersisted](Document& aExternalResource) {
  12643    aExternalResource.OnPageShow(aPersisted, nullptr);
  12644    return CallState::Continue;
  12645  });
  12646 
  12647  if (mAnimationController) {
  12648    mAnimationController->OnPageShow();
  12649  }
  12650 
  12651  if (!mIsBeingUsedAsImage) {
  12652    // Dispatch observer notification to notify observers page is shown.
  12653    nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
  12654    if (os) {
  12655      nsIPrincipal* principal = NodePrincipal();
  12656      os->NotifyObservers(ToSupports(this),
  12657                          principal->IsSystemPrincipal() ? "chrome-page-shown"
  12658                                                         : "content-page-shown",
  12659                          nullptr);
  12660    }
  12661 
  12662    nsCOMPtr<EventTarget> target = aDispatchStartTarget;
  12663    if (!target) {
  12664      target = do_QueryInterface(GetWindow());
  12665    }
  12666    DispatchPageTransition(target, u"pageshow"_ns, inFrameLoaderSwap,
  12667                           aPersisted, aOnlySystemGroup);
  12668  }
  12669 
  12670  if (auto* wgc = GetWindowGlobalChild()) {
  12671    wgc->UnblockBFCacheFor(BFCacheStatus::PAGE_LOADING);
  12672  }
  12673 
  12674  mIsCompletelyLoaded = true;
  12675 }
  12676 
  12677 static void DispatchFullscreenChange(Document& aDocument, nsINode* aTarget) {
  12678  aDocument.AddPendingFullscreenEvent(
  12679      MakeUnique<PendingFullscreenEvent>(FullscreenEventType::Change, aTarget));
  12680 }
  12681 
  12682 void Document::OnPageHide(bool aPersisted, EventTarget* aDispatchStartTarget,
  12683                          bool aOnlySystemGroup) {
  12684  if (MOZ_LOG_TEST(gSHIPBFCacheLog, LogLevel::Debug)) {
  12685    nsCString uri;
  12686    if (GetDocumentURI()) {
  12687      uri = GetDocumentURI()->GetSpecOrDefault();
  12688    }
  12689    MOZ_LOG(gSHIPBFCacheLog, LogLevel::Debug,
  12690            ("Document::OnPageHide %s persisted=%i", uri.get(), aPersisted));
  12691  }
  12692 
  12693  const bool inFrameLoaderSwap = !!aDispatchStartTarget;
  12694  MOZ_DIAGNOSTIC_ASSERT(
  12695      inFrameLoaderSwap ==
  12696      (mDocumentContainer && mDocumentContainer->InFrameSwap()));
  12697 
  12698  if (mAnimationController) {
  12699    mAnimationController->OnPageHide();
  12700  }
  12701 
  12702  if (inFrameLoaderSwap) {
  12703    if (RefPtr transition = mActiveViewTransition) {
  12704      transition->SkipTransition(SkipTransitionReason::PageSwap);
  12705    }
  12706  } else {
  12707    if (aPersisted) {
  12708      // We do not stop the animations (bug 1024343) when the page is refreshing
  12709      // while being dragged out.
  12710      SetImageAnimationState(false);
  12711    }
  12712 
  12713    // Set mIsShowing before firing events, in case those event handlers
  12714    // move us around.
  12715    mIsShowing = false;
  12716    mVisible = false;
  12717  }
  12718 
  12719  // https://wicg.github.io/document-picture-in-picture/#close-on-destroy
  12720  CloseAnyAssociatedDocumentPiPWindows();
  12721 
  12722  PointerLockManager::Unlock("Document::OnPageHide", this);
  12723 
  12724  if (!mIsBeingUsedAsImage) {
  12725    // Dispatch observer notification to notify observers page is hidden.
  12726    nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
  12727    if (os) {
  12728      nsIPrincipal* principal = NodePrincipal();
  12729      os->NotifyObservers(ToSupports(this),
  12730                          principal->IsSystemPrincipal()
  12731                              ? "chrome-page-hidden"
  12732                              : "content-page-hidden",
  12733                          nullptr);
  12734    }
  12735 
  12736    // Now send out a PageHide event.
  12737    nsCOMPtr<EventTarget> target = aDispatchStartTarget;
  12738    if (!target) {
  12739      target = do_QueryInterface(GetWindow());
  12740    }
  12741    {
  12742      PageUnloadingEventTimeStamp timeStamp(this);
  12743      DispatchPageTransition(target, u"pagehide"_ns, inFrameLoaderSwap,
  12744                             aPersisted, aOnlySystemGroup);
  12745    }
  12746  }
  12747 
  12748  if (!inFrameLoaderSwap) {
  12749    UpdateVisibilityState();
  12750  }
  12751 
  12752  EnumerateExternalResources([aPersisted](Document& aExternalResource) {
  12753    aExternalResource.OnPageHide(aPersisted, nullptr);
  12754    return CallState::Continue;
  12755  });
  12756  NotifyActivityChanged();
  12757 
  12758  ClearPendingFullscreenRequests(this);
  12759  if (Fullscreen()) {
  12760    // If this document was fullscreen, we should exit fullscreen in this
  12761    // doctree branch. This ensures that if the user navigates while in
  12762    // fullscreen mode we don't leave its still visible ancestor documents
  12763    // in fullscreen mode. So exit fullscreen in the document's fullscreen
  12764    // root document, as this will exit fullscreen in all the root's
  12765    // descendant documents. Note that documents are removed from the
  12766    // doctree by the time OnPageHide() is called, so we must store a
  12767    // reference to the root (in Document::mFullscreenRoot) since we can't
  12768    // just traverse the doctree to get the root.
  12769    Document::ExitFullscreenInDocTree(this);
  12770 
  12771    // Since the document is removed from the doctree before OnPageHide() is
  12772    // called, ExitFullscreen() can't traverse from the root down to *this*
  12773    // document, so we must manually call CleanupFullscreenState() below too.
  12774    // Note that CleanupFullscreenState() clears Document::mFullscreenRoot,
  12775    // so we *must* call it after ExitFullscreen(), not before.
  12776    // OnPageHide() is called in every hidden (i.e. descendant) document,
  12777    // so calling CleanupFullscreenState() here will ensure all hidden
  12778    // documents have their fullscreen state reset.
  12779    CleanupFullscreenState();
  12780 
  12781    // The fullscreenchange event is to be queued in the refresh driver,
  12782    // however a hidden page wouldn't trigger that again, so it makes no
  12783    // sense to dispatch such event here.
  12784  }
  12785 }
  12786 
  12787 void Document::WillRemoveRoot() {
  12788 #ifdef DEBUG
  12789  mStyledLinksCleared = true;
  12790 #endif
  12791  mStyledLinks.Clear();
  12792  // Notify ID change listeners before clearing the identifier map.
  12793  for (auto iter = mIdentifierMap.Iter(); !iter.Done(); iter.Next()) {
  12794    iter.Get()->ClearAndNotify();
  12795  }
  12796  mIdentifierMap.Clear();
  12797  mComposedShadowRoots.Clear();
  12798  mResponsiveContent.Clear();
  12799 
  12800  // Skip any active view transition, since the view transition pseudo-element
  12801  // tree is attached to our root element. This is not in the spec (yet), but
  12802  // prevents the view transition pseudo tree from being in an inconsistent
  12803  // state. See https://github.com/w3c/csswg-drafts/issues/12149
  12804  if (RefPtr transition = mActiveViewTransition) {
  12805    transition->SkipTransition(SkipTransitionReason::RootRemoved);
  12806  }
  12807 
  12808  RemoveCustomContentContainer();
  12809  IncrementExpandoGeneration(*this);
  12810 }
  12811 
  12812 void Document::RefreshLinkHrefs() {
  12813  // Get a list of all links we know about.  We will reset them, which will
  12814  // remove them from the document, so we need a copy of what is in the
  12815  // hashtable.
  12816  const nsTArray<Link*> linksToNotify = ToArray(mStyledLinks);
  12817 
  12818  // Reset all of our styled links.
  12819  nsAutoScriptBlocker scriptBlocker;
  12820  for (Link* link : linksToNotify) {
  12821    link->ResetLinkState(true);
  12822  }
  12823 }
  12824 
  12825 nsresult Document::CloneDocHelper(Document* clone) const {
  12826  clone->mIsStaticDocument = mCreatingStaticClone;
  12827 
  12828  // Init document
  12829  nsresult rv = clone->Init(NodePrincipal(), mPartitionedPrincipal);
  12830  NS_ENSURE_SUCCESS(rv, rv);
  12831 
  12832  if (mCreatingStaticClone) {
  12833    if (mOriginalDocument) {
  12834      clone->mOriginalDocument = mOriginalDocument;
  12835    } else {
  12836      clone->mOriginalDocument = const_cast<Document*>(this);
  12837    }
  12838    clone->mOriginalDocument->mLatestStaticClone = clone;
  12839    clone->mOriginalDocument->mStaticCloneCount++;
  12840 
  12841    nsCOMPtr<nsILoadGroup> loadGroup;
  12842 
  12843    // |mDocumentContainer| is the container of the document that is being
  12844    // created and not the original container. See CreateStaticClone function().
  12845    nsCOMPtr<nsIDocumentLoader> docLoader(mDocumentContainer);
  12846    if (docLoader) {
  12847      docLoader->GetLoadGroup(getter_AddRefs(loadGroup));
  12848    }
  12849    nsCOMPtr<nsIChannel> channel = GetChannel();
  12850    nsCOMPtr<nsIURI> uri;
  12851    if (channel) {
  12852      NS_GetFinalChannelURI(channel, getter_AddRefs(uri));
  12853    } else {
  12854      uri = Document::GetDocumentURI();
  12855    }
  12856    clone->mChannel = channel;
  12857    clone->mShouldResistFingerprinting = mShouldResistFingerprinting;
  12858    if (uri) {
  12859      clone->ResetToURI(uri, loadGroup, NodePrincipal(), mPartitionedPrincipal);
  12860    }
  12861 
  12862    clone->mIsSrcdocDocument = mIsSrcdocDocument;
  12863    clone->SetContainer(mDocumentContainer);
  12864 
  12865    // Setup the navigation time. This will be needed by any animations in the
  12866    // document, even if they are only paused.
  12867    MOZ_ASSERT(!clone->GetNavigationTiming(),
  12868               "Navigation time was already set?");
  12869    if (mTiming) {
  12870      RefPtr<nsDOMNavigationTiming> timing =
  12871          mTiming->CloneNavigationTime(nsDocShell::Cast(clone->GetDocShell()));
  12872      clone->SetNavigationTiming(timing);
  12873    }
  12874    clone->SetPolicyContainer(mPolicyContainer);
  12875  }
  12876 
  12877  // Now ensure that our clone has the same URI, base URI, and principal as us.
  12878  // We do this after the mCreatingStaticClone block above, because that block
  12879  // can set the base URI to an incorrect value in cases when base URI
  12880  // information came from the channel.  So we override explicitly, and do it
  12881  // for all these properties, in case ResetToURI messes with any of the rest of
  12882  // them.
  12883  clone->SetDocumentURI(Document::GetDocumentURI());
  12884  clone->SetChromeXHRDocURI(mChromeXHRDocURI);
  12885  clone->mActiveStoragePrincipal = mActiveStoragePrincipal;
  12886  clone->mActiveCookiePrincipal = mActiveCookiePrincipal;
  12887  // NOTE(emilio): Intentionally setting this to the GetDocBaseURI rather than
  12888  // just mDocumentBaseURI, so that srcdoc iframes get the right base URI even
  12889  // when printed standalone via window.print() (where there won't be a parent
  12890  // document to grab the URI from).
  12891  clone->mDocumentBaseURI = GetDocBaseURI();
  12892  clone->SetChromeXHRDocBaseURI(mChromeXHRDocBaseURI);
  12893  clone->mReferrerInfo =
  12894      static_cast<dom::ReferrerInfo*>(mReferrerInfo.get())->Clone();
  12895  clone->mPreloadReferrerInfo = clone->mReferrerInfo;
  12896 
  12897  bool hasHadScriptObject = true;
  12898  nsIScriptGlobalObject* scriptObject =
  12899      GetScriptHandlingObject(hasHadScriptObject);
  12900  NS_ENSURE_STATE(scriptObject || !hasHadScriptObject);
  12901  if (mCreatingStaticClone) {
  12902    // If we're doing a static clone (print, print preview), then we're going to
  12903    // be setting a scope object after the clone. It's better to set it only
  12904    // once, so we don't do that here. However, we do want to act as if there is
  12905    // a script handling object. So we set mHasHadScriptHandlingObject.
  12906    clone->mHasHadScriptHandlingObject = true;
  12907  } else if (scriptObject) {
  12908    clone->SetScriptHandlingObject(scriptObject);
  12909  } else {
  12910    clone->SetScopeObject(GetScopeObject());
  12911  }
  12912  // Make the clone a data document
  12913  clone->SetLoadedAsData(
  12914      true,
  12915      /* aConsiderForMemoryReporting */ !mCreatingStaticClone);
  12916 
  12917  // Misc state
  12918 
  12919  // State from Document
  12920  clone->mCharacterSet = mCharacterSet;
  12921  clone->mCharacterSetSource = mCharacterSetSource;
  12922  clone->SetCompatibilityMode(mCompatMode);
  12923  clone->mBidiOptions = mBidiOptions;
  12924  clone->mContentLanguage = mContentLanguage;
  12925  clone->SetContentType(GetContentTypeInternal());
  12926  clone->mSecurityInfo = mSecurityInfo;
  12927 
  12928  // State from Document
  12929  clone->mType = mType;
  12930  clone->mXMLDeclarationBits = mXMLDeclarationBits;
  12931  clone->mBaseTarget = mBaseTarget;
  12932  clone->mAllowDeclarativeShadowRoots = mAllowDeclarativeShadowRoots;
  12933 
  12934  return NS_OK;
  12935 }
  12936 
  12937 void Document::NotifyLoading(bool aNewParentIsLoading,
  12938                             const ReadyState& aCurrentState,
  12939                             ReadyState aNewState) {
  12940  // Mirror the top-level loading state down to all subdocuments
  12941  bool was_loading = mAncestorIsLoading ||
  12942                     aCurrentState == READYSTATE_LOADING ||
  12943                     aCurrentState == READYSTATE_INTERACTIVE;
  12944  bool is_loading = aNewParentIsLoading || aNewState == READYSTATE_LOADING ||
  12945                    aNewState == READYSTATE_INTERACTIVE;  // new value for state
  12946  bool set_load_state = was_loading != is_loading;
  12947 
  12948  MOZ_LOG(
  12949      gTimeoutDeferralLog, mozilla::LogLevel::Debug,
  12950      ("NotifyLoading for doc %p: currentAncestor: %d, newParent: %d, "
  12951       "currentState %d newState: %d, was_loading: %d, is_loading: %d, "
  12952       "set_load_state: %d",
  12953       (void*)this, mAncestorIsLoading, aNewParentIsLoading, (int)aCurrentState,
  12954       (int)aNewState, was_loading, is_loading, set_load_state));
  12955 
  12956  mAncestorIsLoading = aNewParentIsLoading;
  12957  if (set_load_state && StaticPrefs::dom_timeout_defer_during_load() &&
  12958      !NodePrincipal()->IsURIInPrefList(
  12959          "dom.timeout.defer_during_load.force-disable")) {
  12960    // Tell our innerwindow (and thus TimeoutManager)
  12961    nsPIDOMWindowInner* inner = GetInnerWindow();
  12962    if (inner) {
  12963      inner->SetActiveLoadingState(is_loading);
  12964    }
  12965    BrowsingContext* context = GetBrowsingContext();
  12966    if (context) {
  12967      // Don't use PreOrderWalk to mirror this down; go down one level as a
  12968      // time so we can set mAncestorIsLoading and take into account the
  12969      // readystates of the subdocument.  In the child process it will call
  12970      // NotifyLoading() to notify the innerwindow/TimeoutManager, and then
  12971      // iterate it's children
  12972      for (auto& child : context->Children()) {
  12973        MOZ_LOG(gTimeoutDeferralLog, mozilla::LogLevel::Debug,
  12974                ("bc: %p SetAncestorLoading(%d)", (void*)child, is_loading));
  12975        // Setting ancestor loading on a discarded browsing context has no
  12976        // effect.
  12977        (void)child->SetAncestorLoading(is_loading);
  12978      }
  12979    }
  12980  }
  12981 }
  12982 
  12983 void Document::SetReadyStateInternal(ReadyState aReadyState,
  12984                                     bool aUpdateTimingInformation) {
  12985  if (aReadyState == READYSTATE_UNINITIALIZED) {
  12986    // Transition back to uninitialized happens only to keep assertions happy
  12987    // right before readyState transitions to something else. Make this
  12988    // transition undetectable by Web content.
  12989    mReadyState = aReadyState;
  12990    return;
  12991  }
  12992 
  12993  if (IsTopLevelContentDocument()) {
  12994    if (aReadyState == READYSTATE_LOADING) {
  12995      AddToplevelLoadingDocument(this);
  12996    } else if (aReadyState == READYSTATE_COMPLETE) {
  12997      RemoveToplevelLoadingDocument(this);
  12998    }
  12999  }
  13000 
  13001  if (aUpdateTimingInformation && READYSTATE_LOADING == aReadyState) {
  13002    SetLoadingOrRestoredFromBFCacheTimeStampToNow();
  13003  }
  13004  NotifyLoading(mAncestorIsLoading, mReadyState, aReadyState);
  13005  mReadyState = aReadyState;
  13006  if (aUpdateTimingInformation && mTiming) {
  13007    switch (aReadyState) {
  13008      case READYSTATE_LOADING:
  13009        mTiming->NotifyDOMLoading(GetDocumentURI());
  13010        break;
  13011      case READYSTATE_INTERACTIVE:
  13012        mTiming->NotifyDOMInteractive(GetDocumentURI());
  13013        break;
  13014      case READYSTATE_COMPLETE:
  13015        mTiming->NotifyDOMComplete(GetDocumentURI());
  13016        break;
  13017      default:
  13018        MOZ_ASSERT_UNREACHABLE("Unexpected ReadyState value");
  13019        break;
  13020    }
  13021  }
  13022  // At the time of loading start, we don't have timing object, record time.
  13023 
  13024  if (READYSTATE_INTERACTIVE == aReadyState &&
  13025      NodePrincipal()->IsSystemPrincipal()) {
  13026    if (!mXULPersist && XRE_IsParentProcess()) {
  13027      mXULPersist = new XULPersist(this);
  13028      mXULPersist->Init();
  13029    }
  13030    if (!mChromeObserver) {
  13031      mChromeObserver = new ChromeObserver(this);
  13032      mChromeObserver->Init();
  13033    }
  13034  }
  13035 
  13036  if (aUpdateTimingInformation) {
  13037    RecordNavigationTiming(aReadyState);
  13038  }
  13039 
  13040  AsyncEventDispatcher::RunDOMEventWhenSafe(
  13041      *this, u"readystatechange"_ns, CanBubble::eNo, ChromeOnlyDispatch::eNo);
  13042 }
  13043 
  13044 void Document::GetReadyState(nsAString& aReadyState) const {
  13045  switch (mReadyState) {
  13046    case READYSTATE_LOADING:
  13047      aReadyState.AssignLiteral(u"loading");
  13048      break;
  13049    case READYSTATE_INTERACTIVE:
  13050      aReadyState.AssignLiteral(u"interactive");
  13051      break;
  13052    case READYSTATE_COMPLETE:
  13053      aReadyState.AssignLiteral(u"complete");
  13054      break;
  13055    default:
  13056      aReadyState.AssignLiteral(u"uninitialized");
  13057  }
  13058 }
  13059 
  13060 void Document::SuppressEventHandling(uint32_t aIncrease) {
  13061  mEventsSuppressed += aIncrease;
  13062  if (mEventsSuppressed == aIncrease) {
  13063    if (WindowGlobalChild* wgc = GetWindowGlobalChild()) {
  13064      wgc->BlockBFCacheFor(BFCacheStatus::EVENT_HANDLING_SUPPRESSED);
  13065    }
  13066  }
  13067  if (mScriptLoader) {
  13068    for (uint32_t i = 0; i < aIncrease; ++i) {
  13069      mScriptLoader->AddExecuteBlocker();
  13070    }
  13071  }
  13072 
  13073  EnumerateSubDocuments([aIncrease](Document& aSubDoc) {
  13074    aSubDoc.SuppressEventHandling(aIncrease);
  13075    return CallState::Continue;
  13076  });
  13077 }
  13078 
  13079 void Document::NotifyAbortedLoad() {
  13080  // If we still have outstanding work blocking DOMContentLoaded,
  13081  // then don't try to change the readystate now, but wait until
  13082  // they finish and then do so.
  13083  if (mBlockDOMContentLoaded > 0 && !mDidFireDOMContentLoaded) {
  13084    mSetCompleteAfterDOMContentLoaded = true;
  13085    return;
  13086  }
  13087 
  13088  // Otherwise we're fully done at this point, so set the
  13089  // readystate to complete.
  13090  if (GetReadyStateEnum() == Document::READYSTATE_INTERACTIVE) {
  13091    SetReadyStateInternal(Document::READYSTATE_COMPLETE);
  13092  }
  13093 }
  13094 
  13095 MOZ_CAN_RUN_SCRIPT static void FireOrClearDelayedEvents(
  13096    nsTArray<nsCOMPtr<Document>>&& aDocuments, bool aFireEvents) {
  13097  RefPtr<nsFocusManager> fm = nsFocusManager::GetFocusManager();
  13098  if (MOZ_UNLIKELY(!fm)) {
  13099    return;
  13100  }
  13101 
  13102  nsTArray<nsCOMPtr<Document>> documents = std::move(aDocuments);
  13103  for (uint32_t i = 0; i < documents.Length(); ++i) {
  13104    nsCOMPtr<Document> document = std::move(documents[i]);
  13105    // NB: Don't bother trying to fire delayed events on documents that were
  13106    // closed before this event ran.
  13107    if (!document->EventHandlingSuppressed()) {
  13108      fm->FireDelayedEvents(document);
  13109      RefPtr<PresShell> presShell = document->GetPresShell();
  13110      if (presShell) {
  13111        // Only fire events for active documents.
  13112        bool fire = aFireEvents && document->GetInnerWindow() &&
  13113                    document->GetInnerWindow()->IsCurrentInnerWindow();
  13114        presShell->FireOrClearDelayedEvents(fire);
  13115      }
  13116      document->FireOrClearPostMessageEvents(aFireEvents);
  13117    }
  13118  }
  13119 }
  13120 
  13121 void Document::PreloadPictureClosed() {
  13122  MOZ_ASSERT(mPreloadPictureDepth > 0);
  13123  mPreloadPictureDepth--;
  13124  if (mPreloadPictureDepth == 0) {
  13125    mPreloadPictureFoundSource.SetIsVoid(true);
  13126  }
  13127 }
  13128 
  13129 void Document::PreloadPictureImageSource(const nsAString& aSrcsetAttr,
  13130                                         const nsAString& aSizesAttr,
  13131                                         const nsAString& aTypeAttr,
  13132                                         const nsAString& aMediaAttr) {
  13133  // Nested pictures are not valid syntax, so while we'll eventually load them,
  13134  // it's not worth tracking sources mixed between nesting levels to preload
  13135  // them effectively.
  13136  if (mPreloadPictureDepth == 1 && mPreloadPictureFoundSource.IsVoid()) {
  13137    // <picture> selects the first matching source, so if this returns a URI we
  13138    // needn't consider new sources until a new <picture> is encountered.
  13139    bool found = HTMLImageElement::SelectSourceForTagWithAttrs(
  13140        this, true, VoidString(), aSrcsetAttr, aSizesAttr, aTypeAttr,
  13141        aMediaAttr, mPreloadPictureFoundSource);
  13142    if (found && mPreloadPictureFoundSource.IsVoid()) {
  13143      // Found an empty source, which counts
  13144      mPreloadPictureFoundSource.SetIsVoid(false);
  13145    }
  13146  }
  13147 }
  13148 
  13149 already_AddRefed<nsIURI> Document::ResolvePreloadImage(
  13150    nsIURI* aBaseURI, const nsAString& aSrcAttr, const nsAString& aSrcsetAttr,
  13151    const nsAString& aSizesAttr, bool* aIsImgSet) {
  13152  nsString sourceURL;
  13153  bool isImgSet;
  13154  if (mPreloadPictureDepth == 1 && !mPreloadPictureFoundSource.IsVoid()) {
  13155    // We're in a <picture> element and found a URI from a source previous to
  13156    // this image, use it.
  13157    sourceURL = mPreloadPictureFoundSource;
  13158    isImgSet = true;
  13159  } else {
  13160    // Otherwise try to use this <img> as a source
  13161    HTMLImageElement::SelectSourceForTagWithAttrs(
  13162        this, false, aSrcAttr, aSrcsetAttr, aSizesAttr, VoidString(),
  13163        VoidString(), sourceURL);
  13164    isImgSet = !aSrcsetAttr.IsEmpty();
  13165  }
  13166 
  13167  // Empty sources are not loaded by <img> (i.e. not resolved to the baseURI)
  13168  if (sourceURL.IsEmpty()) {
  13169    return nullptr;
  13170  }
  13171 
  13172  // Construct into URI using passed baseURI (the parser may know of base URI
  13173  // changes that have not reached us)
  13174  nsresult rv;
  13175  nsCOMPtr<nsIURI> uri;
  13176  rv = nsContentUtils::NewURIWithDocumentCharset(getter_AddRefs(uri), sourceURL,
  13177                                                 this, aBaseURI);
  13178  if (NS_FAILED(rv)) {
  13179    return nullptr;
  13180  }
  13181 
  13182  *aIsImgSet = isImgSet;
  13183 
  13184  // We don't clear mPreloadPictureFoundSource because subsequent <img> tags in
  13185  // this this <picture> share the same <sources> (though this is not valid per
  13186  // spec)
  13187  return uri.forget();
  13188 }
  13189 
  13190 void Document::PreLoadImage(nsIURI* aUri, const nsAString& aCrossOriginAttr,
  13191                            ReferrerPolicyEnum aReferrerPolicy, bool aIsImgSet,
  13192                            bool aLinkPreload, uint64_t aEarlyHintPreloaderId,
  13193                            const nsAString& aFetchPriority) {
  13194  nsLoadFlags loadFlags = nsIRequest::LOAD_NORMAL |
  13195                          nsContentUtils::CORSModeToLoadImageFlags(
  13196                              Element::StringToCORSMode(aCrossOriginAttr));
  13197 
  13198  nsContentPolicyType policyType =
  13199      aIsImgSet ? nsIContentPolicy::TYPE_IMAGESET
  13200                : nsIContentPolicy::TYPE_INTERNAL_IMAGE_PRELOAD;
  13201 
  13202  nsCOMPtr<nsIReferrerInfo> referrerInfo =
  13203      ReferrerInfo::CreateFromDocumentAndPolicyOverride(this, aReferrerPolicy);
  13204 
  13205  RefPtr<imgRequestProxy> request;
  13206 
  13207  nsLiteralString initiator = aEarlyHintPreloaderId
  13208                                  ? u"early-hints"_ns
  13209                                  : (aLinkPreload ? u"link"_ns : u"img"_ns);
  13210 
  13211  nsresult rv = nsContentUtils::LoadImage(
  13212      aUri, static_cast<nsINode*>(this), this, NodePrincipal(), 0, referrerInfo,
  13213      nullptr /* no observer */, loadFlags, initiator, getter_AddRefs(request),
  13214      policyType, false /* urgent */, aLinkPreload, aEarlyHintPreloaderId,
  13215      nsGenericHTMLElement::ToFetchPriority(aFetchPriority));
  13216 
  13217  // Pin image-reference to avoid evicting it from the img-cache before
  13218  // the "real" load occurs. Unpinned in DispatchContentLoadedEvents and
  13219  // unlink
  13220  if (!aLinkPreload && NS_SUCCEEDED(rv)) {
  13221    mPreloadingImages.InsertOrUpdate(aUri, std::move(request));
  13222  }
  13223 }
  13224 
  13225 void Document::MaybePreLoadImage(nsIURI* aUri,
  13226                                 const nsAString& aCrossOriginAttr,
  13227                                 ReferrerPolicyEnum aReferrerPolicy,
  13228                                 bool aIsImgSet, bool aLinkPreload,
  13229                                 const nsAString& aFetchPriority) {
  13230  const CORSMode corsMode = dom::Element::StringToCORSMode(aCrossOriginAttr);
  13231  if (aLinkPreload) {
  13232    // Check if the image was already preloaded in this document to avoid
  13233    // duplicate preloading.
  13234    PreloadHashKey key =
  13235        PreloadHashKey::CreateAsImage(aUri, NodePrincipal(), corsMode);
  13236    if (!mPreloadService.PreloadExists(key)) {
  13237      PreLoadImage(aUri, aCrossOriginAttr, aReferrerPolicy, aIsImgSet,
  13238                   aLinkPreload, 0, aFetchPriority);
  13239    }
  13240    return;
  13241  }
  13242 
  13243  // Early exit if the img is already present in the img-cache
  13244  // which indicates that the "real" load has already started and
  13245  // that we shouldn't preload it.
  13246  if (nsContentUtils::IsImageAvailable(aUri, NodePrincipal(), corsMode, this)) {
  13247    return;
  13248  }
  13249 
  13250  // Image not in cache - trigger preload
  13251  PreLoadImage(aUri, aCrossOriginAttr, aReferrerPolicy, aIsImgSet, aLinkPreload,
  13252               0, aFetchPriority);
  13253 }
  13254 
  13255 void Document::MaybePreconnect(nsIURI* aOrigURI, mozilla::CORSMode aCORSMode) {
  13256  if (!StaticPrefs::network_preconnect()) {
  13257    return;
  13258  }
  13259 
  13260  NS_MutateURI mutator(aOrigURI);
  13261  if (NS_FAILED(mutator.GetStatus())) {
  13262    return;
  13263  }
  13264 
  13265  // The URI created here is used in 2 contexts. One is nsISpeculativeConnect
  13266  // which ignores the path and uses only the origin. The other is for the
  13267  // document mPreloadedPreconnects de-duplication hash. Anonymous vs
  13268  // non-Anonymous preconnects create different connections on the wire and
  13269  // therefore should not be considred duplicates of each other and we
  13270  // normalize the path before putting it in the hash to accomplish that.
  13271 
  13272  if (aCORSMode == CORS_ANONYMOUS) {
  13273    mutator.SetPathQueryRef("/anonymous"_ns);
  13274  } else {
  13275    mutator.SetPathQueryRef("/"_ns);
  13276  }
  13277 
  13278  nsCOMPtr<nsIURI> uri;
  13279  nsresult rv = mutator.Finalize(uri);
  13280  if (NS_FAILED(rv)) {
  13281    return;
  13282  }
  13283 
  13284  const bool existingEntryFound =
  13285      mPreloadedPreconnects.WithEntryHandle(uri, [](auto&& entry) {
  13286        if (entry) {
  13287          return true;
  13288        }
  13289        entry.Insert(true);
  13290        return false;
  13291      });
  13292  if (existingEntryFound) {
  13293    return;
  13294  }
  13295 
  13296  nsCOMPtr<nsISpeculativeConnect> speculator =
  13297      mozilla::components::IO::Service();
  13298  if (!speculator) {
  13299    return;
  13300  }
  13301 
  13302  OriginAttributes oa;
  13303  StoragePrincipalHelper::GetOriginAttributesForNetworkState(this, oa);
  13304  speculator->SpeculativeConnectWithOriginAttributesNative(
  13305      uri, std::move(oa), nullptr, aCORSMode == CORS_ANONYMOUS);
  13306 }
  13307 
  13308 void Document::ForgetImagePreload(nsIURI* aURI) {
  13309  // Checking count is faster than hashing the URI in the common
  13310  // case of empty table.
  13311  if (mPreloadingImages.Count() != 0) {
  13312    nsCOMPtr<imgIRequest> req;
  13313    mPreloadingImages.Remove(aURI, getter_AddRefs(req));
  13314    if (req) {
  13315      // Make sure to cancel the request so imagelib knows it's gone.
  13316      req->CancelAndForgetObserver(NS_BINDING_ABORTED);
  13317    }
  13318  }
  13319 }
  13320 
  13321 void Document::UpdateDocumentStates(DocumentState aMaybeChangedStates,
  13322                                    bool aNotify) {
  13323  const DocumentState oldStates = mState;
  13324  if (aMaybeChangedStates.HasAtLeastOneOfStates(
  13325          DocumentState::ALL_LOCALEDIR_BITS)) {
  13326    mState &= ~DocumentState::ALL_LOCALEDIR_BITS;
  13327    if (IsDocumentRightToLeft()) {
  13328      mState |= DocumentState::RTL_LOCALE;
  13329    } else {
  13330      mState |= DocumentState::LTR_LOCALE;
  13331    }
  13332  }
  13333 
  13334  if (aMaybeChangedStates.HasState(DocumentState::WINDOW_INACTIVE)) {
  13335    BrowsingContext* bc = GetBrowsingContext();
  13336    if (!bc || !bc->GetIsActiveBrowserWindow()) {
  13337      mState |= DocumentState::WINDOW_INACTIVE;
  13338    } else {
  13339      mState &= ~DocumentState::WINDOW_INACTIVE;
  13340    }
  13341  }
  13342 
  13343  const DocumentState changedStates = oldStates ^ mState;
  13344  if (aNotify && !changedStates.IsEmpty()) {
  13345    if (PresShell* ps = GetObservingPresShell()) {
  13346      ps->DocumentStatesChanged(changedStates);
  13347    }
  13348  }
  13349 }
  13350 
  13351 namespace {
  13352 
  13353 /**
  13354 * Stub for LoadSheet(), since all we want is to get the sheet into
  13355 * the CSSLoader's style cache
  13356 */
  13357 class StubCSSLoaderObserver final : public nsICSSLoaderObserver {
  13358  ~StubCSSLoaderObserver() = default;
  13359 
  13360 public:
  13361  NS_IMETHOD
  13362  StyleSheetLoaded(StyleSheet*, bool, nsresult) override { return NS_OK; }
  13363  NS_DECL_ISUPPORTS
  13364 };
  13365 NS_IMPL_ISUPPORTS(StubCSSLoaderObserver, nsICSSLoaderObserver)
  13366 
  13367 }  // namespace
  13368 
  13369 SheetPreloadStatus Document::PreloadStyle(
  13370    nsIURI* uri, const Encoding* aEncoding, const nsAString& aCrossOriginAttr,
  13371    const enum ReferrerPolicy aReferrerPolicy, const nsAString& aNonce,
  13372    const nsAString& aIntegrity, css::StylePreloadKind aKind,
  13373    uint64_t aEarlyHintPreloaderId, const nsAString& aFetchPriority) {
  13374  MOZ_ASSERT(aKind != css::StylePreloadKind::None);
  13375  if (!mCSSLoader) {
  13376    return SheetPreloadStatus::Errored;
  13377  }
  13378 
  13379  // The CSSLoader will retain this object after we return.
  13380  nsCOMPtr<nsICSSLoaderObserver> obs = new StubCSSLoaderObserver();
  13381 
  13382  nsCOMPtr<nsIReferrerInfo> referrerInfo =
  13383      ReferrerInfo::CreateFromDocumentAndPolicyOverride(this, aReferrerPolicy);
  13384 
  13385  // Charset names are always ASCII.
  13386  auto result = mCSSLoader->LoadSheet(
  13387      uri, aKind, aEncoding, referrerInfo, obs, aEarlyHintPreloaderId,
  13388      Element::StringToCORSMode(aCrossOriginAttr), aNonce, aIntegrity,
  13389      nsGenericHTMLElement::ToFetchPriority(aFetchPriority));
  13390  if (result.isErr()) {
  13391    return SheetPreloadStatus::Errored;
  13392  }
  13393  RefPtr<StyleSheet> sheet = result.unwrap();
  13394  if (sheet->IsComplete()) {
  13395    return SheetPreloadStatus::AlreadyComplete;
  13396  }
  13397  return SheetPreloadStatus::InProgress;
  13398 }
  13399 
  13400 void Document::ResetDocumentDirection() {
  13401  if (!nsContentUtils::IsChromeDoc(this)) {
  13402    return;
  13403  }
  13404  UpdateDocumentStates(DocumentState::ALL_LOCALEDIR_BITS, true);
  13405 }
  13406 
  13407 bool Document::IsDocumentRightToLeft() {
  13408  if (!nsContentUtils::IsChromeDoc(this)) {
  13409    return false;
  13410  }
  13411  // setting the localedir attribute on the root element forces a
  13412  // specific direction for the document.
  13413  Element* element = GetRootElement();
  13414  if (element) {
  13415    static Element::AttrValuesArray strings[] = {nsGkAtoms::ltr, nsGkAtoms::rtl,
  13416                                                 nullptr};
  13417    switch (element->FindAttrValueIn(kNameSpaceID_None, nsGkAtoms::localedir,
  13418                                     strings, eCaseMatters)) {
  13419      case 0:
  13420        return false;
  13421      case 1:
  13422        return true;
  13423      default:
  13424        break;  // otherwise, not a valid value, so fall through
  13425    }
  13426  }
  13427 
  13428  if (!mDocumentURI->SchemeIs("chrome") && !mDocumentURI->SchemeIs("about") &&
  13429      !mDocumentURI->SchemeIs("resource")) {
  13430    return false;
  13431  }
  13432 
  13433  return intl::LocaleService::GetInstance()->IsAppLocaleRTL();
  13434 }
  13435 
  13436 class nsDelayedEventDispatcher : public Runnable {
  13437 public:
  13438  explicit nsDelayedEventDispatcher(nsTArray<nsCOMPtr<Document>>&& aDocuments)
  13439      : mozilla::Runnable("nsDelayedEventDispatcher"),
  13440        mDocuments(std::move(aDocuments)) {}
  13441  virtual ~nsDelayedEventDispatcher() = default;
  13442 
  13443  // MOZ_CAN_RUN_SCRIPT_BOUNDARY until Runnable::Run is MOZ_CAN_RUN_SCRIPT.  See
  13444  // bug 1535398.
  13445  MOZ_CAN_RUN_SCRIPT_BOUNDARY NS_IMETHOD Run() override {
  13446    FireOrClearDelayedEvents(std::move(mDocuments), true);
  13447    return NS_OK;
  13448  }
  13449 
  13450 private:
  13451  nsTArray<nsCOMPtr<Document>> mDocuments;
  13452 };
  13453 
  13454 static void GetAndUnsuppressSubDocuments(
  13455    Document& aDocument, nsTArray<nsCOMPtr<Document>>& aDocuments) {
  13456  if (aDocument.EventHandlingSuppressed() > 0) {
  13457    aDocument.DecreaseEventSuppression();
  13458    if (dom::ScriptLoader* loader = aDocument.GetScriptLoader()) {
  13459      loader->RemoveExecuteBlocker();
  13460    }
  13461  }
  13462  aDocuments.AppendElement(&aDocument);
  13463  aDocument.EnumerateSubDocuments([&aDocuments](Document& aSubDoc) {
  13464    GetAndUnsuppressSubDocuments(aSubDoc, aDocuments);
  13465    return CallState::Continue;
  13466  });
  13467 }
  13468 
  13469 void Document::UnsuppressEventHandlingAndFireEvents(bool aFireEvents) {
  13470  nsTArray<nsCOMPtr<Document>> documents;
  13471  GetAndUnsuppressSubDocuments(*this, documents);
  13472 
  13473  for (nsCOMPtr<Document>& doc : documents) {
  13474    if (!doc->EventHandlingSuppressed()) {
  13475      if (WindowGlobalChild* wgc = doc->GetWindowGlobalChild()) {
  13476        wgc->UnblockBFCacheFor(BFCacheStatus::EVENT_HANDLING_SUPPRESSED);
  13477      }
  13478 
  13479      MOZ_ASSERT(NS_IsMainThread());
  13480      nsTArray<RefPtr<net::ChannelEventQueue>> queues =
  13481          std::move(doc->mSuspendedQueues);
  13482      for (net::ChannelEventQueue* queue : queues) {
  13483        queue->Resume();
  13484      }
  13485    }
  13486  }
  13487 
  13488  if (aFireEvents) {
  13489    MOZ_RELEASE_ASSERT(NS_IsMainThread());
  13490    nsCOMPtr<nsIRunnable> ded =
  13491        new nsDelayedEventDispatcher(std::move(documents));
  13492    Dispatch(ded.forget());
  13493  } else {
  13494    FireOrClearDelayedEvents(std::move(documents), false);
  13495  }
  13496 }
  13497 
  13498 bool Document::AreClipboardCommandsUnconditionallyEnabled() const {
  13499  return IsHTMLOrXHTML() && !nsContentUtils::IsChromeDoc(this);
  13500 }
  13501 
  13502 void Document::AddSuspendedChannelEventQueue(net::ChannelEventQueue* aQueue) {
  13503  MOZ_ASSERT(NS_IsMainThread());
  13504  MOZ_ASSERT(EventHandlingSuppressed());
  13505  mSuspendedQueues.AppendElement(aQueue);
  13506 }
  13507 
  13508 bool Document::SuspendPostMessageEvent(PostMessageEvent* aEvent) {
  13509  MOZ_ASSERT(NS_IsMainThread());
  13510 
  13511  if (EventHandlingSuppressed() || !mSuspendedPostMessageEvents.IsEmpty()) {
  13512    mSuspendedPostMessageEvents.AppendElement(aEvent);
  13513    return true;
  13514  }
  13515  return false;
  13516 }
  13517 
  13518 void Document::FireOrClearPostMessageEvents(bool aFireEvents) {
  13519  nsTArray<RefPtr<PostMessageEvent>> events =
  13520      std::move(mSuspendedPostMessageEvents);
  13521 
  13522  if (aFireEvents) {
  13523    for (PostMessageEvent* event : events) {
  13524      event->Run();
  13525    }
  13526  }
  13527 }
  13528 
  13529 void Document::SetSuppressedEventListener(EventListener* aListener) {
  13530  mSuppressedEventListener = aListener;
  13531  EnumerateSubDocuments([&](Document& aDocument) {
  13532    aDocument.SetSuppressedEventListener(aListener);
  13533    return CallState::Continue;
  13534  });
  13535 }
  13536 
  13537 bool Document::IsActive() const {
  13538  return mDocumentContainer && !mRemovedFromDocShell && GetBrowsingContext() &&
  13539         !GetBrowsingContext()->IsInBFCache();
  13540 }
  13541 
  13542 uint32_t Document::LastScrollGeneration() const {
  13543  if (nsPresContext* pc = GetPresContext()) {
  13544    pc->LastScrollGeneration();
  13545  }
  13546 
  13547  return 0;
  13548 }
  13549 
  13550 bool Document::HasBeenScrolledSince(
  13551    const uint32_t& aLastScrollGeneration) const {
  13552  if (nsPresContext* pc = GetPresContext()) {
  13553    pc->HasBeenScrolledSince(aLastScrollGeneration);
  13554  }
  13555 
  13556  return false;
  13557 }
  13558 
  13559 bool Document::CanRewriteURL(nsIURI* aTargetURL, bool aReportErrors) const {
  13560  if (nsContentUtils::URIIsLocalFile(aTargetURL)) {
  13561    // It's a file:// URI
  13562    nsCOMPtr<nsIPrincipal> principal = NodePrincipal();
  13563    if (aReportErrors) {
  13564      return NS_SUCCEEDED(principal->CheckMayLoadWithReporting(
  13565          aTargetURL, false, InnerWindowID()));
  13566    }
  13567    return NS_SUCCEEDED(principal->CheckMayLoad(aTargetURL, false));
  13568  }
  13569 
  13570  nsCOMPtr<nsIScriptSecurityManager> secMan =
  13571      do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID);
  13572  if (!secMan) {
  13573    return false;
  13574  }
  13575 
  13576  // It's very important that we check that aTargetURL is of the same
  13577  // origin as mDocumentURI, not docBaseURI, because a page can
  13578  // set docBaseURI arbitrarily to any domain.
  13579  bool isPrivateWin =
  13580      NodePrincipal()->OriginAttributesRef().IsPrivateBrowsing();
  13581  if (NS_FAILED(secMan->CheckSameOriginURI(mDocumentURI, aTargetURL,
  13582                                           aReportErrors, isPrivateWin))) {
  13583    return false;
  13584  }
  13585 
  13586  // In addition to checking that the security manager says that
  13587  // the new URI has the same origin as our current URI, we also
  13588  // check that the two URIs have the same userpass. (The
  13589  // security manager says that |http://foo.com| and
  13590  // |http://me@foo.com| have the same origin.)  mDocumentURI
  13591  // won't contain the password part of the userpass, so this
  13592  // means that it's never valid to specify a password in a
  13593  // pushState or replaceState URI.
  13594  nsAutoCString currentUserPass, newUserPass;
  13595  (void)mDocumentURI->GetUserPass(currentUserPass);
  13596  (void)aTargetURL->GetUserPass(newUserPass);
  13597 
  13598  return currentUserPass.Equals(newUserPass);
  13599 }
  13600 
  13601 nsISupports* Document::GetCurrentContentSink() {
  13602  return mParser ? mParser->GetContentSink() : nullptr;
  13603 }
  13604 
  13605 Document* Document::GetTemplateContentsOwner() {
  13606  if (!mTemplateContentsOwner) {
  13607    bool hasHadScriptObject = true;
  13608    nsIScriptGlobalObject* scriptObject =
  13609        GetScriptHandlingObject(hasHadScriptObject);
  13610 
  13611    nsCOMPtr<Document> document;
  13612    nsresult rv = NS_NewDOMDocument(
  13613        getter_AddRefs(document),
  13614        u""_ns,   // aNamespaceURI
  13615        u""_ns,   // aQualifiedName
  13616        nullptr,  // aDoctype
  13617        Document::GetDocumentURI(), Document::GetDocBaseURI(), NodePrincipal(),
  13618        LoadedAsData::AsData,  // aLoadedAsData
  13619        scriptObject,          // aEventObject
  13620        IsHTMLDocument() ? DocumentFlavor::HTML : DocumentFlavor::XML);
  13621    NS_ENSURE_SUCCESS(rv, nullptr);
  13622 
  13623    mTemplateContentsOwner = document;
  13624    NS_ENSURE_TRUE(mTemplateContentsOwner, nullptr);
  13625 
  13626    if (!scriptObject) {
  13627      mTemplateContentsOwner->SetScopeObject(GetScopeObject());
  13628    }
  13629 
  13630    mTemplateContentsOwner->mHasHadScriptHandlingObject = hasHadScriptObject;
  13631 
  13632    // Set |mTemplateContentsOwner| as the template contents owner of itself so
  13633    // that it is the template contents owner of nested template elements.
  13634    mTemplateContentsOwner->mTemplateContentsOwner = mTemplateContentsOwner;
  13635  }
  13636 
  13637  MOZ_ASSERT(mTemplateContentsOwner->IsTemplateContentsOwner());
  13638  return mTemplateContentsOwner;
  13639 }
  13640 
  13641 // https://html.spec.whatwg.org/#the-autofocus-attribute
  13642 void Document::ElementWithAutoFocusInserted(Element* aAutoFocusCandidate) {
  13643  BrowsingContext* bc = GetBrowsingContext();
  13644  if (!bc) {
  13645    return;
  13646  }
  13647 
  13648  // If target is not fully active, then return.
  13649  if (!IsCurrentActiveDocument()) {
  13650    return;
  13651  }
  13652 
  13653  // If target's active sandboxing flag set has the sandboxed automatic features
  13654  // browsing context flag, then return.
  13655  if (GetSandboxFlags() & SANDBOXED_AUTOMATIC_FEATURES) {
  13656    return;
  13657  }
  13658 
  13659  // For each ancestorBC of target's browsing context's ancestor browsing
  13660  // contexts: if ancestorBC's active document's origin is not same origin with
  13661  // target's origin, then return.
  13662  while (bc) {
  13663    BrowsingContext* parent = bc->GetParent();
  13664    if (!parent) {
  13665      break;
  13666    }
  13667    // AncestorBC is not the same site
  13668    if (!parent->IsInProcess()) {
  13669      return;
  13670    }
  13671 
  13672    Document* currentDocument = bc->GetDocument();
  13673    if (!currentDocument) {
  13674      return;
  13675    }
  13676 
  13677    Document* parentDocument = parent->GetDocument();
  13678    if (!parentDocument) {
  13679      return;
  13680    }
  13681 
  13682    // Not same origin
  13683    if (!currentDocument->NodePrincipal()->Equals(
  13684            parentDocument->NodePrincipal())) {
  13685      return;
  13686    }
  13687 
  13688    bc = parent;
  13689  }
  13690  MOZ_ASSERT(bc->IsTop());
  13691 
  13692  Document* topDocument = bc->GetDocument();
  13693  MOZ_ASSERT(topDocument);
  13694  topDocument->AppendAutoFocusCandidateToTopDocument(aAutoFocusCandidate);
  13695 }
  13696 
  13697 void Document::ScheduleFlushAutoFocusCandidates() {
  13698  MOZ_ASSERT(HasAutoFocusCandidates());
  13699  MaybeScheduleRenderingPhases({RenderingPhase::FlushAutoFocusCandidates});
  13700 }
  13701 
  13702 void Document::AppendAutoFocusCandidateToTopDocument(
  13703    Element* aAutoFocusCandidate) {
  13704  MOZ_ASSERT(GetBrowsingContext()->IsTop());
  13705  if (mAutoFocusFired) {
  13706    return;
  13707  }
  13708 
  13709  const bool hadCandidates = HasAutoFocusCandidates();
  13710  nsWeakPtr element = do_GetWeakReference(aAutoFocusCandidate);
  13711  mAutoFocusCandidates.RemoveElement(element);
  13712  mAutoFocusCandidates.AppendElement(element);
  13713  if (!hadCandidates) {
  13714    ScheduleFlushAutoFocusCandidates();
  13715  }
  13716 }
  13717 
  13718 void Document::SetAutoFocusFired() {
  13719  mAutoFocusCandidates.Clear();
  13720  mAutoFocusFired = true;
  13721 }
  13722 
  13723 // https://html.spec.whatwg.org/#flush-autofocus-candidates
  13724 void Document::FlushAutoFocusCandidates() {
  13725  MOZ_ASSERT(GetBrowsingContext()->IsTop());
  13726  if (mAutoFocusFired) {
  13727    return;
  13728  }
  13729 
  13730  if (!mPresShell) {
  13731    return;
  13732  }
  13733 
  13734  MOZ_ASSERT(HasAutoFocusCandidates());
  13735  MOZ_ASSERT(mPresShell->DidInitialize());
  13736 
  13737  nsCOMPtr<nsPIDOMWindowOuter> topWindow = GetWindow();
  13738  // We should be the top document
  13739  if (!topWindow) {
  13740    return;
  13741  }
  13742 
  13743 #ifdef DEBUG
  13744  {
  13745    // Trying to find the top window (equivalent to window.top).
  13746    nsCOMPtr<nsPIDOMWindowOuter> top = topWindow->GetInProcessTop();
  13747    MOZ_ASSERT(topWindow == top);
  13748  }
  13749 #endif
  13750 
  13751  // Don't steal the focus from the user
  13752  if (topWindow->GetFocusedElement()) {
  13753    SetAutoFocusFired();
  13754    return;
  13755  }
  13756 
  13757  MOZ_ASSERT(mDocumentURI);
  13758  nsAutoCString ref;
  13759  // GetRef never fails
  13760  nsresult rv = mDocumentURI->GetRef(ref);
  13761  if (NS_SUCCEEDED(rv) &&
  13762      nsContentUtils::GetTargetElement(this, NS_ConvertUTF8toUTF16(ref))) {
  13763    SetAutoFocusFired();
  13764    return;
  13765  }
  13766 
  13767  nsTObserverArray<nsWeakPtr>::ForwardIterator iter(mAutoFocusCandidates);
  13768  while (iter.HasMore()) {
  13769    nsWeakPtr weakElement = iter.GetNext();
  13770    nsCOMPtr<Element> autoFocusElement = do_QueryReferent(weakElement);
  13771    if (!autoFocusElement) {
  13772      continue;
  13773    }
  13774    RefPtr<Document> autoFocusElementDoc = autoFocusElement->OwnerDoc();
  13775    // Get the latest info about the frame and allow scripts
  13776    // to run which might affect the focusability of this element.
  13777    autoFocusElementDoc->FlushPendingNotifications(FlushType::Frames);
  13778 
  13779    // Above layout flush may cause the PresShell to disappear or autofocus to
  13780    // complete. The iterator position might have changed.
  13781    if (!mPresShell || mAutoFocusFired) {
  13782      return;
  13783    }
  13784 
  13785    // Re-get the element because the ownerDoc() might have changed
  13786    autoFocusElementDoc = autoFocusElement->OwnerDoc();
  13787    BrowsingContext* bc = autoFocusElementDoc->GetBrowsingContext();
  13788    if (!bc) {
  13789      continue;
  13790    }
  13791 
  13792    // If doc is not fully active, then remove element from candidates, and
  13793    // continue.
  13794    if (!autoFocusElementDoc->IsCurrentActiveDocument()) {
  13795      mAutoFocusCandidates.RemoveElement(weakElement);
  13796      continue;
  13797    }
  13798 
  13799    nsCOMPtr<nsIContentSink> sink =
  13800        do_QueryInterface(autoFocusElementDoc->GetCurrentContentSink());
  13801    if (sink) {
  13802      nsHtml5TreeOpExecutor* executor =
  13803          static_cast<nsHtml5TreeOpExecutor*>(sink->AsExecutor());
  13804      if (executor) {
  13805        // This is a HTML5 document
  13806        MOZ_ASSERT(autoFocusElementDoc->IsHTMLDocument());
  13807        // If doc's script-blocking style sheet counter is greater than 0, th
  13808        // return.
  13809        if (executor->WaitForPendingSheets()) {
  13810          // In this case, element is the currently-best candidate, but doc is
  13811          // not ready for autofocusing. We'll try again next time flush
  13812          // autofocus candidates is called.
  13813          ScheduleFlushAutoFocusCandidates();
  13814          return;
  13815        }
  13816      }
  13817    }
  13818 
  13819    // The autofocus element could be moved to a different
  13820    // top level BC.
  13821    if (bc->Top()->GetDocument() != this) {
  13822      continue;
  13823    }
  13824 
  13825    mAutoFocusCandidates.RemoveElement(weakElement);
  13826 
  13827    // Let inclusiveAncestorDocuments be a list consisting of doc, plus the
  13828    // active documents of each of doc's browsing context's ancestor browsing
  13829    // contexts.
  13830    // If any Document in inclusiveAncestorDocuments has non-null target
  13831    // element, then continue.
  13832    bool shouldFocus = true;
  13833    while (bc) {
  13834      Document* doc = bc->GetDocument();
  13835      if (!doc) {
  13836        shouldFocus = false;
  13837        break;
  13838      }
  13839 
  13840      nsIURI* uri = doc->GetDocumentURI();
  13841      if (!uri) {
  13842        shouldFocus = false;
  13843        break;
  13844      }
  13845 
  13846      nsAutoCString ref;
  13847      nsresult rv = uri->GetRef(ref);
  13848      // If there is an element in the document tree that has an ID equal to
  13849      // fragment
  13850      if (NS_SUCCEEDED(rv) &&
  13851          nsContentUtils::GetTargetElement(doc, NS_ConvertUTF8toUTF16(ref))) {
  13852        shouldFocus = false;
  13853        break;
  13854      }
  13855      bc = bc->GetParent();
  13856    }
  13857 
  13858    if (!shouldFocus) {
  13859      continue;
  13860    }
  13861 
  13862    MOZ_ASSERT(topWindow);
  13863    if (TryAutoFocusCandidate(*autoFocusElement)) {
  13864      // We've successfully autofocused an element, don't
  13865      // need to try to focus the rest.
  13866      SetAutoFocusFired();
  13867      break;
  13868    }
  13869  }
  13870 
  13871  if (HasAutoFocusCandidates()) {
  13872    ScheduleFlushAutoFocusCandidates();
  13873  }
  13874 }
  13875 
  13876 bool Document::TryAutoFocusCandidate(Element& aElement) {
  13877  const FocusOptions options;
  13878  if (RefPtr<Element> target = nsFocusManager::GetTheFocusableArea(
  13879          &aElement, nsFocusManager::ProgrammaticFocusFlags(options))) {
  13880    target->Focus(options, CallerType::NonSystem, IgnoreErrors());
  13881    return true;
  13882  }
  13883 
  13884  return false;
  13885 }
  13886 
  13887 void Document::SetScrollToRef(nsIURI* aDocumentURI) {
  13888  if (!aDocumentURI) {
  13889    return;
  13890  }
  13891 
  13892  nsAutoCString ref;
  13893 
  13894  // Since all URI's that pass through here aren't URL's we can't
  13895  // rely on the nsIURI implementation for providing a way for
  13896  // finding the 'ref' part of the URI, we'll haveto revert to
  13897  // string routines for finding the data past '#'
  13898 
  13899  nsresult rv = aDocumentURI->GetSpec(ref);
  13900  if (NS_FAILED(rv)) {
  13901    (void)aDocumentURI->GetRef(mScrollToRef);
  13902    return;
  13903  }
  13904 
  13905  nsReadingIterator<char> start, end;
  13906 
  13907  ref.BeginReading(start);
  13908  ref.EndReading(end);
  13909 
  13910  if (FindCharInReadable('#', start, end)) {
  13911    ++start;  // Skip over the '#'
  13912 
  13913    mScrollToRef = Substring(start, end);
  13914  }
  13915 }
  13916 
  13917 // https://html.spec.whatwg.org/#scrolling-to-a-fragment
  13918 void Document::ScrollToRef() {
  13919  RefPtr<PresShell> presShell = GetPresShell();
  13920  if (!presShell) {
  13921    return;
  13922  }
  13923 
  13924  // https://wicg.github.io/scroll-to-text-fragment/#invoking-text-directives
  13925  // Monkeypatching HTML § 7.4.6.3 Scrolling to a fragment:
  13926  // 1. Let text directives be the document's pending text directives.
  13927  const RefPtr fragmentDirective = FragmentDirective();
  13928  const nsTArray<RefPtr<nsRange>> textDirectives =
  13929      fragmentDirective->FindTextFragmentsInDocument();
  13930  // 2. If ranges is non-empty, then:
  13931  // 2.1 Let firstRange be the first item of ranges
  13932  const RefPtr<nsRange> textDirectiveToScroll =
  13933      !textDirectives.IsEmpty() ? textDirectives[0] : nullptr;
  13934  // 2.2 Visually indicate each range in ranges in an implementation-defined
  13935  // way. The indication must not be observable from author script. See § 3.7
  13936  // Indicating The Text Match.
  13937  fragmentDirective->HighlightTextDirectives(textDirectives);
  13938 
  13939  // In a subsequent call to `ScrollToRef()` during page load, `textDirectives`
  13940  // would only contain text directives that were not found in the previous
  13941  // runs. If an earlier call during the same page load already found a text
  13942  // directive to scroll to, only highlighting of the text directives needs to
  13943  // be done.
  13944  // This is indicated by `mScrolledToRefAlready`.
  13945  if (mScrolledToRefAlready) {
  13946    presShell->ScrollToAnchor();
  13947    return;
  13948  }
  13949  // 2. If fragment is the empty string and no text directives have been
  13950  // scrolled to, then return the special value top of the document.
  13951  if (!textDirectiveToScroll && mScrollToRef.IsEmpty()) {
  13952    return;
  13953  }
  13954 
  13955  // TODO(mccr8): This will incorrectly block scrolling from a same-document
  13956  // navigation triggered before the document is full loaded. See bug 1898630.
  13957  if (ForceLoadAtTop()) {
  13958    return;
  13959  }
  13960 
  13961  // 3. Let potentialIndicatedElement be the result of finding a potential
  13962  // indicated element given document and fragment.
  13963  NS_ConvertUTF8toUTF16 ref(mScrollToRef);
  13964  // This also covers 2.3 of the Monkeypatch for text fragments mentioned above:
  13965  // 2.3 Set firstRange as document's indicated part, return.
  13966 
  13967  const bool scrollToTextDirective =
  13968      textDirectiveToScroll
  13969          ? fragmentDirective->IsTextDirectiveAllowedToBeScrolledTo()
  13970          : mChangeScrollPosWhenScrollingToRef;
  13971 
  13972  auto rv =
  13973      presShell->GoToAnchor(ref, textDirectiveToScroll, scrollToTextDirective);
  13974 
  13975  // 4. If potentialIndicatedElement is not null, then return
  13976  // potentialIndicatedElement.
  13977  if (NS_SUCCEEDED(rv)) {
  13978    mScrolledToRefAlready = true;
  13979    return;
  13980  }
  13981 
  13982  // 5. Let fragmentBytes be the result of percent-decoding fragment.
  13983  nsAutoCString fragmentBytes;
  13984  const bool unescaped =
  13985      NS_UnescapeURL(mScrollToRef.Data(), mScrollToRef.Length(),
  13986                     /* aFlags = */ 0, fragmentBytes);
  13987 
  13988  if (!unescaped || fragmentBytes.IsEmpty()) {
  13989    // Another attempt is only necessary if characters were unescaped.
  13990    return;
  13991  }
  13992 
  13993  // 6. Let decodedFragment be the result of running UTF-8 decode without BOM on
  13994  // fragmentBytes.
  13995  nsAutoString decodedFragment;
  13996  rv = UTF_8_ENCODING->DecodeWithoutBOMHandling(fragmentBytes, decodedFragment);
  13997  NS_ENSURE_SUCCESS_VOID(rv);
  13998 
  13999  // 7. Set potentialIndicatedElement to the result of finding a potential
  14000  // indicated element given document and decodedFragment.
  14001  rv = presShell->GoToAnchor(decodedFragment, nullptr,
  14002                             mChangeScrollPosWhenScrollingToRef);
  14003  if (NS_SUCCEEDED(rv)) {
  14004    mScrolledToRefAlready = true;
  14005  }
  14006 }
  14007 
  14008 void Document::RegisterActivityObserver(nsISupports* aSupports) {
  14009  if (!mActivityObservers) {
  14010    mActivityObservers = MakeUnique<nsTHashSet<nsISupports*>>();
  14011  }
  14012  mActivityObservers->Insert(aSupports);
  14013 }
  14014 
  14015 bool Document::UnregisterActivityObserver(nsISupports* aSupports) {
  14016  if (!mActivityObservers) {
  14017    return false;
  14018  }
  14019  return mActivityObservers->EnsureRemoved(aSupports);
  14020 }
  14021 
  14022 void Document::EnumerateActivityObservers(
  14023    ActivityObserverEnumerator aEnumerator) {
  14024  if (!mActivityObservers) {
  14025    return;
  14026  }
  14027 
  14028  const auto keyArray =
  14029      ToTArray<nsTArray<nsCOMPtr<nsISupports>>>(*mActivityObservers);
  14030  for (auto& observer : keyArray) {
  14031    aEnumerator(observer.get());
  14032  }
  14033 }
  14034 
  14035 void Document::RegisterPendingLinkUpdate(Link* aLink) {
  14036  if (aLink->HasPendingLinkUpdate()) {
  14037    return;
  14038  }
  14039 
  14040  aLink->SetHasPendingLinkUpdate();
  14041 
  14042  if (!mHasLinksToUpdateRunnable && !mFlushingPendingLinkUpdates) {
  14043    nsCOMPtr<nsIRunnable> event =
  14044        NewRunnableMethod("Document::FlushPendingLinkUpdates", this,
  14045                          &Document::FlushPendingLinkUpdates);
  14046    // Do this work in a second in the worst case.
  14047    nsresult rv = NS_DispatchToCurrentThreadQueue(event.forget(), 1000,
  14048                                                  EventQueuePriority::Idle);
  14049    if (NS_FAILED(rv)) {
  14050      // If during shutdown posting a runnable doesn't succeed, we probably
  14051      // don't need to update link states.
  14052      return;
  14053    }
  14054    mHasLinksToUpdateRunnable = true;
  14055  }
  14056 
  14057  mLinksToUpdate.InfallibleAppend(aLink);
  14058 }
  14059 
  14060 void Document::FlushPendingLinkUpdates() {
  14061  MOZ_DIAGNOSTIC_ASSERT(!mFlushingPendingLinkUpdates);
  14062  MOZ_ASSERT(mHasLinksToUpdateRunnable);
  14063  mHasLinksToUpdateRunnable = false;
  14064 
  14065  auto restore = MakeScopeExit([&] { mFlushingPendingLinkUpdates = false; });
  14066  mFlushingPendingLinkUpdates = true;
  14067 
  14068  while (!mLinksToUpdate.IsEmpty()) {
  14069    LinksToUpdateList links(std::move(mLinksToUpdate));
  14070    for (auto iter = links.Iter(); !iter.Done(); iter.Next()) {
  14071      Link* link = iter.Get();
  14072      Element* element = link->GetElement();
  14073      if (element->OwnerDoc() == this) {
  14074        link->ClearHasPendingLinkUpdate();
  14075        if (element->IsInComposedDoc()) {
  14076          link->TriggerLinkUpdate(/* aNotify = */ true);
  14077        }
  14078      }
  14079    }
  14080  }
  14081 }
  14082 
  14083 /**
  14084 * Retrieves the node in a static-clone document that corresponds to aOrigNode,
  14085 * which is a node in the original document from which aStaticClone was cloned.
  14086 */
  14087 static nsINode* GetCorrespondingNodeInDocument(const nsINode* aOrigNode,
  14088                                               Document& aStaticClone) {
  14089  MOZ_ASSERT(aOrigNode);
  14090 
  14091  // Selections in anonymous subtrees aren't supported.
  14092  if (NS_WARN_IF(aOrigNode->IsInNativeAnonymousSubtree())) {
  14093    return nullptr;
  14094  }
  14095 
  14096  // If the node is disconnected, this is a bug in the selection code, but it
  14097  // can happen with shadow DOM so handle it.
  14098  if (NS_WARN_IF(!aOrigNode->IsInComposedDoc())) {
  14099    return nullptr;
  14100  }
  14101 
  14102  AutoTArray<Maybe<uint32_t>, 32> indexArray;
  14103  const nsINode* current = aOrigNode;
  14104  while (const nsINode* parent = current->GetParentNode()) {
  14105    Maybe<uint32_t> index = parent->ComputeIndexOf(current);
  14106    NS_ENSURE_TRUE(index.isSome(), nullptr);
  14107    indexArray.AppendElement(std::move(index));
  14108    current = parent;
  14109  }
  14110  MOZ_ASSERT(current->IsDocument() || current->IsShadowRoot());
  14111  nsINode* correspondingNode = [&]() -> nsINode* {
  14112    if (current->IsDocument()) {
  14113      return &aStaticClone;
  14114    }
  14115    const auto* shadow = ShadowRoot::FromNode(*current);
  14116    if (!shadow) {
  14117      return nullptr;
  14118    }
  14119    nsINode* correspondingHost =
  14120        GetCorrespondingNodeInDocument(shadow->Host(), aStaticClone);
  14121    if (NS_WARN_IF(!correspondingHost || !correspondingHost->IsElement())) {
  14122      return nullptr;
  14123    }
  14124    return correspondingHost->AsElement()->GetShadowRoot();
  14125  }();
  14126 
  14127  if (NS_WARN_IF(!correspondingNode)) {
  14128    return nullptr;
  14129  }
  14130  for (const Maybe<uint32_t>& index : Reversed(indexArray)) {
  14131    correspondingNode = correspondingNode->GetChildAt_Deprecated(*index);
  14132    NS_ENSURE_TRUE(correspondingNode, nullptr);
  14133  }
  14134  return correspondingNode;
  14135 }
  14136 
  14137 /**
  14138 * Caches the selection ranges from the source document onto the static clone in
  14139 * case the "Print Selection Only" functionality is invoked.
  14140 *
  14141 * Note that we cannot use the selection obtained from GetOriginalDocument()
  14142 * since that selection may have mutated after the print was invoked.
  14143 *
  14144 * Note also that because nsRange objects point into a specific document's
  14145 * nodes, we cannot reuse an array of nsRange objects across multiple static
  14146 * clone documents. For that reason we cache a new array of ranges on each
  14147 * static clone that we create.
  14148 *
  14149 * TODO(emilio): This can be simplified once we don't re-clone from static
  14150 * documents.
  14151 *
  14152 * @param aSourceDoc the document from which we are caching selection ranges
  14153 * @param aStaticClone the document that will hold the cache
  14154 * @return true if a selection range was cached
  14155 */
  14156 static void CachePrintSelectionRanges(const Document& aSourceDoc,
  14157                                      Document& aStaticClone) {
  14158  MOZ_ASSERT(aStaticClone.IsStaticDocument());
  14159  MOZ_ASSERT(!aStaticClone.GetProperty(nsGkAtoms::printisfocuseddoc));
  14160  MOZ_ASSERT(!aStaticClone.GetProperty(nsGkAtoms::printselectionranges));
  14161 
  14162  bool sourceDocIsStatic = aSourceDoc.IsStaticDocument();
  14163 
  14164  // When the user opts to "Print Selection Only", the print code prefers any
  14165  // selection in the static clone corresponding to the focused frame. If this
  14166  // is that static clone, flag it for the printing code:
  14167  const bool isFocusedDoc = [&] {
  14168    if (sourceDocIsStatic) {
  14169      return bool(aSourceDoc.GetProperty(nsGkAtoms::printisfocuseddoc));
  14170    }
  14171    nsPIDOMWindowOuter* window = aSourceDoc.GetWindow();
  14172    if (!window) {
  14173      return false;
  14174    }
  14175    nsCOMPtr<nsPIDOMWindowOuter> rootWindow = window->GetPrivateRoot();
  14176    if (!rootWindow) {
  14177      return false;
  14178    }
  14179    nsCOMPtr<nsPIDOMWindowOuter> focusedWindow;
  14180    nsFocusManager::GetFocusedDescendant(rootWindow,
  14181                                         nsFocusManager::eIncludeAllDescendants,
  14182                                         getter_AddRefs(focusedWindow));
  14183    return focusedWindow && focusedWindow->GetExtantDoc() == &aSourceDoc;
  14184  }();
  14185  if (isFocusedDoc) {
  14186    aStaticClone.SetProperty(nsGkAtoms::printisfocuseddoc,
  14187                             reinterpret_cast<void*>(true));
  14188  }
  14189 
  14190  const Selection* origSelection = nullptr;
  14191  const nsTArray<RefPtr<nsRange>>* origRanges = nullptr;
  14192 
  14193  if (sourceDocIsStatic) {
  14194    origRanges = static_cast<nsTArray<RefPtr<nsRange>>*>(
  14195        aSourceDoc.GetProperty(nsGkAtoms::printselectionranges));
  14196  } else if (PresShell* shell = aSourceDoc.GetPresShell()) {
  14197    origSelection = shell->GetCurrentSelection(SelectionType::eNormal);
  14198  }
  14199 
  14200  if (!origSelection && !origRanges) {
  14201    return;
  14202  }
  14203 
  14204  const uint32_t rangeCount =
  14205      sourceDocIsStatic ? origRanges->Length() : origSelection->RangeCount();
  14206  auto printRanges = MakeUnique<nsTArray<RefPtr<nsRange>>>(rangeCount);
  14207 
  14208  for (const uint32_t i : IntegerRange(rangeCount)) {
  14209    MOZ_ASSERT_IF(!sourceDocIsStatic,
  14210                  origSelection->RangeCount() == rangeCount);
  14211    const nsRange* range = sourceDocIsStatic ? origRanges->ElementAt(i).get()
  14212                                             : origSelection->GetRangeAt(i);
  14213    MOZ_ASSERT(range);
  14214    nsINode* startContainer = range->GetMayCrossShadowBoundaryStartContainer();
  14215    nsINode* endContainer = range->GetMayCrossShadowBoundaryEndContainer();
  14216 
  14217    if (!startContainer || !endContainer) {
  14218      continue;
  14219    }
  14220 
  14221    nsINode* startNode =
  14222        GetCorrespondingNodeInDocument(startContainer, aStaticClone);
  14223    nsINode* endNode =
  14224        GetCorrespondingNodeInDocument(endContainer, aStaticClone);
  14225 
  14226    if (NS_WARN_IF(!startNode || !endNode)) {
  14227      continue;
  14228    }
  14229 
  14230    RefPtr<nsRange> clonedRange = nsRange::Create(
  14231        startNode, range->MayCrossShadowBoundaryStartOffset(), endNode,
  14232        range->MayCrossShadowBoundaryEndOffset(), IgnoreErrors(),
  14233        StaticPrefs::dom_shadowdom_selection_across_boundary_enabled()
  14234            ? AllowRangeCrossShadowBoundary::Yes
  14235            : AllowRangeCrossShadowBoundary::No);
  14236    if (clonedRange &&
  14237        !clonedRange->AreNormalRangeAndCrossShadowBoundaryRangeCollapsed()) {
  14238      printRanges->AppendElement(std::move(clonedRange));
  14239    }
  14240  }
  14241 
  14242  if (printRanges->IsEmpty()) {
  14243    return;
  14244  }
  14245 
  14246  aStaticClone.SetProperty(nsGkAtoms::printselectionranges,
  14247                           printRanges.release(),
  14248                           nsINode::DeleteProperty<nsTArray<RefPtr<nsRange>>>);
  14249 }
  14250 
  14251 already_AddRefed<Document> Document::CreateStaticClone(
  14252    nsIDocShell* aCloneContainer, nsIDocumentViewer* aViewer,
  14253    nsIPrintSettings* aPrintSettings, bool* aOutHasInProcessPrintCallbacks) {
  14254  MOZ_ASSERT(!mCreatingStaticClone);
  14255  MOZ_ASSERT(!GetProperty(nsGkAtoms::adoptedsheetclones));
  14256  MOZ_DIAGNOSTIC_ASSERT(aViewer);
  14257 
  14258  mCreatingStaticClone = true;
  14259  SetProperty(nsGkAtoms::adoptedsheetclones, new AdoptedStyleSheetCloneCache(),
  14260              nsINode::DeleteProperty<AdoptedStyleSheetCloneCache>);
  14261 
  14262  auto raii = MakeScopeExit([&] {
  14263    RemoveProperty(nsGkAtoms::adoptedsheetclones);
  14264    mCreatingStaticClone = false;
  14265  });
  14266 
  14267  // Make document use different container during cloning.
  14268  //
  14269  // FIXME(emilio): Why is this needed?
  14270  RefPtr<nsDocShell> originalShell = mDocumentContainer.get();
  14271  SetContainer(nsDocShell::Cast(aCloneContainer));
  14272  IgnoredErrorResult rv;
  14273  nsCOMPtr<nsINode> clonedNode = CloneNode(true, rv);
  14274  SetContainer(originalShell);
  14275  if (rv.Failed()) {
  14276    return nullptr;
  14277  }
  14278 
  14279  nsCOMPtr<Document> clonedDoc = do_QueryInterface(clonedNode);
  14280  if (!clonedDoc) {
  14281    return nullptr;
  14282  }
  14283 
  14284  // Copy any stylesheet not referenced somewhere in the DOM tree (e.g. by
  14285  // `<style>`).
  14286 
  14287  clonedDoc->CloneAdoptedSheetsFrom(*this);
  14288 
  14289  for (int t = 0; t < AdditionalSheetTypeCount; ++t) {
  14290    auto& sheets = mAdditionalSheets[additionalSheetType(t)];
  14291    for (StyleSheet* sheet : sheets) {
  14292      if (sheet->IsApplicable()) {
  14293        clonedDoc->AddAdditionalStyleSheet(additionalSheetType(t), sheet);
  14294      }
  14295    }
  14296  }
  14297 
  14298  // Font faces created with the JS API will not be reflected in the
  14299  // stylesheets and need to be copied over to the cloned document.
  14300  if (const FontFaceSet* set = GetFonts()) {
  14301    set->CopyNonRuleFacesTo(clonedDoc->Fonts());
  14302  }
  14303 
  14304  clonedDoc->mReferrerInfo =
  14305      static_cast<dom::ReferrerInfo*>(mReferrerInfo.get())->Clone();
  14306  clonedDoc->mPreloadReferrerInfo = clonedDoc->mReferrerInfo;
  14307  CachePrintSelectionRanges(*this, *clonedDoc);
  14308 
  14309  // We're done with the clone, embed ourselves into the document viewer and
  14310  // clone our children. The order here is pretty important, because our
  14311  // document our document needs to have an owner global before we can create
  14312  // the frame loaders for subdocuments.
  14313  aViewer->SetDocument(clonedDoc);
  14314 
  14315  *aOutHasInProcessPrintCallbacks |= clonedDoc->HasPrintCallbacks();
  14316 
  14317  auto pendingClones = std::move(clonedDoc->mPendingFrameStaticClones);
  14318  for (const auto& clone : pendingClones) {
  14319    RefPtr<Element> element = do_QueryObject(clone.mElement);
  14320    RefPtr<nsFrameLoader> frameLoader =
  14321        nsFrameLoader::Create(element, /* aNetworkCreated */ false);
  14322 
  14323    if (NS_WARN_IF(!frameLoader)) {
  14324      continue;
  14325    }
  14326 
  14327    clone.mElement->SetFrameLoader(frameLoader);
  14328 
  14329    nsresult rv = frameLoader->FinishStaticClone(
  14330        clone.mStaticCloneOf, aPrintSettings, aOutHasInProcessPrintCallbacks);
  14331    (void)NS_WARN_IF(NS_FAILED(rv));
  14332  }
  14333 
  14334  return clonedDoc.forget();
  14335 }
  14336 
  14337 void Document::UnlinkOriginalDocumentIfStatic() {
  14338  if (IsStaticDocument() && mOriginalDocument) {
  14339    MOZ_ASSERT(mOriginalDocument->mStaticCloneCount > 0);
  14340    mOriginalDocument->mStaticCloneCount--;
  14341    mOriginalDocument = nullptr;
  14342  }
  14343  MOZ_ASSERT(!mOriginalDocument);
  14344 }
  14345 
  14346 nsresult Document::ScheduleFrameRequestCallback(FrameRequestCallback& aCallback,
  14347                                                uint32_t* aHandle) {
  14348  const bool wasEmpty = mFrameRequestManager.IsEmpty();
  14349  MOZ_TRY(mFrameRequestManager.Schedule(aCallback, aHandle));
  14350  if (wasEmpty) {
  14351    MaybeScheduleFrameRequestCallbacks();
  14352  }
  14353  return NS_OK;
  14354 }
  14355 
  14356 void Document::CancelFrameRequestCallback(uint32_t aHandle) {
  14357  mFrameRequestManager.Cancel(aHandle);
  14358 }
  14359 
  14360 void Document::ScheduleVideoFrameCallbacks(HTMLVideoElement* aElement) {
  14361  const bool wasEmpty = mFrameRequestManager.IsEmpty();
  14362  mFrameRequestManager.Schedule(aElement);
  14363  if (wasEmpty) {
  14364    MaybeScheduleFrameRequestCallbacks();
  14365  }
  14366 }
  14367 
  14368 void Document::CancelVideoFrameCallbacks(HTMLVideoElement* aElement) {
  14369  mFrameRequestManager.Cancel(aElement);
  14370 }
  14371 
  14372 nsresult Document::GetStateObject(JS::MutableHandle<JS::Value> aState) {
  14373  // Get the document's current state object. This is the object backing both
  14374  // history.state and popStateEvent.state.
  14375  //
  14376  // mStateObjectContainer may be null; this just means that there's no
  14377  // current state object.
  14378 
  14379  if (!mCachedStateObjectValid) {
  14380    if (mStateObjectContainer) {
  14381      AutoJSAPI jsapi;
  14382      // Init with null is "OK" in the sense that it will just fail.
  14383      if (!jsapi.Init(GetScopeObject())) {
  14384        return NS_ERROR_UNEXPECTED;
  14385      }
  14386      JS::Rooted<JS::Value> value(jsapi.cx());
  14387      nsresult rv =
  14388          mStateObjectContainer->DeserializeToJsval(jsapi.cx(), &value);
  14389      NS_ENSURE_SUCCESS(rv, rv);
  14390 
  14391      mCachedStateObject = value;
  14392      if (!value.isNullOrUndefined()) {
  14393        mozilla::HoldJSObjects(this);
  14394      }
  14395    } else {
  14396      mCachedStateObject = JS::NullValue();
  14397    }
  14398    mCachedStateObjectValid = true;
  14399  }
  14400 
  14401  aState.set(mCachedStateObject);
  14402  return NS_OK;
  14403 }
  14404 
  14405 void Document::SetNavigationTiming(nsDOMNavigationTiming* aTiming) {
  14406  mTiming = aTiming;
  14407  if (!mLoadingOrRestoredFromBFCacheTimeStamp.IsNull() && mTiming) {
  14408    mTiming->SetDOMLoadingTimeStamp(GetDocumentURI(),
  14409                                    mLoadingOrRestoredFromBFCacheTimeStamp);
  14410  }
  14411 
  14412  // If there's already the DocumentTimeline instance, tell it since the
  14413  // DocumentTimeline is based on both the navigation start time stamp and the
  14414  // refresh driver timestamp.
  14415  if (mDocumentTimeline) {
  14416    mDocumentTimeline->UpdateLastRefreshDriverTime();
  14417  }
  14418 }
  14419 
  14420 nsContentList* Document::ImageMapList() {
  14421  if (!mImageMaps) {
  14422    mImageMaps = new nsContentList(this, kNameSpaceID_XHTML, nsGkAtoms::map,
  14423                                   nsGkAtoms::map);
  14424  }
  14425 
  14426  return mImageMaps;
  14427 }
  14428 
  14429 #define DEPRECATED_OPERATION(_op) #_op "Warning",
  14430 static const char* kDeprecationWarnings[] = {
  14431 #include "nsDeprecatedOperationList.h"
  14432    nullptr};
  14433 #undef DEPRECATED_OPERATION
  14434 
  14435 #define DOCUMENT_WARNING(_op) #_op "Warning",
  14436 static const char* kDocumentWarnings[] = {
  14437 #include "nsDocumentWarningList.h"
  14438    nullptr};
  14439 #undef DOCUMENT_WARNING
  14440 
  14441 static UseCounter OperationToUseCounter(DeprecatedOperations aOperation) {
  14442  switch (aOperation) {
  14443 #define DEPRECATED_OPERATION(_op)    \
  14444  case DeprecatedOperations::e##_op: \
  14445    return eUseCounter_##_op;
  14446 #include "nsDeprecatedOperationList.h"
  14447 #undef DEPRECATED_OPERATION
  14448    default:
  14449      MOZ_CRASH();
  14450  }
  14451 }
  14452 
  14453 bool Document::HasWarnedAbout(DeprecatedOperations aOperation) const {
  14454  return mDeprecationWarnedAbout[static_cast<size_t>(aOperation)];
  14455 }
  14456 
  14457 void Document::WarnOnceAbout(
  14458    DeprecatedOperations aOperation, bool asError /* = false */,
  14459    const nsTArray<nsString>& aParams /* = empty array */) const {
  14460  MOZ_ASSERT(NS_IsMainThread());
  14461  if (HasWarnedAbout(aOperation)) {
  14462    return;
  14463  }
  14464  mDeprecationWarnedAbout[static_cast<size_t>(aOperation)] = true;
  14465  const_cast<Document*>(this)->SetUseCounter(OperationToUseCounter(aOperation));
  14466  uint32_t flags =
  14467      asError ? nsIScriptError::errorFlag : nsIScriptError::warningFlag;
  14468  nsContentUtils::ReportToConsole(
  14469      flags, "DOM Core"_ns, this, nsContentUtils::eDOM_PROPERTIES,
  14470      kDeprecationWarnings[static_cast<size_t>(aOperation)], aParams);
  14471 }
  14472 
  14473 bool Document::HasWarnedAbout(DocumentWarnings aWarning) const {
  14474  return mDocWarningWarnedAbout[aWarning];
  14475 }
  14476 
  14477 void Document::WarnOnceAbout(
  14478    DocumentWarnings aWarning, bool asError /* = false */,
  14479    const nsTArray<nsString>& aParams /* = empty array */) const {
  14480  MOZ_ASSERT(NS_IsMainThread());
  14481  if (HasWarnedAbout(aWarning)) {
  14482    return;
  14483  }
  14484  mDocWarningWarnedAbout[aWarning] = true;
  14485  uint32_t flags =
  14486      asError ? nsIScriptError::errorFlag : nsIScriptError::warningFlag;
  14487  nsContentUtils::ReportToConsole(flags, "DOM Core"_ns, this,
  14488                                  nsContentUtils::eDOM_PROPERTIES,
  14489                                  kDocumentWarnings[aWarning], aParams);
  14490 }
  14491 
  14492 void Document::TrackImage(imgIRequest* aImage) {
  14493  MOZ_ASSERT(aImage);
  14494  bool newAnimation = false;
  14495  mTrackedImages.WithEntryHandle(aImage, [&](auto&& entry) {
  14496    if (entry) {
  14497      // The image is already in the hashtable.  Increment its count.
  14498      uint32_t oldCount = entry.Data();
  14499      MOZ_ASSERT(oldCount > 0, "Entry in the image tracker with count 0!");
  14500      entry.Data() = oldCount + 1;
  14501    } else {
  14502      // A new entry was inserted - set the count to 1.
  14503      entry.Insert(1);
  14504 
  14505      // If we're locking images, lock this image too.
  14506      if (mLockingImages) {
  14507        aImage->LockImage();
  14508      }
  14509 
  14510      // If we're animating images, request that this image be animated too.
  14511      if (mAnimatingImages) {
  14512        aImage->IncrementAnimationConsumers();
  14513        newAnimation = true;
  14514      }
  14515    }
  14516  });
  14517  if (newAnimation) {
  14518    AnimatedImageStateMaybeChanged(true);
  14519  }
  14520 }
  14521 
  14522 void Document::UntrackImage(imgIRequest* aImage,
  14523                            RequestDiscard aRequestDiscard) {
  14524  MOZ_ASSERT(aImage);
  14525 
  14526  // Get the old count. It should exist and be > 0.
  14527  auto entry = mTrackedImages.Lookup(aImage);
  14528  if (!entry) {
  14529    MOZ_ASSERT_UNREACHABLE("Removing image that wasn't in the tracker!");
  14530    return;
  14531  }
  14532  MOZ_ASSERT(entry.Data() > 0, "Entry in the image tracker with count 0!");
  14533  // If the count becomes zero, remove it from the tracker.
  14534  if (--entry.Data() == 0) {
  14535    entry.Remove();
  14536  } else {
  14537    return;
  14538  }
  14539 
  14540  // Now that we're no longer tracking this image, unlock it if we'd
  14541  // previously locked it.
  14542  if (mLockingImages) {
  14543    aImage->UnlockImage();
  14544  }
  14545 
  14546  // If we're animating images, remove our request to animate this one.
  14547  if (mAnimatingImages) {
  14548    aImage->DecrementAnimationConsumers();
  14549    AnimatedImageStateMaybeChanged(false);
  14550  }
  14551 
  14552  if (aRequestDiscard == RequestDiscard::Yes) {
  14553    // Do this even if !mLocking, because even if we didn't just unlock
  14554    // this image, it might still be a candidate for discarding.
  14555    aImage->RequestDiscard();
  14556  }
  14557 }
  14558 
  14559 void Document::PropagateMediaFeatureChangeToTrackedImages(
  14560    const MediaFeatureChange& aChange) {
  14561  // Inform every content image used in the document that media feature values
  14562  // have changed. Pull the images out into a set and iterate over them, in case
  14563  // the image notifications do something that ends up modifying the table.
  14564  nsTHashSet<nsRefPtrHashKey<imgIContainer>> images;
  14565  for (imgIRequest* req : mTrackedImages.Keys()) {
  14566    nsCOMPtr<imgIContainer> image;
  14567    req->GetImage(getter_AddRefs(image));
  14568    if (!image) {
  14569      continue;
  14570    }
  14571    image = image->Unwrap();
  14572    images.Insert(image);
  14573  }
  14574  for (imgIContainer* image : images) {
  14575    image->MediaFeatureValuesChangedAllDocuments(aChange);
  14576  }
  14577 }
  14578 
  14579 void Document::SetLockingImages(bool aLocking) {
  14580  // If there's no change, there's nothing to do.
  14581  if (mLockingImages == aLocking) {
  14582    return;
  14583  }
  14584 
  14585  // Otherwise, iterate over our images and perform the appropriate action.
  14586  for (imgIRequest* image : mTrackedImages.Keys()) {
  14587    if (aLocking) {
  14588      image->LockImage();
  14589    } else {
  14590      image->UnlockImage();
  14591    }
  14592  }
  14593 
  14594  // Update state.
  14595  mLockingImages = aLocking;
  14596 }
  14597 
  14598 void Document::SetImageAnimationState(bool aAnimating) {
  14599  // If there's no change, there's nothing to do.
  14600  if (mAnimatingImages == aAnimating) {
  14601    return;
  14602  }
  14603 
  14604  // Otherwise, iterate over our images and perform the appropriate action.
  14605  for (imgIRequest* image : mTrackedImages.Keys()) {
  14606    if (aAnimating) {
  14607      image->IncrementAnimationConsumers();
  14608    } else {
  14609      image->DecrementAnimationConsumers();
  14610    }
  14611  }
  14612 
  14613  AnimatedImageStateMaybeChanged(aAnimating);
  14614 
  14615  // Update state.
  14616  mAnimatingImages = aAnimating;
  14617 }
  14618 
  14619 void Document::AnimatedImageStateMaybeChanged(bool aAnimating) {
  14620  auto* ps = GetPresShell();
  14621  if (!ps) {
  14622    return;
  14623  }
  14624  auto* pc = ps->GetPresContext();
  14625  if (!pc) {
  14626    return;
  14627  }
  14628  auto* rd = pc->RefreshDriver();
  14629  if (aAnimating) {
  14630    rd->StartTimerForAnimatedImagesIfNeeded();
  14631  } else {
  14632    rd->StopTimerForAnimatedImagesIfNeeded();
  14633  }
  14634 }
  14635 
  14636 void Document::ScheduleSVGUseElementShadowTreeUpdate(
  14637    SVGUseElement& aUseElement) {
  14638  MOZ_ASSERT(aUseElement.IsInComposedDoc());
  14639 
  14640  if (MOZ_UNLIKELY(mIsStaticDocument)) {
  14641    // Printing doesn't deal well with dynamic DOM mutations.
  14642    return;
  14643  }
  14644 
  14645  mSVGUseElementsNeedingShadowTreeUpdate.Insert(&aUseElement);
  14646 
  14647  if (PresShell* presShell = GetPresShell()) {
  14648    presShell->EnsureStyleFlush();
  14649  }
  14650 }
  14651 
  14652 void Document::DoUpdateSVGUseElementShadowTrees() {
  14653  MOZ_ASSERT(!mSVGUseElementsNeedingShadowTreeUpdate.IsEmpty());
  14654 
  14655  MOZ_ASSERT(!mCloningForSVGUse);
  14656  nsAutoScriptBlockerSuppressNodeRemoved blocker;
  14657  mCloningForSVGUse = true;
  14658 
  14659  do {
  14660    const auto useElementsToUpdate = ToTArray<nsTArray<RefPtr<SVGUseElement>>>(
  14661        mSVGUseElementsNeedingShadowTreeUpdate);
  14662    mSVGUseElementsNeedingShadowTreeUpdate.Clear();
  14663 
  14664    for (const auto& useElement : useElementsToUpdate) {
  14665      if (MOZ_UNLIKELY(!useElement->IsInComposedDoc())) {
  14666        // The element was in another <use> shadow tree which we processed
  14667        // already and also needed an update, and is removed from the document
  14668        // now, so nothing to do here.
  14669        MOZ_ASSERT(useElementsToUpdate.Length() > 1);
  14670        continue;
  14671      }
  14672      useElement->UpdateShadowTree();
  14673    }
  14674  } while (!mSVGUseElementsNeedingShadowTreeUpdate.IsEmpty());
  14675 
  14676  mCloningForSVGUse = false;
  14677 }
  14678 
  14679 void Document::NotifyMediaFeatureValuesChanged() {
  14680  for (RefPtr<HTMLImageElement> imageElement : mResponsiveContent) {
  14681    imageElement->MediaFeatureValuesChanged();
  14682  }
  14683 }
  14684 
  14685 already_AddRefed<Touch> Document::CreateTouch(
  14686    nsGlobalWindowInner* aView, EventTarget* aTarget, int32_t aIdentifier,
  14687    int32_t aPageX, int32_t aPageY, int32_t aScreenX, int32_t aScreenY,
  14688    int32_t aClientX, int32_t aClientY, int32_t aRadiusX, int32_t aRadiusY,
  14689    float aRotationAngle, float aForce) {
  14690  RefPtr<Touch> touch =
  14691      new Touch(aTarget, aIdentifier, aPageX, aPageY, aScreenX, aScreenY,
  14692                aClientX, aClientY, aRadiusX, aRadiusY, aRotationAngle, aForce);
  14693  return touch.forget();
  14694 }
  14695 
  14696 already_AddRefed<TouchList> Document::CreateTouchList() {
  14697  RefPtr<TouchList> retval = new TouchList(ToSupports(this));
  14698  return retval.forget();
  14699 }
  14700 
  14701 already_AddRefed<TouchList> Document::CreateTouchList(
  14702    Touch& aTouch, const Sequence<OwningNonNull<Touch>>& aTouches) {
  14703  RefPtr<TouchList> retval = new TouchList(ToSupports(this));
  14704  retval->Append(&aTouch);
  14705  for (uint32_t i = 0; i < aTouches.Length(); ++i) {
  14706    retval->Append(aTouches[i].get());
  14707  }
  14708  return retval.forget();
  14709 }
  14710 
  14711 already_AddRefed<TouchList> Document::CreateTouchList(
  14712    const Sequence<OwningNonNull<Touch>>& aTouches) {
  14713  RefPtr<TouchList> retval = new TouchList(ToSupports(this));
  14714  for (uint32_t i = 0; i < aTouches.Length(); ++i) {
  14715    retval->Append(aTouches[i].get());
  14716  }
  14717  return retval.forget();
  14718 }
  14719 
  14720 // https://drafts.csswg.org/cssom-view/Overview#dom-document-caretpositionfrompoint
  14721 already_AddRefed<nsDOMCaretPosition> Document::CaretPositionFromPoint(
  14722    float aX, float aY, const CaretPositionFromPointOptions& aOptions) {
  14723  using FrameForPointOption = nsLayoutUtils::FrameForPointOption;
  14724 
  14725  nscoord x = nsPresContext::CSSPixelsToAppUnits(aX);
  14726  nscoord y = nsPresContext::CSSPixelsToAppUnits(aY);
  14727  nsPoint pt(x, y);
  14728 
  14729  FlushPendingNotifications(FlushType::Layout);
  14730 
  14731  PresShell* presShell = GetPresShell();
  14732  if (!presShell) {
  14733    return nullptr;
  14734  }
  14735 
  14736  nsIFrame* rootFrame = presShell->GetRootFrame();
  14737 
  14738  // XUL docs, unlike HTML, have no frame tree until everything's done loading
  14739  if (!rootFrame) {
  14740    return nullptr;
  14741  }
  14742 
  14743  nsIFrame* ptFrame = nsLayoutUtils::GetFrameForPoint(
  14744      RelativeTo{rootFrame}, pt,
  14745      {{FrameForPointOption::IgnorePaintSuppression,
  14746        FrameForPointOption::IgnoreCrossDoc}});
  14747  if (!ptFrame) {
  14748    return nullptr;
  14749  }
  14750 
  14751  // We require frame-relative coordinates for GetContentOffsetsFromPoint.
  14752  nsPoint adjustedPoint = pt;
  14753  if (nsLayoutUtils::TransformPoint(RelativeTo{rootFrame}, RelativeTo{ptFrame},
  14754                                    adjustedPoint) !=
  14755      nsLayoutUtils::TRANSFORM_SUCCEEDED) {
  14756    return nullptr;
  14757  }
  14758 
  14759  nsIFrame::ContentOffsets offsets =
  14760      ptFrame->GetContentOffsetsFromPoint(adjustedPoint);
  14761 
  14762  nsCOMPtr<nsINode> node = offsets.content;
  14763  uint32_t offset = offsets.offset;
  14764  nsCOMPtr<nsINode> anonNode = node;
  14765  bool nodeIsAnonymous = node && node->IsInNativeAnonymousSubtree();
  14766  if (nodeIsAnonymous) {
  14767    node = ptFrame->GetContent();
  14768    nsINode* nonChrome =
  14769        node->AsContent()->FindFirstNonChromeOnlyAccessContent();
  14770    HTMLTextAreaElement* textArea = HTMLTextAreaElement::FromNode(nonChrome);
  14771    nsTextControlFrame* textFrame =
  14772        do_QueryFrame(nonChrome->AsContent()->GetPrimaryFrame());
  14773    if (!textFrame) {
  14774      return nullptr;
  14775    }
  14776 
  14777    // If the anonymous content node has a child, then we need to make sure
  14778    // that we get the appropriate child, as otherwise the offset may not be
  14779    // correct when we construct a range for it.
  14780    nsCOMPtr<nsINode> firstChild = anonNode->GetFirstChild();
  14781    if (firstChild) {
  14782      anonNode = firstChild;
  14783    }
  14784 
  14785    if (textArea) {
  14786      offset = nsContentUtils::GetAdjustedOffsetInTextControl(ptFrame, offset);
  14787    }
  14788 
  14789    node = nonChrome;
  14790  }
  14791 
  14792  bool offsetAndNodeNeedsAdjustment = false;
  14793 
  14794  if (StaticPrefs::
  14795          dom_shadowdom_new_caretPositionFromPoint_behavior_enabled()) {
  14796    while (node->IsInShadowTree() &&
  14797           !aOptions.mShadowRoots.Contains(node->GetContainingShadow())) {
  14798      node = node->GetContainingShadowHost();
  14799      offsetAndNodeNeedsAdjustment = true;
  14800    }
  14801  }
  14802 
  14803  if (offsetAndNodeNeedsAdjustment) {
  14804    const Maybe<uint32_t> maybeIndex = node->ComputeIndexInParentContent();
  14805    if (MOZ_UNLIKELY(maybeIndex.isNothing())) {
  14806      // Unlikely to happen, but still return nullptr to avoid leaking
  14807      // information about the shadow tree.
  14808      return nullptr;
  14809    }
  14810    // 5.3.1: Set startOffset to index of startNode’s root's host.
  14811    offset = maybeIndex.value();
  14812    // 5.3.2: Set startNode to startNode’s root's host's parent.
  14813    node = node->GetParentNode();
  14814  }
  14815 
  14816  RefPtr<nsDOMCaretPosition> aCaretPos = new nsDOMCaretPosition(node, offset);
  14817  if (nodeIsAnonymous) {
  14818    aCaretPos->SetAnonymousContentNode(anonNode);
  14819  }
  14820  return aCaretPos.forget();
  14821 }
  14822 
  14823 bool Document::IsPotentiallyScrollable(HTMLBodyElement* aBody) {
  14824  // We rely on correct frame information here, so need to flush frames.
  14825  FlushPendingNotifications(FlushType::Frames);
  14826 
  14827  // An element that is the HTML body element is potentially scrollable if all
  14828  // of the following conditions are true:
  14829 
  14830  // The element has an associated CSS layout box.
  14831  nsIFrame* bodyFrame = nsLayoutUtils::GetStyleFrame(aBody);
  14832  if (!bodyFrame) {
  14833    return false;
  14834  }
  14835 
  14836  // The element's parent element's computed value of the overflow-x and
  14837  // overflow-y properties are visible.
  14838  MOZ_ASSERT(aBody->GetParent() == aBody->OwnerDoc()->GetRootElement());
  14839  nsIFrame* parentFrame = nsLayoutUtils::GetStyleFrame(aBody->GetParent());
  14840  if (parentFrame &&
  14841      parentFrame->StyleDisplay()->OverflowIsVisibleInBothAxis()) {
  14842    return false;
  14843  }
  14844 
  14845  // The element's computed value of the overflow-x or overflow-y properties is
  14846  // not visible.
  14847  return !bodyFrame->StyleDisplay()->OverflowIsVisibleInBothAxis();
  14848 }
  14849 
  14850 Element* Document::GetScrollingElement() {
  14851  // Keep this in sync with IsScrollingElement.
  14852  if (GetCompatibilityMode() == eCompatibility_NavQuirks) {
  14853    RefPtr<HTMLBodyElement> body = GetBodyElement();
  14854    if (body && !IsPotentiallyScrollable(body)) {
  14855      return body;
  14856    }
  14857 
  14858    return nullptr;
  14859  }
  14860 
  14861  return GetRootElement();
  14862 }
  14863 
  14864 bool Document::IsScrollingElement(Element* aElement) {
  14865  // Keep this in sync with GetScrollingElement.
  14866  MOZ_ASSERT(aElement);
  14867 
  14868  if (GetCompatibilityMode() != eCompatibility_NavQuirks) {
  14869    return aElement == GetRootElement();
  14870  }
  14871 
  14872  // In the common case when aElement != body, avoid refcounting.
  14873  HTMLBodyElement* body = GetBodyElement();
  14874  if (aElement != body) {
  14875    return false;
  14876  }
  14877 
  14878  // Now we know body is non-null, since aElement is not null.  It's the
  14879  // scrolling element for the document if it itself is not potentially
  14880  // scrollable.
  14881  RefPtr<HTMLBodyElement> strongBody(body);
  14882  return !IsPotentiallyScrollable(strongBody);
  14883 }
  14884 
  14885 class UnblockParsingPromiseHandler final : public PromiseNativeHandler {
  14886 public:
  14887  NS_DECL_CYCLE_COLLECTING_ISUPPORTS
  14888  NS_DECL_CYCLE_COLLECTION_CLASS(UnblockParsingPromiseHandler)
  14889 
  14890  explicit UnblockParsingPromiseHandler(Document* aDocument, Promise* aPromise,
  14891                                        const BlockParsingOptions& aOptions)
  14892      : mPromise(aPromise) {
  14893    nsCOMPtr<nsIParser> parser = aDocument->CreatorParserOrNull();
  14894    // Parser blocking is not allowed for about:blank
  14895    if (parser && !parser->IsAboutBlankMode() &&
  14896        (aOptions.mBlockScriptCreated || !parser->IsScriptCreated())) {
  14897      parser->BlockParser();
  14898      mParser = do_GetWeakReference(parser);
  14899      mDocument = aDocument;
  14900      mDocument->BlockOnload();
  14901      mDocument->BlockDOMContentLoaded();
  14902    }
  14903  }
  14904 
  14905  void ResolvedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue,
  14906                        ErrorResult& aRv) override {
  14907    MaybeUnblockParser();
  14908 
  14909    mPromise->MaybeResolve(aValue);
  14910  }
  14911 
  14912  void RejectedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue,
  14913                        ErrorResult& aRv) override {
  14914    MaybeUnblockParser();
  14915 
  14916    mPromise->MaybeReject(aValue);
  14917  }
  14918 
  14919 protected:
  14920  virtual ~UnblockParsingPromiseHandler() {
  14921    // If we're being cleaned up by the cycle collector, our mDocument reference
  14922    // may have been unlinked while our mParser weak reference is still alive.
  14923    if (mDocument) {
  14924      MaybeUnblockParser();
  14925    }
  14926  }
  14927 
  14928 private:
  14929  void MaybeUnblockParser() {
  14930    nsCOMPtr<nsIParser> parser = do_QueryReferent(mParser);
  14931    if (parser) {
  14932      MOZ_DIAGNOSTIC_ASSERT(mDocument);
  14933      nsCOMPtr<nsIParser> docParser = mDocument->CreatorParserOrNull();
  14934      if (parser == docParser) {
  14935        parser->UnblockParser();
  14936        parser->ContinueInterruptedParsingAsync();
  14937      }
  14938    }
  14939    if (mDocument) {
  14940      // We blocked DOMContentLoaded and load events on this document.  Unblock
  14941      // them.  Note that we want to do that no matter what's going on with the
  14942      // parser state for this document.  Maybe someone caused it to stop being
  14943      // parsed, so CreatorParserOrNull() is returning null, but we still want
  14944      // to unblock these.
  14945      mDocument->UnblockDOMContentLoaded();
  14946      mDocument->UnblockOnload(false);
  14947    }
  14948    mParser = nullptr;
  14949    mDocument = nullptr;
  14950  }
  14951 
  14952  nsWeakPtr mParser;
  14953  RefPtr<Promise> mPromise;
  14954  RefPtr<Document> mDocument;
  14955 };
  14956 
  14957 NS_IMPL_CYCLE_COLLECTION(UnblockParsingPromiseHandler, mDocument, mPromise)
  14958 
  14959 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(UnblockParsingPromiseHandler)
  14960  NS_INTERFACE_MAP_ENTRY(nsISupports)
  14961 NS_INTERFACE_MAP_END
  14962 
  14963 NS_IMPL_CYCLE_COLLECTING_ADDREF(UnblockParsingPromiseHandler)
  14964 NS_IMPL_CYCLE_COLLECTING_RELEASE(UnblockParsingPromiseHandler)
  14965 
  14966 void Document::SetForceNonNativeTheme(bool aForce) {
  14967  if (mForceNonNativeTheme == aForce) {
  14968    return;
  14969  }
  14970  mForceNonNativeTheme = aForce;
  14971  if (auto* pc = GetPresContext()) {
  14972    pc->MediaFeatureValuesChanged(
  14973        {MediaFeatureChangeReason::PreferenceChange},
  14974        MediaFeatureChangePropagation::JustThisDocument);
  14975  }
  14976 }
  14977 
  14978 already_AddRefed<Promise> Document::BlockParsing(
  14979    Promise& aPromise, const BlockParsingOptions& aOptions, ErrorResult& aRv) {
  14980  RefPtr<Promise> resultPromise =
  14981      Promise::Create(aPromise.GetParentObject(), aRv);
  14982  if (aRv.Failed()) {
  14983    return nullptr;
  14984  }
  14985 
  14986  RefPtr<PromiseNativeHandler> promiseHandler =
  14987      new UnblockParsingPromiseHandler(this, resultPromise, aOptions);
  14988  aPromise.AppendNativeHandler(promiseHandler);
  14989 
  14990  return resultPromise.forget();
  14991 }
  14992 
  14993 already_AddRefed<nsIURI> Document::GetMozDocumentURIIfNotForErrorPages() {
  14994  if (mFailedChannel) {
  14995    nsCOMPtr<nsIURI> failedURI;
  14996    if (NS_SUCCEEDED(mFailedChannel->GetURI(getter_AddRefs(failedURI)))) {
  14997      return failedURI.forget();
  14998    }
  14999  }
  15000 
  15001  nsCOMPtr<nsIURI> uri = GetDocumentURIObject();
  15002  if (!uri) {
  15003    return nullptr;
  15004  }
  15005 
  15006  return uri.forget();
  15007 }
  15008 
  15009 Promise* Document::GetDocumentReadyForIdle(ErrorResult& aRv) {
  15010  if (mIsGoingAway) {
  15011    aRv.Throw(NS_ERROR_NOT_AVAILABLE);
  15012    return nullptr;
  15013  }
  15014 
  15015  if (!mReadyForIdle) {
  15016    nsIGlobalObject* global = GetScopeObject();
  15017    if (!global) {
  15018      aRv.Throw(NS_ERROR_NOT_AVAILABLE);
  15019      return nullptr;
  15020    }
  15021 
  15022    mReadyForIdle = Promise::Create(global, aRv);
  15023    if (aRv.Failed()) {
  15024      return nullptr;
  15025    }
  15026  }
  15027 
  15028  return mReadyForIdle;
  15029 }
  15030 
  15031 void Document::MaybeResolveReadyForIdle() {
  15032  IgnoredErrorResult rv;
  15033  Promise* readyPromise = GetDocumentReadyForIdle(rv);
  15034  if (readyPromise) {
  15035    readyPromise->MaybeResolveWithUndefined();
  15036  }
  15037 }
  15038 
  15039 mozilla::dom::FeaturePolicy* Document::FeaturePolicy() const {
  15040  // The policy is created when the document is initialized. We _must_ have a
  15041  // policy here even if the featurePolicy pref is off. If this assertion fails,
  15042  // it means that ::FeaturePolicy() is called before ::StartDocumentLoad().
  15043  MOZ_ASSERT(mFeaturePolicy);
  15044  return mFeaturePolicy;
  15045 }
  15046 
  15047 nsIDOMXULCommandDispatcher* Document::GetCommandDispatcher() {
  15048  // Only chrome documents are allowed to use command dispatcher.
  15049  if (!nsContentUtils::IsChromeDoc(this)) {
  15050    return nullptr;
  15051  }
  15052  if (!mCommandDispatcher) {
  15053    // Create our command dispatcher and hook it up.
  15054    mCommandDispatcher = new nsXULCommandDispatcher(this);
  15055  }
  15056  return mCommandDispatcher;
  15057 }
  15058 
  15059 void Document::InitializeXULBroadcastManager() {
  15060  if (mXULBroadcastManager) {
  15061    return;
  15062  }
  15063  mXULBroadcastManager = new XULBroadcastManager(this);
  15064 }
  15065 
  15066 namespace {
  15067 
  15068 class DevToolsMutationObserver final : public nsStubMutationObserver {
  15069  NS_DECL_ISUPPORTS
  15070  NS_DECL_NSIMUTATIONOBSERVER_ATTRIBUTECHANGED
  15071  NS_DECL_NSIMUTATIONOBSERVER_CONTENTAPPENDED
  15072  NS_DECL_NSIMUTATIONOBSERVER_CONTENTINSERTED
  15073 
  15074  // We handle this in nsContentUtils::MaybeFireNodeRemoved, since devtools
  15075  // relies on the event firing _before_ the removal happens.
  15076  // NS_DECL_NSIMUTATIONOBSERVER_CONTENTREMOVED
  15077 
  15078  // NOTE(emilio, bug 1694627): DevTools doesn't seem to deal with character
  15079  // data changes right now (maybe intentionally?).
  15080  // NS_DECL_NSIMUTATIONOBSERVER_CHARACTERDATACHANGED
  15081 
  15082  DevToolsMutationObserver() = default;
  15083 
  15084 private:
  15085  void FireEvent(nsINode* aTarget, const nsAString& aType);
  15086 
  15087  ~DevToolsMutationObserver() = default;
  15088 };
  15089 
  15090 NS_IMPL_ISUPPORTS(DevToolsMutationObserver, nsIMutationObserver)
  15091 
  15092 void DevToolsMutationObserver::FireEvent(nsINode* aTarget,
  15093                                         const nsAString& aType) {
  15094  AsyncEventDispatcher::RunDOMEventWhenSafe(*aTarget, aType, CanBubble::eNo,
  15095                                            ChromeOnlyDispatch::eYes,
  15096                                            Composed::eYes);
  15097 }
  15098 
  15099 void DevToolsMutationObserver::AttributeChanged(Element* aElement,
  15100                                                int32_t aNamespaceID,
  15101                                                nsAtom* aAttribute, AttrModType,
  15102                                                const nsAttrValue* aOldValue) {
  15103  FireEvent(aElement, u"devtoolsattrmodified"_ns);
  15104 }
  15105 
  15106 void DevToolsMutationObserver::ContentAppended(nsIContent* aFirstNewContent,
  15107                                               const ContentAppendInfo& aInfo) {
  15108  for (nsIContent* c = aFirstNewContent; c; c = c->GetNextSibling()) {
  15109    ContentInserted(c, aInfo);
  15110  }
  15111 }
  15112 
  15113 void DevToolsMutationObserver::ContentInserted(nsIContent* aChild,
  15114                                               const ContentInsertInfo&) {
  15115  FireEvent(aChild, u"devtoolschildinserted"_ns);
  15116 }
  15117 
  15118 static StaticRefPtr<DevToolsMutationObserver> sDevToolsMutationObserver;
  15119 
  15120 }  // namespace
  15121 
  15122 void Document::SetDevToolsWatchingDOMMutations(bool aValue) {
  15123  if (mDevToolsWatchingDOMMutations == aValue || mIsGoingAway) {
  15124    return;
  15125  }
  15126  mDevToolsWatchingDOMMutations = aValue;
  15127  if (aValue) {
  15128    if (MOZ_UNLIKELY(!sDevToolsMutationObserver)) {
  15129      sDevToolsMutationObserver = new DevToolsMutationObserver();
  15130      ClearOnShutdown(&sDevToolsMutationObserver);
  15131    }
  15132    AddMutationObserver(sDevToolsMutationObserver);
  15133  } else if (sDevToolsMutationObserver) {
  15134    RemoveMutationObserver(sDevToolsMutationObserver);
  15135  }
  15136 }
  15137 
  15138 void EvaluateMediaQueryLists(nsTArray<RefPtr<MediaQueryList>>& aListsToNotify,
  15139                             Document& aDocument) {
  15140  if (nsPresContext* pc = aDocument.GetPresContext()) {
  15141    pc->FlushPendingMediaFeatureValuesChanged();
  15142  }
  15143 
  15144  for (MediaQueryList* mql : aDocument.MediaQueryLists()) {
  15145    if (mql->EvaluateOnRenderingUpdate()) {
  15146      aListsToNotify.AppendElement(mql);
  15147    }
  15148  }
  15149 }
  15150 
  15151 void Document::EvaluateMediaQueriesAndReportChanges() {
  15152  AutoTArray<RefPtr<MediaQueryList>, 32> mqls;
  15153  EvaluateMediaQueryLists(mqls, *this);
  15154  for (auto& mql : mqls) {
  15155    mql->FireChangeEvent();
  15156  }
  15157 }
  15158 
  15159 nsIHTMLCollection* Document::Children() {
  15160  if (!mChildrenCollection) {
  15161    mChildrenCollection =
  15162        new nsContentList(this, kNameSpaceID_Wildcard, nsGkAtoms::_asterisk,
  15163                          nsGkAtoms::_asterisk, false);
  15164  }
  15165 
  15166  return mChildrenCollection;
  15167 }
  15168 
  15169 uint32_t Document::ChildElementCount() { return Children()->Length(); }
  15170 
  15171 // Singleton class to manage the list of fullscreen documents which are the
  15172 // root of a branch which contains fullscreen documents. We maintain this list
  15173 // so that we can easily exit all windows from fullscreen when the user
  15174 // presses the escape key.
  15175 class FullscreenRoots {
  15176 public:
  15177  // Adds the root of given document to the manager. Calling this method
  15178  // with a document whose root is already contained has no effect.
  15179  static void Add(Document* aDoc);
  15180 
  15181  // Iterates over every root in the root list, and calls aFunction, passing
  15182  // each root once to aFunction. It is safe to call Add() and Remove() while
  15183  // iterating over the list (i.e. in aFunction). Documents that are removed
  15184  // from the manager during traversal are not traversed, and documents that
  15185  // are added to the manager during traversal are also not traversed.
  15186  static void ForEach(void (*aFunction)(Document* aDoc));
  15187 
  15188  // Removes the root of a specific document from the manager.
  15189  static void Remove(Document* aDoc);
  15190 
  15191  // Returns true if all roots added to the list have been removed.
  15192  static bool IsEmpty();
  15193 
  15194 private:
  15195  MOZ_COUNTED_DEFAULT_CTOR(FullscreenRoots)
  15196  MOZ_COUNTED_DTOR(FullscreenRoots)
  15197 
  15198  using RootsArray = nsTArray<WeakPtr<Document>>;
  15199 
  15200  // Returns true if aRoot is in the list of fullscreen roots.
  15201  static bool Contains(Document* aRoot);
  15202 
  15203  // Singleton instance of the FullscreenRoots. This is instantiated when a
  15204  // root is added, and it is deleted when the last root is removed.
  15205  static FullscreenRoots* sInstance;
  15206 
  15207  // List of weak pointers to roots.
  15208  RootsArray mRoots;
  15209 };
  15210 
  15211 FullscreenRoots* FullscreenRoots::sInstance = nullptr;
  15212 
  15213 /* static */
  15214 void FullscreenRoots::ForEach(void (*aFunction)(Document* aDoc)) {
  15215  if (!sInstance) {
  15216    return;
  15217  }
  15218  // Create a copy of the roots array, and iterate over the copy. This is so
  15219  // that if an element is removed from mRoots we don't mess up our iteration.
  15220  RootsArray roots(sInstance->mRoots.Clone());
  15221  // Call aFunction on all entries.
  15222  for (uint32_t i = 0; i < roots.Length(); i++) {
  15223    nsCOMPtr<Document> root(roots[i]);
  15224    // Check that the root isn't in the manager. This is so that new additions
  15225    // while we were running don't get traversed.
  15226    if (root && FullscreenRoots::Contains(root)) {
  15227      aFunction(root);
  15228    }
  15229  }
  15230 }
  15231 
  15232 /* static */
  15233 bool FullscreenRoots::Contains(Document* aRoot) {
  15234  return sInstance && sInstance->mRoots.Contains(aRoot);
  15235 }
  15236 
  15237 /* static */
  15238 void FullscreenRoots::Add(Document* aDoc) {
  15239  nsCOMPtr<Document> root =
  15240      nsContentUtils::GetInProcessSubtreeRootDocument(aDoc);
  15241  if (!FullscreenRoots::Contains(root)) {
  15242    if (!sInstance) {
  15243      sInstance = new FullscreenRoots();
  15244    }
  15245    sInstance->mRoots.AppendElement(root);
  15246  }
  15247 }
  15248 
  15249 /* static */
  15250 void FullscreenRoots::Remove(Document* aDoc) {
  15251  nsCOMPtr<Document> root =
  15252      nsContentUtils::GetInProcessSubtreeRootDocument(aDoc);
  15253  if (!sInstance || !sInstance->mRoots.RemoveElement(root)) {
  15254    NS_ERROR("Should only try to remove roots which are still added!");
  15255    return;
  15256  }
  15257  if (sInstance->mRoots.IsEmpty()) {
  15258    delete sInstance;
  15259    sInstance = nullptr;
  15260  }
  15261 }
  15262 
  15263 /* static */
  15264 bool FullscreenRoots::IsEmpty() { return !sInstance; }
  15265 
  15266 // Any fullscreen change waiting for the widget to finish transition
  15267 // is queued here. This is declared static instead of a member of
  15268 // Document because in the majority of time, there would be at most
  15269 // one document requesting or exiting fullscreen. We shouldn't waste
  15270 // the space to hold for it in every document.
  15271 class PendingFullscreenChangeList {
  15272 public:
  15273  PendingFullscreenChangeList() = delete;
  15274 
  15275  template <typename T>
  15276  static void Add(UniquePtr<T> aChange) {
  15277    sList.insertBack(aChange.release());
  15278  }
  15279 
  15280  static const FullscreenChange* GetLast() { return sList.getLast(); }
  15281 
  15282  enum IteratorOption {
  15283    // When we are committing fullscreen changes or preparing for
  15284    // that, we generally want to iterate all requests in the same
  15285    // window with eDocumentsWithSameRoot option.
  15286    eDocumentsWithSameRoot,
  15287    // If we are removing a document from the tree, we would only
  15288    // want to remove the requests from the given document and its
  15289    // descendants. For that case, use eInclusiveDescendants.
  15290    eInclusiveDescendants
  15291  };
  15292 
  15293  template <typename T>
  15294  class Iterator {
  15295   public:
  15296    explicit Iterator(Document* aDoc, IteratorOption aOption)
  15297        : mCurrent(PendingFullscreenChangeList::sList.getFirst()) {
  15298      if (mCurrent) {
  15299        if (aDoc->GetBrowsingContext()) {
  15300          mRootBCForIteration = aDoc->GetBrowsingContext();
  15301          if (aOption == eDocumentsWithSameRoot) {
  15302            BrowsingContext* bc =
  15303                GetParentIgnoreChromeBoundary(mRootBCForIteration);
  15304            while (bc) {
  15305              mRootBCForIteration = bc;
  15306              bc = GetParentIgnoreChromeBoundary(mRootBCForIteration);
  15307            }
  15308          }
  15309        }
  15310        SkipToNextMatch();
  15311      }
  15312    }
  15313 
  15314    UniquePtr<T> TakeAndNext() {
  15315      auto thisChange = TakeAndNextInternal();
  15316      SkipToNextMatch();
  15317      return thisChange;
  15318    }
  15319    bool AtEnd() const { return mCurrent == nullptr; }
  15320 
  15321   private:
  15322    static BrowsingContext* GetParentIgnoreChromeBoundary(
  15323        BrowsingContext* aBC) {
  15324      // Chrome BrowsingContexts are only available in the parent process, so if
  15325      // we're in a content process, we only worry about the context tree.
  15326      if (XRE_IsParentProcess()) {
  15327        return aBC->Canonical()->GetParentCrossChromeBoundary();
  15328      }
  15329      return aBC->GetParent();
  15330    }
  15331 
  15332    UniquePtr<T> TakeAndNextInternal() {
  15333      FullscreenChange* thisChange = mCurrent;
  15334      MOZ_ASSERT(thisChange->Type() == T::kType);
  15335      mCurrent = mCurrent->removeAndGetNext();
  15336      return WrapUnique(static_cast<T*>(thisChange));
  15337    }
  15338    void SkipToNextMatch() {
  15339      while (mCurrent) {
  15340        if (mCurrent->Type() == T::kType) {
  15341          BrowsingContext* bc = mCurrent->Document()->GetBrowsingContext();
  15342          if (!bc) {
  15343            // Always automatically drop fullscreen changes which are
  15344            // from a document detached from the doc shell.
  15345            UniquePtr<T> change = TakeAndNextInternal();
  15346            change->MayRejectPromise("Document is not active");
  15347            continue;
  15348          }
  15349          while (bc && bc != mRootBCForIteration) {
  15350            bc = GetParentIgnoreChromeBoundary(bc);
  15351          }
  15352          if (bc) {
  15353            break;
  15354          }
  15355        }
  15356        // The current one either don't have matched type, or isn't
  15357        // inside the given subtree, so skip this item.
  15358        mCurrent = mCurrent->getNext();
  15359      }
  15360    }
  15361 
  15362    FullscreenChange* mCurrent;
  15363    RefPtr<BrowsingContext> mRootBCForIteration;
  15364  };
  15365 
  15366 private:
  15367  static LinkedList<FullscreenChange> sList;
  15368 };
  15369 
  15370 /* static */
  15371 MOZ_RUNINIT LinkedList<FullscreenChange> PendingFullscreenChangeList::sList;
  15372 
  15373 size_t Document::CountFullscreenElements() const {
  15374  size_t count = 0;
  15375  for (const nsWeakPtr& ptr : mTopLayer) {
  15376    if (nsCOMPtr<Element> elem = do_QueryReferent(ptr)) {
  15377      if (elem->State().HasState(ElementState::FULLSCREEN)) {
  15378        count++;
  15379      }
  15380    }
  15381  }
  15382  return count;
  15383 }
  15384 
  15385 // https://github.com/whatwg/html/issues/9143
  15386 // We need to consider the precedence between active modal dialog, topmost auto
  15387 // popover and fullscreen element once it's specified.
  15388 // TODO: https://bugzilla.mozilla.org/show_bug.cgi?id=1859702 This can be
  15389 // removed after CloseWatcher has shipped.
  15390 void Document::HandleEscKey() {
  15391  for (const nsWeakPtr& weakPtr : Reversed(mTopLayer)) {
  15392    nsCOMPtr<Element> element(do_QueryReferent(weakPtr));
  15393    if (RefPtr popoverHTMLEl = nsGenericHTMLElement::FromNodeOrNull(element)) {
  15394      if (element->IsPopoverOpenedInMode(PopoverAttributeState::Auto)) {
  15395        popoverHTMLEl->HidePopover(IgnoreErrors());
  15396        return;
  15397      }
  15398    }
  15399    if (RefPtr dialogElement = HTMLDialogElement::FromNodeOrNull(element)) {
  15400      if (StaticPrefs::dom_dialog_light_dismiss_enabled()) {
  15401        if (dialogElement->GetClosedBy() != HTMLDialogElement::ClosedBy::None) {
  15402          const mozilla::dom::Optional<nsAString> returnValue;
  15403          dialogElement->RequestClose(returnValue);
  15404        }
  15405      } else {
  15406        dialogElement->QueueCancelDialog();
  15407      }
  15408      // If the dialog element's `closedby` attribute is "none", then this
  15409      // means the dialog is effectively blocking the Esc key from
  15410      // functioning. Returning without closing is the correct behaviour - as
  15411      // this is the topmost element "handling" the esc key press.
  15412      return;
  15413    }
  15414  }
  15415  // Not all dialogs exist in the top layer, so despite already iterating
  15416  // through all top layer elements we also need to check open dialogs that are
  15417  // _not_ open via the top-layer (showModal).
  15418  // The top-most dialog in mOpenDialogs may need to be closed.
  15419  if (RefPtr<HTMLDialogElement> dialog =
  15420          mOpenDialogs.SafeLastElement(nullptr)) {
  15421    if (dialog->GetClosedBy() != HTMLDialogElement::ClosedBy::None) {
  15422      MOZ_ASSERT(StaticPrefs::dom_dialog_light_dismiss_enabled(),
  15423                 "Light Dismiss must have been enabled for GetClosedBy() "
  15424                 "returns != ClosedBy::None");
  15425      const mozilla::dom::Optional<nsAString> returnValue;
  15426      dialog->RequestClose(returnValue);
  15427    }
  15428  }
  15429 }
  15430 
  15431 MOZ_CAN_RUN_SCRIPT void Document::ProcessCloseRequest() {
  15432  if (RefPtr win = GetInnerWindow()) {
  15433    if (win->IsFullyActive()) {
  15434      RefPtr manager = win->EnsureCloseWatcherManager();
  15435      manager->ProcessCloseRequest();
  15436    }
  15437  }
  15438 }
  15439 
  15440 already_AddRefed<Promise> Document::ExitFullscreen(ErrorResult& aRv) {
  15441  UniquePtr<FullscreenExit> exit = FullscreenExit::Create(this, aRv);
  15442  RefPtr<Promise> promise = exit->GetPromise();
  15443  RestorePreviousFullscreenState(std::move(exit));
  15444  return promise.forget();
  15445 }
  15446 
  15447 static void AskWindowToExitFullscreen(Document* aDoc) {
  15448  if (XRE_GetProcessType() == GeckoProcessType_Content) {
  15449    nsContentUtils::DispatchEventOnlyToChrome(
  15450        aDoc, aDoc, u"MozDOMFullscreen:Exit"_ns, CanBubble::eYes,
  15451        Cancelable::eNo, /* DefaultAction */ nullptr);
  15452  } else {
  15453    if (nsPIDOMWindowOuter* win = aDoc->GetWindow()) {
  15454      win->SetFullscreenInternal(FullscreenReason::ForFullscreenAPI, false);
  15455    }
  15456  }
  15457 }
  15458 
  15459 class nsCallExitFullscreen : public Runnable {
  15460 public:
  15461  explicit nsCallExitFullscreen(Document* aDoc)
  15462      : mozilla::Runnable("nsCallExitFullscreen"), mDoc(aDoc) {}
  15463 
  15464  NS_IMETHOD Run() final {
  15465    if (!mDoc) {
  15466      FullscreenRoots::ForEach(&AskWindowToExitFullscreen);
  15467    } else {
  15468      AskWindowToExitFullscreen(mDoc);
  15469    }
  15470    return NS_OK;
  15471  }
  15472 
  15473 private:
  15474  nsCOMPtr<Document> mDoc;
  15475 };
  15476 
  15477 /* static */
  15478 void Document::AsyncExitFullscreen(Document* aDoc) {
  15479  MOZ_RELEASE_ASSERT(NS_IsMainThread());
  15480  nsCOMPtr<nsIRunnable> exit = new nsCallExitFullscreen(aDoc);
  15481  NS_DispatchToCurrentThread(exit.forget());
  15482 }
  15483 
  15484 static uint32_t CountFullscreenSubDocuments(Document& aDoc) {
  15485  uint32_t count = 0;
  15486  // FIXME(emilio): Should this be recursive and dig into our nested subdocs?
  15487  aDoc.EnumerateSubDocuments([&count](Document& aSubDoc) {
  15488    if (aSubDoc.Fullscreen()) {
  15489      count++;
  15490    }
  15491    return CallState::Continue;
  15492  });
  15493  return count;
  15494 }
  15495 
  15496 bool Document::IsFullscreenLeaf() {
  15497  // A fullscreen leaf document is fullscreen, and has no fullscreen
  15498  // subdocuments.
  15499  //
  15500  // FIXME(emilio): This doesn't seem to account for fission iframes, is that
  15501  // ok?
  15502  return Fullscreen() && CountFullscreenSubDocuments(*this) == 0;
  15503 }
  15504 
  15505 static Document* GetFullscreenLeaf(Document& aDoc) {
  15506  if (aDoc.IsFullscreenLeaf()) {
  15507    return &aDoc;
  15508  }
  15509  if (!aDoc.Fullscreen()) {
  15510    return nullptr;
  15511  }
  15512  Document* leaf = nullptr;
  15513  aDoc.EnumerateSubDocuments([&leaf](Document& aSubDoc) {
  15514    leaf = GetFullscreenLeaf(aSubDoc);
  15515    return leaf ? CallState::Stop : CallState::Continue;
  15516  });
  15517  return leaf;
  15518 }
  15519 
  15520 static Document* GetFullscreenLeaf(Document* aDoc) {
  15521  if (Document* leaf = GetFullscreenLeaf(*aDoc)) {
  15522    return leaf;
  15523  }
  15524  // Otherwise we could be either in a non-fullscreen doc tree, or we're
  15525  // below the fullscreen doc. Start the search from the root.
  15526  Document* root = nsContentUtils::GetInProcessSubtreeRootDocument(aDoc);
  15527  return GetFullscreenLeaf(*root);
  15528 }
  15529 
  15530 static CallState ResetFullscreen(Document& aDocument) {
  15531  if (Element* fsElement = aDocument.GetUnretargetedFullscreenElement()) {
  15532    NS_ASSERTION(CountFullscreenSubDocuments(aDocument) <= 1,
  15533                 "Should have at most 1 fullscreen subdocument.");
  15534    aDocument.CleanupFullscreenState();
  15535    NS_ASSERTION(!aDocument.Fullscreen(), "Should reset fullscreen");
  15536    DispatchFullscreenChange(aDocument, fsElement);
  15537    aDocument.EnumerateSubDocuments(ResetFullscreen);
  15538  }
  15539  return CallState::Continue;
  15540 }
  15541 
  15542 // Since Document::ExitFullscreenInDocTree() could be called from
  15543 // Element::UnbindFromTree() where it is not safe to synchronously run
  15544 // script. This runnable is the script part of that function.
  15545 class ExitFullscreenScriptRunnable : public Runnable {
  15546 public:
  15547  explicit ExitFullscreenScriptRunnable(Document* aRoot, Document* aLeaf)
  15548      : mozilla::Runnable("ExitFullscreenScriptRunnable"),
  15549        mRoot(aRoot),
  15550        mLeaf(aLeaf) {}
  15551 
  15552  NS_IMETHOD Run() override {
  15553    // Dispatch MozDOMFullscreen:Exited to the original fullscreen leaf
  15554    // document since we want this event to follow the same path that
  15555    // MozDOMFullscreen:Entered was dispatched.
  15556    nsContentUtils::DispatchEventOnlyToChrome(
  15557        mLeaf, mLeaf, u"MozDOMFullscreen:Exited"_ns, CanBubble::eYes,
  15558        Cancelable::eNo, /* DefaultAction */ nullptr);
  15559    // Ensure the window exits fullscreen, as long as we don't have
  15560    // pending fullscreen requests.
  15561    if (nsPIDOMWindowOuter* win = mRoot->GetWindow()) {
  15562      if (!mRoot->HasPendingFullscreenRequests()) {
  15563        win->SetFullscreenInternal(FullscreenReason::ForForceExitFullscreen,
  15564                                   false);
  15565      }
  15566    }
  15567    return NS_OK;
  15568  }
  15569 
  15570 private:
  15571  nsCOMPtr<Document> mRoot;
  15572  nsCOMPtr<Document> mLeaf;
  15573 };
  15574 
  15575 /* static */
  15576 void Document::ExitFullscreenInDocTree(Document* aMaybeNotARootDoc) {
  15577  MOZ_ASSERT(aMaybeNotARootDoc);
  15578 
  15579  // Unlock the pointer
  15580  PointerLockManager::Unlock("Document::ExitFullscreenInDocTree");
  15581 
  15582  // Resolve all promises which waiting for exit fullscreen.
  15583  PendingFullscreenChangeList::Iterator<FullscreenExit> iter(
  15584      aMaybeNotARootDoc, PendingFullscreenChangeList::eDocumentsWithSameRoot);
  15585  while (!iter.AtEnd()) {
  15586    UniquePtr<FullscreenExit> exit = iter.TakeAndNext();
  15587    exit->MayResolvePromise();
  15588  }
  15589 
  15590  nsCOMPtr<Document> root = aMaybeNotARootDoc->GetFullscreenRoot();
  15591  if (!root || !root->Fullscreen()) {
  15592    // If a document was detached before exiting from fullscreen, it is
  15593    // possible that the root had left fullscreen state. In this case,
  15594    // we would not get anything from the ResetFullscreen() call. Root's
  15595    // not being a fullscreen doc also means the widget should have
  15596    // exited fullscreen state. It means even if we do not return here,
  15597    // we would actually do nothing below except crashing ourselves via
  15598    // dispatching the "MozDOMFullscreen:Exited" event to an nonexistent
  15599    // document.
  15600    return;
  15601  }
  15602 
  15603  // Record the fullscreen leaf document for MozDOMFullscreen:Exited.
  15604  // See ExitFullscreenScriptRunnable::Run for details. We have to
  15605  // record it here because we don't have such information after we
  15606  // reset the fullscreen state below.
  15607  Document* fullscreenLeaf = GetFullscreenLeaf(root);
  15608 
  15609  // Walk the tree of fullscreen documents, and reset their fullscreen state.
  15610  ResetFullscreen(*root);
  15611 
  15612  NS_ASSERTION(!root->Fullscreen(),
  15613               "Fullscreen root should no longer be a fullscreen doc...");
  15614 
  15615  // Move the top-level window out of fullscreen mode.
  15616  FullscreenRoots::Remove(root);
  15617 
  15618  nsContentUtils::AddScriptRunner(
  15619      new ExitFullscreenScriptRunnable(root, fullscreenLeaf));
  15620 }
  15621 
  15622 static void DispatchFullscreenNewOriginEvent(Document* aDoc) {
  15623  RefPtr<AsyncEventDispatcher> asyncDispatcher =
  15624      new AsyncEventDispatcher(aDoc, u"MozDOMFullscreen:NewOrigin"_ns,
  15625                               CanBubble::eYes, ChromeOnlyDispatch::eYes);
  15626  asyncDispatcher->PostDOMEvent();
  15627 }
  15628 
  15629 void Document::RestorePreviousFullscreenState(UniquePtr<FullscreenExit> aExit) {
  15630  NS_ASSERTION(!Fullscreen() || !FullscreenRoots::IsEmpty(),
  15631               "Should have at least 1 fullscreen root when fullscreen!");
  15632 
  15633  if (!GetWindow()) {
  15634    aExit->MayRejectPromise("No active window");
  15635    return;
  15636  }
  15637  if (!Fullscreen() || FullscreenRoots::IsEmpty()) {
  15638    aExit->MayRejectPromise("Not in fullscreen mode");
  15639    return;
  15640  }
  15641 
  15642  nsCOMPtr<Document> fullScreenDoc = GetFullscreenLeaf(this);
  15643  AutoTArray<Element*, 8> exitElements;
  15644 
  15645  Document* doc = fullScreenDoc;
  15646  // Collect all subdocuments.
  15647  for (; doc != this; doc = doc->GetInProcessParentDocument()) {
  15648    Element* fsElement = doc->GetUnretargetedFullscreenElement();
  15649    MOZ_ASSERT(fsElement,
  15650               "Parent document of "
  15651               "a fullscreen document without fullscreen element?");
  15652    exitElements.AppendElement(fsElement);
  15653  }
  15654  MOZ_ASSERT(doc == this, "Must have reached this doc");
  15655  // Collect all ancestor documents which we are going to change.
  15656  for (; doc; doc = doc->GetInProcessParentDocument()) {
  15657    Element* fsElement = doc->GetUnretargetedFullscreenElement();
  15658    MOZ_ASSERT(fsElement,
  15659               "Ancestor of fullscreen document must also be in fullscreen");
  15660    if (doc != this) {
  15661      if (auto* iframe = HTMLIFrameElement::FromNode(fsElement)) {
  15662        if (iframe->FullscreenFlag()) {
  15663          // If this is an iframe, and it explicitly requested
  15664          // fullscreen, don't rollback it automatically.
  15665          break;
  15666        }
  15667      }
  15668    }
  15669    exitElements.AppendElement(fsElement);
  15670    if (doc->CountFullscreenElements() > 1) {
  15671      break;
  15672    }
  15673  }
  15674 
  15675  Document* lastDoc = exitElements.LastElement()->OwnerDoc();
  15676  size_t fullscreenCount = lastDoc->CountFullscreenElements();
  15677  if (!lastDoc->GetInProcessParentDocument() && fullscreenCount == 1) {
  15678    // If we are fully exiting fullscreen, don't touch anything here,
  15679    // just wait for the window to get out from fullscreen first.
  15680    PendingFullscreenChangeList::Add(std::move(aExit));
  15681    AskWindowToExitFullscreen(this);
  15682    return;
  15683  }
  15684 
  15685  // If fullscreen mode is updated the pointer should be unlocked
  15686  PointerLockManager::Unlock("Document::RestorePreviousFullscreenState");
  15687  // All documents listed in the array except the last one are going to
  15688  // completely exit from the fullscreen state.
  15689  for (auto i : IntegerRange(exitElements.Length() - 1)) {
  15690    exitElements[i]->OwnerDoc()->CleanupFullscreenState();
  15691  }
  15692  // The last document will either rollback one fullscreen element, or
  15693  // completely exit from the fullscreen state as well.
  15694  Document* newFullscreenDoc;
  15695  if (fullscreenCount > 1) {
  15696    DebugOnly<bool> removedFullscreenElement = lastDoc->PopFullscreenElement();
  15697    MOZ_ASSERT(removedFullscreenElement);
  15698    newFullscreenDoc = lastDoc;
  15699  } else {
  15700    lastDoc->CleanupFullscreenState();
  15701    newFullscreenDoc = lastDoc->GetInProcessParentDocument();
  15702  }
  15703  // Dispatch the fullscreenchange event to all document listed. Note
  15704  // that the loop order is reversed so that events are dispatched in
  15705  // the tree order as indicated in the spec.
  15706  for (Element* e : Reversed(exitElements)) {
  15707    DispatchFullscreenChange(*e->OwnerDoc(), e);
  15708  }
  15709  aExit->MayResolvePromise();
  15710 
  15711  MOZ_ASSERT(newFullscreenDoc,
  15712             "If we were going to exit from fullscreen on "
  15713             "all documents in this doctree, we should've asked the window to "
  15714             "exit first instead of reaching here.");
  15715  if (fullScreenDoc != newFullscreenDoc &&
  15716      !nsContentUtils::HaveEqualPrincipals(fullScreenDoc, newFullscreenDoc)) {
  15717    // We've popped so enough off the stack that we've rolled back to
  15718    // a fullscreen element in a parent document. If this document is
  15719    // cross origin, dispatch an event to chrome so it knows to show
  15720    // the warning UI.
  15721    DispatchFullscreenNewOriginEvent(newFullscreenDoc);
  15722  }
  15723 }
  15724 
  15725 static void UpdateViewportScrollbarOverrideForFullscreen(Document* aDoc) {
  15726  if (nsPresContext* presContext = aDoc->GetPresContext()) {
  15727    presContext->UpdateViewportScrollStylesOverride();
  15728  }
  15729 }
  15730 
  15731 static void NotifyFullScreenChangedForMediaElement(Element& aElement) {
  15732  // When a media element enters the fullscreen, we would like to notify that
  15733  // to the media controller in order to update its status.
  15734  if (auto* mediaElem = HTMLMediaElement::FromNode(aElement)) {
  15735    mediaElem->NotifyFullScreenChanged();
  15736  }
  15737 }
  15738 
  15739 void Document::CleanupFullscreenState() {
  15740  while (PopFullscreenElement(UpdateViewport::No)) {
  15741    // Remove the next one if appropriate
  15742  }
  15743 
  15744  UpdateViewportScrollbarOverrideForFullscreen(this);
  15745  mFullscreenRoot = nullptr;
  15746 
  15747  // Restore the zoom level that was in place prior to entering fullscreen.
  15748  if (PresShell* presShell = GetPresShell()) {
  15749    if (presShell->GetMobileViewportManager()) {
  15750      presShell->SetResolutionAndScaleTo(
  15751          mSavedResolution, ResolutionChangeOrigin::MainThreadRestore);
  15752    }
  15753  }
  15754 }
  15755 
  15756 bool Document::PopFullscreenElement(UpdateViewport aUpdateViewport) {
  15757  Element* removedElement = TopLayerPop([](Element* element) -> bool {
  15758    return element->State().HasState(ElementState::FULLSCREEN);
  15759  });
  15760 
  15761  if (!removedElement) {
  15762    return false;
  15763  }
  15764 
  15765  MOZ_ASSERT(removedElement->State().HasState(ElementState::FULLSCREEN));
  15766  removedElement->RemoveStates(ElementState::FULLSCREEN | ElementState::MODAL);
  15767  NotifyFullScreenChangedForMediaElement(*removedElement);
  15768  // Reset iframe fullscreen flag.
  15769  if (auto* iframe = HTMLIFrameElement::FromNode(removedElement)) {
  15770    iframe->SetFullscreenFlag(false);
  15771  }
  15772  if (aUpdateViewport == UpdateViewport::Yes) {
  15773    UpdateViewportScrollbarOverrideForFullscreen(this);
  15774  }
  15775  return true;
  15776 }
  15777 
  15778 void Document::SetFullscreenElement(Element& aElement) {
  15779  ElementState statesToAdd = ElementState::FULLSCREEN;
  15780  if (!IsInChromeDocShell()) {
  15781    // Don't make the document modal in chrome documents, since we don't want
  15782    // the browser UI like the context menu / etc to be inert.
  15783    statesToAdd |= ElementState::MODAL;
  15784  }
  15785  aElement.AddStates(statesToAdd);
  15786  TopLayerPush(aElement);
  15787  NotifyFullScreenChangedForMediaElement(aElement);
  15788  UpdateViewportScrollbarOverrideForFullscreen(this);
  15789 }
  15790 
  15791 void Document::TopLayerPush(Element& aElement) {
  15792  const bool modal = aElement.State().HasState(ElementState::MODAL);
  15793 
  15794  TopLayerPop(aElement);
  15795  if (nsIFrame* f = aElement.GetPrimaryFrame()) {
  15796    f->MarkNeedsDisplayItemRebuild();
  15797  }
  15798 
  15799  mTopLayer.AppendElement(do_GetWeakReference(&aElement));
  15800  NS_ASSERTION(GetTopLayerTop() == &aElement, "Should match");
  15801 
  15802  if (modal) {
  15803    aElement.AddStates(ElementState::TOPMOST_MODAL);
  15804 
  15805    bool foundExistingModalElement = false;
  15806    for (const nsWeakPtr& weakPtr : Reversed(mTopLayer)) {
  15807      nsCOMPtr<Element> element(do_QueryReferent(weakPtr));
  15808      if (element && element != &aElement &&
  15809          element->State().HasState(ElementState::TOPMOST_MODAL)) {
  15810        element->RemoveStates(ElementState::TOPMOST_MODAL);
  15811        foundExistingModalElement = true;
  15812        break;
  15813      }
  15814    }
  15815 
  15816    if (!foundExistingModalElement) {
  15817      Element* root = GetRootElement();
  15818      MOZ_RELEASE_ASSERT(root, "top layer element outside of document?");
  15819      if (&aElement != root) {
  15820        // Add inert to the root element so that the inertness is applied to the
  15821        // entire document.
  15822        root->AddStates(ElementState::INERT);
  15823      }
  15824    }
  15825  }
  15826 }
  15827 
  15828 void Document::AddModalDialog(HTMLDialogElement& aDialogElement) {
  15829  aDialogElement.AddStates(ElementState::MODAL);
  15830  TopLayerPush(aDialogElement);
  15831 }
  15832 
  15833 void Document::RemoveModalDialog(HTMLDialogElement& aDialogElement) {
  15834  DebugOnly<Element*> removedElement = TopLayerPop(aDialogElement);
  15835  MOZ_ASSERT(removedElement == &aDialogElement);
  15836  aDialogElement.RemoveStates(ElementState::MODAL);
  15837 }
  15838 
  15839 void Document::AddOpenDialog(HTMLDialogElement& aElement) {
  15840  MOZ_ASSERT(aElement.IsInComposedDoc(),
  15841             "Disconnected Dialogs shouldn't go in Open Dialogs list");
  15842  MOZ_ASSERT(!mOpenDialogs.Contains(&aElement),
  15843             "Dialog already in Open Dialogs list!");
  15844  mOpenDialogs.AppendElement(&aElement);
  15845 }
  15846 
  15847 void Document::RemoveOpenDialog(HTMLDialogElement& aElement) {
  15848  mOpenDialogs.RemoveElement(&aElement);
  15849 }
  15850 
  15851 void Document::SetLastDialogPointerdownTarget(HTMLDialogElement& aElement) {
  15852  mLastDialogPointerdownTarget = do_GetWeakReference(&aElement);
  15853 }
  15854 
  15855 HTMLDialogElement* Document::GetLastDialogPointerdownTarget() {
  15856  nsCOMPtr<Element> element(do_QueryReferent(mLastDialogPointerdownTarget));
  15857  return HTMLDialogElement::FromNodeOrNull(element);
  15858 }
  15859 
  15860 bool Document::HasOpenDialogs() const { return !mOpenDialogs.IsEmpty(); }
  15861 
  15862 HTMLDialogElement* Document::GetTopMostOpenDialog() {
  15863  return mOpenDialogs.SafeLastElement(nullptr);
  15864 }
  15865 
  15866 bool Document::DialogIsInOpenDialogsList(HTMLDialogElement& aDialog) {
  15867  return mOpenDialogs.Contains(&aDialog);
  15868 }
  15869 
  15870 Element* Document::TopLayerPop(FunctionRef<bool(Element*)> aPredicate) {
  15871  if (mTopLayer.IsEmpty()) {
  15872    return nullptr;
  15873  }
  15874 
  15875  // Remove the topmost element that qualifies aPredicate; This
  15876  // is required is because the top layer contains not only
  15877  // fullscreen elements, but also dialog elements.
  15878  Element* removedElement = nullptr;
  15879  for (auto i : Reversed(IntegerRange(mTopLayer.Length()))) {
  15880    nsCOMPtr<Element> element(do_QueryReferent(mTopLayer[i]));
  15881    if (element && aPredicate(element)) {
  15882      removedElement = element;
  15883      if (nsIFrame* f = element->GetPrimaryFrame()) {
  15884        f->MarkNeedsDisplayItemRebuild();
  15885      }
  15886      mTopLayer.RemoveElementAt(i);
  15887      break;
  15888    }
  15889  }
  15890 
  15891  // Pop from the stack null elements (references to elements which have
  15892  // been GC'd since they were added to the stack) and elements which are
  15893  // no longer in this document.
  15894  //
  15895  // FIXME(emilio): If this loop does something, it'd violate the assertions
  15896  // from GetTopLayerTop()... What gives?
  15897  while (!mTopLayer.IsEmpty()) {
  15898    Element* element = GetTopLayerTop();
  15899    if (!element || element->GetComposedDoc() != this) {
  15900      if (element) {
  15901        if (nsIFrame* f = element->GetPrimaryFrame()) {
  15902          f->MarkNeedsDisplayItemRebuild();
  15903        }
  15904      }
  15905 
  15906      mTopLayer.RemoveLastElement();
  15907    } else {
  15908      // The top element of the stack is now an in-doc element. Return here.
  15909      break;
  15910    }
  15911  }
  15912 
  15913  if (!removedElement) {
  15914    return nullptr;
  15915  }
  15916 
  15917  const bool modal = removedElement->State().HasState(ElementState::MODAL);
  15918 
  15919  if (modal) {
  15920    removedElement->RemoveStates(ElementState::TOPMOST_MODAL);
  15921    bool foundExistingModalElement = false;
  15922    for (const nsWeakPtr& weakPtr : Reversed(mTopLayer)) {
  15923      nsCOMPtr<Element> element(do_QueryReferent(weakPtr));
  15924      if (element && element->State().HasState(ElementState::MODAL)) {
  15925        element->AddStates(ElementState::TOPMOST_MODAL);
  15926        foundExistingModalElement = true;
  15927        break;
  15928      }
  15929    }
  15930    // No more modal elements, make the document not inert anymore.
  15931    if (!foundExistingModalElement) {
  15932      Element* root = GetRootElement();
  15933      if (root && !root->GetBoolAttr(nsGkAtoms::inert)) {
  15934        root->RemoveStates(ElementState::INERT);
  15935      }
  15936    }
  15937  }
  15938 
  15939  return removedElement;
  15940 }
  15941 
  15942 Element* Document::TopLayerPop(Element& aElement) {
  15943  auto predictFunc = [&aElement](Element* element) {
  15944    return element == &aElement;
  15945  };
  15946  return TopLayerPop(predictFunc);
  15947 }
  15948 
  15949 void Document::GetWireframe(bool aIncludeNodes,
  15950                            Nullable<Wireframe>& aWireframe) {
  15951  FlushPendingNotifications(FlushType::Layout);
  15952  GetWireframeWithoutFlushing(aIncludeNodes, aWireframe);
  15953 }
  15954 
  15955 void Document::GetWireframeWithoutFlushing(bool aIncludeNodes,
  15956                                           Nullable<Wireframe>& aWireframe) {
  15957  using FrameForPointOptions = nsLayoutUtils::FrameForPointOptions;
  15958  using FrameForPointOption = nsLayoutUtils::FrameForPointOption;
  15959 
  15960  PresShell* shell = GetPresShell();
  15961  if (!shell) {
  15962    return;
  15963  }
  15964 
  15965  nsPresContext* pc = shell->GetPresContext();
  15966  if (!pc) {
  15967    return;
  15968  }
  15969 
  15970  nsIFrame* rootFrame = shell->GetRootFrame();
  15971  if (!rootFrame) {
  15972    return;
  15973  }
  15974 
  15975  auto& wireframe = aWireframe.SetValue();
  15976  wireframe.mCanvasBackground =
  15977      shell->ComputeCanvasBackground().mViewport.mColor;
  15978 
  15979  FrameForPointOptions options;
  15980  options.mBits += FrameForPointOption::IgnoreCrossDoc;
  15981  options.mBits += FrameForPointOption::IgnorePaintSuppression;
  15982  options.mBits += FrameForPointOption::OnlyVisible;
  15983 
  15984  AutoTArray<nsIFrame*, 32> frames;
  15985  const RelativeTo relativeTo{rootFrame, mozilla::ViewportType::Layout};
  15986  nsLayoutUtils::GetFramesForArea(relativeTo, pc->GetVisibleArea(), frames,
  15987                                  options);
  15988 
  15989  // TODO(emilio): We could rewrite hit testing to return nsDisplayItem*s or
  15990  // something perhaps, but seems hard / like it'd involve at least some extra
  15991  // copying around, since they don't outlive GetFramesForArea.
  15992  auto& rects = wireframe.mRects.Construct();
  15993  if (!rects.SetCapacity(frames.Length(), fallible)) {
  15994    return;
  15995  }
  15996  for (nsIFrame* frame : Reversed(frames)) {
  15997    auto [rectColor,
  15998          rectType] = [&]() -> std::tuple<nscolor, WireframeRectType> {
  15999      if (frame->IsTextFrame()) {
  16000        return {frame->StyleText()->mWebkitTextFillColor.CalcColor(frame),
  16001                WireframeRectType::Text};
  16002      }
  16003      if (frame->IsImageFrame() || frame->IsSVGOuterSVGFrame()) {
  16004        return {0, WireframeRectType::Image};
  16005      }
  16006      if (frame->IsThemed()) {
  16007        return {0, WireframeRectType::Background};
  16008      }
  16009      bool drawImage = false;
  16010      bool drawColor = false;
  16011      if (const auto* bgStyle = nsCSSRendering::FindBackground(frame)) {
  16012        const nscolor color = nsCSSRendering::DetermineBackgroundColor(
  16013            pc, bgStyle, frame, drawImage, drawColor);
  16014        if (drawImage &&
  16015            !bgStyle->StyleBackground()->mImage.BottomLayer().mImage.IsNone()) {
  16016          return {color, WireframeRectType::Image};
  16017        }
  16018        if (drawColor && !frame->IsCanvasFrame()) {
  16019          // Canvas frame background already accounted for in mCanvasBackground.
  16020          return {color, WireframeRectType::Background};
  16021        }
  16022      }
  16023      return {0, WireframeRectType::Unknown};
  16024    }();
  16025 
  16026    if (rectType == WireframeRectType::Unknown) {
  16027      continue;
  16028    }
  16029 
  16030    const auto r =
  16031        CSSRect::FromAppUnits(nsLayoutUtils::TransformFrameRectToAncestor(
  16032            frame, frame->GetRectRelativeToSelf(), relativeTo));
  16033    if ((uint32_t)r.Area() <
  16034        StaticPrefs::browser_history_wireframeAreaThreshold()) {
  16035      continue;
  16036    }
  16037 
  16038    // Can't really fail because SetCapacity succeeded.
  16039    auto& taggedRect = *rects.AppendElement(fallible);
  16040 
  16041    if (aIncludeNodes) {
  16042      if (nsIContent* c = frame->GetContent()) {
  16043        taggedRect.mNode.Construct(c);
  16044      }
  16045    }
  16046    taggedRect.mX = r.x;
  16047    taggedRect.mY = r.y;
  16048    taggedRect.mWidth = r.width;
  16049    taggedRect.mHeight = r.height;
  16050    taggedRect.mColor = rectColor;
  16051    taggedRect.mType.Construct(rectType);
  16052  }
  16053 }
  16054 
  16055 Element* Document::GetTopLayerTop() {
  16056  if (mTopLayer.IsEmpty()) {
  16057    return nullptr;
  16058  }
  16059  uint32_t last = mTopLayer.Length() - 1;
  16060  nsCOMPtr<Element> element(do_QueryReferent(mTopLayer[last]));
  16061  NS_ASSERTION(element, "Should have a top layer element!");
  16062  NS_ASSERTION(element->IsInComposedDoc(),
  16063               "Top layer element should be in doc");
  16064  NS_ASSERTION(element->OwnerDoc() == this,
  16065               "Top layer element should be in this doc");
  16066  return element;
  16067 }
  16068 
  16069 Element* Document::GetUnretargetedFullscreenElement() const {
  16070  for (const nsWeakPtr& weakPtr : Reversed(mTopLayer)) {
  16071    nsCOMPtr<Element> element(do_QueryReferent(weakPtr));
  16072    // Per spec, the fullscreen element is the topmost element in the document’s
  16073    // top layer whose fullscreen flag is set, if any, and null otherwise.
  16074    if (element && element->State().HasState(ElementState::FULLSCREEN)) {
  16075      return element;
  16076    }
  16077  }
  16078  return nullptr;
  16079 }
  16080 
  16081 nsTArray<Element*> Document::GetTopLayer() const {
  16082  nsTArray<Element*> elements;
  16083  for (const nsWeakPtr& ptr : mTopLayer) {
  16084    if (nsCOMPtr<Element> elem = do_QueryReferent(ptr)) {
  16085      elements.AppendElement(elem);
  16086    }
  16087  }
  16088  return elements;
  16089 }
  16090 
  16091 bool Document::TopLayerContains(Element& aElement) const {
  16092  if (mTopLayer.IsEmpty()) {
  16093    return false;
  16094  }
  16095  nsWeakPtr weakElement = do_GetWeakReference(&aElement);
  16096  return mTopLayer.Contains(weakElement);
  16097 }
  16098 
  16099 // https://html.spec.whatwg.org/#close-entire-popover-list
  16100 void Document::CloseEntirePopoverList(PopoverAttributeState aMode,
  16101                                      bool aFocusPreviousElement,
  16102                                      bool aFireEvents) {
  16103  // 1. While popoverList is not empty:
  16104  // XXX: Rather than computing the list, find from top layer elements
  16105  while (RefPtr popover = GetTopmostPopoverOf(aMode)) {
  16106    // 1.1. Run the hide popover algorithm given popoverList's last item,
  16107    // focusPreviousElement, fireEvents, false, and null.
  16108    HidePopover(*popover, aFocusPreviousElement, aFireEvents,
  16109                /* aSource */ nullptr, IgnoreErrors());
  16110  }
  16111 }
  16112 
  16113 // https://html.spec.whatwg.org/#hide-all-popovers-until
  16114 void Document::HideAllPopoversUntil(nsINode& aEndpoint,
  16115                                    bool aFocusPreviousElement,
  16116                                    bool aFireEvents) {
  16117  const auto* endpointHTMLEl = nsGenericHTMLElement::FromNodeOrNull(&aEndpoint);
  16118 
  16119  // 1. If endpoint is an HTML element and endpoint is not in the popover
  16120  // showing state, then return.
  16121  if (endpointHTMLEl && !endpointHTMLEl->IsPopoverOpen()) {
  16122    return;
  16123  }
  16124 
  16125  // 2. Let document be endpoint's node document.
  16126  MOZ_ASSERT(aEndpoint.OwnerDoc() == this);
  16127  // 3. Assert: endpoint is a Document or endpoint's popover visibility state is
  16128  // showing.
  16129  // 4. Assert: endpoint is a Document or endpoint's popover attribute is in the
  16130  // Auto state or endpoint's popover attribute is in the Hint state.
  16131  // todo(keithamus): Implement this
  16132 
  16133  // 5. If endpoint is a Document:
  16134  if (&aEndpoint == this) {
  16135    // 5.1. Run close entire popover list given document's showing hint popover
  16136    // list, focusPreviousElement, and fireEvents.
  16137    // 5.2. Run close entire popover list given document's showing auto popover
  16138    // list, focusPreviousElement, and fireEvents.
  16139    CloseEntirePopoverList(PopoverAttributeState::Auto, aFocusPreviousElement,
  16140                           aFireEvents);
  16141    // 5.3. Return.
  16142    return;
  16143  }
  16144 
  16145  // 6. If document's showing hint popover list contains endpoint:
  16146  // 6.1. Assert: endpoint's popover attribute is in the Hint state.
  16147  // 6.2. Run hide popover stack until given endpoint, document's showing hint
  16148  // popover list, focusPreviousElement, and fireEvents.
  16149  // 6.3. Return.
  16150  // todo(keithamus): Implement this
  16151 
  16152  // 7. Run close entire popover list given document's showing hint popover
  16153  // list, focusPreviousElement, and fireEvents.
  16154  // 8. If document's showing auto popover list does not contain endpoint, then
  16155  // return.
  16156 
  16157  // 9. Run hide popover stack until given endpoint, document's showing auto
  16158  // popover list, focusPreviousElement, and fireEvents.
  16159  HidePopoverStackUntil(PopoverAttributeState::Auto, aEndpoint,
  16160                        aFocusPreviousElement, aFireEvents);
  16161 }
  16162 
  16163 // https://html.spec.whatwg.org/#hide-popover-stack-until
  16164 void Document::HidePopoverStackUntil(PopoverAttributeState aMode,
  16165                                     nsINode& aEndpoint,
  16166                                     bool aFocusPreviousElement,
  16167                                     bool aFireEvents) {
  16168  auto needRepeatingHide = [&]() {
  16169    auto autoList = PopoverListOf(aMode);
  16170    return autoList.Contains(&aEndpoint) &&
  16171           &aEndpoint != autoList.LastElement();
  16172  };
  16173 
  16174  // 1. Let repeatingHide be false.
  16175  bool repeatingHide = false;
  16176  bool fireEvents = aFireEvents;
  16177 
  16178  // 2. Perform the following steps at least once:
  16179  do {
  16180    // 2.1. Let lastToHide be null.
  16181    RefPtr<const Element> lastToHide = nullptr;
  16182    bool foundEndpoint = false;
  16183    // 2.2. For each popover in popoverList:
  16184    for (const Element* popover : PopoverListOf(aMode)) {
  16185      // 2.2.1. If popover is endpoint, then break.
  16186      // todo(keithamus): Get this logic closer to spec.
  16187      if (popover == &aEndpoint) {
  16188        foundEndpoint = true;
  16189      } else if (foundEndpoint) {
  16190        // 2.2.2. Set lastToHide to popover.
  16191        lastToHide = popover;
  16192        break;
  16193      }
  16194    }
  16195 
  16196    // 2.3. If lastToHide is null, then return.
  16197    if (!foundEndpoint) {
  16198      CloseEntirePopoverList(PopoverAttributeState::Auto, aFocusPreviousElement,
  16199                             fireEvents);
  16200      return;
  16201    }
  16202 
  16203    // 2.4. While lastToHide's popover visibility state is showing:
  16204    while (lastToHide && lastToHide->IsPopoverOpen()) {
  16205      // 2.4.1. Assert: popoverList is not empty.
  16206      // todo(keithamus): Assert
  16207 
  16208      // 2.4.2. Run the hide popover algorithm given the last item in
  16209      // popoverList, focusPreviousElement, fireEvents, false, and null.
  16210      RefPtr<Element> topmost =
  16211          GetTopmostPopoverOf(PopoverAttributeState::Auto);
  16212      if (!topmost) {
  16213        break;
  16214      }
  16215      HidePopover(*topmost, aFocusPreviousElement, fireEvents,
  16216                  /* aSource */ nullptr, IgnoreErrors());
  16217    }
  16218 
  16219    // 2.5. Assert: repeatingHide is false or popoverList's last item is
  16220    // endpoint.
  16221    // todo(keithamus): Assert
  16222 
  16223    // 2.6. Set repeatingHide to true if popoverList contains endpoint and
  16224    // popoverList's last item is not endpoint, otherwise false.
  16225    repeatingHide = needRepeatingHide();
  16226    // 2.7. If repeatingHide is true, then set fireEvents to false.
  16227    if (repeatingHide) {
  16228      fireEvents = false;
  16229    }
  16230    // ... and keep performing them while repeatingHide is true.
  16231  } while (repeatingHide);
  16232 }
  16233 
  16234 // https://html.spec.whatwg.org/#hide-popover-algorithm
  16235 void Document::HidePopover(Element& aPopover, bool aFocusPreviousElement,
  16236                           bool aFireEvents, Element* aSource,
  16237                           ErrorResult& aRv) {
  16238  RefPtr<nsGenericHTMLElement> popoverHTMLEl =
  16239      nsGenericHTMLElement::FromNode(aPopover);
  16240  NS_ASSERTION(popoverHTMLEl, "Not a HTML element");
  16241 
  16242  // 1. If the result of running check popover validity given element, true,
  16243  // throwExceptions, and null is false, then return.
  16244  if (!popoverHTMLEl->CheckPopoverValidity(PopoverVisibilityState::Showing,
  16245                                           nullptr, aRv)) {
  16246    return;
  16247  }
  16248 
  16249  // 2. Let document be element's node document.
  16250 
  16251  // 3. Let nestedHide be element's popover showing or hiding.
  16252  bool wasShowingOrHiding =
  16253      popoverHTMLEl->GetPopoverData()->IsShowingOrHiding();
  16254 
  16255  // 4. Set element's popover showing or hiding to true.
  16256  popoverHTMLEl->GetPopoverData()->SetIsShowingOrHiding(true);
  16257 
  16258  // 5. If nestedHide is true, then set fireEvents to false.
  16259  const bool fireEvents = aFireEvents && !wasShowingOrHiding;
  16260 
  16261  // 6. Let cleanupSteps be the following steps:
  16262  auto cleanupHidingFlag = MakeScopeExit([&]() {
  16263    if (auto* popoverData = popoverHTMLEl->GetPopoverData()) {
  16264      // 6.1. If nestedHide is false, then set element's popover showing or
  16265      // hiding to false.
  16266      popoverData->SetIsShowingOrHiding(wasShowingOrHiding);
  16267      // 6.2. If element's popover close watcher is not null, then:
  16268      // 6.2.1. Destroy element's popover close watcher.
  16269      // 6.2.2. Set element's popover close watcher to null.
  16270      popoverData->DestroyCloseWatcher();
  16271    }
  16272  });
  16273 
  16274  PopoverData* popoverData = popoverHTMLEl->GetPopoverData();
  16275 
  16276  // 7. If element's opened in popover mode is "auto" or "hint", then:
  16277  if (popoverData &&
  16278      popoverData->GetOpenedInMode() == PopoverAttributeState::Auto) {
  16279    // 7.1. Run hide all popovers until given element, focusPreviousElement, and
  16280    // fireEvents.
  16281    HideAllPopoversUntil(*popoverHTMLEl, aFocusPreviousElement, fireEvents);
  16282 
  16283    // 7.2. If the result of running check popover validity given element, true,
  16284    // and throwExceptions is false, then run cleanupSteps and return.
  16285    if (!popoverHTMLEl->CheckPopoverValidity(PopoverVisibilityState::Showing,
  16286                                             nullptr, aRv)) {
  16287      return;
  16288    }
  16289 
  16290    // TODO: we can't always guarantee:
  16291    // The last item in document's auto popover list is popoverHTMLEl.
  16292    // See, https://github.com/whatwg/html/issues/9197
  16293    // If popoverHTMLEl is not on top, hide popovers again without firing
  16294    // events.
  16295    if (NS_WARN_IF(GetTopmostPopoverOf(PopoverAttributeState::Auto) !=
  16296                   popoverHTMLEl)) {
  16297      HideAllPopoversUntil(*popoverHTMLEl, aFocusPreviousElement, false);
  16298      if (!popoverHTMLEl->CheckPopoverValidity(PopoverVisibilityState::Showing,
  16299                                               nullptr, aRv)) {
  16300        return;
  16301      }
  16302      MOZ_ASSERT(
  16303          GetTopmostPopoverOf(PopoverAttributeState::Auto) == popoverHTMLEl,
  16304          "popoverHTMLEl should be on top of auto popover list");
  16305    }
  16306  }
  16307 
  16308  auto* data = popoverHTMLEl->GetPopoverData();
  16309  MOZ_ASSERT(data, "Should have popover data");
  16310 
  16311  // 9. If fireEvents is true:
  16312  // Fire beforetoggle event and re-check popover validity.
  16313  if (fireEvents) {
  16314    // 9.1. Fire an event named beforetoggle, using ToggleEvent, with the
  16315    // oldState attribute initialized to "open" and the newState attribute
  16316    // initialized to "closed" at element. Intentionally ignore the return value
  16317    // here as only on open event for beforetoggle the cancelable attribute is
  16318    // initialized to true.
  16319    popoverHTMLEl->FireToggleEvent(u"open"_ns, u"closed"_ns, u"beforetoggle"_ns,
  16320                                   aSource);
  16321 
  16322    // 9.2. If autoPopoverListContainsElement is true and document's showing
  16323    // auto popover list's last item is not element, then run hide all popovers
  16324    // until given element, focusPreviousElement, and false. Hide all popovers
  16325    // when beforetoggle shows a popover.
  16326    if (popoverHTMLEl->IsPopoverOpenedInMode(PopoverAttributeState::Auto) &&
  16327        GetTopmostPopoverOf(PopoverAttributeState::Auto) != popoverHTMLEl) {
  16328      HideAllPopoversUntil(*popoverHTMLEl, aFocusPreviousElement, false);
  16329    }
  16330 
  16331    // 9.3. If the result of running check popover validity given element, true,
  16332    // throwExceptions, and null is false, then run cleanupSteps and return.
  16333    if (!popoverHTMLEl->CheckPopoverValidity(PopoverVisibilityState::Showing,
  16334                                             nullptr, aRv)) {
  16335      return;
  16336    }
  16337 
  16338    // 9.4. XXX: See below
  16339 
  16340    // 9.5. Set element's implicit anchor element to null.
  16341    data->SetInvoker(nullptr);
  16342  }
  16343 
  16344  // 9.4. Request an element to be removed from the top layer given element.
  16345  // 10. Otherwise, remove an element from the top layer immediately given
  16346  // element.
  16347  RemovePopoverFromTopLayer(aPopover);
  16348 
  16349  // 11. Set element's popover invoker to null.
  16350  data->SetInvoker(nullptr);
  16351 
  16352  // 12. Set element's opened in popover mode to null.
  16353  popoverHTMLEl->GetPopoverData()->SetOpenedInMode(PopoverAttributeState::None);
  16354 
  16355  // 13. Set element's popover visibility state to hidden.
  16356  popoverHTMLEl->PopoverPseudoStateUpdate(false, true);
  16357  popoverHTMLEl->GetPopoverData()->SetPopoverVisibilityState(
  16358      PopoverVisibilityState::Hidden);
  16359 
  16360  // 14. If fireEvents is true, then queue a popover toggle event task given
  16361  // element, "open", and "closed". Queue popover toggle event task.
  16362  if (fireEvents) {
  16363    popoverHTMLEl->QueuePopoverEventTask(PopoverVisibilityState::Showing,
  16364                                         aSource);
  16365  }
  16366 
  16367  // 15. Let previouslyFocusedElement be element's previously focused element.
  16368  // 16. If previouslyFocusedElement is not null, then:
  16369  if (aFocusPreviousElement) {
  16370    // 16.1. Set element's previously focused element to null.
  16371    // 16.2. If focusPreviousElement is true and document's focused area of the
  16372    // document's DOM anchor is a shadow-including inclusive descendant of
  16373    // element, then run the focusing steps for previouslyFocusedElement; the
  16374    // viewport should not be scrolled by doing this step.
  16375    popoverHTMLEl->FocusPreviousElementAfterHidingPopover();
  16376  } else {
  16377    popoverHTMLEl->ForgetPreviouslyFocusedElementAfterHidingPopover();
  16378  }
  16379 }
  16380 
  16381 nsTArray<Element*> Document::PopoverListOf(PopoverAttributeState aMode) const {
  16382  nsTArray<Element*> elements;
  16383  for (const nsWeakPtr& ptr : mTopLayer) {
  16384    if (nsCOMPtr<Element> element = do_QueryReferent(ptr)) {
  16385      if (element && element->IsPopoverOpenedInMode(aMode)) {
  16386        elements.AppendElement(element);
  16387      }
  16388    }
  16389  }
  16390  return elements;
  16391 }
  16392 
  16393 Element* Document::GetTopmostPopoverOf(PopoverAttributeState aMode) const {
  16394  for (const nsWeakPtr& weakPtr : Reversed(mTopLayer)) {
  16395    nsCOMPtr<Element> element(do_QueryReferent(weakPtr));
  16396    if (element && element->IsPopoverOpenedInMode(aMode)) {
  16397      return element;
  16398    }
  16399  }
  16400  return nullptr;
  16401 }
  16402 
  16403 void Document::AddPopoverToTopLayer(Element& aElement) {
  16404  MOZ_ASSERT(aElement.GetPopoverData());
  16405  TopLayerPush(aElement);
  16406 }
  16407 
  16408 void Document::RemovePopoverFromTopLayer(Element& aElement) {
  16409  MOZ_ASSERT(aElement.GetPopoverData());
  16410  TopLayerPop(aElement);
  16411 }
  16412 
  16413 // Returns true if aDoc browsing context is focused.
  16414 bool IsInFocusedTab(Document* aDoc) {
  16415  BrowsingContext* bc = aDoc->GetBrowsingContext();
  16416  if (!bc) {
  16417    return false;
  16418  }
  16419 
  16420  nsFocusManager* fm = nsFocusManager::GetFocusManager();
  16421  if (!fm) {
  16422    return false;
  16423  }
  16424 
  16425  if (XRE_IsParentProcess()) {
  16426    // Keep dom/tests/mochitest/chrome/test_MozDomFullscreen_event.xhtml happy
  16427    // by retaining the old code path for the parent process.
  16428    nsIDocShell* docshell = aDoc->GetDocShell();
  16429    if (!docshell) {
  16430      return false;
  16431    }
  16432    nsCOMPtr<nsIDocShellTreeItem> rootItem;
  16433    docshell->GetInProcessRootTreeItem(getter_AddRefs(rootItem));
  16434    if (!rootItem) {
  16435      return false;
  16436    }
  16437    nsCOMPtr<nsPIDOMWindowOuter> rootWin = rootItem->GetWindow();
  16438    if (!rootWin) {
  16439      return false;
  16440    }
  16441 
  16442    nsCOMPtr<nsPIDOMWindowOuter> activeWindow;
  16443    activeWindow = fm->GetActiveWindow();
  16444    if (!activeWindow) {
  16445      return false;
  16446    }
  16447 
  16448    return activeWindow == rootWin;
  16449  }
  16450 
  16451  return fm->GetActiveBrowsingContext() == bc->Top();
  16452 }
  16453 
  16454 // Returns true if aDoc browsing context is focused and is also active.
  16455 bool IsInActiveTab(Document* aDoc) {
  16456  if (!IsInFocusedTab(aDoc)) {
  16457    return false;
  16458  }
  16459 
  16460  BrowsingContext* bc = aDoc->GetBrowsingContext();
  16461  MOZ_ASSERT(bc, "With no BrowsingContext, we should have failed earlier.");
  16462  return bc->IsActive();
  16463 }
  16464 
  16465 void Document::RemoteFrameFullscreenChanged(Element* aFrameElement) {
  16466  // Ensure the frame element is the fullscreen element in this document.
  16467  // If the frame element is already the fullscreen element in this document,
  16468  // this has no effect.
  16469  auto request = FullscreenRequest::CreateForRemote(aFrameElement);
  16470  RequestFullscreen(std::move(request), XRE_IsContentProcess());
  16471 }
  16472 
  16473 void Document::RemoteFrameFullscreenReverted() {
  16474  UniquePtr<FullscreenExit> exit = FullscreenExit::CreateForRemote(this);
  16475  RestorePreviousFullscreenState(std::move(exit));
  16476 }
  16477 
  16478 static bool HasFullscreenSubDocument(Document& aDoc) {
  16479  uint32_t count = CountFullscreenSubDocuments(aDoc);
  16480  NS_ASSERTION(count <= 1,
  16481               "Fullscreen docs should have at most 1 fullscreen child!");
  16482  return count >= 1;
  16483 }
  16484 
  16485 // Returns nullptr if a request for Fullscreen API is currently enabled
  16486 // in the given document. Returns a static string indicates the reason
  16487 // why it is not enabled otherwise.
  16488 const char* Document::GetFullscreenError(CallerType aCallerType) {
  16489  if (!StaticPrefs::full_screen_api_enabled()) {
  16490    return "FullscreenDeniedDisabled";
  16491  }
  16492 
  16493  BrowsingContext* bc = GetBrowsingContext();
  16494  // https://github.com/WICG/document-picture-in-picture/issues/133
  16495  if (!bc || bc->Top()->GetIsDocumentPiP()) {
  16496    return "FullscreenDeniedPiP";
  16497  }
  16498 
  16499  if (aCallerType == CallerType::System) {
  16500    // Chrome code can always use the fullscreen API, provided it's not
  16501    // explicitly disabled.
  16502    return nullptr;
  16503  }
  16504 
  16505  if (!IsVisible()) {
  16506    return "FullscreenDeniedHidden";
  16507  }
  16508 
  16509  if (!FeaturePolicyUtils::IsFeatureAllowed(this, u"fullscreen"_ns)) {
  16510    return "FullscreenDeniedFeaturePolicy";
  16511  }
  16512 
  16513  // Ensure that all containing elements are <iframe> and have allowfullscreen
  16514  // attribute set.
  16515  if (!bc->FullscreenAllowed()) {
  16516    return "FullscreenDeniedContainerNotAllowed";
  16517  }
  16518 
  16519  return nullptr;
  16520 }
  16521 
  16522 // Informs JSWA Fullscreen implementation to resume via sending
  16523 // "MozDOMFullscreen:Entered".
  16524 static inline void PropagateFullscreenRequest(Document* aDoc,
  16525                                              Element* aElement) {
  16526  nsContentUtils::DispatchEventOnlyToChrome(
  16527      aDoc, aElement, u"MozDOMFullscreen:Entered"_ns, CanBubble::eYes,
  16528      Cancelable::eNo, /* DefaultAction */ nullptr);
  16529 }
  16530 
  16531 static bool ElementIsRemoteFrame(Element* aElement) {
  16532  MOZ_ASSERT(aElement);
  16533  RefPtr<nsFrameLoader> loader;
  16534  if (RefPtr<nsFrameLoaderOwner> loaderOwner = do_QueryObject(aElement)) {
  16535    loader = loaderOwner->GetFrameLoader();
  16536  }
  16537  return loader && loader->IsRemoteFrame();
  16538 }
  16539 
  16540 bool Document::FullscreenElementReadyCheck(FullscreenRequest& aRequest) {
  16541  Element* elem = aRequest.Element();
  16542  // Strictly speaking, this isn't part of the fullscreen element ready
  16543  // check in the spec, but per steps in the spec, when an element which
  16544  // is already the fullscreen element requests fullscreen, nothing
  16545  // should change and no event should be dispatched, but we still need
  16546  // to resolve the returned promise.
  16547  Element* fullscreenElement = GetUnretargetedFullscreenElement();
  16548  if (NS_WARN_IF(elem == fullscreenElement)) {
  16549    // But this introduces behavior that we now need to account for;
  16550    // because we can have arbitrary depth of OOP-frames, we may hit this check
  16551    // for a process that already is fullscreen, e.g. the parent process.
  16552    // If the target element is a frame or we're the parent process, just resume
  16553    // the JS Window Actor messaging without doing any more work.
  16554    // We know for sure, that the document must be fullscreened already, so
  16555    // there is no request to the OS for fullscreen that needs to be made, for
  16556    // instance. Note: this is just for JSWA not the platform-only fullscreen
  16557    // implementation.
  16558    if (ElementIsRemoteFrame(elem)) {
  16559      PropagateFullscreenRequest(this, elem);
  16560    }
  16561    aRequest.MayResolvePromise();
  16562    return false;
  16563  }
  16564  if (!elem->IsInComposedDoc()) {
  16565    aRequest.Reject("FullscreenDeniedNotInDocument");
  16566    return false;
  16567  }
  16568  if (elem->IsPopoverOpen()) {
  16569    aRequest.Reject("FullscreenDeniedPopoverOpen");
  16570    return false;
  16571  }
  16572  if (elem->OwnerDoc() != this) {
  16573    aRequest.Reject("FullscreenDeniedMovedDocument");
  16574    return false;
  16575  }
  16576  if (!GetWindow()) {
  16577    aRequest.Reject("FullscreenDeniedLostWindow");
  16578    return false;
  16579  }
  16580  if (const char* msg = GetFullscreenError(aRequest.mCallerType)) {
  16581    aRequest.Reject(msg);
  16582    return false;
  16583  }
  16584  if (HasFullscreenSubDocument(*this)) {
  16585    aRequest.Reject("FullscreenDeniedSubDocFullScreen");
  16586    return false;
  16587  }
  16588  if (elem->IsHTMLElement(nsGkAtoms::dialog)) {
  16589    aRequest.Reject("FullscreenDeniedHTMLDialog");
  16590    return false;
  16591  }
  16592  if (!nsContentUtils::IsChromeDoc(this) && !IsInFocusedTab(this)) {
  16593    aRequest.Reject("FullscreenDeniedNotFocusedTab");
  16594    return false;
  16595  }
  16596  return true;
  16597 }
  16598 
  16599 static nsCOMPtr<nsPIDOMWindowOuter> GetRootWindow(Document* aDoc) {
  16600  MOZ_ASSERT(XRE_IsParentProcess());
  16601  nsIDocShell* docShell = aDoc->GetDocShell();
  16602  if (!docShell) {
  16603    return nullptr;
  16604  }
  16605  nsCOMPtr<nsIDocShellTreeItem> rootItem;
  16606  docShell->GetInProcessRootTreeItem(getter_AddRefs(rootItem));
  16607  return rootItem ? rootItem->GetWindow() : nullptr;
  16608 }
  16609 
  16610 static bool ShouldApplyFullscreenDirectly(Document* aDoc,
  16611                                          nsPIDOMWindowOuter* aRootWin) {
  16612  MOZ_ASSERT(XRE_IsParentProcess());
  16613  // If we are in the chrome process, and the window has not been in
  16614  // fullscreen, we certainly need to make that fullscreen first.
  16615  if (!aRootWin->GetFullScreen()) {
  16616    return false;
  16617  }
  16618  // The iterator not being at end indicates there is still some
  16619  // pending fullscreen request relates to this document. We have to
  16620  // push the request to the pending queue so requests are handled
  16621  // in the correct order.
  16622  PendingFullscreenChangeList::Iterator<FullscreenRequest> iter(
  16623      aDoc, PendingFullscreenChangeList::eDocumentsWithSameRoot);
  16624  if (!iter.AtEnd()) {
  16625    return false;
  16626  }
  16627 
  16628  // Same thing for exits. If we have any pending, we have to push
  16629  // to the pending queue.
  16630  PendingFullscreenChangeList::Iterator<FullscreenExit> iterExit(
  16631      aDoc, PendingFullscreenChangeList::eDocumentsWithSameRoot);
  16632  if (!iterExit.AtEnd()) {
  16633    return false;
  16634  }
  16635 
  16636  // We have to apply the fullscreen state directly in this case,
  16637  // because nsGlobalWindow::SetFullscreenInternal() will do nothing
  16638  // if it is already in fullscreen. If we do not apply the state but
  16639  // instead add it to the queue and wait for the window as normal,
  16640  // we would get stuck.
  16641  return true;
  16642 }
  16643 
  16644 static bool CheckFullscreenAllowedElementType(const Element* elem) {
  16645  // Per spec only HTML, <svg>, and <math> should be allowed, but
  16646  // we also need to allow XUL elements right now.
  16647  return elem->IsHTMLElement() || elem->IsXULElement() ||
  16648         elem->IsSVGElement(nsGkAtoms::svg) ||
  16649         elem->IsMathMLElement(nsGkAtoms::math);
  16650 }
  16651 
  16652 void Document::RequestFullscreen(UniquePtr<FullscreenRequest> aRequest,
  16653                                 bool aApplyFullscreenDirectly) {
  16654  if (XRE_IsContentProcess()) {
  16655    RequestFullscreenInContentProcess(std::move(aRequest),
  16656                                      aApplyFullscreenDirectly);
  16657  } else {
  16658    RequestFullscreenInParentProcess(std::move(aRequest),
  16659                                     aApplyFullscreenDirectly);
  16660  }
  16661 }
  16662 
  16663 void Document::RequestFullscreenInContentProcess(
  16664    UniquePtr<FullscreenRequest> aRequest, bool aApplyFullscreenDirectly) {
  16665  MOZ_ASSERT(XRE_IsContentProcess());
  16666 
  16667  // If we are in the content process, we can apply the fullscreen
  16668  // state directly only if we have been in DOM fullscreen, because
  16669  // otherwise we always need to notify the chrome.
  16670  if (aApplyFullscreenDirectly ||
  16671      nsContentUtils::GetInProcessSubtreeRootDocument(this)->Fullscreen()) {
  16672    ApplyFullscreen(std::move(aRequest));
  16673    return;
  16674  }
  16675 
  16676  if (!CheckFullscreenAllowedElementType(aRequest->Element())) {
  16677    aRequest->Reject("FullscreenDeniedNotHTMLSVGOrMathML");
  16678    return;
  16679  }
  16680 
  16681  // We don't need to check element ready before this point, because
  16682  // if we called ApplyFullscreen, it would check that for us.
  16683  if (!FullscreenElementReadyCheck(*aRequest)) {
  16684    return;
  16685  }
  16686 
  16687  PendingFullscreenChangeList::Add(std::move(aRequest));
  16688  // If we are not the top level process, dispatch an event to make
  16689  // our parent process go fullscreen first.
  16690  Dispatch(NS_NewRunnableFunction(
  16691      "Document::RequestFullscreenInContentProcess", [self = RefPtr{this}] {
  16692        if (!self->HasPendingFullscreenRequests()) {
  16693          return;
  16694        }
  16695        nsContentUtils::DispatchEventOnlyToChrome(
  16696            self, self, u"MozDOMFullscreen:Request"_ns, CanBubble::eYes,
  16697            Cancelable::eNo, /* DefaultAction */ nullptr);
  16698      }));
  16699 }
  16700 
  16701 void Document::RequestFullscreenInParentProcess(
  16702    UniquePtr<FullscreenRequest> aRequest, bool aApplyFullscreenDirectly) {
  16703  MOZ_ASSERT(XRE_IsParentProcess());
  16704  nsCOMPtr<nsPIDOMWindowOuter> rootWin = GetRootWindow(this);
  16705  if (!rootWin) {
  16706    aRequest->MayRejectPromise("No active window");
  16707    return;
  16708  }
  16709 
  16710  if (aApplyFullscreenDirectly ||
  16711      ShouldApplyFullscreenDirectly(this, rootWin)) {
  16712    ApplyFullscreen(std::move(aRequest));
  16713    return;
  16714  }
  16715 
  16716  if (!CheckFullscreenAllowedElementType(aRequest->Element())) {
  16717    aRequest->Reject("FullscreenDeniedNotHTMLSVGOrMathML");
  16718    return;
  16719  }
  16720 
  16721  // See if we're waiting on an exit. If so, just make this one pending.
  16722  PendingFullscreenChangeList::Iterator<FullscreenExit> iter(
  16723      this, PendingFullscreenChangeList::eDocumentsWithSameRoot);
  16724  if (!iter.AtEnd()) {
  16725    PendingFullscreenChangeList::Add(std::move(aRequest));
  16726    rootWin->SetFullscreenInternal(FullscreenReason::ForFullscreenAPI, true);
  16727    return;
  16728  }
  16729 
  16730  // We don't need to check element ready before this point, because
  16731  // if we called ApplyFullscreen, it would check that for us.
  16732  if (!FullscreenElementReadyCheck(*aRequest)) {
  16733    return;
  16734  }
  16735 
  16736  PendingFullscreenChangeList::Add(std::move(aRequest));
  16737  // Make the window fullscreen.
  16738  rootWin->SetFullscreenInternal(FullscreenReason::ForFullscreenAPI, true);
  16739 }
  16740 
  16741 /* static */
  16742 bool Document::HandlePendingFullscreenRequests(Document* aDoc) {
  16743  bool handled = false;
  16744  PendingFullscreenChangeList::Iterator<FullscreenRequest> iter(
  16745      aDoc, PendingFullscreenChangeList::eDocumentsWithSameRoot);
  16746  while (!iter.AtEnd()) {
  16747    UniquePtr<FullscreenRequest> request = iter.TakeAndNext();
  16748    Document* doc = request->Document();
  16749    if (doc->ApplyFullscreen(std::move(request))) {
  16750      handled = true;
  16751    }
  16752  }
  16753  return handled;
  16754 }
  16755 
  16756 /* static */
  16757 void Document::ClearPendingFullscreenRequests(Document* aDoc) {
  16758  PendingFullscreenChangeList::Iterator<FullscreenRequest> iter(
  16759      aDoc, PendingFullscreenChangeList::eInclusiveDescendants);
  16760  while (!iter.AtEnd()) {
  16761    UniquePtr<FullscreenRequest> request = iter.TakeAndNext();
  16762    request->MayRejectPromise("Fullscreen request aborted");
  16763  }
  16764 }
  16765 
  16766 void Document::AddPendingFullscreenEvent(
  16767    UniquePtr<PendingFullscreenEvent> aPendingEvent) {
  16768  const bool wasEmpty = mPendingFullscreenEvents.IsEmpty();
  16769  mPendingFullscreenEvents.AppendElement(std::move(aPendingEvent));
  16770  if (wasEmpty) {
  16771    MaybeScheduleRenderingPhases({RenderingPhase::FullscreenSteps});
  16772  }
  16773 }
  16774 
  16775 // https://fullscreen.spec.whatwg.org/#run-the-fullscreen-steps
  16776 void Document::RunFullscreenSteps() {
  16777  auto events = std::move(mPendingFullscreenEvents);
  16778  for (auto& event : events) {
  16779    event->Dispatch(this);
  16780  }
  16781 }
  16782 
  16783 bool Document::HasPendingFullscreenRequests() {
  16784  PendingFullscreenChangeList::Iterator<FullscreenRequest> iter(
  16785      this, PendingFullscreenChangeList::eDocumentsWithSameRoot);
  16786  return !iter.AtEnd();
  16787 }
  16788 
  16789 MOZ_CAN_RUN_SCRIPT_BOUNDARY
  16790 bool Document::ApplyFullscreen(UniquePtr<FullscreenRequest> aRequest) {
  16791  if (!FullscreenElementReadyCheck(*aRequest)) {
  16792    return false;
  16793  }
  16794 
  16795  Element* elem = aRequest->Element();
  16796 
  16797  RefPtr<nsINode> hideUntil = elem->GetTopmostPopoverAncestor(
  16798      PopoverAttributeState::Auto, nullptr, false);
  16799  if (!hideUntil) {
  16800    hideUntil = OwnerDoc();
  16801  }
  16802 
  16803  RefPtr<Document> doc = aRequest->Document();
  16804  doc->HideAllPopoversUntil(*hideUntil, false, true);
  16805 
  16806  // Stash a reference to any existing fullscreen doc, we'll use this later
  16807  // to detect if the origin which is fullscreen has changed.
  16808  nsCOMPtr<Document> previousFullscreenDoc = GetFullscreenLeaf(this);
  16809 
  16810  // Stores a list of documents which we must dispatch "fullscreenchange"
  16811  // too. We're required by the spec to dispatch the events in root-to-leaf
  16812  // order, but we traverse the doctree in a leaf-to-root order, so we save
  16813  // references to the documents we must dispatch to so that we get the order
  16814  // as specified.
  16815  AutoTArray<Document*, 8> changed;
  16816 
  16817  // Remember the root document, so that if a fullscreen document is hidden
  16818  // we can reset fullscreen state in the remaining visible fullscreen
  16819  // documents.
  16820  Document* fullScreenRootDoc =
  16821      nsContentUtils::GetInProcessSubtreeRootDocument(this);
  16822 
  16823  // If a document is already in fullscreen, then unlock the mouse pointer
  16824  // before setting a new document to fullscreen
  16825  PointerLockManager::Unlock("Document::ApplyFullscreen");
  16826 
  16827  // Set the fullscreen element. This sets the fullscreen style on the
  16828  // element, and the fullscreen-ancestor styles on ancestors of the element
  16829  // in this document.
  16830  SetFullscreenElement(*elem);
  16831  // Set the iframe fullscreen flag.
  16832  if (auto* iframe = HTMLIFrameElement::FromNode(elem)) {
  16833    iframe->SetFullscreenFlag(true);
  16834  }
  16835  changed.AppendElement(this);
  16836 
  16837  // Propagate up the document hierarchy, setting the fullscreen element as
  16838  // the element's container in ancestor documents. This also sets the
  16839  // appropriate css styles as well. Note we don't propagate down the
  16840  // document hierarchy, the fullscreen element (or its container) is not
  16841  // visible there. Stop when we reach the root document.
  16842  Document* child = this;
  16843  while (true) {
  16844    child->SetFullscreenRoot(fullScreenRootDoc);
  16845 
  16846    // When entering fullscreen, reset the RCD's resolution to the intrinsic
  16847    // resolution, otherwise the fullscreen content could be sized larger than
  16848    // the screen (since fullscreen is implemented using position:fixed and
  16849    // fixed elements are sized to the layout viewport).
  16850    // This also ensures that things like video controls aren't zoomed in
  16851    // when in fullscreen mode.
  16852    if (PresShell* presShell = child->GetPresShell()) {
  16853      if (RefPtr<MobileViewportManager> manager =
  16854              presShell->GetMobileViewportManager()) {
  16855        // Save the previous resolution so it can be restored.
  16856        child->mSavedResolution = presShell->GetResolution();
  16857        presShell->SetResolutionAndScaleTo(
  16858            manager->ComputeIntrinsicResolution(),
  16859            ResolutionChangeOrigin::MainThreadRestore);
  16860      }
  16861    }
  16862 
  16863    NS_ASSERTION(child->GetFullscreenRoot() == fullScreenRootDoc,
  16864                 "Fullscreen root should be set!");
  16865    if (child == fullScreenRootDoc) {
  16866      break;
  16867    }
  16868 
  16869    Element* element = child->GetEmbedderElement();
  16870    if (!element) {
  16871      // We've reached the root.No more changes need to be made
  16872      // to the top layer stacks of documents further up the tree.
  16873      break;
  16874    }
  16875 
  16876    Document* parent = child->GetInProcessParentDocument();
  16877 
  16878    // If this is true, nothing above this node will have changed, stopping here
  16879    // prevents us from sending duplicate events.
  16880    if (parent->GetUnretargetedFullscreenElement() == element) {
  16881      break;
  16882    }
  16883    parent->SetFullscreenElement(*element);
  16884    changed.AppendElement(parent);
  16885    child = parent;
  16886  }
  16887 
  16888  FullscreenRoots::Add(this);
  16889 
  16890  // If it is the first entry of the fullscreen, trigger an event so
  16891  // that the UI can response to this change, e.g. hide chrome, or
  16892  // notifying parent process to enter fullscreen. Note that chrome
  16893  // code may also want to listen to MozDOMFullscreen:NewOrigin event
  16894  // to pop up warning UI.
  16895  // We also need to propagate the message in the JSWA message chain,
  16896  // so that if out of process sub-frames, that has requested fs
  16897  // gets notified to resume it's request. We can know, that the request did not
  16898  // originate from this process, if the JS promise is null.
  16899  if (!aRequest->GetPromise() || !previousFullscreenDoc) {
  16900    MOZ_ASSERT(
  16901        (previousFullscreenDoc &&
  16902         ElementIsRemoteFrame(child->GetUnretargetedFullscreenElement())) ||
  16903        !previousFullscreenDoc);
  16904    PropagateFullscreenRequest(this, elem);
  16905  }
  16906 
  16907  // The origin which is fullscreen gets changed. Trigger an event so
  16908  // that the chrome knows to pop up a warning UI. Note that
  16909  // previousFullscreenDoc == nullptr upon first entry, we show the warning UI
  16910  // directly as soon as chrome document goes into fullscreen state. Also note
  16911  // that, in a multi-process browser, the code in content process is
  16912  // responsible for sending message with the origin to its parent, and the
  16913  // parent shouldn't rely on this event itself.
  16914  if (aRequest->mShouldNotifyNewOrigin && previousFullscreenDoc &&
  16915      !nsContentUtils::HaveEqualPrincipals(previousFullscreenDoc, this)) {
  16916    DispatchFullscreenNewOriginEvent(this);
  16917  }
  16918 
  16919  // Dispatch "fullscreenchange" events. Note that the loop order is
  16920  // reversed so that events are dispatched in the tree order as
  16921  // indicated in the spec.
  16922  for (Document* d : Reversed(changed)) {
  16923    DispatchFullscreenChange(*d, d->GetUnretargetedFullscreenElement());
  16924  }
  16925  aRequest->MayResolvePromise();
  16926  return true;
  16927 }
  16928 
  16929 void Document::ClearOrientationPendingPromise() {
  16930  mOrientationPendingPromise = nullptr;
  16931 }
  16932 
  16933 bool Document::SetOrientationPendingPromise(Promise* aPromise) {
  16934  if (mIsGoingAway) {
  16935    return false;
  16936  }
  16937 
  16938  mOrientationPendingPromise = aPromise;
  16939  return true;
  16940 }
  16941 
  16942 void Document::MaybeSkipTransitionAfterVisibilityChange() {
  16943  if (Hidden() && mActiveViewTransition) {
  16944    mActiveViewTransition->SkipTransition(SkipTransitionReason::DocumentHidden);
  16945  }
  16946 }
  16947 
  16948 // https://drafts.csswg.org/css-view-transitions-1/#schedule-the-update-callback
  16949 void Document::ScheduleViewTransitionUpdateCallback(ViewTransition* aVt) {
  16950  MOZ_ASSERT(aVt);
  16951  const bool hasTasks = !mViewTransitionUpdateCallbacks.IsEmpty();
  16952 
  16953  // 1. Append transition to transition’s relevant settings object’s update
  16954  // callback queue.
  16955  mViewTransitionUpdateCallbacks.AppendElement(aVt);
  16956 
  16957  // 2. Queue a global task on the DOM manipulation task source, given
  16958  // transition’s relevant global object, to flush the update callback queue.
  16959  // Note: |hasTasks| means the list was not empty, so there must be a global
  16960  // task there already.
  16961  if (!hasTasks) {
  16962    Dispatch(NewRunnableMethod(
  16963        "Document::FlushViewTransitionUpdateCallbackQueue", this,
  16964        &Document::FlushViewTransitionUpdateCallbackQueue));
  16965  }
  16966 }
  16967 
  16968 // https://drafts.csswg.org/css-view-transitions-1/#flush-the-update-callback-queue
  16969 void Document::FlushViewTransitionUpdateCallbackQueue() {
  16970  // 1. For each transition in document’s update callback queue, call the update
  16971  // callback given transition.
  16972  // Note: we move mViewTransitionUpdateCallbacks into a temporary array to make
  16973  // sure no one updates the array when iterating.
  16974  auto callbacks = std::move(mViewTransitionUpdateCallbacks);
  16975  MOZ_ASSERT(mViewTransitionUpdateCallbacks.IsEmpty());
  16976  for (RefPtr<ViewTransition>& vt : callbacks) {
  16977    MOZ_KnownLive(vt)->CallUpdateCallback(IgnoreErrors());
  16978  }
  16979 
  16980  // 2. Set document’s update callback queue to an empty list.
  16981  // mViewTransitionUpdateCallbacks is empty after the 1st step.
  16982 }
  16983 
  16984 // https://html.spec.whatwg.org/#update-the-visibility-state
  16985 void Document::UpdateVisibilityState(DispatchVisibilityChange aDispatchEvent) {
  16986  const dom::VisibilityState visibilityState = ComputeVisibilityState();
  16987  if (mVisibilityState == visibilityState) {
  16988    // 1. If document's visibility state equals visibilityState, then return.
  16989    return;
  16990  }
  16991  // 2. Set document's visibility state to visibilityState.
  16992  mVisibilityState = visibilityState;
  16993  if (aDispatchEvent == DispatchVisibilityChange::Yes) {
  16994    // 3. Fire an event named visibilitychange at document, with its bubbles
  16995    // attribute initialized to true.
  16996    nsContentUtils::DispatchTrustedEvent(this, this, u"visibilitychange"_ns,
  16997                                         CanBubble::eYes, Cancelable::eNo);
  16998  }
  16999  // TODO 4. Run the screen orientation change steps with document.
  17000 
  17001  // 5. Run the view transition page visibility change steps with document.
  17002  // https://drafts.csswg.org/css-view-transitions/#view-transition-page-visibility-change-steps
  17003  const bool visible = !Hidden();
  17004  if (mActiveViewTransition && !visible) {
  17005    // The mActiveViewTransition check is an optimization: We don't allow
  17006    // creating transitions while the doc is hidden.
  17007    Dispatch(
  17008        NewRunnableMethod("MaybeSkipTransitionAfterVisibilityChange", this,
  17009                          &Document::MaybeSkipTransitionAfterVisibilityChange));
  17010  }
  17011 
  17012  NotifyActivityChanged();
  17013  if (visible) {
  17014    MaybeScheduleRendering();
  17015    MaybeActiveMediaComponents();
  17016  }
  17017 
  17018  for (auto* listener : mWorkerListeners) {
  17019    listener->OnVisible(visible);
  17020  }
  17021 
  17022  // https://w3c.github.io/screen-wake-lock/#handling-document-loss-of-visibility
  17023  if (!visible) {
  17024    UnlockAllWakeLocks(WakeLockType::Screen);
  17025  }
  17026 }
  17027 
  17028 void Document::AddWorkerDocumentListener(WorkerDocumentListener* aListener) {
  17029  mWorkerListeners.Insert(aListener);
  17030  aListener->OnVisible(!Hidden());
  17031 }
  17032 
  17033 void Document::RemoveWorkerDocumentListener(WorkerDocumentListener* aListener) {
  17034  mWorkerListeners.Remove(aListener);
  17035 }
  17036 
  17037 VisibilityState Document::ComputeVisibilityState() const {
  17038  // We have to check a few pieces of information here:
  17039  // 1)  Are we in bfcache (!IsVisible())?  If so, nothing else matters.
  17040  // 2)  Do we have an outer window?  If not, we're hidden.  Note that we don't
  17041  //     want to use GetWindow here because it does weird groveling for windows
  17042  //     in some cases.
  17043  // 3)  Is our outer window background?  If so, we're hidden.
  17044  // Otherwise, we're visible.
  17045  if (!IsVisible() || !mWindow || !mWindow->GetOuterWindow() ||
  17046      mWindow->GetOuterWindow()->IsBackground()) {
  17047    return dom::VisibilityState::Hidden;
  17048  }
  17049 
  17050  return dom::VisibilityState::Visible;
  17051 }
  17052 
  17053 void Document::PostVisibilityUpdateEvent() {
  17054  nsCOMPtr<nsIRunnable> event = NewRunnableMethod<DispatchVisibilityChange>(
  17055      "Document::UpdateVisibilityState", this, &Document::UpdateVisibilityState,
  17056      DispatchVisibilityChange::Yes);
  17057  Dispatch(event.forget());
  17058 }
  17059 
  17060 void Document::MaybeActiveMediaComponents() {
  17061  auto* window = GetWindow();
  17062  if (!window || !window->ShouldDelayMediaFromStart()) {
  17063    return;
  17064  }
  17065  window->ActivateMediaComponents();
  17066 }
  17067 
  17068 void Document::DocAddSizeOfExcludingThis(nsWindowSizes& aWindowSizes) const {
  17069  nsINode::AddSizeOfExcludingThis(aWindowSizes,
  17070                                  &aWindowSizes.mDOMSizes.mDOMOtherSize);
  17071 
  17072  for (nsIContent* kid = GetFirstChild(); kid; kid = kid->GetNextSibling()) {
  17073    AddSizeOfNodeTree(*kid, aWindowSizes);
  17074  }
  17075 
  17076  // IMPORTANT: for our ComputedValues measurements, we want to measure
  17077  // ComputedValues accessible from DOM elements before ComputedValues not
  17078  // accessible from DOM elements (i.e. accessible only from the frame tree).
  17079  //
  17080  // Therefore, the measurement of the Document superclass must happen after
  17081  // the measurement of DOM nodes (above), because Document contains the
  17082  // PresShell, which contains the frame tree.
  17083  if (mPresShell) {
  17084    mPresShell->AddSizeOfIncludingThis(aWindowSizes);
  17085  }
  17086 
  17087  if (mStyleSet) {
  17088    mStyleSet->AddSizeOfIncludingThis(aWindowSizes);
  17089  }
  17090 
  17091  aWindowSizes.mPropertyTablesSize +=
  17092      mPropertyTable.SizeOfExcludingThis(aWindowSizes.mState.mMallocSizeOf);
  17093 
  17094  if (EventListenerManager* elm = GetExistingListenerManager()) {
  17095    aWindowSizes.mDOMEventListenersCount += elm->ListenerCount();
  17096  }
  17097 
  17098  if (mNodeInfoManager) {
  17099    mNodeInfoManager->AddSizeOfIncludingThis(aWindowSizes);
  17100  }
  17101 
  17102  aWindowSizes.mDOMSizes.mDOMMediaQueryLists +=
  17103      mDOMMediaQueryLists.sizeOfExcludingThis(
  17104          aWindowSizes.mState.mMallocSizeOf);
  17105 
  17106  for (const MediaQueryList* mql : mDOMMediaQueryLists) {
  17107    aWindowSizes.mDOMSizes.mDOMMediaQueryLists +=
  17108        mql->SizeOfExcludingThis(aWindowSizes.mState.mMallocSizeOf);
  17109  }
  17110 
  17111  DocumentOrShadowRoot::AddSizeOfExcludingThis(aWindowSizes);
  17112 
  17113  for (auto& sheetArray : mAdditionalSheets) {
  17114    AddSizeOfOwnedSheetArrayExcludingThis(aWindowSizes, sheetArray);
  17115  }
  17116  // Lumping in the loader with the style-sheets size is not ideal,
  17117  // but most of the things in there are in fact stylesheets, so it
  17118  // doesn't seem worthwhile to separate it out.
  17119  // This can be null if we've already been unlinked.
  17120  if (mCSSLoader) {
  17121    aWindowSizes.mLayoutStyleSheetsSize +=
  17122        mCSSLoader->SizeOfIncludingThis(aWindowSizes.mState.mMallocSizeOf);
  17123  }
  17124 
  17125  aWindowSizes.mDOMSizes.mDOMResizeObserverControllerSize +=
  17126      mResizeObservers.ShallowSizeOfExcludingThis(
  17127          aWindowSizes.mState.mMallocSizeOf);
  17128 
  17129  if (mAttributeStyles) {
  17130    aWindowSizes.mDOMSizes.mDOMOtherSize +=
  17131        mAttributeStyles->DOMSizeOfIncludingThis(
  17132            aWindowSizes.mState.mMallocSizeOf);
  17133  }
  17134 
  17135  if (mRadioGroupContainer) {
  17136    aWindowSizes.mDOMSizes.mDOMOtherSize +=
  17137        mRadioGroupContainer->SizeOfIncludingThis(
  17138            aWindowSizes.mState.mMallocSizeOf);
  17139  }
  17140 
  17141  aWindowSizes.mDOMSizes.mDOMOtherSize +=
  17142      mStyledLinks.ShallowSizeOfExcludingThis(
  17143          aWindowSizes.mState.mMallocSizeOf);
  17144 
  17145  // Measurement of the following members may be added later if DMD finds it
  17146  // is worthwhile:
  17147  // - mMidasCommandManager
  17148  // - many!
  17149 }
  17150 
  17151 void Document::DocAddSizeOfIncludingThis(nsWindowSizes& aWindowSizes) const {
  17152  aWindowSizes.mDOMSizes.mDOMOtherSize +=
  17153      aWindowSizes.mState.mMallocSizeOf(this);
  17154  DocAddSizeOfExcludingThis(aWindowSizes);
  17155 }
  17156 
  17157 void Document::AddSizeOfExcludingThis(nsWindowSizes& aSizes,
  17158                                      size_t* aNodeSize) const {
  17159  // This AddSizeOfExcludingThis() overrides the one from nsINode.  But
  17160  // nsDocuments can only appear at the top of the DOM tree, and we use the
  17161  // specialized DocAddSizeOfExcludingThis() in that case.  So this should never
  17162  // be called.
  17163  MOZ_CRASH();
  17164 }
  17165 
  17166 /* static */
  17167 void Document::AddSizeOfNodeTree(nsINode& aNode, nsWindowSizes& aWindowSizes) {
  17168  size_t nodeSize = 0;
  17169  aNode.AddSizeOfIncludingThis(aWindowSizes, &nodeSize);
  17170 
  17171  // This is where we transfer the nodeSize obtained from
  17172  // nsINode::AddSizeOfIncludingThis() to a value in nsWindowSizes.
  17173  switch (aNode.NodeType()) {
  17174    case nsINode::ELEMENT_NODE:
  17175      aWindowSizes.mDOMSizes.mDOMElementNodesSize += nodeSize;
  17176      break;
  17177    case nsINode::TEXT_NODE:
  17178      aWindowSizes.mDOMSizes.mDOMTextNodesSize += nodeSize;
  17179      break;
  17180    case nsINode::CDATA_SECTION_NODE:
  17181      aWindowSizes.mDOMSizes.mDOMCDATANodesSize += nodeSize;
  17182      break;
  17183    case nsINode::COMMENT_NODE:
  17184      aWindowSizes.mDOMSizes.mDOMCommentNodesSize += nodeSize;
  17185      break;
  17186    default:
  17187      aWindowSizes.mDOMSizes.mDOMOtherSize += nodeSize;
  17188      break;
  17189  }
  17190 
  17191  if (EventListenerManager* elm = aNode.GetExistingListenerManager()) {
  17192    aWindowSizes.mDOMEventListenersCount += elm->ListenerCount();
  17193  }
  17194 
  17195  if (aNode.IsContent()) {
  17196    nsTArray<nsIContent*> anonKids;
  17197    nsContentUtils::AppendNativeAnonymousChildren(aNode.AsContent(), anonKids,
  17198                                                  nsIContent::eAllChildren);
  17199    for (nsIContent* anonKid : anonKids) {
  17200      AddSizeOfNodeTree(*anonKid, aWindowSizes);
  17201    }
  17202 
  17203    if (auto* element = Element::FromNode(aNode)) {
  17204      if (ShadowRoot* shadow = element->GetShadowRoot()) {
  17205        AddSizeOfNodeTree(*shadow, aWindowSizes);
  17206      }
  17207    }
  17208  }
  17209 
  17210  // NOTE(emilio): If you feel smart and want to change this function to use
  17211  // GetNextNode(), think twice, since you'd need to handle <xbl:content> in a
  17212  // sane way, and kids of <content> won't point to the parent, so we'd never
  17213  // find the root node where we should stop at.
  17214  for (nsIContent* kid = aNode.GetFirstChild(); kid;
  17215       kid = kid->GetNextSibling()) {
  17216    AddSizeOfNodeTree(*kid, aWindowSizes);
  17217  }
  17218 }
  17219 
  17220 already_AddRefed<Document> Document::Constructor(const GlobalObject& aGlobal,
  17221                                                 ErrorResult& rv) {
  17222  nsCOMPtr<nsIScriptGlobalObject> global =
  17223      do_QueryInterface(aGlobal.GetAsSupports());
  17224  if (!global) {
  17225    rv.Throw(NS_ERROR_UNEXPECTED);
  17226    return nullptr;
  17227  }
  17228 
  17229  nsCOMPtr<nsIScriptObjectPrincipal> prin =
  17230      do_QueryInterface(aGlobal.GetAsSupports());
  17231  if (!prin) {
  17232    rv.Throw(NS_ERROR_UNEXPECTED);
  17233    return nullptr;
  17234  }
  17235 
  17236  nsCOMPtr<nsIURI> uri;
  17237  NS_NewURI(getter_AddRefs(uri), "about:blank");
  17238  if (!uri) {
  17239    rv.Throw(NS_ERROR_OUT_OF_MEMORY);
  17240    return nullptr;
  17241  }
  17242 
  17243  nsCOMPtr<Document> doc;
  17244  nsresult res =
  17245      NS_NewDOMDocument(getter_AddRefs(doc), VoidString(), u""_ns, nullptr, uri,
  17246                        uri, prin->GetPrincipal(), LoadedAsData::AsData, global,
  17247                        DocumentFlavor::Plain);
  17248  if (NS_FAILED(res)) {
  17249    rv.Throw(res);
  17250    return nullptr;
  17251  }
  17252 
  17253  doc->SetReadyStateInternal(Document::READYSTATE_COMPLETE);
  17254 
  17255  return doc.forget();
  17256 }
  17257 
  17258 UniquePtr<XPathExpression> Document::CreateExpression(
  17259    const nsAString& aExpression, XPathNSResolver* aResolver, ErrorResult& rv) {
  17260  return XPathEvaluator()->CreateExpression(aExpression, aResolver, rv);
  17261 }
  17262 
  17263 nsINode* Document::CreateNSResolver(nsINode& aNodeResolver) {
  17264  return XPathEvaluator()->CreateNSResolver(aNodeResolver);
  17265 }
  17266 
  17267 already_AddRefed<XPathResult> Document::Evaluate(
  17268    JSContext* aCx, const nsAString& aExpression, nsINode& aContextNode,
  17269    XPathNSResolver* aResolver, uint16_t aType, JS::Handle<JSObject*> aResult,
  17270    ErrorResult& rv) {
  17271  return XPathEvaluator()->Evaluate(aCx, aExpression, aContextNode, aResolver,
  17272                                    aType, aResult, rv);
  17273 }
  17274 
  17275 already_AddRefed<nsIAppWindow> Document::GetAppWindowIfToplevelChrome() const {
  17276  nsCOMPtr<nsIDocShellTreeItem> item = GetDocShell();
  17277  if (!item) {
  17278    return nullptr;
  17279  }
  17280  nsCOMPtr<nsIDocShellTreeOwner> owner;
  17281  item->GetTreeOwner(getter_AddRefs(owner));
  17282  nsCOMPtr<nsIAppWindow> appWin = do_GetInterface(owner);
  17283  if (!appWin) {
  17284    return nullptr;
  17285  }
  17286  nsCOMPtr<nsIDocShell> appWinShell;
  17287  appWin->GetDocShell(getter_AddRefs(appWinShell));
  17288  if (!SameCOMIdentity(appWinShell, item)) {
  17289    return nullptr;
  17290  }
  17291  return appWin.forget();
  17292 }
  17293 
  17294 WindowContext* Document::GetTopLevelWindowContext() const {
  17295  WindowContext* windowContext = GetWindowContext();
  17296  return windowContext ? windowContext->TopWindowContext() : nullptr;
  17297 }
  17298 
  17299 Document* Document::GetTopLevelContentDocumentIfSameProcess() {
  17300  Document* parent;
  17301 
  17302  if (!mLoadedAsData) {
  17303    parent = this;
  17304  } else {
  17305    nsCOMPtr<nsPIDOMWindowInner> window = do_QueryInterface(GetScopeObject());
  17306    if (!window) {
  17307      return nullptr;
  17308    }
  17309 
  17310    parent = window->GetExtantDoc();
  17311    if (!parent) {
  17312      return nullptr;
  17313    }
  17314  }
  17315 
  17316  do {
  17317    if (parent->IsTopLevelContentDocument()) {
  17318      break;
  17319    }
  17320 
  17321    // If we ever have a non-content parent before we hit a toplevel content
  17322    // parent, then we're never going to find one.  Just bail.
  17323    if (!parent->IsContentDocument()) {
  17324      return nullptr;
  17325    }
  17326 
  17327    parent = parent->GetInProcessParentDocument();
  17328  } while (parent);
  17329 
  17330  return parent;
  17331 }
  17332 
  17333 const Document* Document::GetTopLevelContentDocumentIfSameProcess() const {
  17334  return const_cast<Document*>(this)->GetTopLevelContentDocumentIfSameProcess();
  17335 }
  17336 
  17337 void Document::PropagateImageUseCounters(Document* aReferencingDocument) {
  17338  MOZ_ASSERT(IsBeingUsedAsImage());
  17339  MOZ_ASSERT(aReferencingDocument);
  17340 
  17341  if (!aReferencingDocument->mShouldReportUseCounters) {
  17342    // No need to propagate use counters to a document that itself won't report
  17343    // use counters.
  17344    return;
  17345  }
  17346 
  17347  MOZ_LOG(gUseCountersLog, LogLevel::Debug,
  17348          ("PropagateImageUseCounters from %s to %s",
  17349           nsContentUtils::TruncatedURLForDisplay(mDocumentURI).get(),
  17350           nsContentUtils::TruncatedURLForDisplay(
  17351               aReferencingDocument->mDocumentURI)
  17352               .get()));
  17353 
  17354  if (aReferencingDocument->IsBeingUsedAsImage()) {
  17355    NS_WARNING(
  17356        "Page use counters from nested image documents may not "
  17357        "propagate to the top-level document (bug 1657805)");
  17358  }
  17359 
  17360  SetCssUseCounterBits();
  17361  aReferencingDocument->mChildDocumentUseCounters |= mUseCounters;
  17362  aReferencingDocument->mChildDocumentUseCounters |= mChildDocumentUseCounters;
  17363 }
  17364 
  17365 bool Document::HasScriptsBlockedBySandbox() const {
  17366  return mSandboxFlags & SANDBOXED_SCRIPTS;
  17367 }
  17368 
  17369 void Document::SetCssUseCounterBits() {
  17370  if (StaticPrefs::layout_css_use_counters_enabled()) {
  17371    for (size_t i = 0; i < eCSSProperty_COUNT_with_aliases; ++i) {
  17372      auto id = NonCustomCSSPropertyId(i);
  17373      if (Servo_IsPropertyIdRecordedInUseCounter(mStyleUseCounters.get(), id)) {
  17374        SetUseCounter(nsCSSProps::UseCounterFor(id));
  17375      }
  17376    }
  17377  }
  17378 
  17379  if (StaticPrefs::layout_css_use_counters_unimplemented_enabled()) {
  17380    for (size_t i = 0; i < size_t(CountedUnknownProperty::Count); ++i) {
  17381      auto id = CountedUnknownProperty(i);
  17382      if (Servo_IsUnknownPropertyRecordedInUseCounter(mStyleUseCounters.get(),
  17383                                                      id)) {
  17384        SetUseCounter(UseCounter(eUseCounter_FirstCountedUnknownProperty + i));
  17385      }
  17386    }
  17387  }
  17388 }
  17389 
  17390 void Document::InitUseCounters() {
  17391  // We can be called more than once, e.g. when session history navigation shows
  17392  // us a second time.
  17393  if (mUseCountersInitialized) {
  17394    return;
  17395  }
  17396  mUseCountersInitialized = true;
  17397 
  17398  if (!ShouldIncludeInTelemetry()) {
  17399    return;
  17400  }
  17401 
  17402  // Now we know for sure that we should report use counters from this document.
  17403  mShouldReportUseCounters = true;
  17404 
  17405  WindowContext* top = GetWindowContextForPageUseCounters();
  17406  if (!top) {
  17407    // This is the case for SVG image documents.  They are not displayed in a
  17408    // window, but we still do want to record document use counters for them.
  17409    //
  17410    // Page use counter propagation is handled in PropagateImageUseCounters,
  17411    // so there is no need to use the cross-process machinery to send them.
  17412    MOZ_LOG(gUseCountersLog, LogLevel::Debug,
  17413            ("InitUseCounters for a non-displayed document [%s]",
  17414             nsContentUtils::TruncatedURLForDisplay(mDocumentURI).get()));
  17415    return;
  17416  }
  17417 
  17418  RefPtr<WindowGlobalChild> wgc = GetWindowGlobalChild();
  17419  if (!wgc) {
  17420    return;
  17421  }
  17422 
  17423  MOZ_LOG(gUseCountersLog, LogLevel::Debug,
  17424          ("InitUseCounters for a displayed document: %" PRIu64 " -> %" PRIu64
  17425           " [from %s]",
  17426           wgc->InnerWindowId(), top->Id(),
  17427           nsContentUtils::TruncatedURLForDisplay(mDocumentURI).get()));
  17428 
  17429  // Inform the parent process that we will send it page use counters later on.
  17430  wgc->SendExpectPageUseCounters(top);
  17431  mShouldSendPageUseCounters = true;
  17432 }
  17433 
  17434 // We keep separate counts for individual documents and top-level
  17435 // pages to more accurately track how many web pages might break if
  17436 // certain features were removed.  Consider the case of a single
  17437 // HTML document with several SVG images and/or iframes with
  17438 // sub-documents of their own.  If we maintained a single set of use
  17439 // counters and all the sub-documents use a particular feature, then
  17440 // telemetry would indicate that we would be breaking N documents if
  17441 // that feature were removed.  Whereas with a document/top-level
  17442 // page split, we can see that N documents would be affected, but
  17443 // only a single web page would be affected.
  17444 //
  17445 // The difference between the values of these two counts and the
  17446 // related use counters below tell us how many pages did *not* use
  17447 // the feature in question.  For instance, if we see that a given
  17448 // session has destroyed 30 content documents, but a particular use
  17449 // counter shows only a count of 5, we can infer that the use
  17450 // counter was *not* used in 25 of those 30 documents.
  17451 //
  17452 // We do things this way, rather than accumulating a boolean flag
  17453 // for each use counter, to avoid sending data for features
  17454 // that don't get widely used.  Doing things in this fashion means
  17455 // smaller telemetry payloads and faster processing on the server
  17456 // side.
  17457 void Document::ReportDocumentUseCounters() {
  17458  if (!mShouldReportUseCounters || mReportedDocumentUseCounters) {
  17459    return;
  17460  }
  17461 
  17462  mReportedDocumentUseCounters = true;
  17463 
  17464  // Note that a document is being destroyed.  See the comment above for how
  17465  // use counter data are interpreted relative to this measurement.
  17466  glean::use_counter::content_documents_destroyed.Add();
  17467 
  17468  // Ask all of our resource documents to report their own document use
  17469  // counters.
  17470  EnumerateExternalResources([](Document& aDoc) {
  17471    aDoc.ReportDocumentUseCounters();
  17472    return CallState::Continue;
  17473  });
  17474 
  17475  // Copy StyleUseCounters into our document use counters.
  17476  SetCssUseCounterBits();
  17477 
  17478  Maybe<nsCString> urlForLogging;
  17479  const bool dumpCounters = StaticPrefs::dom_use_counters_dump_document();
  17480  if (dumpCounters) {
  17481    urlForLogging.emplace(
  17482        nsContentUtils::TruncatedURLForDisplay(GetDocumentURI()));
  17483  }
  17484 
  17485  // Report our per-document use counters.
  17486  for (int32_t c = 0; c < eUseCounter_Count; ++c) {
  17487    auto uc = static_cast<UseCounter>(c);
  17488    if (!mUseCounters[uc]) {
  17489      continue;
  17490    }
  17491 
  17492    const char* metricName = IncrementUseCounter(uc, /* aIsPage = */ false);
  17493    if (dumpCounters) {
  17494      printf_stderr("USE_COUNTER_DOCUMENT: %s - %s\n", metricName,
  17495                    urlForLogging->get());
  17496    }
  17497  }
  17498 }
  17499 
  17500 void Document::ReportShadowedProperties() {
  17501  if (!ShouldIncludeInTelemetry()) {
  17502    return;
  17503  }
  17504 
  17505  for (const nsString& property : mShadowedHTMLDocumentProperties) {
  17506    glean::security::ShadowedHtmlDocumentPropertyAccessExtra extra = {};
  17507    extra.name = Some(NS_ConvertUTF16toUTF8(property));
  17508    glean::security::shadowed_html_document_property_access.Record(Some(extra));
  17509  }
  17510 }
  17511 
  17512 void Document::ReportLCP() {
  17513  // Do not record LCP in any histogram if the same value is being recorded
  17514  // in the domain pageload event.
  17515  if (mPageloadEventData.HasDomain()) {
  17516    return;
  17517  }
  17518 
  17519  const nsDOMNavigationTiming* timing = GetNavigationTiming();
  17520 
  17521  if (!ShouldIncludeInTelemetry() || !IsTopLevelContentDocument() || !timing ||
  17522      !timing->DocShellHasBeenActiveSinceNavigationStart()) {
  17523    return;
  17524  }
  17525 
  17526  TimeStamp lcpTime = timing->GetLargestContentfulRenderTimeStamp();
  17527 
  17528  if (!lcpTime) {
  17529    return;
  17530  }
  17531 
  17532  mozilla::glean::perf::largest_contentful_paint.AccumulateRawDuration(
  17533      lcpTime - timing->GetNavigationStartTimeStamp());
  17534 
  17535  if (!GetChannel()) {
  17536    return;
  17537  }
  17538 
  17539  nsCOMPtr<nsITimedChannel> timedChannel(do_QueryInterface(GetChannel()));
  17540  if (!timedChannel) {
  17541    return;
  17542  }
  17543 
  17544  TimeStamp responseStart;
  17545  timedChannel->GetResponseStart(&responseStart);
  17546 
  17547  if (!responseStart) {
  17548    // This happens when getting a response from the cache.
  17549    mozilla::glean::perf::largest_contentful_paint_from_response_start
  17550        .AccumulateRawDuration(lcpTime - timing->GetNavigationStartTimeStamp());
  17551  } else {
  17552    mozilla::glean::perf::largest_contentful_paint_from_response_start
  17553        .AccumulateRawDuration(lcpTime - responseStart);
  17554  }
  17555 
  17556  if (profiler_thread_is_being_profiled_for_markers()) {
  17557    MarkerInnerWindowId innerWindowID =
  17558        MarkerInnerWindowIdFromDocShell(GetDocShell());
  17559    GetNavigationTiming()->MaybeAddLCPProfilerMarker(innerWindowID);
  17560  }
  17561 }
  17562 
  17563 void Document::SendPageUseCounters() {
  17564  if (!mShouldReportUseCounters || !mShouldSendPageUseCounters) {
  17565    return;
  17566  }
  17567 
  17568  // Ask all of our resource documents to send their own document use
  17569  // counters to the parent process to be counted as page use counters.
  17570  EnumerateExternalResources([](Document& aDoc) {
  17571    aDoc.SendPageUseCounters();
  17572    return CallState::Continue;
  17573  });
  17574 
  17575  // Send our use counters to the parent process to accumulate them towards the
  17576  // page use counters for the top-level document.
  17577  //
  17578  // We take our own document use counters (those in mUseCounters) and any child
  17579  // document use counters (those in mChildDocumentUseCounters) that have been
  17580  // explicitly propagated up to us, which includes resource documents, static
  17581  // clones, and SVG images.
  17582  RefPtr<WindowGlobalChild> wgc = GetWindowGlobalChild();
  17583  if (!wgc) {
  17584    MOZ_ASSERT_UNREACHABLE(
  17585        "SendPageUseCounters should be called while we still have access "
  17586        "to our WindowContext");
  17587    MOZ_LOG(gUseCountersLog, LogLevel::Debug,
  17588            (" > too late to send page use counters"));
  17589    return;
  17590  }
  17591 
  17592  MOZ_LOG(gUseCountersLog, LogLevel::Debug,
  17593          ("Sending page use counters: from WindowContext %" PRIu64 " [%s]",
  17594           wgc->WindowContext()->Id(),
  17595           nsContentUtils::TruncatedURLForDisplay(GetDocumentURI()).get()));
  17596 
  17597  // Copy StyleUseCounters into our document use counters.
  17598  SetCssUseCounterBits();
  17599 
  17600  UseCounters counters = mUseCounters | mChildDocumentUseCounters;
  17601  wgc->SendAccumulatePageUseCounters(counters);
  17602 }
  17603 
  17604 void Document::MaybeRecomputePartitionKey() {
  17605  // We only need to recompute the partition key for the top-level content
  17606  // document.
  17607  if (!IsTopLevelContentDocument()) {
  17608    return;
  17609  }
  17610 
  17611  // Bail out early if there is no cookieJarSettings for this document. For
  17612  // example, a chrome document.
  17613  if (!mCookieJarSettings) {
  17614    return;
  17615  }
  17616 
  17617  // Check whether the partition key matches the document's node principal. They
  17618  // can be different if the document is sandboxed. In this case, the node
  17619  // principal is a null principal. But the partitionKey of the
  17620  // cookieJarSettings was derived from the channel URI of the top-level
  17621  // loading, which isn't a null principal. Therefore, we need to recompute
  17622  // the partition Key from the document's node principal to reflect the actual
  17623  // partition Key.
  17624  nsAutoCString originNoSuffix;
  17625  nsresult rv = NodePrincipal()->GetOriginNoSuffix(originNoSuffix);
  17626  NS_ENSURE_SUCCESS_VOID(rv);
  17627 
  17628  nsCOMPtr<nsIURI> originURI;
  17629  rv = NS_NewURI(getter_AddRefs(originURI), originNoSuffix);
  17630  NS_ENSURE_SUCCESS_VOID(rv);
  17631 
  17632  // Bail out early if we don't have a principal URI.
  17633  if (!originURI) {
  17634    return;
  17635  }
  17636 
  17637  OriginAttributes attrs;
  17638  attrs.SetPartitionKey(originURI, false);
  17639 
  17640  // We don't need to set the partition key if the cooieJarSettings'
  17641  // partitionKey matches the document's node principal.
  17642  if (attrs.mPartitionKey.Equals(
  17643          net::CookieJarSettings::Cast(mCookieJarSettings)
  17644              ->GetPartitionKey())) {
  17645    return;
  17646  }
  17647 
  17648  // Set the partition key to the document's node principal. So we will use the
  17649  // right partition key afterward.
  17650  mozilla::net::CookieJarSettings::Cast(mCookieJarSettings)
  17651      ->SetPartitionKey(originURI);
  17652 }
  17653 
  17654 bool Document::RecomputeResistFingerprinting(bool aForceRefreshRTPCallerType) {
  17655  mOverriddenFingerprintingSettings.reset();
  17656  const bool previous = mShouldResistFingerprinting;
  17657 
  17658  RefPtr<BrowsingContext> opener =
  17659      GetBrowsingContext() ? GetBrowsingContext()->GetOpener() : nullptr;
  17660  // If we have a parent or opener document, defer to it only when we have a
  17661  // null principal (e.g. a sandboxed iframe or a data: uri) or when the
  17662  // document's principal matches.  This means we will defer about:blank,
  17663  // about:srcdoc, blob and same-origin iframes/popups to the parent/opener,
  17664  // but not cross-origin ones.  Cross-origin iframes/popups may inherit a
  17665  // CookieJarSettings.mShouldRFP = false bit however, which will be respected.
  17666  auto shouldInheritFrom = [this](Document* aDoc) {
  17667    return aDoc && (this->NodePrincipal()->Equals(aDoc->NodePrincipal()) ||
  17668                    this->NodePrincipal()->GetIsNullPrincipal());
  17669  };
  17670 
  17671  if (shouldInheritFrom(mParentDocument)) {
  17672    MOZ_LOG(
  17673        nsContentUtils::ResistFingerprintingLog(), LogLevel::Debug,
  17674        ("Inside RecomputeResistFingerprinting with URI %s and deferring "
  17675         "to parent document %s",
  17676         GetDocumentURI() ? GetDocumentURI()->GetSpecOrDefault().get() : "null",
  17677         mParentDocument->GetDocumentURI()->GetSpecOrDefault().get()));
  17678    mShouldResistFingerprinting = mParentDocument->ShouldResistFingerprinting(
  17679        RFPTarget::IsAlwaysEnabledForPrecompute);
  17680    mOverriddenFingerprintingSettings =
  17681        mParentDocument->mOverriddenFingerprintingSettings;
  17682  } else if (opener && shouldInheritFrom(opener->GetDocument())) {
  17683    MOZ_LOG(
  17684        nsContentUtils::ResistFingerprintingLog(), LogLevel::Debug,
  17685        ("Inside RecomputeResistFingerprinting with URI %s and deferring to "
  17686         "opener document %s",
  17687         GetDocumentURI() ? GetDocumentURI()->GetSpecOrDefault().get() : "null",
  17688         opener->GetDocument()->GetDocumentURI()->GetSpecOrDefault().get()));
  17689    mShouldResistFingerprinting =
  17690        opener->GetDocument()->ShouldResistFingerprinting(
  17691            RFPTarget::IsAlwaysEnabledForPrecompute);
  17692    mOverriddenFingerprintingSettings =
  17693        opener->GetDocument()->mOverriddenFingerprintingSettings;
  17694  } else if (nsContentUtils::IsChromeDoc(this)) {
  17695    MOZ_LOG(nsContentUtils::ResistFingerprintingLog(), LogLevel::Debug,
  17696            ("Inside RecomputeResistFingerprinting with a ChromeDoc"));
  17697 
  17698    mShouldResistFingerprinting = false;
  17699    mOverriddenFingerprintingSettings.reset();
  17700  } else if (mChannel) {
  17701    MOZ_LOG(nsContentUtils::ResistFingerprintingLog(), LogLevel::Debug,
  17702            ("Inside RecomputeResistFingerprinting with URI %s",
  17703             GetDocumentURI() ? GetDocumentURI()->GetSpecOrDefault().get()
  17704                              : "null"));
  17705    mShouldResistFingerprinting = nsContentUtils::ShouldResistFingerprinting(
  17706        mChannel, RFPTarget::IsAlwaysEnabledForPrecompute);
  17707 
  17708    nsCOMPtr<nsILoadInfo> loadInfo = mChannel->LoadInfo();
  17709    mOverriddenFingerprintingSettings =
  17710        loadInfo->GetOverriddenFingerprintingSettings();
  17711  } else {
  17712    MOZ_LOG(nsContentUtils::ResistFingerprintingLog(), LogLevel::Debug,
  17713            ("Inside RecomputeResistFingerprinting fallback case."));
  17714    // We still preserve the behavior in the fallback case. But, it means there
  17715    // might be some cases we haven't considered yet and we need to investigate
  17716    // them.
  17717    mShouldResistFingerprinting = nsContentUtils::ShouldResistFingerprinting(
  17718        mChannel, RFPTarget::IsAlwaysEnabledForPrecompute);
  17719    mOverriddenFingerprintingSettings.reset();
  17720  }
  17721 
  17722  MOZ_LOG(nsContentUtils::ResistFingerprintingLog(), LogLevel::Debug,
  17723          ("Finished RecomputeResistFingerprinting with result %x",
  17724           mShouldResistFingerprinting));
  17725 
  17726  bool changed = previous != mShouldResistFingerprinting;
  17727  if (changed || aForceRefreshRTPCallerType) {
  17728    if (auto win = nsGlobalWindowInner::Cast(GetInnerWindow())) {
  17729      win->RefreshReduceTimerPrecisionCallerType();
  17730    }
  17731  }
  17732  return changed;
  17733 }
  17734 
  17735 bool Document::ShouldResistFingerprinting(RFPTarget aTarget) const {
  17736  return mShouldResistFingerprinting &&
  17737         nsRFPService::IsRFPEnabledFor(this->IsInPrivateBrowsing(), aTarget,
  17738                                       mOverriddenFingerprintingSettings);
  17739 }
  17740 
  17741 void Document::RecordCanvasUsage(CanvasUsage& aUsage) {
  17742  // Limit the number of recent canvas extraction uses that are tracked.
  17743  // Because we are now covering more canvas extraction sources, we need to
  17744  // increase this a bit
  17745  const size_t kTrackedCanvasLimit = 15;
  17746  // Timeout between different canvas extractions.
  17747  const uint64_t kTimeoutUsec = 3000 * 1000;
  17748 
  17749  uint64_t now = PR_Now();
  17750 
  17751  nsCString originNoSuffix;
  17752  if (NS_FAILED(NodePrincipal()->GetOriginNoSuffix(originNoSuffix))) {
  17753    MOZ_LOG(gFingerprinterDetection, LogLevel::Error,
  17754            ("Document:: %p Could not get originsuffix", this));
  17755    return;
  17756  }
  17757  nsCOMPtr<nsIURI> uri = NodePrincipal()->GetURI();
  17758  if (!uri) {
  17759    MOZ_LOG(gFingerprinterDetection, LogLevel::Error,
  17760            ("Document:: %p Could not get uri", this));
  17761    return;
  17762  }
  17763  if (MOZ_LOG_TEST(gFingerprinterDetection, LogLevel::Debug)) {
  17764    nsAutoCString filename;
  17765    uint32_t lineNum = 0;
  17766    filename.AssignLiteral("<unknown>");
  17767    JSContext* cx = nsContentUtils::GetCurrentJSContext();
  17768    if (cx) {
  17769      JS::AutoFilename scriptFilename;
  17770      JS::ColumnNumberOneOrigin colOneOrigin;
  17771      if (JS::DescribeScriptedCaller(&scriptFilename, cx, &lineNum,
  17772                                     &colOneOrigin)) {
  17773        if (const char* file = scriptFilename.get()) {
  17774          filename = nsDependentCString(file);
  17775        }
  17776      }
  17777    }
  17778 
  17779    nsAutoCString uriString;
  17780    (void)uri->GetSpec(uriString);
  17781 
  17782    MOZ_LOG(gFingerprinterDetection, LogLevel::Debug,
  17783            ("Document:: %p %s recording canvas usage of type %s on %s in %s",
  17784             this, originNoSuffix.get(),
  17785             CanvasUsageSourceToString(aUsage.mUsageSource).get(),
  17786             uriString.get(), filename.get()));
  17787  }
  17788 
  17789  // Check if we need to clear the usage data for this source.
  17790  if (mCanvasUsageLastTimestamp != 0 &&
  17791      (now - mCanvasUsageLastTimestamp) > kTimeoutUsec) {
  17792    MOZ_LOG(
  17793        gFingerprinterDetection, LogLevel::Verbose,
  17794        ("Document:: %p %s clearing canvas array", this, originNoSuffix.get()));
  17795    mCanvasUsageData.Clear();
  17796  } else if (mCanvasUsageData.Length() > kTrackedCanvasLimit) {
  17797    MOZ_LOG(gFingerprinterDetection, LogLevel::Verbose,
  17798            ("Document:: %p %s removing oldest canvas "
  17799             "usage in array",
  17800             this, originNoSuffix.get()));
  17801    mCanvasUsageData.RemoveElementAt(0);
  17802  } else {
  17803    MOZ_LOG(gFingerprinterDetection, LogLevel::Verbose,
  17804            ("Document:: %p %s recorded canvas "
  17805             "usage of type %s in array, records: %zu",
  17806             this, originNoSuffix.get(),
  17807             CanvasUsageSourceToString(aUsage.mUsageSource).get(),
  17808             static_cast<size_t>(mCanvasUsageData.Length() + 1)));
  17809  }
  17810 
  17811  // Update the timestamp and append the new usage.
  17812  mCanvasUsageLastTimestamp = now;
  17813  mCanvasUsageData.AppendElement(aUsage);
  17814 
  17815  nsIChannel* channel = GetChannel();
  17816  if (!channel) {
  17817    MOZ_LOG(
  17818        gFingerprinterDetection, LogLevel::Warning,
  17819        ("Document:: %p %s no channel available", this, originNoSuffix.get()));
  17820 
  17821    // Borrowed from ReComputeResistFingerprinting
  17822    // which tells me this is probably a common problem...
  17823    auto shouldInheritFrom = [this](Document* aDoc) {
  17824      return aDoc && this->NodePrincipal() &&
  17825             (this->NodePrincipal()->Equals(aDoc->NodePrincipal()) ||
  17826              this->NodePrincipal()->GetIsNullPrincipal());
  17827    };
  17828 
  17829    // Climb parent documents until we find a channel.
  17830    Document* docToCheck = this;
  17831    while (docToCheck && !channel) {
  17832      if (docToCheck->mParentDocument &&
  17833          shouldInheritFrom(docToCheck->mParentDocument)) {
  17834        channel = docToCheck->mParentDocument->GetChannel();
  17835      }
  17836      docToCheck = docToCheck->mParentDocument;
  17837    }
  17838 
  17839    docToCheck = this;
  17840    while (docToCheck && !channel) {
  17841      RefPtr<BrowsingContext> opener =
  17842          docToCheck->GetBrowsingContext()
  17843              ? docToCheck->GetBrowsingContext()->GetOpener()
  17844              : nullptr;
  17845      docToCheck = opener ? opener->GetDocument() : nullptr;
  17846 
  17847      if (docToCheck && shouldInheritFrom(docToCheck)) {
  17848        channel = docToCheck->GetChannel();
  17849      }
  17850    }
  17851 
  17852    if (!channel) {
  17853      MOZ_LOG(gFingerprinterDetection, LogLevel::Warning,
  17854              ("Document:: %p %s still could not find a channel", this,
  17855               originNoSuffix.get()));
  17856    }
  17857  }
  17858 
  17859  nsRFPService::MaybeReportCanvasFingerprinter(mCanvasUsageData, channel, uri,
  17860                                               originNoSuffix);
  17861 }
  17862 
  17863 void Document::RecordFontFingerprinting() {
  17864  nsCString originNoSuffix;
  17865  if (NS_FAILED(NodePrincipal()->GetOriginNoSuffix(originNoSuffix))) {
  17866    return;
  17867  }
  17868  nsCOMPtr<nsIURI> uri = NodePrincipal()->GetURI();
  17869  if (!uri) {
  17870    return;
  17871  }
  17872 
  17873  nsRFPService::MaybeReportFontFingerprinter(GetChannel(), uri, originNoSuffix);
  17874 }
  17875 
  17876 bool Document::IsInPrivateBrowsing() const { return mIsInPrivateBrowsing; }
  17877 
  17878 WindowContext* Document::GetWindowContextForPageUseCounters() const {
  17879  if (mDisplayDocument) {
  17880    // If we are a resource document, then go through it to find the
  17881    // top-level document.
  17882    return mDisplayDocument->GetWindowContextForPageUseCounters();
  17883  }
  17884 
  17885  if (mOriginalDocument) {
  17886    // For static clones (print preview documents), contribute page use counters
  17887    // towards the original document.
  17888    return mOriginalDocument->GetWindowContextForPageUseCounters();
  17889  }
  17890 
  17891  WindowContext* wc = GetTopLevelWindowContext();
  17892  if (!wc || !wc->GetBrowsingContext()->IsContent()) {
  17893    return nullptr;
  17894  }
  17895 
  17896  return wc;
  17897 }
  17898 
  17899 void Document::UpdateIntersections(TimeStamp aNowTime) {
  17900  if (!mIntersectionObservers.IsEmpty()) {
  17901    DOMHighResTimeStamp time = 0;
  17902    if (nsPIDOMWindowInner* win = GetInnerWindow()) {
  17903      if (Performance* perf = win->GetPerformance()) {
  17904        time = perf->TimeStampToDOMHighResForRendering(aNowTime);
  17905      }
  17906    }
  17907    for (DOMIntersectionObserver* observer : mIntersectionObservers) {
  17908      observer->Update(*this, time);
  17909    }
  17910    Dispatch(NewRunnableMethod("Document::NotifyIntersectionObservers", this,
  17911                               &Document::NotifyIntersectionObservers));
  17912  }
  17913 }
  17914 
  17915 static void UpdateEffectsOnBrowsingContext(BrowsingContext* aBc,
  17916                                           const IntersectionInput& aInput,
  17917                                           bool aIncludeInactive) {
  17918  Element* el = aBc->GetEmbedderElement();
  17919  if (!el) {
  17920    return;
  17921  }
  17922  auto* rb = RemoteBrowser::GetFrom(el);
  17923  if (!rb) {
  17924    return;
  17925  }
  17926  const bool isInactiveTop = aBc->IsTop() && !aBc->IsActive();
  17927  nsSubDocumentFrame* subDocFrame = do_QueryFrame(el->GetPrimaryFrame());
  17928  rb->UpdateEffects([&] {
  17929    if (isInactiveTop) {
  17930      // We don't use the visible rect of top browsers, so if they're inactive
  17931      // don't waste time computing it. Note that for OOP iframes, it might seem
  17932      // a bit wasteful to bother computing this in background tabs, but:
  17933      //  * We need it so that child frames know if they're visible or not, in
  17934      //    case they're preserving layers for example.
  17935      //  * If we're hidden, our refresh driver is throttled and we don't run
  17936      //    this code very often anyways.
  17937      return EffectsInfo::FullyHidden();
  17938    }
  17939    if (MOZ_UNLIKELY(!subDocFrame)) {
  17940      // <frame> not inside a <frameset> might not create a subdoc frame,
  17941      // for example.
  17942      return EffectsInfo::FullyHidden();
  17943    }
  17944    const bool inPopup = subDocFrame->HasAnyStateBits(NS_FRAME_IN_POPUP);
  17945    Maybe<nsRect> visibleRect;
  17946    if (inPopup) {
  17947      nsMenuPopupFrame* popup =
  17948          do_QueryFrame(nsLayoutUtils::GetDisplayRootFrame(subDocFrame));
  17949      MOZ_ASSERT(popup);
  17950      if (!popup || !popup->IsVisibleOrShowing()) {
  17951        return EffectsInfo::FullyHidden();
  17952      }
  17953      // Be a bit conservative on popups and assume remote frames in there are
  17954      // fully visible.
  17955      visibleRect = Some(subDocFrame->GetDestRect());
  17956    } else {
  17957      const IntersectionOutput output = DOMIntersectionObserver::Intersect(
  17958          aInput, *el, DOMIntersectionObserver::BoxToUse::Content);
  17959      if (!output.Intersects()) {
  17960        // XXX do we want to pass the scale and such down even if out of the
  17961        // viewport?
  17962        return EffectsInfo::FullyHidden();
  17963      }
  17964      visibleRect = subDocFrame->GetVisibleRect();
  17965      if (!visibleRect) {
  17966        // If we have no visible rect (e.g., because we are zero-sized) we
  17967        // still want to provide the intersection rect in order to get the
  17968        // right throttling behavior.
  17969        visibleRect.emplace(*output.mIntersectionRect -
  17970                            output.mTargetRect.TopLeft());
  17971      }
  17972      // If we're paginated, the visible rect from the display list might not be
  17973      // reasonable, because there can be multiple display items for the frame
  17974      // and the rect would be the last one painted. We assume the frame is
  17975      // fully visible, lacking something better.
  17976      if (subDocFrame->PresContext()->IsPaginated()) {
  17977        visibleRect = Some(subDocFrame->GetDestRect());
  17978      }
  17979    }
  17980    gfx::MatrixScales rasterScale = subDocFrame->GetRasterScale();
  17981    ParentLayerToScreenScale2D transformToAncestorScale =
  17982        ParentLayerToParentLayerScale(
  17983            subDocFrame->PresShell()->GetCumulativeResolution()) *
  17984        nsLayoutUtils::GetTransformToAncestorScaleCrossProcessForFrameMetrics(
  17985            subDocFrame);
  17986    return EffectsInfo::VisibleWithinRect(visibleRect, rasterScale,
  17987                                          transformToAncestorScale);
  17988  }());
  17989  if (subDocFrame && (!isInactiveTop || aIncludeInactive)) {
  17990    if (nsFrameLoader* fl = subDocFrame->FrameLoader()) {
  17991      fl->UpdatePositionAndSize(subDocFrame);
  17992    }
  17993  }
  17994 }
  17995 
  17996 void Document::UpdateRemoteFrameEffects(bool aIncludeInactive) {
  17997  const IntersectionInput input =
  17998      DOMIntersectionObserver::ComputeInputForIframeThrottling(*this);
  17999  if (auto* wc = GetWindowContext()) {
  18000    for (const RefPtr<BrowsingContext>& child : wc->Children()) {
  18001      UpdateEffectsOnBrowsingContext(child, input, aIncludeInactive);
  18002    }
  18003  }
  18004  if (XRE_IsParentProcess()) {
  18005    if (auto* bc = GetBrowsingContext(); bc && bc->IsTop()) {
  18006      bc->Canonical()->CallOnTopDescendants(
  18007          [&](CanonicalBrowsingContext* aDescendant) {
  18008            UpdateEffectsOnBrowsingContext(aDescendant, input,
  18009                                           aIncludeInactive);
  18010            return CallState::Continue;
  18011          },
  18012          CanonicalBrowsingContext::TopDescendantKind::NonNested);
  18013    }
  18014  }
  18015  EnumerateSubDocuments([aIncludeInactive](Document& aDoc) {
  18016    aDoc.UpdateRemoteFrameEffects(aIncludeInactive);
  18017    return CallState::Continue;
  18018  });
  18019 }
  18020 
  18021 void Document::SynchronouslyUpdateRemoteBrowserDimensions(
  18022    bool aIncludeInactive) {
  18023  FlushPendingNotifications(FlushType::Layout);
  18024  UpdateRemoteFrameEffects(aIncludeInactive);
  18025 }
  18026 
  18027 void Document::NotifyIntersectionObservers() {
  18028  const auto observers = ToTArray<nsTArray<RefPtr<DOMIntersectionObserver>>>(
  18029      mIntersectionObservers);
  18030  for (const auto& observer : observers) {
  18031    // MOZ_KnownLive because the 'observers' array guarantees to keep it
  18032    // alive.
  18033    MOZ_KnownLive(observer)->Notify();
  18034  }
  18035 }
  18036 
  18037 DOMIntersectionObserver& Document::EnsureLazyLoadObserver() {
  18038  if (!mLazyLoadObserver) {
  18039    mLazyLoadObserver = DOMIntersectionObserver::CreateLazyLoadObserver(*this);
  18040  }
  18041  return *mLazyLoadObserver;
  18042 }
  18043 
  18044 void Document::ObserveForLastRememberedSize(Element& aElement) {
  18045  if (NS_WARN_IF(!IsActive())) {
  18046    return;
  18047  }
  18048  mElementsObservedForLastRememberedSize.Insert(&aElement);
  18049 }
  18050 
  18051 void Document::UnobserveForLastRememberedSize(Element& aElement) {
  18052  mElementsObservedForLastRememberedSize.Remove(&aElement);
  18053 }
  18054 
  18055 void Document::UpdateLastRememberedSizes() {
  18056  auto shouldRemoveElement = [&](auto* element) {
  18057    if (element->GetComposedDoc() != this) {
  18058      element->RemoveLastRememberedBSize();
  18059      element->RemoveLastRememberedISize();
  18060      return true;
  18061    }
  18062    return !element->GetPrimaryFrame();
  18063  };
  18064 
  18065  for (auto it = mElementsObservedForLastRememberedSize.begin(),
  18066            end = mElementsObservedForLastRememberedSize.end();
  18067       it != end; ++it) {
  18068    if (shouldRemoveElement(*it)) {
  18069      mElementsObservedForLastRememberedSize.Remove(it);
  18070      continue;
  18071    }
  18072    auto* element = *it;
  18073    MOZ_ASSERT(element->GetComposedDoc() == this);
  18074    nsIFrame* frame = element->GetPrimaryFrame();
  18075    MOZ_ASSERT(frame);
  18076 
  18077    // As for ResizeObserver, skip nodes hidden `content-visibility`.
  18078    if (frame->IsHiddenByContentVisibilityOnAnyAncestor()) {
  18079      continue;
  18080    }
  18081 
  18082    MOZ_ASSERT(!frame->IsLineParticipant() || frame->IsReplaced(),
  18083               "Should have unobserved non-replaced inline.");
  18084    MOZ_ASSERT(!frame->HidesContent(),
  18085               "Should have unobserved element skipping its contents.");
  18086    const nsStylePosition* stylePos = frame->StylePosition();
  18087    const WritingMode wm = frame->GetWritingMode();
  18088    bool canUpdateBSize = stylePos->ContainIntrinsicBSize(wm).HasAuto();
  18089    bool canUpdateISize = stylePos->ContainIntrinsicISize(wm).HasAuto();
  18090    MOZ_ASSERT(canUpdateBSize || !element->HasLastRememberedBSize(),
  18091               "Should have removed the last remembered block size.");
  18092    MOZ_ASSERT(canUpdateISize || !element->HasLastRememberedISize(),
  18093               "Should have removed the last remembered inline size.");
  18094    MOZ_ASSERT(canUpdateBSize || canUpdateISize,
  18095               "Should have unobserved if we can't update any size.");
  18096 
  18097    AutoTArray<LogicalPixelSize, 1> contentSizeList =
  18098        ResizeObserver::CalculateBoxSize(element,
  18099                                         ResizeObserverBoxOptions::Content_box,
  18100                                         /* aForceFragmentHandling */ true);
  18101    MOZ_ASSERT(!contentSizeList.IsEmpty());
  18102 
  18103    if (canUpdateBSize) {
  18104      float bSize = 0;
  18105      for (const auto& current : contentSizeList) {
  18106        bSize += current.BSize();
  18107      }
  18108      element->SetLastRememberedBSize(bSize);
  18109    }
  18110    if (canUpdateISize) {
  18111      float iSize = 0;
  18112      for (const auto& current : contentSizeList) {
  18113        iSize = std::max(iSize, current.ISize());
  18114      }
  18115      element->SetLastRememberedISize(iSize);
  18116    }
  18117  }
  18118 }
  18119 
  18120 void Document::SetAncestorOriginsList(
  18121    nsTArray<nsString>&& aAncestorOriginsList) {
  18122  mAncestorOriginsList = std::move(aAncestorOriginsList);
  18123 }
  18124 
  18125 Span<const nsString> Document::GetAncestorOriginsList() const {
  18126  return mAncestorOriginsList;
  18127 }
  18128 
  18129 already_AddRefed<DOMStringList> Document::AncestorOrigins() const {
  18130  RefPtr<DOMStringList> list = new DOMStringList();
  18131  for (const auto& origin : mAncestorOriginsList) {
  18132    list->Add(origin);
  18133  }
  18134  return list.forget();
  18135 }
  18136 
  18137 void Document::NotifyLayerManagerRecreated() {
  18138  NotifyActivityChanged();
  18139  EnumerateSubDocuments([](Document& aSubDoc) {
  18140    aSubDoc.NotifyLayerManagerRecreated();
  18141    return CallState::Continue;
  18142  });
  18143 }
  18144 
  18145 XPathEvaluator* Document::XPathEvaluator() {
  18146  if (!mXPathEvaluator) {
  18147    mXPathEvaluator.reset(new dom::XPathEvaluator(this));
  18148  }
  18149  return mXPathEvaluator.get();
  18150 }
  18151 
  18152 already_AddRefed<nsIDocumentEncoder> Document::GetCachedEncoder() {
  18153  return mCachedEncoder.forget();
  18154 }
  18155 
  18156 void Document::SetCachedEncoder(already_AddRefed<nsIDocumentEncoder> aEncoder) {
  18157  mCachedEncoder = aEncoder;
  18158 }
  18159 
  18160 nsILoadContext* Document::GetLoadContext() const { return mDocumentContainer; }
  18161 
  18162 nsIDocShell* Document::GetDocShell() const { return mDocumentContainer; }
  18163 
  18164 void Document::SetStateObject(nsIStructuredCloneContainer* scContainer) {
  18165  mStateObjectContainer = scContainer;
  18166  mCachedStateObject = JS::UndefinedValue();
  18167  mCachedStateObjectValid = false;
  18168 }
  18169 
  18170 already_AddRefed<Element> Document::CreateHTMLElement(nsAtom* aTag) {
  18171  RefPtr<mozilla::dom::NodeInfo> nodeInfo;
  18172  nodeInfo = mNodeInfoManager->GetNodeInfo(aTag, nullptr, kNameSpaceID_XHTML,
  18173                                           ELEMENT_NODE);
  18174  MOZ_ASSERT(nodeInfo, "GetNodeInfo should never fail");
  18175 
  18176  nsCOMPtr<Element> element;
  18177  DebugOnly<nsresult> rv =
  18178      NS_NewHTMLElement(getter_AddRefs(element), nodeInfo.forget(),
  18179                        mozilla::dom::NOT_FROM_PARSER);
  18180 
  18181  MOZ_ASSERT(NS_SUCCEEDED(rv), "NS_NewHTMLElement should never fail");
  18182  return element.forget();
  18183 }
  18184 
  18185 void AutoWalkBrowsingContextGroup::SuppressBrowsingContext(
  18186    BrowsingContext* aContext) {
  18187  aContext->PreOrderWalk([&](BrowsingContext* aBC) {
  18188    if (nsCOMPtr<nsPIDOMWindowOuter> win = aBC->GetDOMWindow()) {
  18189      if (RefPtr<Document> doc = win->GetExtantDoc()) {
  18190        SuppressDocument(doc);
  18191        mDocuments.AppendElement(doc);
  18192      }
  18193    }
  18194  });
  18195 }
  18196 
  18197 void AutoWalkBrowsingContextGroup::SuppressBrowsingContextGroup(
  18198    BrowsingContextGroup* aGroup) {
  18199  for (const auto& bc : aGroup->Toplevels()) {
  18200    SuppressBrowsingContext(bc);
  18201  }
  18202 }
  18203 
  18204 nsAutoSyncOperation::nsAutoSyncOperation(Document* aDoc,
  18205                                         SyncOperationBehavior aSyncBehavior)
  18206    : mSyncBehavior(aSyncBehavior) {
  18207  mMicroTaskLevel = 0;
  18208  if (CycleCollectedJSContext* ccjs = CycleCollectedJSContext::Get()) {
  18209    mMicroTaskLevel = ccjs->MicroTaskLevel();
  18210    ccjs->SetMicroTaskLevel(0);
  18211    ccjs->EnterSyncOperation();
  18212  }
  18213  if (aDoc) {
  18214    mBrowsingContext = aDoc->GetBrowsingContext();
  18215    if (InputTaskManager::CanSuspendInputEvent()) {
  18216      if (auto* bcg = aDoc->GetDocGroup()->GetBrowsingContextGroup()) {
  18217        SuppressBrowsingContextGroup(bcg);
  18218      }
  18219    } else if (mBrowsingContext) {
  18220      SuppressBrowsingContext(mBrowsingContext->Top());
  18221    }
  18222    if (mBrowsingContext &&
  18223        mSyncBehavior == SyncOperationBehavior::eSuspendInput &&
  18224        InputTaskManager::CanSuspendInputEvent()) {
  18225      mBrowsingContext->Group()->IncInputEventSuspensionLevel();
  18226    }
  18227  }
  18228 }
  18229 
  18230 void nsAutoSyncOperation::SuppressDocument(Document* aDoc) {
  18231  if (RefPtr<nsGlobalWindowInner> win =
  18232          nsGlobalWindowInner::Cast(aDoc->GetInnerWindow())) {
  18233    win->GetTimeoutManager()->BeginSyncOperation();
  18234  }
  18235  aDoc->SetIsInSyncOperation(true);
  18236 }
  18237 
  18238 void nsAutoSyncOperation::UnsuppressDocument(Document* aDoc) {
  18239  if (RefPtr<nsGlobalWindowInner> win =
  18240          nsGlobalWindowInner::Cast(aDoc->GetInnerWindow())) {
  18241    win->GetTimeoutManager()->EndSyncOperation();
  18242  }
  18243  aDoc->SetIsInSyncOperation(false);
  18244 }
  18245 
  18246 nsAutoSyncOperation::~nsAutoSyncOperation() {
  18247  UnsuppressDocuments();
  18248  CycleCollectedJSContext* ccjs = CycleCollectedJSContext::Get();
  18249  if (ccjs) {
  18250    ccjs->SetMicroTaskLevel(mMicroTaskLevel);
  18251    ccjs->LeaveSyncOperation();
  18252  }
  18253  if (mBrowsingContext &&
  18254      mSyncBehavior == SyncOperationBehavior::eSuspendInput &&
  18255      InputTaskManager::CanSuspendInputEvent()) {
  18256    mBrowsingContext->Group()->DecInputEventSuspensionLevel();
  18257  }
  18258 }
  18259 
  18260 void Document::SetIsInSyncOperation(bool aSync) {
  18261  if (CycleCollectedJSContext* ccjs = CycleCollectedJSContext::Get()) {
  18262    ccjs->UpdateMicroTaskSuppressionGeneration();
  18263  }
  18264 
  18265  if (aSync) {
  18266    ++mInSyncOperationCount;
  18267  } else {
  18268    --mInSyncOperationCount;
  18269  }
  18270 }
  18271 
  18272 gfxUserFontSet* Document::GetUserFontSet() {
  18273  if (!mFontFaceSet) {
  18274    return nullptr;
  18275  }
  18276 
  18277  return mFontFaceSet->GetImpl();
  18278 }
  18279 
  18280 void Document::FlushUserFontSet() {
  18281  if (!mFontFaceSetDirty) {
  18282    return;
  18283  }
  18284 
  18285  mFontFaceSetDirty = false;
  18286 
  18287  if (gfxPlatform::GetPlatform()->DownloadableFontsEnabled()) {
  18288    nsTArray<nsFontFaceRuleContainer> rules;
  18289    RefPtr<PresShell> presShell = GetPresShell();
  18290    if (presShell) {
  18291      MOZ_ASSERT(mStyleSetFilled);
  18292      EnsureStyleSet().AppendFontFaceRules(rules);
  18293    }
  18294 
  18295    if (!mFontFaceSet && !rules.IsEmpty()) {
  18296      mFontFaceSet = FontFaceSet::CreateForDocument(this);
  18297    }
  18298 
  18299    bool changed = false;
  18300    if (mFontFaceSet) {
  18301      changed = mFontFaceSet->UpdateRules(rules);
  18302    }
  18303 
  18304    // We need to enqueue a style change reflow (for later) to
  18305    // reflect that we're modifying @font-face rules.  (However,
  18306    // without a reflow, nothing will happen to start any downloads
  18307    // that are needed.)
  18308    if (changed && presShell) {
  18309      if (nsPresContext* presContext = presShell->GetPresContext()) {
  18310        presContext->UserFontSetUpdated();
  18311      }
  18312    }
  18313  }
  18314 }
  18315 
  18316 void Document::MarkUserFontSetDirty() {
  18317  if (mFontFaceSetDirty) {
  18318    return;
  18319  }
  18320  mFontFaceSetDirty = true;
  18321  if (PresShell* presShell = GetPresShell()) {
  18322    presShell->EnsureStyleFlush();
  18323  }
  18324 }
  18325 
  18326 FontFaceSet* Document::Fonts() {
  18327  if (!mFontFaceSet) {
  18328    mFontFaceSet = FontFaceSet::CreateForDocument(this);
  18329    FlushUserFontSet();
  18330  }
  18331  return mFontFaceSet;
  18332 }
  18333 
  18334 void Document::ReportHasScrollLinkedEffect(
  18335    const TimeStamp& aTimeStamp, ReportToConsole aReportToConsole /* = Yes */) {
  18336  MOZ_ASSERT(!aTimeStamp.IsNull());
  18337 
  18338  if (!mLastScrollLinkedEffectDetectionTime.IsNull() &&
  18339      mLastScrollLinkedEffectDetectionTime >= aTimeStamp) {
  18340    return;
  18341  }
  18342 
  18343  if (aReportToConsole == ReportToConsole::Yes &&
  18344      mLastScrollLinkedEffectDetectionTime.IsNull()) {
  18345    // Report to console just once.
  18346    nsContentUtils::ReportToConsole(
  18347        nsIScriptError::warningFlag, "Async Pan/Zoom"_ns, this,
  18348        nsContentUtils::eLAYOUT_PROPERTIES, "ScrollLinkedEffectFound3");
  18349  }
  18350 
  18351  mLastScrollLinkedEffectDetectionTime = aTimeStamp;
  18352 }
  18353 
  18354 bool Document::HasScrollLinkedEffect() const {
  18355  if (nsPresContext* pc = GetPresContext()) {
  18356    return mLastScrollLinkedEffectDetectionTime ==
  18357           pc->RefreshDriver()->MostRecentRefresh();
  18358  }
  18359 
  18360  return false;
  18361 }
  18362 
  18363 void Document::SetSHEntryHasUserInteraction(bool aHasInteraction) {
  18364  if (RefPtr<WindowContext> topWc = GetTopLevelWindowContext()) {
  18365    // Setting has user interaction on a discarded browsing context has
  18366    // no effect.
  18367    (void)topWc->SetSHEntryHasUserInteraction(aHasInteraction);
  18368  }
  18369 
  18370  // For when SHIP is not enabled, we need to get the current entry
  18371  // directly from the docshell.
  18372  nsIDocShell* docShell = GetDocShell();
  18373  if (docShell) {
  18374    nsCOMPtr<nsISHEntry> currentEntry;
  18375    bool oshe;
  18376    nsresult rv =
  18377        docShell->GetCurrentSHEntry(getter_AddRefs(currentEntry), &oshe);
  18378    if (!NS_WARN_IF(NS_FAILED(rv)) && currentEntry) {
  18379      currentEntry->SetHasUserInteraction(aHasInteraction);
  18380    }
  18381  }
  18382 }
  18383 
  18384 bool Document::GetSHEntryHasUserInteraction() {
  18385  if (RefPtr<WindowContext> topWc = GetTopLevelWindowContext()) {
  18386    return topWc->GetSHEntryHasUserInteraction();
  18387  }
  18388  return false;
  18389 }
  18390 
  18391 void Document::SetUserHasInteracted() {
  18392  MOZ_LOG(gUserInteractionPRLog, LogLevel::Debug,
  18393          ("Document %p has been interacted by user.", this));
  18394 
  18395  // We maybe need to update the user-interaction permission.
  18396  bool alreadyHadUserInteractionPermission =
  18397      ContentBlockingUserInteraction::Exists(NodePrincipal());
  18398  MaybeStoreUserInteractionAsPermission();
  18399 
  18400  // For purposes of reducing irrelevant session history entries on
  18401  // the back button, we annotate entries with whether they had user
  18402  // interaction. This is gated on its own flag on the WindowContext
  18403  // (instead of mUserHasInteracted) to account for the fact that multiple
  18404  // top-level SH entries can be associated with the same document.
  18405  // Thus, whenever we create a new SH entry for this document,
  18406  // this flag is reset.
  18407  if (!GetSHEntryHasUserInteraction()) {
  18408    SetSHEntryHasUserInteraction(true);
  18409  }
  18410 
  18411  if (mUserHasInteracted) {
  18412    return;
  18413  }
  18414 
  18415  mUserHasInteracted = true;
  18416 
  18417  if (mChannel) {
  18418    nsCOMPtr<nsILoadInfo> loadInfo = mChannel->LoadInfo();
  18419    loadInfo->SetDocumentHasUserInteracted(true);
  18420  }
  18421  // Tell the parent process about user interaction
  18422  if (auto* wgc = GetWindowGlobalChild()) {
  18423    wgc->SendUpdateDocumentHasUserInteracted(true);
  18424  }
  18425 
  18426  if (alreadyHadUserInteractionPermission) {
  18427    MaybeAllowStorageForOpenerAfterUserInteraction();
  18428  }
  18429 }
  18430 
  18431 BrowsingContext* Document::GetBrowsingContext() const {
  18432  return mDocumentContainer ? mDocumentContainer->GetBrowsingContext()
  18433                            : nullptr;
  18434 }
  18435 
  18436 static void PropagateUserGestureActivationBetweenPiP(
  18437    BrowsingContext* currentBC, UserActivation::Modifiers aModifiers) {
  18438  // https://wicg.github.io/document-picture-in-picture/#user-activation-propagation
  18439  // Monkey patch to activation notification
  18440  if (currentBC->Top()->GetIsDocumentPiP()) {
  18441    // 5. If we are in a PIP window, give transient activation to the opener
  18442    // window
  18443    // This means activation in a cross-origin subframe in the PIP window
  18444    // will cause the opener to get activation.
  18445    RefPtr<BrowsingContext> opener = currentBC->Top()->GetOpener();
  18446    if (!opener) {
  18447      return;
  18448    }
  18449    WindowContext* wc = opener->GetCurrentWindowContext();
  18450    NS_ENSURE_TRUE_VOID(wc);
  18451    wc->NotifyUserGestureActivation(aModifiers);
  18452  } else {
  18453    // 6. Get top-level navigable's last opened PiP window
  18454    // this means activation in a cross-origin subframe in the opener will
  18455    // cause the PIP window to get activation.
  18456    nsPIDOMWindowOuter* outer = currentBC->Top()->GetDOMWindow();
  18457    if (!outer) {
  18458      // We don't warn on failure due to the frequency. See bug 2008394.
  18459      return;
  18460    }
  18461    nsPIDOMWindowInner* inner = outer->GetCurrentInnerWindow();
  18462    NS_ENSURE_TRUE_VOID(inner);
  18463    DocumentPictureInPicture* dpip = inner->GetExtantDocumentPictureInPicture();
  18464    if (!dpip) {
  18465      return;
  18466    }
  18467    nsGlobalWindowInner* pip = dpip->GetWindow();
  18468    if (!pip) {
  18469      return;
  18470    }
  18471 
  18472    // 7. Give transient activation to the pip window and it's same origin
  18473    // descendants
  18474    BrowsingContext* pipBC = pip->GetBrowsingContext();
  18475    NS_ENSURE_TRUE_VOID(pipBC);
  18476    WindowContext* pipWC = pipBC->GetCurrentWindowContext();
  18477    NS_ENSURE_TRUE_VOID(pipWC);
  18478    pipBC->PreOrderWalk([&](BrowsingContext* bc) {
  18479      WindowContext* wc = bc->GetCurrentWindowContext();
  18480      if (!wc) {
  18481        return;
  18482      }
  18483 
  18484      // Check same-origin as current document
  18485      WindowGlobalChild* wgc = wc->GetWindowGlobalChild();
  18486      if (!wgc || !wgc->IsSameOriginWith(pipWC)) {
  18487        return;
  18488      }
  18489 
  18490      wc->NotifyUserGestureActivation(aModifiers);
  18491    });
  18492  }
  18493 }
  18494 
  18495 void Document::NotifyUserGestureActivation(
  18496    UserActivation::Modifiers
  18497        aModifiers /* = UserActivation::Modifiers::None() */) {
  18498  // https://html.spec.whatwg.org/multipage/interaction.html#activation-notification
  18499  // 1. "Assert: document is fully active."
  18500  RefPtr<BrowsingContext> currentBC = GetBrowsingContext();
  18501  if (!currentBC) {
  18502    return;
  18503  }
  18504 
  18505  RefPtr<WindowContext> currentWC = GetWindowContext();
  18506  if (!currentWC) {
  18507    return;
  18508  }
  18509 
  18510  // 2. "Let windows be « document's relevant global object"
  18511  // Instead of assembling a list, we just call notify for wanted windows as we
  18512  // find them
  18513  currentWC->NotifyUserGestureActivation(aModifiers);
  18514 
  18515  // 3. "...windows with the active window of each of document's ancestor
  18516  // navigables."
  18517  for (WindowContext* wc = currentWC->GetParentWindowContext(); wc;
  18518       wc = wc->GetParentWindowContext()) {
  18519    wc->NotifyUserGestureActivation(aModifiers);
  18520  }
  18521 
  18522  // 4. "windows with the active window of each of document's descendant
  18523  // navigables, filtered to include only those navigables whose active
  18524  // document's origin is same origin with document's origin"
  18525  currentBC->PreOrderWalk([&](BrowsingContext* bc) {
  18526    WindowContext* wc = bc->GetCurrentWindowContext();
  18527    // currentWC has already been notified
  18528    if (!wc || wc == currentWC) {
  18529      return;
  18530    }
  18531 
  18532    // Check same-origin as current document
  18533    WindowGlobalChild* wgc = wc->GetWindowGlobalChild();
  18534    if (!wgc || !wgc->IsSameOriginWith(currentWC)) {
  18535      return;
  18536    }
  18537 
  18538    wc->NotifyUserGestureActivation(aModifiers);
  18539  });
  18540 
  18541  PropagateUserGestureActivationBetweenPiP(currentBC, aModifiers);
  18542 
  18543  // If there has been a user activation, mark the current session history entry
  18544  // as having been interacted with.
  18545  SetSHEntryHasUserInteraction(true);
  18546 }
  18547 
  18548 bool Document::HasBeenUserGestureActivated() {
  18549  RefPtr<WindowContext> wc = GetWindowContext();
  18550  return wc && wc->HasBeenUserGestureActivated();
  18551 }
  18552 
  18553 bool Document::ConsumeTextDirectiveUserActivation() {
  18554  if (!mChannel) {
  18555    return false;
  18556  }
  18557  nsCOMPtr<nsILoadInfo> loadInfo = mChannel->LoadInfo();
  18558  if (!loadInfo) {
  18559    return false;
  18560  }
  18561  const bool textDirectiveUserActivation =
  18562      loadInfo->GetTextDirectiveUserActivation();
  18563  loadInfo->SetTextDirectiveUserActivation(false);
  18564  return textDirectiveUserActivation;
  18565 }
  18566 
  18567 DOMHighResTimeStamp Document::LastUserGestureTimeStamp() {
  18568  if (RefPtr<WindowContext> wc = GetWindowContext()) {
  18569    if (nsGlobalWindowInner* innerWindow = wc->GetInnerWindow()) {
  18570      if (Performance* perf = innerWindow->GetPerformance()) {
  18571        return perf->GetDOMTiming()->TimeStampToDOMHighRes(
  18572            wc->GetUserGestureStart());
  18573      }
  18574    }
  18575  }
  18576 
  18577  NS_WARNING(
  18578      "Unable to calculate DOMHighResTimeStamp for LastUserGestureTimeStamp");
  18579  return 0;
  18580 }
  18581 
  18582 void Document::ClearUserGestureActivation() {
  18583  if (RefPtr<BrowsingContext> bc = GetBrowsingContext()) {
  18584    bc = bc->Top();
  18585    bc->PreOrderWalk([&](BrowsingContext* aBC) {
  18586      if (WindowContext* windowContext = aBC->GetCurrentWindowContext()) {
  18587        windowContext->NotifyResetUserGestureActivation();
  18588      }
  18589    });
  18590  }
  18591 }
  18592 
  18593 bool Document::HasValidTransientUserGestureActivation() const {
  18594  RefPtr<WindowContext> wc = GetWindowContext();
  18595  return wc && wc->HasValidTransientUserGestureActivation();
  18596 }
  18597 
  18598 bool Document::ConsumeTransientUserGestureActivation() {
  18599  RefPtr<WindowContext> wc = GetWindowContext();
  18600  return wc && wc->ConsumeTransientUserGestureActivation();
  18601 }
  18602 
  18603 bool Document::GetTransientUserGestureActivationModifiers(
  18604    UserActivation::Modifiers* aModifiers) {
  18605  RefPtr<WindowContext> wc = GetWindowContext();
  18606  return wc && wc->GetTransientUserGestureActivationModifiers(aModifiers);
  18607 }
  18608 
  18609 void Document::SetDocTreeHadMedia() {
  18610  RefPtr<WindowContext> topWc = GetTopLevelWindowContext();
  18611  if (topWc && !topWc->IsDiscarded() && !topWc->GetDocTreeHadMedia()) {
  18612    MOZ_ALWAYS_SUCCEEDS(topWc->SetDocTreeHadMedia(true));
  18613  }
  18614 }
  18615 
  18616 void Document::MaybeAllowStorageForOpenerAfterUserInteraction() {
  18617  if (!CookieJarSettings()->GetRejectThirdPartyContexts()) {
  18618    return;
  18619  }
  18620 
  18621  // This will probably change for project fission, but currently this document
  18622  // and the opener are on the same process. In the future, we should make this
  18623  // part async.
  18624  nsPIDOMWindowInner* inner = GetInnerWindow();
  18625  if (NS_WARN_IF(!inner)) {
  18626    return;
  18627  }
  18628 
  18629  // Don't trigger navigation heuristic for first-party trackers if the pref
  18630  // says so.
  18631  if (StaticPrefs::
  18632          privacy_restrict3rdpartystorage_heuristic_exclude_third_party_trackers() &&
  18633      nsContentUtils::IsFirstPartyTrackingResourceWindow(inner)) {
  18634    return;
  18635  }
  18636 
  18637  auto* outer = nsGlobalWindowOuter::Cast(inner->GetOuterWindow());
  18638  if (NS_WARN_IF(!outer)) {
  18639    return;
  18640  }
  18641 
  18642  RefPtr<BrowsingContext> openerBC = outer->GetOpenerBrowsingContext();
  18643  if (!openerBC) {
  18644    // No opener.
  18645    return;
  18646  }
  18647 
  18648  // We want to ensure the following check works for both fission mode and
  18649  // non-fission mode:
  18650  // "If the opener is not a 3rd party and if this window is not a 3rd party
  18651  // with respect to the opener, we should not continue."
  18652  //
  18653  // In non-fission mode, the opener and the opened window are in the same
  18654  // process, we can use AntiTrackingUtils::IsThirdPartyWindow to do the check.
  18655  // In fission mode, if this window is not a 3rd party with respect to the
  18656  // opener, they must be in the same process, so we can still use
  18657  // IsThirdPartyWindow(openerInner) to continue to check if the opener is a 3rd
  18658  // party.
  18659  if (openerBC->IsInProcess()) {
  18660    nsCOMPtr<nsPIDOMWindowOuter> outerOpener = openerBC->GetDOMWindow();
  18661    if (NS_WARN_IF(!outerOpener)) {
  18662      return;
  18663    }
  18664 
  18665    nsCOMPtr<nsPIDOMWindowInner> openerInner =
  18666        outerOpener->GetCurrentInnerWindow();
  18667    if (NS_WARN_IF(!openerInner)) {
  18668      return;
  18669    }
  18670 
  18671    RefPtr<Document> openerDocument = openerInner->GetExtantDoc();
  18672    if (NS_WARN_IF(!openerDocument)) {
  18673      return;
  18674    }
  18675 
  18676    nsCOMPtr<nsIURI> openerURI = openerDocument->GetDocumentURI();
  18677    if (NS_WARN_IF(!openerURI)) {
  18678      return;
  18679    }
  18680 
  18681    // If the opener is not a 3rd party and if this window is not
  18682    // a 3rd party with respect to the opener, we should not continue.
  18683    if (!AntiTrackingUtils::IsThirdPartyWindow(inner, openerURI) &&
  18684        !AntiTrackingUtils::IsThirdPartyWindow(openerInner, nullptr)) {
  18685      return;
  18686    }
  18687  }
  18688 
  18689  RefPtr<Document> self(this);
  18690  WebIdentityHandler* identityHandler = inner->GetOrCreateWebIdentityHandler();
  18691  MOZ_ASSERT(identityHandler);
  18692  identityHandler->IsContinuationWindow()->Then(
  18693      GetCurrentSerialEventTarget(), __func__,
  18694      [self, openerBC](const MozPromise<bool, nsresult,
  18695                                        true>::ResolveOrRejectValue& result) {
  18696        if (!result.IsResolve() || !result.ResolveValue()) {
  18697          if (XRE_IsParentProcess()) {
  18698            (void)StorageAccessAPIHelper::AllowAccessForOnParentProcess(
  18699                self->NodePrincipal(), openerBC,
  18700                ContentBlockingNotifier::eOpenerAfterUserInteraction);
  18701          } else {
  18702            (void)StorageAccessAPIHelper::AllowAccessForOnChildProcess(
  18703                self->NodePrincipal(), openerBC,
  18704                ContentBlockingNotifier::eOpenerAfterUserInteraction);
  18705          }
  18706        }
  18707      });
  18708 }
  18709 
  18710 namespace {
  18711 
  18712 // Documents can stay alive for days. We don't want to update the permission
  18713 // value at any user-interaction, and, using a timer triggered any X seconds
  18714 // should be good enough. 'X' is taken from
  18715 // privacy.userInteraction.document.interval pref.
  18716 //  We also want to store the user-interaction before shutting down, and, for
  18717 //  this reason, this class implements nsIAsyncShutdownBlocker interface.
  18718 class UserInteractionTimer final : public Runnable,
  18719                                   public nsITimerCallback,
  18720                                   public nsIAsyncShutdownBlocker {
  18721 public:
  18722  NS_DECL_ISUPPORTS_INHERITED
  18723 
  18724  explicit UserInteractionTimer(Document* aDocument)
  18725      : Runnable("UserInteractionTimer"),
  18726        mPrincipal(aDocument->NodePrincipal()),
  18727        mDocument(aDocument) {
  18728    static int32_t userInteractionTimerId = 0;
  18729    // Blocker names must be unique. Let's create it now because when needed,
  18730    // the document could be already gone.
  18731    mBlockerName.AppendPrintf("UserInteractionTimer %d for document %p",
  18732                              ++userInteractionTimerId, aDocument);
  18733 
  18734    // For ContentBlockingUserInteraction we care about user-interaction stored
  18735    // only for top-level documents and documents with access to the Storage
  18736    // Access API
  18737    if (aDocument->IsTopLevelContentDocument()) {
  18738      mShouldRecordContentBlockingUserInteraction = true;
  18739    } else {
  18740      bool hasSA;
  18741      nsresult rv = aDocument->HasStorageAccessSync(hasSA);
  18742      mShouldRecordContentBlockingUserInteraction = NS_SUCCEEDED(rv) && hasSA;
  18743    }
  18744  }
  18745 
  18746  // Runnable interface
  18747 
  18748  NS_IMETHOD
  18749  Run() override {
  18750    uint32_t interval =
  18751        StaticPrefs::privacy_userInteraction_document_interval();
  18752    if (!interval) {
  18753      return NS_OK;
  18754    }
  18755 
  18756    RefPtr<UserInteractionTimer> self = this;
  18757    auto raii =
  18758        MakeScopeExit([self] { self->CancelTimerAndStoreUserInteraction(); });
  18759 
  18760    nsresult rv = NS_NewTimerWithCallback(
  18761        getter_AddRefs(mTimer), this, interval * 1000, nsITimer::TYPE_ONE_SHOT);
  18762    NS_ENSURE_SUCCESS(rv, NS_OK);
  18763 
  18764    nsCOMPtr<nsIAsyncShutdownClient> phase = GetShutdownPhase();
  18765    NS_ENSURE_TRUE(!!phase, NS_OK);
  18766 
  18767    rv = phase->AddBlocker(this, NS_LITERAL_STRING_FROM_CSTRING(__FILE__),
  18768                           __LINE__, u"UserInteractionTimer shutdown"_ns);
  18769    NS_ENSURE_SUCCESS(rv, NS_OK);
  18770 
  18771    raii.release();
  18772    return NS_OK;
  18773  }
  18774 
  18775  // nsITimerCallback interface
  18776 
  18777  NS_IMETHOD
  18778  Notify(nsITimer* aTimer) override {
  18779    StoreUserInteraction();
  18780    return NS_OK;
  18781  }
  18782 
  18783 #ifdef MOZ_COLLECTING_RUNNABLE_TELEMETRY
  18784  using nsINamed::GetName;
  18785 #endif
  18786 
  18787  // nsIAsyncShutdownBlocker interface
  18788 
  18789  NS_IMETHOD
  18790  GetName(nsAString& aName) override {
  18791    aName = mBlockerName;
  18792    return NS_OK;
  18793  }
  18794 
  18795  NS_IMETHOD
  18796  BlockShutdown(nsIAsyncShutdownClient* aClient) override {
  18797    CancelTimerAndStoreUserInteraction();
  18798    return NS_OK;
  18799  }
  18800 
  18801  NS_IMETHOD
  18802  GetState(nsIPropertyBag**) override { return NS_OK; }
  18803 
  18804 private:
  18805  ~UserInteractionTimer() = default;
  18806 
  18807  void StoreUserInteraction() {
  18808    // Remove the shutting down blocker
  18809    nsCOMPtr<nsIAsyncShutdownClient> phase = GetShutdownPhase();
  18810    if (phase) {
  18811      phase->RemoveBlocker(this);
  18812    }
  18813 
  18814    // If the document is not gone, let's reset its timer flag.
  18815    nsCOMPtr<Document> document(mDocument);
  18816    if (document) {
  18817      if (mShouldRecordContentBlockingUserInteraction) {
  18818        ContentBlockingUserInteraction::Observe(mPrincipal);
  18819      }
  18820      (void)BounceTrackingProtection::RecordUserActivation(
  18821          mDocument->GetWindowContext());
  18822      document->ResetUserInteractionTimer();
  18823    }
  18824  }
  18825 
  18826  void CancelTimerAndStoreUserInteraction() {
  18827    if (mTimer) {
  18828      mTimer->Cancel();
  18829      mTimer = nullptr;
  18830    }
  18831 
  18832    StoreUserInteraction();
  18833  }
  18834 
  18835  static already_AddRefed<nsIAsyncShutdownClient> GetShutdownPhase() {
  18836    nsCOMPtr<nsIAsyncShutdownService> svc = services::GetAsyncShutdownService();
  18837    NS_ENSURE_TRUE(!!svc, nullptr);
  18838 
  18839    nsCOMPtr<nsIAsyncShutdownClient> phase;
  18840    nsresult rv = svc->GetXpcomWillShutdown(getter_AddRefs(phase));
  18841    NS_ENSURE_SUCCESS(rv, nullptr);
  18842 
  18843    return phase.forget();
  18844  }
  18845 
  18846  nsCOMPtr<nsIPrincipal> mPrincipal;
  18847  WeakPtr<Document> mDocument;
  18848  bool mShouldRecordContentBlockingUserInteraction = false;
  18849 
  18850  nsCOMPtr<nsITimer> mTimer;
  18851 
  18852  nsString mBlockerName;
  18853 };
  18854 
  18855 NS_IMPL_ISUPPORTS_INHERITED(UserInteractionTimer, Runnable, nsITimerCallback,
  18856                            nsIAsyncShutdownBlocker)
  18857 
  18858 }  // namespace
  18859 
  18860 void Document::MaybeStoreUserInteractionAsPermission() {
  18861  if (!mUserHasInteracted) {
  18862    // First interaction, let's store this info now.
  18863    (void)BounceTrackingProtection::RecordUserActivation(GetWindowContext());
  18864 
  18865    // For ContentBlockingUserInteraction we care about user-interaction stored
  18866    // only for top-level documents and documents with access to the Storage
  18867    // Access API
  18868    if (!IsTopLevelContentDocument()) {
  18869      bool hasSA;
  18870      nsresult rv = HasStorageAccessSync(hasSA);
  18871      if (NS_FAILED(rv) || !hasSA) {
  18872        return;
  18873      }
  18874    }
  18875    ContentBlockingUserInteraction::Observe(NodePrincipal());
  18876    return;
  18877  }
  18878 
  18879  if (mHasUserInteractionTimerScheduled) {
  18880    return;
  18881  }
  18882 
  18883  nsCOMPtr<nsIRunnable> task = new UserInteractionTimer(this);
  18884  nsresult rv = NS_DispatchToCurrentThreadQueue(task.forget(), 2500,
  18885                                                EventQueuePriority::Idle);
  18886  if (NS_WARN_IF(NS_FAILED(rv))) {
  18887    return;
  18888  }
  18889 
  18890  // This value will be reset by the timer.
  18891  mHasUserInteractionTimerScheduled = true;
  18892 }
  18893 
  18894 void Document::ResetUserInteractionTimer() {
  18895  mHasUserInteractionTimerScheduled = false;
  18896 }
  18897 
  18898 bool Document::IsExtensionPage() const {
  18899  return BasePrincipal::Cast(NodePrincipal())->AddonPolicy();
  18900 }
  18901 
  18902 PermissionDelegateHandler* Document::GetPermissionDelegateHandler() {
  18903  if (!mPermissionDelegateHandler) {
  18904    mPermissionDelegateHandler = MakeAndAddRef<PermissionDelegateHandler>(this);
  18905  }
  18906 
  18907  if (!mPermissionDelegateHandler->Initialize()) {
  18908    mPermissionDelegateHandler = nullptr;
  18909  }
  18910 
  18911  return mPermissionDelegateHandler;
  18912 }
  18913 
  18914 void Document::ScheduleResizeObserversNotification() {
  18915  MaybeScheduleRenderingPhases({RenderingPhase::Layout});
  18916 }
  18917 
  18918 static void FlushLayoutForWholeBrowsingContextTree(Document& aDoc,
  18919                                                   const ChangesToFlush& aCtf) {
  18920  BrowsingContext* bc = aDoc.GetBrowsingContext();
  18921  if (bc && bc->GetExtantDocument() == &aDoc) {
  18922    RefPtr<BrowsingContext> top = bc->Top();
  18923    top->PreOrderWalk([aCtf](BrowsingContext* aCur) {
  18924      if (Document* doc = aCur->GetExtantDocument()) {
  18925        doc->FlushPendingNotifications(aCtf);
  18926      }
  18927    });
  18928  } else {
  18929    // If there is no browsing context, or we're not the current document of the
  18930    // browsing context, then we just flush this document itself.
  18931    aDoc.FlushPendingNotifications(aCtf);
  18932  }
  18933 }
  18934 
  18935 // https://html.spec.whatwg.org/#update-the-rendering steps 16 and 17
  18936 void Document::DetermineProximityToViewportAndNotifyResizeObservers() {
  18937  RefPtr ps = GetPresShell();
  18938  if (!ps) {
  18939    return;
  18940  }
  18941 
  18942  // Try to do an interruptible reflow if it wouldn't be observable by the page
  18943  // in any obvious way.
  18944  const bool interruptible = !ps->HasContentVisibilityAutoFrames() &&
  18945                             !HasResizeObservers() &&
  18946                             !HasElementsWithLastRememberedSize();
  18947  ps->ResetWasLastReflowInterrupted();
  18948 
  18949  // https://github.com/whatwg/html/issues/11210 for this not being in the spec.
  18950  ps->UpdateRelevancyOfContentVisibilityAutoFrames();
  18951 
  18952  // 1. Let resizeObserverDepth be 0.
  18953  uint32_t resizeObserverDepth = 0;
  18954  bool initialResetOfScrolledIntoViewFlagsDone = false;
  18955  const ChangesToFlush ctf(
  18956      interruptible ? FlushType::InterruptibleLayout : FlushType::Layout,
  18957      /* aFlushAnimations = */ false, /* aUpdateRelevancy = */ false);
  18958 
  18959  bool initialAnchorOverflowDone = false;
  18960 
  18961  // 2. While true:
  18962  while (true) {
  18963    // 2.1. Recalculate styles and update layout for doc.
  18964    if (interruptible) {
  18965      ps->FlushPendingNotifications(ctf);
  18966    } else {
  18967      // The ResizeObserver callbacks functions may do changes in its
  18968      // sub-documents or ancestors, so flushing layout for the whole browsing
  18969      // context tree makes sure we don't miss anyone.
  18970      //
  18971      // TODO(emilio): It seems Document::FlushPendingNotifications should be
  18972      // able to take care of ancestors... Can we do this less often?
  18973      FlushLayoutForWholeBrowsingContextTree(*this, ctf);
  18974    }
  18975 
  18976    // Last remembered sizes are recorded "at the time that ResizeObserver
  18977    // events are determined and delivered".
  18978    // https://drafts.csswg.org/css-sizing-4/#last-remembered
  18979    //
  18980    // We do it right after layout to make sure sizes are up-to-date. If we do
  18981    // it after determining the proximities to viewport of
  18982    // 'content-visibility: auto' nodes, and if one of such node ever becomes
  18983    // relevant to the user, then we would be incorrectly recording the size
  18984    // of its rendering when it was skipping its content.
  18985    //
  18986    // https://github.com/whatwg/html/issues/11210 for the timing of this.
  18987    UpdateLastRememberedSizes();
  18988 
  18989    const bool evaluateAllFallbacksIfNeeded = !initialAnchorOverflowDone;
  18990    initialAnchorOverflowDone = true;
  18991    if (AnchorPositioningUtils::TriggerLayoutOnOverflow(
  18992            ps, evaluateAllFallbacksIfNeeded)) {
  18993      // If any of the anchor positioned items overflow its cb, then we trigger
  18994      // a layout for them. If we triggered for any item, we have to restart the
  18995      // loop to flush all layouts.
  18996      continue;
  18997    }
  18998 
  18999    // 2.2. Let hadInitialVisibleContentVisibilityDetermination be false.
  19000    //      (this is part of "result").
  19001    // 2.3. For each element element with 'auto' used value of
  19002    //      'content-visibility' [...] Determine proximity to the viewport for
  19003    //      element.
  19004    auto result = ps->DetermineProximityToViewport();
  19005    if (result.mHadInitialDetermination) {
  19006      // 2.4. If hadInitialVisibleContentVisibilityDetermination is true, then
  19007      //      continue.
  19008      continue;
  19009    }
  19010    if (result.mAnyScrollIntoViewFlag) {
  19011      // Not defined in the spec: It's possible that some elements with
  19012      // content-visibility: auto were forced to be visible in order to
  19013      // perform scrollIntoView() so clear their flags now and restart the
  19014      // loop. See https://github.com/w3c/csswg-drafts/issues/9337
  19015      ps->ClearTemporarilyVisibleForScrolledIntoViewDescendantFlags();
  19016      ps->ScheduleContentRelevancyUpdate(ContentRelevancyReason::Visible);
  19017      if (!initialResetOfScrolledIntoViewFlagsDone) {
  19018        initialResetOfScrolledIntoViewFlagsDone = true;
  19019        continue;
  19020      }
  19021    }
  19022 
  19023    // 2.5. Gather active resize observations at depth resizeObserverDepth for
  19024    // doc.
  19025    GatherAllActiveResizeObservations(resizeObserverDepth);
  19026    // 2.6. If doc has active resize observations: [..] steps below
  19027    if (!HasAnyActiveResizeObservations()) {
  19028      // 2.7. Otherwise, break.
  19029      break;
  19030    }
  19031    // 2.6.1. Set resizeObserverDepth to the result of broadcasting active
  19032    // resize observations given doc.
  19033    DebugOnly<uint32_t> oldResizeObserverDepth = resizeObserverDepth;
  19034    resizeObserverDepth = BroadcastAllActiveResizeObservations();
  19035    NS_ASSERTION(oldResizeObserverDepth < resizeObserverDepth,
  19036                 "resizeObserverDepth should be getting strictly deeper");
  19037    // 2.6.2. Continue.
  19038  }
  19039 
  19040  if (HasAnySkippedResizeObservations()) {
  19041    if (nsCOMPtr<nsPIDOMWindowInner> window = GetInnerWindow()) {
  19042      // Per spec, we deliver an error if the document has any skipped
  19043      // observations. Also, we re-register via ScheduleNotification().
  19044      RootedDictionary<ErrorEventInit> init(RootingCx());
  19045      init.mMessage.AssignLiteral(
  19046          "ResizeObserver loop completed with undelivered notifications.");
  19047      init.mBubbles = false;
  19048      init.mCancelable = false;
  19049 
  19050      nsEventStatus status = nsEventStatus_eIgnore;
  19051      nsCOMPtr<nsIScriptGlobalObject> sgo = do_QueryInterface(window);
  19052      MOZ_ASSERT(sgo);
  19053      if (NS_WARN_IF(sgo->HandleScriptError(init, &status))) {
  19054        status = nsEventStatus_eIgnore;
  19055      }
  19056    } else {
  19057      // We don't fire error events at any global for non-window JS on the main
  19058      // thread.
  19059    }
  19060 
  19061    // We need to deliver pending notifications in next cycle.
  19062    ScheduleResizeObserversNotification();
  19063  }
  19064 
  19065  // Step 17: For each doc of docs, if the focused area of doc is not a
  19066  // focusable area, then run the focusing steps for doc's viewport, and set
  19067  // doc's relevant global object's navigation API's focus changed during
  19068  // ongoing navigation to false.
  19069  //
  19070  // We do it here rather than a separate walk over the docs for convenience,
  19071  // and because I don't think there's a strong reason for it to be a separate
  19072  // walk altogether, see https://github.com/whatwg/html/issues/11211
  19073  const bool fixedUpFocus = ps->FixUpFocus();
  19074  if (fixedUpFocus) {
  19075    FlushPendingNotifications(ctf);
  19076  }
  19077 
  19078  if (NS_WARN_IF(ps->NeedStyleFlush()) || NS_WARN_IF(ps->NeedLayoutFlush()) ||
  19079      NS_WARN_IF(fixedUpFocus && ps->NeedsFocusFixUp())) {
  19080    ps->EnsureLayoutFlush();
  19081  }
  19082 
  19083  // Inform the FontFaceSet that we ticked, so that it can resolve its ready
  19084  // promise if it needs to. See PresShell::MightHavePendingFontLoads.
  19085  ps->NotifyFontFaceSetOnRefresh();
  19086 }
  19087 
  19088 void Document::GatherAllActiveResizeObservations(uint32_t aDepth) {
  19089  for (ResizeObserver* observer : mResizeObservers) {
  19090    observer->GatherActiveObservations(aDepth);
  19091  }
  19092 }
  19093 
  19094 uint32_t Document::BroadcastAllActiveResizeObservations() {
  19095  uint32_t shallowestTargetDepth = std::numeric_limits<uint32_t>::max();
  19096 
  19097  // Copy the observers as this invokes the callbacks and could register and
  19098  // unregister observers at will.
  19099  const auto observers =
  19100      ToTArray<nsTArray<RefPtr<ResizeObserver>>>(mResizeObservers);
  19101  for (const auto& observer : observers) {
  19102    // MOZ_KnownLive because 'observers' is guaranteed to keep it
  19103    // alive.
  19104    //
  19105    // This can go away once
  19106    // https://bugzilla.mozilla.org/show_bug.cgi?id=1620312 is fixed.
  19107    uint32_t targetDepth =
  19108        MOZ_KnownLive(observer)->BroadcastActiveObservations();
  19109    if (targetDepth < shallowestTargetDepth) {
  19110      shallowestTargetDepth = targetDepth;
  19111    }
  19112  }
  19113 
  19114  return shallowestTargetDepth;
  19115 }
  19116 
  19117 bool Document::HasAnySkippedResizeObservations() const {
  19118  for (const auto& observer : mResizeObservers) {
  19119    if (observer->HasSkippedObservations()) {
  19120      return true;
  19121    }
  19122  }
  19123  return false;
  19124 }
  19125 
  19126 bool Document::HasAnyActiveResizeObservations() const {
  19127  for (const auto& observer : mResizeObservers) {
  19128    if (observer->HasActiveObservations()) {
  19129      return true;
  19130    }
  19131  }
  19132  return false;
  19133 }
  19134 
  19135 void Document::ClearStaleServoData() {
  19136  DocumentStyleRootIterator iter(this);
  19137  while (Element* root = iter.GetNextStyleRoot()) {
  19138    RestyleManager::ClearServoDataFromSubtree(root);
  19139  }
  19140 }
  19141 
  19142 // https://drafts.csswg.org/css-view-transitions-1/#dom-document-startviewtransition
  19143 already_AddRefed<ViewTransition> Document::StartViewTransition(
  19144    const ViewTransitionUpdateCallbackOrStartViewTransitionOptions& aOptions) {
  19145  // Steps 1-3
  19146 
  19147  nsTArray<RefPtr<nsAtom>> types;
  19148  ViewTransitionUpdateCallback* cb = nullptr;
  19149  if (aOptions.IsViewTransitionUpdateCallback()) {
  19150    cb = &aOptions.GetAsViewTransitionUpdateCallback();
  19151  } else {
  19152    MOZ_ASSERT(aOptions.IsStartViewTransitionOptions());
  19153    const auto& options = aOptions.GetAsStartViewTransitionOptions();
  19154    cb = options.mUpdate.get();
  19155    if (!options.mTypes.IsNull()) {
  19156      const auto& optionsTypes = options.mTypes.Value();
  19157      types.SetCapacity(optionsTypes.Length());
  19158      for (const auto& type : optionsTypes) {
  19159        // TODO(emilio): should probably de-duplicate here.
  19160        types.AppendElement(NS_AtomizeMainThread(type));
  19161      }
  19162    }
  19163  }
  19164  RefPtr transition = new ViewTransition(*this, cb, std::move(types));
  19165  if (Hidden()) {
  19166    // Step 4:
  19167    //
  19168    // If document's visibility state is "hidden", then skip transition with an
  19169    // "InvalidStateError" DOMException, and return transition.
  19170    transition->SkipTransition(SkipTransitionReason::DocumentHidden);
  19171    return transition.forget();
  19172  }
  19173  if (mActiveViewTransition) {
  19174    // Step 5:
  19175    // If document's active view transition is not null, then skip that view
  19176    // transition with an "AbortError" DOMException in this's relevant Realm.
  19177    mActiveViewTransition->SkipTransition(
  19178        SkipTransitionReason::ClobberedActiveTransition);
  19179  }
  19180  // Step 6: Set document's active view transition to transition.
  19181  mActiveViewTransition = transition;
  19182 
  19183  // Enable :active-view-transition to allow associated styles to
  19184  // be applied during the view transition.
  19185  if (auto* root = this->GetRootElement()) {
  19186    root->AddStates(ElementState::ACTIVE_VIEW_TRANSITION);
  19187  }
  19188 
  19189  EnsureViewTransitionOperationsHappen();
  19190 
  19191  // Step 7: return transition
  19192  return transition.forget();
  19193 }
  19194 
  19195 void Document::ClearActiveViewTransition() { mActiveViewTransition = nullptr; }
  19196 
  19197 void Document::SetRenderingSuppressedForViewTransitions(bool aValue) {
  19198  if (mRenderingSuppressedForViewTransitions == aValue) {
  19199    return;
  19200  }
  19201  mRenderingSuppressedForViewTransitions = aValue;
  19202  if (aValue) {
  19203    return;
  19204  }
  19205  // We might have missed virtually all rendering steps, ensure we run them.
  19206  MaybeScheduleRendering();
  19207 }
  19208 
  19209 void Document::PerformPendingViewTransitionOperations() {
  19210  if (mActiveViewTransition && !RenderingSuppressedForViewTransitions()) {
  19211    RefPtr activeVT = mActiveViewTransition;
  19212    activeVT->PerformPendingOperations();
  19213  }
  19214  EnumerateSubDocuments([](Document& aDoc) MOZ_CAN_RUN_SCRIPT_FOR_DEFINITION {
  19215    // Note: EnumerateSubDocuments keeps aDoc alive in a local array, so it's
  19216    // fine to use MOZ_KnownLive here.
  19217    MOZ_KnownLive(aDoc).PerformPendingViewTransitionOperations();
  19218    return CallState::Continue;
  19219  });
  19220 }
  19221 
  19222 void Document::EnsureViewTransitionOperationsHappen() {
  19223  MaybeScheduleRenderingPhases({RenderingPhase::ViewTransitionOperations});
  19224 }
  19225 
  19226 Selection* Document::GetSelection(ErrorResult& aRv) {
  19227  nsCOMPtr<nsPIDOMWindowInner> window = GetInnerWindow();
  19228  if (!window) {
  19229    return nullptr;
  19230  }
  19231 
  19232  if (!window->IsCurrentInnerWindow()) {
  19233    return nullptr;
  19234  }
  19235 
  19236  return nsGlobalWindowInner::Cast(window)->GetSelection(aRv);
  19237 }
  19238 
  19239 void Document::MakeBrowsingContextNonSynthetic() {
  19240  if (BrowsingContext* bc = GetBrowsingContext()) {
  19241    if (bc->GetIsSyntheticDocumentContainer()) {
  19242      (void)bc->SetIsSyntheticDocumentContainer(false);
  19243    }
  19244  }
  19245 }
  19246 
  19247 nsresult Document::HasStorageAccessSync(bool& aHasStorageAccess) {
  19248  // Step 1: check if cookie permissions are available or denied to this
  19249  // document's principal
  19250  nsCOMPtr<nsPIDOMWindowInner> inner = GetInnerWindow();
  19251  if (!inner) {
  19252    aHasStorageAccess = false;
  19253    return NS_OK;
  19254  }
  19255  Maybe<bool> resultBecauseCookiesApproved =
  19256      StorageAccessAPIHelper::CheckCookiesPermittedDecidesStorageAccessAPI(
  19257          CookieJarSettings(), NodePrincipal());
  19258  if (resultBecauseCookiesApproved.isSome()) {
  19259    if (resultBecauseCookiesApproved.value()) {
  19260      aHasStorageAccess = true;
  19261      return NS_OK;
  19262    } else {
  19263      aHasStorageAccess = false;
  19264      return NS_OK;
  19265    }
  19266  }
  19267 
  19268  // Step 2: Check if the browser settings determine whether or not this
  19269  // document has access to its unpartitioned cookies.
  19270  bool isThirdPartyDocument = AntiTrackingUtils::IsThirdPartyDocument(this);
  19271  bool isOnThirdPartySkipList = false;
  19272  if (mChannel) {
  19273    nsCOMPtr<nsILoadInfo> loadInfo = mChannel->LoadInfo();
  19274    isOnThirdPartySkipList = loadInfo->GetStoragePermission() ==
  19275                             nsILoadInfo::StoragePermissionAllowListed;
  19276  }
  19277  bool isThirdPartyTracker =
  19278      nsContentUtils::IsThirdPartyTrackingResourceWindow(inner);
  19279  Maybe<bool> resultBecauseBrowserSettings =
  19280      StorageAccessAPIHelper::CheckBrowserSettingsDecidesStorageAccessAPI(
  19281          CookieJarSettings(), isThirdPartyDocument, isOnThirdPartySkipList,
  19282          isThirdPartyTracker);
  19283  if (resultBecauseBrowserSettings.isSome()) {
  19284    if (resultBecauseBrowserSettings.value()) {
  19285      aHasStorageAccess = true;
  19286      return NS_OK;
  19287    } else {
  19288      aHasStorageAccess = false;
  19289      return NS_OK;
  19290    }
  19291  }
  19292 
  19293  // Step 3: Check if the location of this call (embedded, top level, same-site)
  19294  // determines if cookies are permitted or not.
  19295  Maybe<bool> resultBecauseCallContext =
  19296      StorageAccessAPIHelper::CheckCallingContextDecidesStorageAccessAPI(this,
  19297                                                                         false);
  19298  if (resultBecauseCallContext.isSome()) {
  19299    if (resultBecauseCallContext.value()) {
  19300      aHasStorageAccess = true;
  19301      return NS_OK;
  19302    } else {
  19303      aHasStorageAccess = false;
  19304      return NS_OK;
  19305    }
  19306  }
  19307 
  19308  // Step 4: Check if the permissions for this document determine if if has
  19309  // access or is denied cookies.
  19310  Maybe<bool> resultBecausePreviousPermission =
  19311      StorageAccessAPIHelper::CheckExistingPermissionDecidesStorageAccessAPI(
  19312          this, false);
  19313  if (resultBecausePreviousPermission.isSome()) {
  19314    if (resultBecausePreviousPermission.value()) {
  19315      aHasStorageAccess = true;
  19316      return NS_OK;
  19317    } else {
  19318      aHasStorageAccess = false;
  19319      return NS_OK;
  19320    }
  19321  }
  19322  // If you get here, we default to not giving you permission.
  19323  aHasStorageAccess = false;
  19324  return NS_OK;
  19325 }
  19326 
  19327 already_AddRefed<mozilla::dom::Promise> Document::HasStorageAccess(
  19328    mozilla::ErrorResult& aRv) {
  19329  nsIGlobalObject* global = GetScopeObject();
  19330  if (!global) {
  19331    aRv.Throw(NS_ERROR_NOT_AVAILABLE);
  19332    return nullptr;
  19333  }
  19334 
  19335  RefPtr<Promise> promise =
  19336      Promise::Create(global, aRv, Promise::ePropagateUserInteraction);
  19337  if (aRv.Failed()) {
  19338    return nullptr;
  19339  }
  19340 
  19341  if (!IsCurrentActiveDocument()) {
  19342    promise->MaybeRejectWithInvalidStateError(
  19343        "hasStorageAccess requires an active document");
  19344    return promise.forget();
  19345  }
  19346 
  19347  bool hasStorageAccess;
  19348  nsresult rv = HasStorageAccessSync(hasStorageAccess);
  19349  if (NS_FAILED(rv)) {
  19350    promise->MaybeRejectWithUndefined();
  19351  } else {
  19352    promise->MaybeResolve(hasStorageAccess);
  19353  }
  19354 
  19355  return promise.forget();
  19356 }
  19357 
  19358 RefPtr<Document::GetContentBlockingEventsPromise>
  19359 Document::GetContentBlockingEvents() {
  19360  RefPtr<WindowGlobalChild> wgc = GetWindowGlobalChild();
  19361  if (!wgc) {
  19362    return nullptr;
  19363  }
  19364 
  19365  return wgc->SendGetContentBlockingEvents()->Then(
  19366      GetCurrentSerialEventTarget(), __func__,
  19367      [](const WindowGlobalChild::GetContentBlockingEventsPromise::
  19368             ResolveOrRejectValue& aValue) {
  19369        if (aValue.IsResolve()) {
  19370          return Document::GetContentBlockingEventsPromise::CreateAndResolve(
  19371              aValue.ResolveValue(), __func__);
  19372        }
  19373 
  19374        return Document::GetContentBlockingEventsPromise::CreateAndReject(
  19375            false, __func__);
  19376      });
  19377 }
  19378 
  19379 StorageAccessAPIHelper::PerformPermissionGrant
  19380 Document::CreatePermissionGrantPromise(
  19381    nsPIDOMWindowInner* aInnerWindow, nsIPrincipal* aPrincipal,
  19382    bool aHasUserInteraction, bool aRequireUserInteraction,
  19383    const Maybe<nsCString>& aTopLevelBaseDomain, bool aFrameOnly) {
  19384  MOZ_ASSERT(aInnerWindow);
  19385  MOZ_ASSERT(aPrincipal);
  19386  RefPtr<Document> self(this);
  19387  RefPtr<nsPIDOMWindowInner> inner(aInnerWindow);
  19388  RefPtr<nsIPrincipal> principal(aPrincipal);
  19389 
  19390  return [inner, self, principal, aHasUserInteraction, aRequireUserInteraction,
  19391          aTopLevelBaseDomain, aFrameOnly]() {
  19392    RefPtr<StorageAccessAPIHelper::StorageAccessPermissionGrantPromise::Private>
  19393        p = new StorageAccessAPIHelper::StorageAccessPermissionGrantPromise::
  19394            Private(__func__);
  19395 
  19396    // Before we prompt, see if we are same-site
  19397    if (aFrameOnly) {
  19398      nsIChannel* channel = self->GetChannel();
  19399      if (channel) {
  19400        nsCOMPtr<nsILoadInfo> loadInfo = channel->LoadInfo();
  19401        if (!loadInfo->GetIsThirdPartyContextToTopWindow()) {
  19402          p->Resolve(StorageAccessAPIHelper::eAllow, __func__);
  19403          return p;
  19404        }
  19405      }
  19406    }
  19407 
  19408    RefPtr<PWindowGlobalChild::GetStorageAccessPermissionPromise> promise;
  19409    // Test the permission
  19410    MOZ_ASSERT(XRE_IsContentProcess());
  19411 
  19412    WindowGlobalChild* wgc = inner->GetWindowGlobalChild();
  19413    MOZ_ASSERT(wgc);
  19414 
  19415    promise = wgc->SendGetStorageAccessPermission(true);
  19416    MOZ_ASSERT(promise);
  19417    promise->Then(
  19418        GetCurrentSerialEventTarget(), __func__,
  19419        [self, p, inner, principal, aHasUserInteraction,
  19420         aRequireUserInteraction, aTopLevelBaseDomain,
  19421         aFrameOnly](uint32_t aAction) {
  19422          if (aAction == nsIPermissionManager::ALLOW_ACTION) {
  19423            p->Resolve(StorageAccessAPIHelper::eAllow, __func__);
  19424            return;
  19425          }
  19426          if (aAction == nsIPermissionManager::DENY_ACTION) {
  19427            p->Reject(false, __func__);
  19428            return;
  19429          }
  19430 
  19431          // We require user activation before conducting a permission request
  19432          // See
  19433          // https://privacycg.github.io/storage-access/#dom-document-requeststorageaccess
  19434          // where we "If has transient activation is false: ..." immediately
  19435          // before we "Let permissionState be the result of requesting
  19436          // permission to use "storage-access"" from in parallel.
  19437          if (!aHasUserInteraction && aRequireUserInteraction) {
  19438            // Report an error to the console for this case
  19439            nsContentUtils::ReportToConsole(
  19440                nsIScriptError::errorFlag,
  19441                nsLiteralCString("requestStorageAccess"), self,
  19442                nsContentUtils::eDOM_PROPERTIES,
  19443                "RequestStorageAccessUserGesture");
  19444            p->Reject(false, __func__);
  19445            return;
  19446          }
  19447 
  19448          // Create the user prompt
  19449          RefPtr<StorageAccessPermissionRequest> sapr =
  19450              StorageAccessPermissionRequest::Create(
  19451                  inner, principal, aTopLevelBaseDomain, aFrameOnly,
  19452                  // Allow
  19453                  [p] {
  19454                    glean::dom::storage_access_api_ui
  19455                        .EnumGet(glean::dom::StorageAccessApiUiLabel::eAllow)
  19456                        .Add();
  19457                    p->Resolve(StorageAccessAPIHelper::eAllow, __func__);
  19458                  },
  19459                  // Block
  19460                  [p] {
  19461                    glean::dom::storage_access_api_ui
  19462                        .EnumGet(glean::dom::StorageAccessApiUiLabel::eDeny)
  19463                        .Add();
  19464                    p->Reject(false, __func__);
  19465                  });
  19466 
  19467          using PromptResult = ContentPermissionRequestBase::PromptResult;
  19468          PromptResult pr = sapr->CheckPromptPrefs();
  19469 
  19470          if (pr == PromptResult::Pending) {
  19471            // We're about to show a prompt, record the request attempt
  19472            glean::dom::storage_access_api_ui
  19473                .EnumGet(glean::dom::StorageAccessApiUiLabel::eRequest)
  19474                .Add();
  19475          }
  19476 
  19477          bool isThirdPartyTracker =
  19478              nsContentUtils::IsThirdPartyTrackingResourceWindow(inner);
  19479 
  19480          // Try to auto-grant the storage access so the user doesn't see the
  19481          // prompt.
  19482          self->AutomaticStorageAccessPermissionCanBeGranted(
  19483                  aHasUserInteraction, isThirdPartyTracker)
  19484              ->Then(
  19485                  GetCurrentSerialEventTarget(), __func__,
  19486                  // If the autogrant check didn't fail, call this function
  19487                  [p, pr, sapr,
  19488                   inner](const Document::
  19489                              AutomaticStorageAccessPermissionGrantPromise::
  19490                                  ResolveOrRejectValue& aValue) -> void {
  19491                    // Make a copy because we can't modified copy-captured
  19492                    // lambda variables.
  19493                    PromptResult pr2 = pr;
  19494 
  19495                    // If the user didn't already click "allow" and we can
  19496                    // autogrant, do that!
  19497                    bool storageAccessCanBeGrantedAutomatically =
  19498                        aValue.IsResolve() && aValue.ResolveValue();
  19499                    bool autoGrant = false;
  19500                    if (pr2 == PromptResult::Pending &&
  19501                        storageAccessCanBeGrantedAutomatically) {
  19502                      pr2 = PromptResult::Granted;
  19503                      autoGrant = true;
  19504 
  19505                      glean::dom::storage_access_api_ui
  19506                          .EnumGet(glean::dom::StorageAccessApiUiLabel::
  19507                                       eAllowautomatically)
  19508                          .Add();
  19509                    }
  19510 
  19511                    // If we can complete the permission request, do so.
  19512                    if (pr2 != PromptResult::Pending) {
  19513                      MOZ_ASSERT_IF(pr2 != PromptResult::Granted,
  19514                                    pr2 == PromptResult::Denied);
  19515                      if (pr2 == PromptResult::Granted) {
  19516                        StorageAccessAPIHelper::StorageAccessPromptChoices
  19517                            choice = StorageAccessAPIHelper::eAllow;
  19518                        if (autoGrant) {
  19519                          choice = StorageAccessAPIHelper::eAllowAutoGrant;
  19520                        }
  19521                        if (!autoGrant) {
  19522                          p->Resolve(choice, __func__);
  19523                        } else {
  19524                          // We capture sapr here to prevent it from destructing
  19525                          // before the callbacks complete.
  19526                          sapr->MaybeDelayAutomaticGrants()->Then(
  19527                              GetCurrentSerialEventTarget(), __func__,
  19528                              [p, sapr, choice] {
  19529                                p->Resolve(choice, __func__);
  19530                              },
  19531                              [p, sapr] { p->Reject(false, __func__); });
  19532                        }
  19533                        return;
  19534                      }
  19535                      p->Reject(false, __func__);
  19536                      return;
  19537                    }
  19538 
  19539                    // If we get here, the auto-decision failed and we need to
  19540                    // wait for the user prompt to complete.
  19541                    sapr->RequestDelayedTask(
  19542                        GetMainThreadSerialEventTarget(),
  19543                        ContentPermissionRequestBase::DelayedTaskType::Request);
  19544                  });
  19545        },
  19546        [p](mozilla::ipc::ResponseRejectReason aError) {
  19547          p->Reject(false, __func__);
  19548          return p;
  19549        });
  19550 
  19551    return p;
  19552  };
  19553 }
  19554 
  19555 already_AddRefed<mozilla::dom::Promise> Document::RequestStorageAccess(
  19556    mozilla::ErrorResult& aRv) {
  19557  nsIGlobalObject* global = GetScopeObject();
  19558  if (!global) {
  19559    aRv.Throw(NS_ERROR_NOT_AVAILABLE);
  19560    return nullptr;
  19561  }
  19562 
  19563  RefPtr<Promise> promise = Promise::Create(global, aRv);
  19564  if (aRv.Failed()) {
  19565    return nullptr;
  19566  }
  19567 
  19568  if (!IsCurrentActiveDocument()) {
  19569    promise->MaybeRejectWithInvalidStateError(
  19570        "requestStorageAccess requires an active document");
  19571    return promise.forget();
  19572  }
  19573 
  19574  // Get a pointer to the inner window- We need this for convenience sake
  19575  RefPtr<nsPIDOMWindowInner> inner = GetInnerWindow();
  19576  if (!inner) {
  19577    ConsumeTransientUserGestureActivation();
  19578    promise->MaybeRejectWithNotAllowedError(
  19579        "requestStorageAccess not allowed"_ns);
  19580    return promise.forget();
  19581  }
  19582 
  19583  // Step 1: Check if the principal calling this has a permission that lets
  19584  // them use cookies or forbids them from using cookies.
  19585  // This is outside of the spec of the StorageAccess API, but makes the return
  19586  // values to have proper semantics.
  19587  Maybe<bool> resultBecauseCookiesApproved =
  19588      StorageAccessAPIHelper::CheckCookiesPermittedDecidesStorageAccessAPI(
  19589          CookieJarSettings(), NodePrincipal());
  19590  if (resultBecauseCookiesApproved.isSome()) {
  19591    if (resultBecauseCookiesApproved.value()) {
  19592      promise->MaybeResolveWithUndefined();
  19593      return promise.forget();
  19594    } else {
  19595      ConsumeTransientUserGestureActivation();
  19596      promise->MaybeRejectWithNotAllowedError(
  19597          "requestStorageAccess not allowed"_ns);
  19598      return promise.forget();
  19599    }
  19600  }
  19601 
  19602  // Step 2: Check if the browser settings always allow or deny cookies.
  19603  // We should always return a resolved promise if the cookieBehavior is ACCEPT.
  19604  // This is outside of the spec of the StorageAccess API, but makes the return
  19605  // values to have proper semantics.
  19606  bool isThirdPartyDocument = AntiTrackingUtils::IsThirdPartyDocument(this);
  19607  bool isOnThirdPartySkipList = false;
  19608  if (mChannel) {
  19609    nsCOMPtr<nsILoadInfo> loadInfo = mChannel->LoadInfo();
  19610    isOnThirdPartySkipList = loadInfo->GetStoragePermission() ==
  19611                             nsILoadInfo::StoragePermissionAllowListed;
  19612  }
  19613  bool isThirdPartyTracker =
  19614      nsContentUtils::IsThirdPartyTrackingResourceWindow(inner);
  19615  Maybe<bool> resultBecauseBrowserSettings =
  19616      StorageAccessAPIHelper::CheckBrowserSettingsDecidesStorageAccessAPI(
  19617          CookieJarSettings(), isThirdPartyDocument, isOnThirdPartySkipList,
  19618          isThirdPartyTracker);
  19619  if (resultBecauseBrowserSettings.isSome()) {
  19620    if (resultBecauseBrowserSettings.value()) {
  19621      promise->MaybeResolveWithUndefined();
  19622      return promise.forget();
  19623    } else {
  19624      ConsumeTransientUserGestureActivation();
  19625      promise->MaybeRejectWithNotAllowedError(
  19626          "requestStorageAccess not allowed"_ns);
  19627      return promise.forget();
  19628    }
  19629  }
  19630 
  19631  // Step 3: Check if the Document calling requestStorageAccess has anything to
  19632  // gain from storage access. It should be embedded, non-null, etc.
  19633  Maybe<bool> resultBecauseCallContext =
  19634      StorageAccessAPIHelper::CheckCallingContextDecidesStorageAccessAPI(this,
  19635                                                                         true);
  19636  if (resultBecauseCallContext.isSome()) {
  19637    if (resultBecauseCallContext.value()) {
  19638      promise->MaybeResolveWithUndefined();
  19639      return promise.forget();
  19640    } else {
  19641      ConsumeTransientUserGestureActivation();
  19642      promise->MaybeRejectWithNotAllowedError(
  19643          "requestStorageAccess not allowed"_ns);
  19644      return promise.forget();
  19645    }
  19646  }
  19647 
  19648  // Step 4: Check if we already allowed or denied storage access for this
  19649  // document's storage key.
  19650  Maybe<bool> resultBecausePreviousPermission =
  19651      StorageAccessAPIHelper::CheckExistingPermissionDecidesStorageAccessAPI(
  19652          this, true);
  19653  if (resultBecausePreviousPermission.isSome()) {
  19654    if (resultBecausePreviousPermission.value()) {
  19655      promise->MaybeResolveWithUndefined();
  19656      return promise.forget();
  19657    } else {
  19658      ConsumeTransientUserGestureActivation();
  19659      promise->MaybeRejectWithNotAllowedError(
  19660          "requestStorageAccess not allowed"_ns);
  19661      return promise.forget();
  19662    }
  19663  }
  19664 
  19665  // Get pointers to some objects that will be used in the async portion
  19666  RefPtr<BrowsingContext> bc = GetBrowsingContext();
  19667  RefPtr<nsGlobalWindowOuter> outer =
  19668      nsGlobalWindowOuter::Cast(inner->GetOuterWindow());
  19669  if (!outer) {
  19670    ConsumeTransientUserGestureActivation();
  19671    promise->MaybeRejectWithNotAllowedError(
  19672        "requestStorageAccess not allowed"_ns);
  19673    return promise.forget();
  19674  }
  19675  RefPtr<Document> self(this);
  19676 
  19677  // Step 5. Start an async call to request storage access. This will either
  19678  // perform an automatic decision or notify the user, then perform some follow
  19679  // on work changing state to reflect the result of the API. If it resolves,
  19680  // the request was granted. If it rejects it was denied.
  19681  StorageAccessAPIHelper::RequestStorageAccessAsyncHelper(
  19682      this, inner, bc, NodePrincipal(),
  19683      self->HasValidTransientUserGestureActivation(), true, true,
  19684      ContentBlockingNotifier::eStorageAccessAPI, true)
  19685      ->Then(
  19686          GetCurrentSerialEventTarget(), __func__,
  19687          [inner] { return inner->SaveStorageAccessPermissionGranted(); },
  19688          [] {
  19689            return GenericPromise::CreateAndReject(NS_ERROR_FAILURE, __func__);
  19690          })
  19691      ->Then(
  19692          GetCurrentSerialEventTarget(), __func__,
  19693          [promise] { promise->MaybeResolveWithUndefined(); },
  19694          [promise, self] {
  19695            self->ConsumeTransientUserGestureActivation();
  19696            promise->MaybeRejectWithNotAllowedError(
  19697                "requestStorageAccess not allowed"_ns);
  19698          });
  19699 
  19700  return promise.forget();
  19701 }
  19702 
  19703 already_AddRefed<mozilla::dom::Promise> Document::RequestStorageAccessForOrigin(
  19704    const nsAString& aThirdPartyOrigin, const bool aRequireUserActivation,
  19705    mozilla::ErrorResult& aRv) {
  19706  nsIGlobalObject* global = GetScopeObject();
  19707  if (!global) {
  19708    aRv.Throw(NS_ERROR_NOT_AVAILABLE);
  19709    return nullptr;
  19710  }
  19711  RefPtr<Promise> promise = Promise::Create(global, aRv);
  19712  if (aRv.Failed()) {
  19713    return nullptr;
  19714  }
  19715 
  19716  // Step 0: Check that we have user activation before proceeding to prevent
  19717  // rapid calls to the API to leak information.
  19718  if (aRequireUserActivation && !HasValidTransientUserGestureActivation()) {
  19719    // Report an error to the console for this case
  19720    nsContentUtils::ReportToConsole(nsIScriptError::errorFlag,
  19721                                    nsLiteralCString("requestStorageAccess"),
  19722                                    this, nsContentUtils::eDOM_PROPERTIES,
  19723                                    "RequestStorageAccessUserGesture");
  19724    ConsumeTransientUserGestureActivation();
  19725    promise->MaybeRejectWithNotAllowedError(
  19726        "requestStorageAccess not allowed"_ns);
  19727    return promise.forget();
  19728  }
  19729 
  19730  // Step 1: Check if the provided URI is different-site to this Document
  19731  nsCOMPtr<nsIURI> thirdPartyURI;
  19732  nsresult rv = NS_NewURI(getter_AddRefs(thirdPartyURI), aThirdPartyOrigin);
  19733  if (NS_WARN_IF(NS_FAILED(rv))) {
  19734    aRv.Throw(rv);
  19735    return nullptr;
  19736  }
  19737  bool isThirdPartyDocument;
  19738  rv = NodePrincipal()->IsThirdPartyURI(thirdPartyURI, &isThirdPartyDocument);
  19739  if (NS_WARN_IF(NS_FAILED(rv))) {
  19740    aRv.Throw(rv);
  19741    return nullptr;
  19742  }
  19743  Maybe<bool> resultBecauseBrowserSettings =
  19744      StorageAccessAPIHelper::CheckBrowserSettingsDecidesStorageAccessAPI(
  19745          CookieJarSettings(), isThirdPartyDocument, false, true);
  19746  if (resultBecauseBrowserSettings.isSome()) {
  19747    if (resultBecauseBrowserSettings.value()) {
  19748      promise->MaybeResolveWithUndefined();
  19749      return promise.forget();
  19750    }
  19751    ConsumeTransientUserGestureActivation();
  19752    promise->MaybeRejectWithNotAllowedError(
  19753        "requestStorageAccess not allowed"_ns);
  19754    return promise.forget();
  19755  }
  19756 
  19757  // Step 2: Check that this Document is same-site to the top, and check that
  19758  // we have user activation if we require it.
  19759  Maybe<bool> resultBecauseCallContext = StorageAccessAPIHelper::
  19760      CheckSameSiteCallingContextDecidesStorageAccessAPI(
  19761          this, aRequireUserActivation);
  19762  if (resultBecauseCallContext.isSome()) {
  19763    if (resultBecauseCallContext.value()) {
  19764      promise->MaybeResolveWithUndefined();
  19765      return promise.forget();
  19766    }
  19767    ConsumeTransientUserGestureActivation();
  19768    promise->MaybeRejectWithNotAllowedError(
  19769        "requestStorageAccess not allowed"_ns);
  19770    return promise.forget();
  19771  }
  19772 
  19773  // Step 3: Get some useful variables that can be captured by the lambda for
  19774  // the asynchronous portion
  19775  RefPtr<BrowsingContext> bc = GetBrowsingContext();
  19776  nsCOMPtr<nsPIDOMWindowInner> inner = GetInnerWindow();
  19777  if (!inner) {
  19778    ConsumeTransientUserGestureActivation();
  19779    promise->MaybeRejectWithNotAllowedError(
  19780        "requestStorageAccess not allowed"_ns);
  19781    return promise.forget();
  19782  }
  19783  RefPtr<nsGlobalWindowOuter> outer =
  19784      nsGlobalWindowOuter::Cast(inner->GetOuterWindow());
  19785  if (!outer) {
  19786    ConsumeTransientUserGestureActivation();
  19787    promise->MaybeRejectWithNotAllowedError(
  19788        "requestStorageAccess not allowed"_ns);
  19789    return promise.forget();
  19790  }
  19791  nsCOMPtr<nsIPrincipal> principal = BasePrincipal::CreateContentPrincipal(
  19792      thirdPartyURI, NodePrincipal()->OriginAttributesRef());
  19793  if (!principal) {
  19794    ConsumeTransientUserGestureActivation();
  19795    promise->MaybeRejectWithNotAllowedError(
  19796        "requestStorageAccess not allowed"_ns);
  19797    return promise.forget();
  19798  }
  19799 
  19800  RefPtr<Document> self(this);
  19801  bool hasUserActivation = HasValidTransientUserGestureActivation();
  19802 
  19803  // Consume user activation before entering the async part of this method.
  19804  // This prevents usage of other transient activation-gated APIs.
  19805  ConsumeTransientUserGestureActivation();
  19806 
  19807  ContentChild* cc = ContentChild::GetSingleton();
  19808  if (!cc) {
  19809    // TODO(bug 1778561): Make this work in non-content processes.
  19810    promise->MaybeRejectWithUndefined();
  19811    return promise.forget();
  19812  }
  19813 
  19814  // Step 4a: Start the async part of this function. Check the cookie
  19815  // permission, but this can't be done in this process. We needs the cookie
  19816  // permission of the URL as if it were embedded on this page, so we need to
  19817  // make this check in the ContentParent.
  19818  StorageAccessAPIHelper::
  19819      AsyncCheckCookiesPermittedDecidesStorageAccessAPIOnChildProcess(
  19820          GetBrowsingContext(), principal)
  19821          ->Then(
  19822              GetCurrentSerialEventTarget(), __func__,
  19823              [inner, thirdPartyURI, bc, principal, hasUserActivation,
  19824               aRequireUserActivation, self,
  19825               promise](Maybe<bool> cookieResult) {
  19826                // Handle the result of the cookie permission check that took
  19827                // place in the ContentParent.
  19828                if (cookieResult.isSome()) {
  19829                  if (cookieResult.value()) {
  19830                    return MozPromise<int, bool, true>::CreateAndResolve(
  19831                        true, __func__);
  19832                  }
  19833                  return MozPromise<int, bool, true>::CreateAndReject(false,
  19834                                                                      __func__);
  19835                }
  19836 
  19837                // Step 4b: Check for the existing storage access permission
  19838                nsAutoCString type;
  19839                bool ok = AntiTrackingUtils::CreateStoragePermissionKey(
  19840                    principal, type);
  19841                if (!ok) {
  19842                  return MozPromise<int, bool, true>::CreateAndReject(false,
  19843                                                                      __func__);
  19844                }
  19845                if (AntiTrackingUtils::CheckStoragePermission(
  19846                        self->NodePrincipal(), type,
  19847                        self->IsInPrivateBrowsing())) {
  19848                  return MozPromise<int, bool, true>::CreateAndResolve(
  19849                      true, __func__);
  19850                }
  19851 
  19852                // Step 4c: Try to request storage access, either automatically
  19853                // or with a user-prompt. This is the part that is async in the
  19854                // typical requestStorageAccess function.
  19855                return StorageAccessAPIHelper::RequestStorageAccessAsyncHelper(
  19856                    self, inner, bc, principal, hasUserActivation,
  19857                    aRequireUserActivation, false,
  19858                    ContentBlockingNotifier::
  19859                        ePrivilegeStorageAccessForOriginAPI,
  19860                    true);
  19861              },
  19862              // If the IPC rejects, we should reject our promise here which
  19863              // will cause a rejection of the promise we already returned
  19864              [promise]() {
  19865                return MozPromise<int, bool, true>::CreateAndReject(false,
  19866                                                                    __func__);
  19867              })
  19868          ->Then(
  19869              GetCurrentSerialEventTarget(), __func__,
  19870              // If the previous handlers resolved, we should reinstate user
  19871              // activation and resolve the promise we returned in Step 5.
  19872              [self, inner, promise] {
  19873                inner->SaveStorageAccessPermissionGranted();
  19874                self->NotifyUserGestureActivation();
  19875                promise->MaybeResolveWithUndefined();
  19876              },
  19877              // If the previous handler rejected, we should reject the promise
  19878              // returned by this function.
  19879              [promise] {
  19880                promise->MaybeRejectWithNotAllowedError(
  19881                    "requestStorageAccess not allowed"_ns);
  19882              });
  19883 
  19884  // Step 5: While the async stuff is happening, we should return the promise so
  19885  // our caller can continue executing.
  19886  return promise.forget();
  19887 }
  19888 
  19889 already_AddRefed<Promise> Document::RequestStorageAccessUnderSite(
  19890    const nsAString& aSerializedSite, ErrorResult& aRv) {
  19891  nsIGlobalObject* global = GetScopeObject();
  19892  if (!global) {
  19893    aRv.Throw(NS_ERROR_NOT_AVAILABLE);
  19894    return nullptr;
  19895  }
  19896  RefPtr<Promise> promise = Promise::Create(global, aRv);
  19897  if (aRv.Failed()) {
  19898    return nullptr;
  19899  }
  19900 
  19901  // Check that we have user activation before proceeding to prevent
  19902  // rapid calls to the API to leak information.
  19903  if (!ConsumeTransientUserGestureActivation()) {
  19904    // Report an error to the console for this case
  19905    nsContentUtils::ReportToConsole(
  19906        nsIScriptError::errorFlag, "requestStorageAccess"_ns, this,
  19907        nsContentUtils::eDOM_PROPERTIES, "RequestStorageAccessUserGesture");
  19908    promise->MaybeRejectWithUndefined();
  19909    return promise.forget();
  19910  }
  19911 
  19912  // Check if the provided URI is different-site to this Document
  19913  nsCOMPtr<nsIURI> siteURI;
  19914  nsresult rv = NS_NewURI(getter_AddRefs(siteURI), aSerializedSite);
  19915  if (NS_WARN_IF(NS_FAILED(rv))) {
  19916    promise->MaybeRejectWithUndefined();
  19917    return promise.forget();
  19918  }
  19919  bool isCrossSiteArgument;
  19920  rv = NodePrincipal()->IsThirdPartyURI(siteURI, &isCrossSiteArgument);
  19921  if (NS_WARN_IF(NS_FAILED(rv))) {
  19922    aRv.Throw(rv);
  19923    return nullptr;
  19924  }
  19925  if (!isCrossSiteArgument) {
  19926    promise->MaybeRejectWithUndefined();
  19927    return promise.forget();
  19928  }
  19929 
  19930  // Check if this party has broad cookie permissions.
  19931  Maybe<bool> resultBecauseCookiesApproved =
  19932      StorageAccessAPIHelper::CheckCookiesPermittedDecidesStorageAccessAPI(
  19933          CookieJarSettings(), NodePrincipal());
  19934  if (resultBecauseCookiesApproved.isSome()) {
  19935    if (resultBecauseCookiesApproved.value()) {
  19936      promise->MaybeResolveWithUndefined();
  19937      return promise.forget();
  19938    }
  19939    promise->MaybeRejectWithUndefined();
  19940    return promise.forget();
  19941  }
  19942 
  19943  // Check if browser settings preclude this document getting storage
  19944  // access under the provided site
  19945  Maybe<bool> resultBecauseBrowserSettings =
  19946      StorageAccessAPIHelper::CheckBrowserSettingsDecidesStorageAccessAPI(
  19947          CookieJarSettings(), true, false, true);
  19948  if (resultBecauseBrowserSettings.isSome()) {
  19949    if (resultBecauseBrowserSettings.value()) {
  19950      promise->MaybeResolveWithUndefined();
  19951      return promise.forget();
  19952    }
  19953    promise->MaybeRejectWithUndefined();
  19954    return promise.forget();
  19955  }
  19956 
  19957  // Check that this Document is same-site to the top
  19958  Maybe<bool> resultBecauseCallContext = StorageAccessAPIHelper::
  19959      CheckSameSiteCallingContextDecidesStorageAccessAPI(this, false);
  19960  if (resultBecauseCallContext.isSome()) {
  19961    if (resultBecauseCallContext.value()) {
  19962      promise->MaybeResolveWithUndefined();
  19963      return promise.forget();
  19964    }
  19965    promise->MaybeRejectWithUndefined();
  19966    return promise.forget();
  19967  }
  19968 
  19969  nsCOMPtr<nsIPrincipal> principal(NodePrincipal());
  19970 
  19971  // Test if the permission this is requesting is already set
  19972  nsCOMPtr<nsIPrincipal> argumentPrincipal =
  19973      BasePrincipal::CreateContentPrincipal(
  19974          siteURI, NodePrincipal()->OriginAttributesRef());
  19975  if (!argumentPrincipal) {
  19976    ConsumeTransientUserGestureActivation();
  19977    promise->MaybeRejectWithUndefined();
  19978    return promise.forget();
  19979  }
  19980  nsCString originNoSuffix;
  19981  rv = NodePrincipal()->GetOriginNoSuffix(originNoSuffix);
  19982  if (NS_WARN_IF(NS_FAILED(rv))) {
  19983    promise->MaybeRejectWithUndefined();
  19984    return promise.forget();
  19985  }
  19986 
  19987  ContentChild* cc = ContentChild::GetSingleton();
  19988  MOZ_ASSERT(cc);
  19989  RefPtr<Document> self(this);
  19990  cc->SendTestStorageAccessPermission(argumentPrincipal, originNoSuffix)
  19991      ->Then(
  19992          GetCurrentSerialEventTarget(), __func__,
  19993          [promise, siteURI,
  19994           self](const ContentChild::TestStorageAccessPermissionPromise::
  19995                     ResolveValueType& aResult) {
  19996            if (aResult) {
  19997              return StorageAccessAPIHelper::
  19998                  StorageAccessPermissionGrantPromise::CreateAndResolve(
  19999                      StorageAccessAPIHelper::eAllow, __func__);
  20000            }
  20001            // Get a grant for the storage access permission that will be set
  20002            // when this is completed in the embedding context
  20003            nsCString serializedSite;
  20004            nsCOMPtr<nsIEffectiveTLDService> etld =
  20005                mozilla::components::EffectiveTLD::Service();
  20006            if (!etld) {
  20007              return StorageAccessAPIHelper::
  20008                  StorageAccessPermissionGrantPromise::CreateAndReject(
  20009                      false, __func__);
  20010            }
  20011            nsresult rv = etld->GetSite(siteURI, serializedSite);
  20012            if (NS_FAILED(rv)) {
  20013              return StorageAccessAPIHelper::
  20014                  StorageAccessPermissionGrantPromise::CreateAndReject(
  20015                      false, __func__);
  20016            }
  20017            return self->CreatePermissionGrantPromise(
  20018                self->GetInnerWindow(), self->NodePrincipal(), true, true,
  20019                Some(serializedSite), false)();
  20020          },
  20021          [](const ContentChild::TestStorageAccessPermissionPromise::
  20022                 RejectValueType& aResult) {
  20023            return StorageAccessAPIHelper::StorageAccessPermissionGrantPromise::
  20024                CreateAndReject(false, __func__);
  20025          })
  20026      ->Then(
  20027          GetCurrentSerialEventTarget(), __func__,
  20028          [promise, principal, siteURI](int result) {
  20029            ContentChild* cc = ContentChild::GetSingleton();
  20030            if (!cc) {
  20031              // TODO(bug 1778561): Make this work in non-content processes.
  20032              promise->MaybeRejectWithUndefined();
  20033              return;
  20034            }
  20035            // Set a permission in the parent process that this document wants
  20036            // storage access under the argument's site, resolving our returned
  20037            // promise on success
  20038            cc->SendSetAllowStorageAccessRequestFlag(principal, siteURI)
  20039                ->Then(
  20040                    GetCurrentSerialEventTarget(), __func__,
  20041                    [promise](bool success) {
  20042                      if (success) {
  20043                        promise->MaybeResolveWithUndefined();
  20044                      } else {
  20045                        promise->MaybeRejectWithUndefined();
  20046                      }
  20047                    },
  20048                    [promise](mozilla::ipc::ResponseRejectReason reason) {
  20049                      promise->MaybeRejectWithUndefined();
  20050                    });
  20051          },
  20052          [promise](bool result) { promise->MaybeRejectWithUndefined(); });
  20053 
  20054  // Return the promise that is resolved in the async handler above
  20055  return promise.forget();
  20056 }
  20057 
  20058 already_AddRefed<Promise> Document::CompleteStorageAccessRequestFromSite(
  20059    const nsAString& aSerializedOrigin, ErrorResult& aRv) {
  20060  nsIGlobalObject* global = GetScopeObject();
  20061  if (!global) {
  20062    aRv.Throw(NS_ERROR_NOT_AVAILABLE);
  20063    return nullptr;
  20064  }
  20065  RefPtr<Promise> promise = Promise::Create(global, aRv);
  20066  if (aRv.Failed()) {
  20067    return nullptr;
  20068  }
  20069 
  20070  // Check that the provided URI is different-site to this Document
  20071  nsCOMPtr<nsIURI> argumentURI;
  20072  nsresult rv = NS_NewURI(getter_AddRefs(argumentURI), aSerializedOrigin);
  20073  if (NS_WARN_IF(NS_FAILED(rv))) {
  20074    promise->MaybeRejectWithUndefined();
  20075    return promise.forget();
  20076  }
  20077  bool isCrossSiteArgument;
  20078  rv = NodePrincipal()->IsThirdPartyURI(argumentURI, &isCrossSiteArgument);
  20079  if (NS_WARN_IF(NS_FAILED(rv))) {
  20080    aRv.Throw(rv);
  20081    return nullptr;
  20082  }
  20083  if (!isCrossSiteArgument) {
  20084    promise->MaybeRejectWithUndefined();
  20085    return promise.forget();
  20086  }
  20087 
  20088  // Check if browser settings preclude this document getting storage
  20089  // access under the provided site
  20090  Maybe<bool> resultBecauseBrowserSettings =
  20091      StorageAccessAPIHelper::CheckBrowserSettingsDecidesStorageAccessAPI(
  20092          CookieJarSettings(), true, false, true);
  20093  if (resultBecauseBrowserSettings.isSome()) {
  20094    if (resultBecauseBrowserSettings.value()) {
  20095      promise->MaybeResolveWithUndefined();
  20096      return promise.forget();
  20097    }
  20098    promise->MaybeRejectWithUndefined();
  20099    return promise.forget();
  20100  }
  20101 
  20102  // Check that this Document is same-site to the top
  20103  Maybe<bool> resultBecauseCallContext = StorageAccessAPIHelper::
  20104      CheckSameSiteCallingContextDecidesStorageAccessAPI(this, false);
  20105  if (resultBecauseCallContext.isSome()) {
  20106    if (resultBecauseCallContext.value()) {
  20107      promise->MaybeResolveWithUndefined();
  20108      return promise.forget();
  20109    }
  20110    promise->MaybeRejectWithUndefined();
  20111    return promise.forget();
  20112  }
  20113 
  20114  // Create principal of the embedded site requesting storage access
  20115  nsCOMPtr<nsIPrincipal> principal = BasePrincipal::CreateContentPrincipal(
  20116      argumentURI, NodePrincipal()->OriginAttributesRef());
  20117  if (!principal) {
  20118    promise->MaybeRejectWithUndefined();
  20119    return promise.forget();
  20120  }
  20121 
  20122  // Get versions of these objects that we can use in lambdas for callbacks
  20123  RefPtr<Document> self(this);
  20124  RefPtr<BrowsingContext> bc = GetBrowsingContext();
  20125  nsCOMPtr<nsPIDOMWindowInner> inner = GetInnerWindow();
  20126 
  20127  // Test that the permission was set by a call to RequestStorageAccessUnderSite
  20128  // from a top level document that is same-site with the argument
  20129  ContentChild* cc = ContentChild::GetSingleton();
  20130  if (!cc) {
  20131    // TODO(bug 1778561): Make this work in non-content processes.
  20132    promise->MaybeRejectWithUndefined();
  20133    return promise.forget();
  20134  }
  20135  cc->SendTestAllowStorageAccessRequestFlag(NodePrincipal(), argumentURI)
  20136      ->Then(
  20137          GetCurrentSerialEventTarget(), __func__,
  20138          [inner, bc, self, principal](bool success) {
  20139            if (success) {
  20140              // If that resolved with true, check that we don't already have a
  20141              // permission that gives cookie access.
  20142              return StorageAccessAPIHelper::
  20143                  AsyncCheckCookiesPermittedDecidesStorageAccessAPIOnChildProcess(
  20144                      bc, principal);
  20145            }
  20146            return MozPromise<Maybe<bool>, nsresult, true>::CreateAndReject(
  20147                NS_ERROR_FAILURE, __func__);
  20148          },
  20149          [](mozilla::ipc::ResponseRejectReason reason) {
  20150            return MozPromise<Maybe<bool>, nsresult, true>::CreateAndReject(
  20151                NS_ERROR_FAILURE, __func__);
  20152          })
  20153      ->Then(
  20154          GetCurrentSerialEventTarget(), __func__,
  20155          [inner, bc, principal, self, promise](Maybe<bool> cookieResult) {
  20156            // Handle the result of the cookie permission check that took place
  20157            // in the ContentParent.
  20158            if (cookieResult.isSome()) {
  20159              if (cookieResult.value()) {
  20160                return StorageAccessAPIHelper::
  20161                    StorageAccessPermissionGrantPromise::CreateAndResolve(
  20162                        StorageAccessAPIHelper::eAllowAutoGrant, __func__);
  20163              }
  20164              return StorageAccessAPIHelper::
  20165                  StorageAccessPermissionGrantPromise::CreateAndReject(
  20166                      false, __func__);
  20167            }
  20168 
  20169            // Check for the existing storage access permission
  20170            nsAutoCString type;
  20171            bool ok =
  20172                AntiTrackingUtils::CreateStoragePermissionKey(principal, type);
  20173            if (!ok) {
  20174              return StorageAccessAPIHelper::
  20175                  StorageAccessPermissionGrantPromise::CreateAndReject(
  20176                      false, __func__);
  20177            }
  20178            if (AntiTrackingUtils::CheckStoragePermission(
  20179                    self->NodePrincipal(), type, self->IsInPrivateBrowsing())) {
  20180              return StorageAccessAPIHelper::
  20181                  StorageAccessPermissionGrantPromise::CreateAndResolve(
  20182                      StorageAccessAPIHelper::eAllowAutoGrant, __func__);
  20183            }
  20184 
  20185            // Try to request storage access, ignoring the final checks.
  20186            // We ignore the final checks because this is where the "grant"
  20187            // either by prompt doorhanger or autogrant takes place. We already
  20188            // gathered an equivalent grant in requestStorageAccessUnderSite.
  20189            return StorageAccessAPIHelper::RequestStorageAccessAsyncHelper(
  20190                self, inner, bc, principal, true, true, false,
  20191                ContentBlockingNotifier::eStorageAccessAPI, false);
  20192          },
  20193          // If the IPC rejects, we should reject our promise here which will
  20194          // cause a rejection of the promise we already returned
  20195          [promise]() {
  20196            return MozPromise<int, bool, true>::CreateAndReject(false,
  20197                                                                __func__);
  20198          })
  20199      ->Then(
  20200          GetCurrentSerialEventTarget(), __func__,
  20201          // If the previous handlers resolved, we should reinstate user
  20202          // activation and resolve the promise we returned in Step 5.
  20203          [self, inner, promise] {
  20204            inner->SaveStorageAccessPermissionGranted();
  20205            promise->MaybeResolveWithUndefined();
  20206          },
  20207          // If the previous handler rejected, we should reject the promise
  20208          // returned by this function.
  20209          [promise] { promise->MaybeRejectWithUndefined(); });
  20210 
  20211  return promise.forget();
  20212 }
  20213 
  20214 nsTHashSet<RefPtr<WakeLockSentinel>>& Document::ActiveWakeLocks(
  20215    WakeLockType aType) {
  20216  return mActiveLocks.LookupOrInsert(aType);
  20217 }
  20218 
  20219 class UnlockAllWakeLockRunnable final : public Runnable {
  20220 public:
  20221  UnlockAllWakeLockRunnable(WakeLockType aType, Document* aDoc)
  20222      : Runnable("UnlockAllWakeLocks"), mType(aType), mDoc(aDoc) {}
  20223 
  20224  // MOZ_CAN_RUN_SCRIPT_BOUNDARY until Runnable::Run is MOZ_CAN_RUN_SCRIPT.  See
  20225  // bug 1535398.
  20226  MOZ_CAN_RUN_SCRIPT_BOUNDARY
  20227  NS_IMETHOD Run() override {
  20228    // Move, as ReleaseWakeLock will try to remove from and possibly allow
  20229    // scripts via onrelease to add to document.[[ActiveLocks]]["screen"]
  20230    nsCOMPtr<Document> doc = mDoc;
  20231    nsTHashSet<RefPtr<WakeLockSentinel>> locks =
  20232        std::move(doc->ActiveWakeLocks(mType));
  20233    for (const auto& lock : locks) {
  20234      // ReleaseWakeLock runs script, which could release other locks
  20235      if (!lock->Released()) {
  20236        ReleaseWakeLock(doc, MOZ_KnownLive(lock), mType);
  20237      }
  20238    }
  20239    return NS_OK;
  20240  }
  20241 
  20242 protected:
  20243  ~UnlockAllWakeLockRunnable() = default;
  20244 
  20245 private:
  20246  WakeLockType mType;
  20247  nsCOMPtr<Document> mDoc;
  20248 };
  20249 
  20250 void Document::UnlockAllWakeLocks(WakeLockType aType) {
  20251  // Perform unlock in a runnable to prevent UnlockAll being MOZ_CAN_RUN_SCRIPT
  20252  if (!ActiveWakeLocks(aType).IsEmpty()) {
  20253    RefPtr<UnlockAllWakeLockRunnable> runnable =
  20254        MakeRefPtr<UnlockAllWakeLockRunnable>(aType, this);
  20255    nsresult rv = NS_DispatchToMainThread(runnable);
  20256    MOZ_DIAGNOSTIC_ASSERT(NS_SUCCEEDED(rv));
  20257    (void)rv;
  20258  }
  20259 }
  20260 
  20261 RefPtr<Document::AutomaticStorageAccessPermissionGrantPromise>
  20262 Document::AutomaticStorageAccessPermissionCanBeGranted(
  20263    bool hasUserActivation, bool isThirdPartyTracker) {
  20264  // requestStorageAccessForOrigin may not require user activation. If we don't
  20265  // have user activation at this point we should always show the prompt.
  20266  if (!hasUserActivation ||
  20267      !StaticPrefs::privacy_antitracking_enableWebcompat()) {
  20268    return AutomaticStorageAccessPermissionGrantPromise::CreateAndResolve(
  20269        false, __func__);
  20270  }
  20271 
  20272  if (isThirdPartyTracker &&
  20273      StaticPrefs::
  20274          dom_storage_access_auto_grants_exclude_third_party_trackers()) {
  20275    return AutomaticStorageAccessPermissionGrantPromise::CreateAndResolve(
  20276        false, __func__);
  20277  }
  20278 
  20279  if (XRE_IsContentProcess()) {
  20280    // In the content process, we need to ask the parent process to compute
  20281    // this.  The reason is that nsIPermissionManager::GetAllWithTypePrefix()
  20282    // isn't accessible in the content process.
  20283    ContentChild* cc = ContentChild::GetSingleton();
  20284    MOZ_ASSERT(cc);
  20285 
  20286    return cc->SendAutomaticStorageAccessPermissionCanBeGranted(NodePrincipal())
  20287        ->Then(GetCurrentSerialEventTarget(), __func__,
  20288               [](const ContentChild::
  20289                      AutomaticStorageAccessPermissionCanBeGrantedPromise::
  20290                          ResolveOrRejectValue& aValue) {
  20291                 if (aValue.IsResolve()) {
  20292                   return AutomaticStorageAccessPermissionGrantPromise::
  20293                       CreateAndResolve(aValue.ResolveValue(), __func__);
  20294                 }
  20295 
  20296                 return AutomaticStorageAccessPermissionGrantPromise::
  20297                     CreateAndReject(false, __func__);
  20298               });
  20299  }
  20300 
  20301  if (XRE_IsParentProcess()) {
  20302    // In the parent process, we can directly compute this.
  20303    return AutomaticStorageAccessPermissionGrantPromise::CreateAndResolve(
  20304        AutomaticStorageAccessPermissionCanBeGranted(NodePrincipal()),
  20305        __func__);
  20306  }
  20307 
  20308  return AutomaticStorageAccessPermissionGrantPromise::CreateAndReject(
  20309      false, __func__);
  20310 }
  20311 
  20312 bool Document::AutomaticStorageAccessPermissionCanBeGranted(
  20313    nsIPrincipal* aPrincipal) {
  20314  if (!StaticPrefs::dom_storage_access_auto_grants()) {
  20315    return false;
  20316  }
  20317 
  20318  if (!ContentBlockingUserInteraction::Exists(aPrincipal)) {
  20319    return false;
  20320  }
  20321 
  20322  nsCOMPtr<nsIBrowserUsage> bu = do_ImportESModule(
  20323      "resource:///modules/BrowserUsageTelemetry.sys.mjs", fallible);
  20324  if (NS_WARN_IF(!bu)) {
  20325    return false;
  20326  }
  20327 
  20328  uint32_t uniqueDomainsVisitedInPast24Hours = 0;
  20329  nsresult rv = bu->GetUniqueDomainsVisitedInPast24Hours(
  20330      &uniqueDomainsVisitedInPast24Hours);
  20331  if (NS_WARN_IF(NS_FAILED(rv))) {
  20332    return false;
  20333  }
  20334 
  20335  Maybe<size_t> maybeOriginsThirdPartyHasAccessTo =
  20336      AntiTrackingUtils::CountSitesAllowStorageAccess(aPrincipal);
  20337  if (maybeOriginsThirdPartyHasAccessTo.isNothing()) {
  20338    return false;
  20339  }
  20340  size_t originsThirdPartyHasAccessTo =
  20341      maybeOriginsThirdPartyHasAccessTo.value();
  20342 
  20343  // one percent of the number of top-levels origins visited in the current
  20344  // session (but not to exceed 24 hours), or the value of the
  20345  // dom.storage_access.max_concurrent_auto_grants preference, whichever is
  20346  // higher.
  20347  size_t maxConcurrentAutomaticGrants = std::max(
  20348      std::max(int(std::floor(uniqueDomainsVisitedInPast24Hours / 100)),
  20349               StaticPrefs::dom_storage_access_max_concurrent_auto_grants()),
  20350      0);
  20351 
  20352  return originsThirdPartyHasAccessTo < maxConcurrentAutomaticGrants;
  20353 }
  20354 
  20355 void Document::RecordNavigationTiming(ReadyState aReadyState) {
  20356  if (!XRE_IsContentProcess()) {
  20357    return;
  20358  }
  20359  if (!IsTopLevelContentDocument()) {
  20360    return;
  20361  }
  20362  // If we dont have the timing yet (mostly because the doc is still loading),
  20363  // get it from docshell.
  20364  RefPtr<nsDOMNavigationTiming> timing = mTiming;
  20365  if (!timing) {
  20366    if (!mDocumentContainer) {
  20367      return;
  20368    }
  20369    timing = mDocumentContainer->GetNavigationTiming();
  20370    if (!timing) {
  20371      return;
  20372    }
  20373  }
  20374  TimeStamp startTime = timing->GetNavigationStartTimeStamp();
  20375  switch (aReadyState) {
  20376    case READYSTATE_LOADING:
  20377      if (!mDOMLoadingSet) {
  20378        glean::performance_time::to_dom_loading.AccumulateRawDuration(
  20379            TimeStamp::Now() - startTime);
  20380        mDOMLoadingSet = true;
  20381      }
  20382      break;
  20383    case READYSTATE_INTERACTIVE:
  20384      if (!mDOMInteractiveSet) {
  20385        glean::performance_time::dom_interactive.AccumulateRawDuration(
  20386            TimeStamp::Now() - startTime);
  20387        mDOMInteractiveSet = true;
  20388      }
  20389      break;
  20390    case READYSTATE_COMPLETE:
  20391      if (!mDOMCompleteSet) {
  20392        glean::performance_time::dom_complete.AccumulateRawDuration(
  20393            TimeStamp::Now() - startTime);
  20394        mDOMCompleteSet = true;
  20395      }
  20396      break;
  20397    default:
  20398      NS_WARNING("Unexpected ReadyState value");
  20399      break;
  20400  }
  20401 }
  20402 
  20403 void Document::ReportShadowDOMUsage() {
  20404  nsPIDOMWindowInner* inner = GetInnerWindow();
  20405  if (NS_WARN_IF(!inner)) {
  20406    return;
  20407  }
  20408 
  20409  WindowContext* wc = inner->GetWindowContext();
  20410  if (NS_WARN_IF(!wc || wc->IsDiscarded())) {
  20411    return;
  20412  }
  20413 
  20414  WindowContext* topWc = wc->TopWindowContext();
  20415  if (topWc->GetHasReportedShadowDOMUsage()) {
  20416    return;
  20417  }
  20418 
  20419  MOZ_ALWAYS_SUCCEEDS(topWc->SetHasReportedShadowDOMUsage(true));
  20420 }
  20421 
  20422 // static
  20423 bool Document::StorageAccessSandboxed(uint32_t aSandboxFlags) {
  20424  return StaticPrefs::dom_storage_access_enabled() &&
  20425         (aSandboxFlags & SANDBOXED_STORAGE_ACCESS) != 0;
  20426 }
  20427 
  20428 bool Document::StorageAccessSandboxed() const {
  20429  return Document::StorageAccessSandboxed(GetSandboxFlags());
  20430 }
  20431 
  20432 bool Document::GetCachedSizes(nsTabSizes* aSizes) {
  20433  if (mCachedTabSizeGeneration == 0 ||
  20434      GetGeneration() != mCachedTabSizeGeneration) {
  20435    return false;
  20436  }
  20437  aSizes->mDom += mCachedTabSizes.mDom;
  20438  aSizes->mStyle += mCachedTabSizes.mStyle;
  20439  aSizes->mOther += mCachedTabSizes.mOther;
  20440  return true;
  20441 }
  20442 
  20443 void Document::SetCachedSizes(nsTabSizes* aSizes) {
  20444  mCachedTabSizes.mDom = aSizes->mDom;
  20445  mCachedTabSizes.mStyle = aSizes->mStyle;
  20446  mCachedTabSizes.mOther = aSizes->mOther;
  20447  mCachedTabSizeGeneration = GetGeneration();
  20448 }
  20449 
  20450 nsAtom* Document::GetContentLanguageAsAtomForStyle() const {
  20451  // Content-Language may be a comma-separated list of language codes,
  20452  // in which case the HTML5 spec says to treat it as unknown
  20453  if (mContentLanguage &&
  20454      !nsDependentAtomString(mContentLanguage).Contains(char16_t(','))) {
  20455    return GetContentLanguage();
  20456  }
  20457 
  20458  return nullptr;
  20459 }
  20460 
  20461 nsAtom* Document::GetLanguageForStyle() const {
  20462  if (nsAtom* lang = GetContentLanguageAsAtomForStyle()) {
  20463    return lang;
  20464  }
  20465  return mLanguageFromCharset;
  20466 }
  20467 
  20468 void Document::GetContentLanguageForBindings(DOMString& aString) const {
  20469  aString.SetKnownLiveAtom(mContentLanguage, DOMString::eTreatNullAsEmpty);
  20470 }
  20471 
  20472 const LangGroupFontPrefs* Document::GetFontPrefsForLang(
  20473    nsAtom* aLanguage, bool* aNeedsToCache) const {
  20474  nsAtom* lang = aLanguage ? aLanguage : mLanguageFromCharset;
  20475  return StaticPresData::Get()->GetFontPrefsForLang(lang, aNeedsToCache);
  20476 }
  20477 
  20478 void Document::DoCacheAllKnownLangPrefs() {
  20479  MOZ_ASSERT(mMayNeedFontPrefsUpdate);
  20480  RefPtr<nsAtom> lang = GetLanguageForStyle();
  20481  StaticPresData* data = StaticPresData::Get();
  20482  data->GetFontPrefsForLang(lang ? lang.get() : mLanguageFromCharset);
  20483  data->GetFontPrefsForLang(nsGkAtoms::x_math);
  20484  // https://bugzilla.mozilla.org/show_bug.cgi?id=1362599#c12
  20485  data->GetFontPrefsForLang(nsGkAtoms::Unicode);
  20486  for (const auto& key : mLanguagesUsed) {
  20487    data->GetFontPrefsForLang(key);
  20488  }
  20489  mMayNeedFontPrefsUpdate = false;
  20490 }
  20491 
  20492 void Document::RecomputeLanguageFromCharset() {
  20493  nsAtom* language = mozilla::intl::EncodingToLang::Lookup(mCharacterSet);
  20494 
  20495  if (language == mLanguageFromCharset) {
  20496    return;
  20497  }
  20498 
  20499  mMayNeedFontPrefsUpdate = true;
  20500  mLanguageFromCharset = language;
  20501 }
  20502 
  20503 nsICookieJarSettings* Document::CookieJarSettings() {
  20504  // If we are here, this is probably a javascript: URL document. In any case,
  20505  // we must have a nsCookieJarSettings. Let's create it.
  20506  if (!mCookieJarSettings) {
  20507    Document* inProcessParent = GetInProcessParentDocument();
  20508 
  20509    auto shouldInheritFrom = [this](Document* aDoc) {
  20510      return aDoc && (this->NodePrincipal()->Equals(aDoc->NodePrincipal()) ||
  20511                      this->NodePrincipal()->GetIsNullPrincipal());
  20512    };
  20513    RefPtr<BrowsingContext> opener =
  20514        GetBrowsingContext() ? GetBrowsingContext()->GetOpener() : nullptr;
  20515 
  20516    if (inProcessParent) {
  20517      mCookieJarSettings = net::CookieJarSettings::Create(
  20518          inProcessParent->CookieJarSettings()->GetCookieBehavior(),
  20519          mozilla::net::CookieJarSettings::Cast(
  20520              inProcessParent->CookieJarSettings())
  20521              ->GetPartitionKey(),
  20522          inProcessParent->CookieJarSettings()->GetIsFirstPartyIsolated(),
  20523          inProcessParent->CookieJarSettings()
  20524              ->GetIsOnContentBlockingAllowList(),
  20525          inProcessParent->CookieJarSettings()
  20526              ->GetShouldResistFingerprinting());
  20527 
  20528      // Inherit the fingerprinting random key from the parent.
  20529      nsTArray<uint8_t> randomKey;
  20530      nsresult rv = inProcessParent->CookieJarSettings()
  20531                        ->GetFingerprintingRandomizationKey(randomKey);
  20532 
  20533      if (NS_SUCCEEDED(rv)) {
  20534        net::CookieJarSettings::Cast(mCookieJarSettings)
  20535            ->SetFingerprintingRandomizationKey(randomKey);
  20536      }
  20537 
  20538      // Inerit the top level windowContext id from the parent.
  20539      net::CookieJarSettings::Cast(mCookieJarSettings)
  20540          ->SetTopLevelWindowContextId(
  20541              net::CookieJarSettings::Cast(inProcessParent->CookieJarSettings())
  20542                  ->GetTopLevelWindowContextId());
  20543    } else if (opener && shouldInheritFrom(opener->GetDocument())) {
  20544      mCookieJarSettings = net::CookieJarSettings::Create(NodePrincipal());
  20545 
  20546      nsTArray<uint8_t> randomKey;
  20547      nsresult rv = opener->GetDocument()
  20548                        ->CookieJarSettings()
  20549                        ->GetFingerprintingRandomizationKey(randomKey);
  20550 
  20551      if (NS_SUCCEEDED(rv)) {
  20552        net::CookieJarSettings::Cast(mCookieJarSettings)
  20553            ->SetFingerprintingRandomizationKey(randomKey);
  20554      }
  20555    } else {
  20556      mCookieJarSettings = net::CookieJarSettings::Create(NodePrincipal());
  20557 
  20558      if (IsTopLevelContentDocument()) {
  20559        net::CookieJarSettings::Cast(mCookieJarSettings)
  20560            ->SetTopLevelWindowContextId(InnerWindowID());
  20561      }
  20562    }
  20563 
  20564    if (auto* wgc = GetWindowGlobalChild()) {
  20565      net::CookieJarSettingsArgs csArgs;
  20566 
  20567      net::CookieJarSettings::Cast(mCookieJarSettings)->Serialize(csArgs);
  20568      // Update cookie settings in the parent process
  20569      if (!wgc->SendUpdateCookieJarSettings(csArgs)) {
  20570        NS_WARNING(
  20571            "Failed to update document's cookie jar settings on the "
  20572            "WindowGlobalParent");
  20573      }
  20574    }
  20575  }
  20576 
  20577  return mCookieJarSettings;
  20578 }
  20579 
  20580 bool Document::UsingStorageAccess() {
  20581  if (WindowContext* wc = GetWindowContext()) {
  20582    return wc->GetUsingStorageAccess();
  20583  }
  20584 
  20585  // If we don't yet have a window context, we have to use the decision
  20586  // from the Document's Channel's LoadInfo directly.
  20587  if (!mChannel) {
  20588    return false;
  20589  }
  20590 
  20591  nsCOMPtr<nsILoadInfo> loadInfo = mChannel->LoadInfo();
  20592  nsILoadInfo::StoragePermissionState storageAccess =
  20593      loadInfo->GetStoragePermission();
  20594  return storageAccess == nsILoadInfo::HasStoragePermission ||
  20595         storageAccess == nsILoadInfo::StoragePermissionAllowListed;
  20596 }
  20597 
  20598 bool Document::IsOn3PCBExceptionList() const {
  20599  if (!mChannel) {
  20600    return false;
  20601  }
  20602  nsCOMPtr<nsILoadInfo> loadInfo = mChannel->LoadInfo();
  20603 
  20604  return loadInfo->GetIsOn3PCBExceptionList();
  20605 }
  20606 
  20607 bool Document::HasStorageAccessPermissionGrantedByAllowList() {
  20608  // We only care about if the document gets the storage permission via the
  20609  // allow list here. So we don't check the storage access cache in the inner
  20610  // window.
  20611 
  20612  if (!mChannel) {
  20613    return false;
  20614  }
  20615 
  20616  nsCOMPtr<nsILoadInfo> loadInfo = mChannel->LoadInfo();
  20617  return loadInfo->GetStoragePermission() ==
  20618         nsILoadInfo::StoragePermissionAllowListed;
  20619 }
  20620 
  20621 bool Document::InAndroidPipMode() const {
  20622  auto* bc = BrowserChild::GetFrom(GetDocShell());
  20623  return bc && bc->InAndroidPipMode();
  20624 }
  20625 
  20626 nsIPrincipal* Document::EffectiveStoragePrincipal() const {
  20627  if (!StaticPrefs::
  20628          privacy_partition_always_partition_third_party_non_cookie_storage()) {
  20629    return EffectiveCookiePrincipal();
  20630  }
  20631 
  20632  nsPIDOMWindowInner* inner = GetInnerWindow();
  20633  if (!inner) {
  20634    return NodePrincipal();
  20635  }
  20636 
  20637  // Return our cached storage principal if one exists.
  20638  if (mActiveStoragePrincipal) {
  20639    return mActiveStoragePrincipal;
  20640  }
  20641 
  20642  // Calling StorageAllowedForDocument will notify the ContentBlockLog. This
  20643  // loads TrackingDBService.sys.mjs, making us potentially
  20644  // fail // browser/base/content/test/performance/browser_startup.js. To avoid
  20645  // that, we short-circuit the check here by allowing storage access to system
  20646  // and addon principles, avoiding the test-failure.
  20647  nsIPrincipal* principal = NodePrincipal();
  20648  if (principal && (principal->IsSystemPrincipal() ||
  20649                    principal->GetIsAddonOrExpandedAddonPrincipal())) {
  20650    return mActiveStoragePrincipal = NodePrincipal();
  20651  }
  20652 
  20653  auto cookieJarSettings = const_cast<Document*>(this)->CookieJarSettings();
  20654  if (cookieJarSettings->GetIsOnContentBlockingAllowList()) {
  20655    return mActiveStoragePrincipal = NodePrincipal();
  20656  }
  20657 
  20658  // We use the lower-level ContentBlocking API here to ensure this
  20659  // check doesn't send notifications.
  20660  uint32_t rejectedReason = 0;
  20661  if (ShouldAllowAccessFor(inner, GetDocumentURI(), false, &rejectedReason)) {
  20662    return mActiveStoragePrincipal = NodePrincipal();
  20663  }
  20664 
  20665  // Let's use the storage principal only if we need to partition the cookie
  20666  // jar. When the permission is granted, access will be different and the
  20667  // normal principal will be used.
  20668  if (ShouldPartitionStorage(rejectedReason) &&
  20669      !StoragePartitioningEnabled(
  20670          rejectedReason, const_cast<Document*>(this)->CookieJarSettings())) {
  20671    return mActiveStoragePrincipal = NodePrincipal();
  20672  }
  20673 
  20674  (void)NS_WARN_IF(NS_FAILED(StoragePrincipalHelper::GetPrincipal(
  20675      nsGlobalWindowInner::Cast(inner),
  20676      StoragePrincipalHelper::eForeignPartitionedPrincipal,
  20677      getter_AddRefs(mActiveStoragePrincipal))));
  20678  return mActiveStoragePrincipal;
  20679 }
  20680 
  20681 nsIPrincipal* Document::EffectiveCookiePrincipal() const {
  20682  nsPIDOMWindowInner* inner = GetInnerWindow();
  20683  if (!inner) {
  20684    return NodePrincipal();
  20685  }
  20686 
  20687  // Return our cached storage principal if one exists.
  20688  //
  20689  // Handle special case where privacy_partition_always_partition_third_party
  20690  // _non_cookie_storage is disabled and the loading document has
  20691  // StorageAccess. The pref will lead to WindowGlobalChild::OnNewDocument
  20692  // setting the documents StoragePrincipal on the parent to the documents
  20693  // EffectiveCookiePrincipal. Since this happens before the WindowContext,
  20694  // including possible StorageAccess, is set the PartitonedPrincipal will be
  20695  // selected and cached. Since no change of permission occured it won't be
  20696  // updated later. Avoid this by not using a cached PartitionedPrincipal if
  20697  // the pref is disabled, this should rarely happen since the pref defaults to
  20698  // true. See Bug 1899570.
  20699  if (mActiveCookiePrincipal &&
  20700      (StaticPrefs::
  20701           privacy_partition_always_partition_third_party_non_cookie_storage() ||
  20702       mActiveCookiePrincipal != mPartitionedPrincipal)) {
  20703    return mActiveCookiePrincipal;
  20704  }
  20705 
  20706  // We use the lower-level ContentBlocking API here to ensure this
  20707  // check doesn't send notifications.
  20708  uint32_t rejectedReason = 0;
  20709  if (ShouldAllowAccessFor(inner, GetDocumentURI(), true, &rejectedReason)) {
  20710    return mActiveCookiePrincipal = NodePrincipal();
  20711  }
  20712 
  20713  // Let's use the storage principal only if we need to partition the cookie
  20714  // jar. When the permission is granted, access will be different and the
  20715  // normal principal will be used.
  20716  if (ShouldPartitionStorage(rejectedReason) &&
  20717      !StoragePartitioningEnabled(
  20718          rejectedReason, const_cast<Document*>(this)->CookieJarSettings())) {
  20719    return mActiveCookiePrincipal = NodePrincipal();
  20720  }
  20721 
  20722  return mActiveCookiePrincipal = mPartitionedPrincipal;
  20723 }
  20724 
  20725 nsIPrincipal* Document::GetPrincipalForPrefBasedHacks() const {
  20726  // If the document is sandboxed document or data: document, we should
  20727  // get URI of the parent document.
  20728  for (const Document* document = this;
  20729       document && document->IsContentDocument();
  20730       document = document->GetInProcessParentDocument()) {
  20731    // The document URI may be about:blank even if it comes from actual web
  20732    // site.  Therefore, we need to check the URI of its principal.
  20733    nsIPrincipal* principal = document->NodePrincipal();
  20734    if (principal->GetIsNullPrincipal()) {
  20735      continue;
  20736    }
  20737    return principal;
  20738  }
  20739  return nullptr;
  20740 }
  20741 
  20742 void Document::SetInitialStatus(InitialStatus aStatus) {
  20743  mInitialStatus = aStatus;
  20744 
  20745  if (aStatus == InitialStatus::IsInitialUncommitted) {
  20746    // Set readyState to complete silently.
  20747    mReadyState = READYSTATE_COMPLETE;
  20748    mSetCompleteAfterDOMContentLoaded = false;
  20749    mSynchronousDOMContentLoaded = true;
  20750  } else if (aStatus == InitialStatus::IsInitialButExplicitlyOpened) {
  20751    mSynchronousDOMContentLoaded = false;
  20752  }
  20753 
  20754  // Asynchronously tell the parent process that we are, or are no longer, the
  20755  // initial document. This happens async.
  20756  if (auto* wgc = GetWindowGlobalChild()) {
  20757    wgc->SendSetIsInitialDocument(IsInitialDocument());
  20758  }
  20759 }
  20760 
  20761 void Document::BeginInitialAboutBlankLoadCompleting(nsIChannel* aChannel) {
  20762  MOZ_ASSERT(aChannel);
  20763  SetInitialStatus(InitialStatus::IsInitialCommitted);
  20764  if (auto* wgc = GetWindowGlobalChild()) {
  20765    wgc->SendCommitToInitialDocument();
  20766  }
  20767  mInitialAboutBlankLoadCompleting = true;
  20768  mChannel = aChannel;
  20769  mChannel->GetSecurityInfo(getter_AddRefs(mSecurityInfo));
  20770 
  20771  // This is the condition under which we would usually set
  20772  // mMaybeServiceWorkerControlled in SetScriptGlobalObject.
  20773  MOZ_ASSERT(mDocumentContainer && mScriptGlobalObject,
  20774             "Should have document container and script global");
  20775  mMaybeServiceWorkerControlled = true;
  20776 }
  20777 
  20778 // static
  20779 void Document::AddToplevelLoadingDocument(Document* aDoc) {
  20780  MOZ_ASSERT(aDoc && aDoc->IsTopLevelContentDocument());
  20781 
  20782  if (!XRE_IsContentProcess()) {
  20783    return;
  20784  }
  20785 
  20786  // Start the JS execution timer.
  20787  {
  20788    AutoJSContext cx;
  20789    if (static_cast<JSContext*>(cx)) {
  20790      JS::SetMeasuringExecutionTimeEnabled(cx, true);
  20791    }
  20792  }
  20793 
  20794  // Currently we're interested in foreground documents only, so bail out early.
  20795  if (aDoc->IsInBackgroundWindow()) {
  20796    return;
  20797  }
  20798 
  20799  if (!sLoadingForegroundTopLevelContentDocument) {
  20800    sLoadingForegroundTopLevelContentDocument = new AutoTArray<Document*, 8>();
  20801    mozilla::ipc::IdleSchedulerChild* idleScheduler =
  20802        mozilla::ipc::IdleSchedulerChild::GetMainThreadIdleScheduler();
  20803    if (idleScheduler) {
  20804      idleScheduler->SendRunningPrioritizedOperation();
  20805    }
  20806  }
  20807  if (!sLoadingForegroundTopLevelContentDocument->Contains(aDoc)) {
  20808    sLoadingForegroundTopLevelContentDocument->AppendElement(aDoc);
  20809  }
  20810 }
  20811 
  20812 // static
  20813 void Document::RemoveToplevelLoadingDocument(Document* aDoc) {
  20814  MOZ_ASSERT(aDoc && aDoc->IsTopLevelContentDocument());
  20815  if (sLoadingForegroundTopLevelContentDocument) {
  20816    sLoadingForegroundTopLevelContentDocument->RemoveElement(aDoc);
  20817    if (sLoadingForegroundTopLevelContentDocument->IsEmpty()) {
  20818      delete sLoadingForegroundTopLevelContentDocument;
  20819      sLoadingForegroundTopLevelContentDocument = nullptr;
  20820 
  20821      mozilla::ipc::IdleSchedulerChild* idleScheduler =
  20822          mozilla::ipc::IdleSchedulerChild::GetMainThreadIdleScheduler();
  20823      if (idleScheduler) {
  20824        idleScheduler->SendPrioritizedOperationDone();
  20825      }
  20826    }
  20827  }
  20828 
  20829  // Stop the JS execution timer once the page is loaded.
  20830  {
  20831    AutoJSContext cx;
  20832    if (static_cast<JSContext*>(cx)) {
  20833      JS::SetMeasuringExecutionTimeEnabled(cx, false);
  20834    }
  20835  }
  20836 }
  20837 
  20838 ColorScheme Document::DefaultColorScheme() const {
  20839  return LookAndFeel::ColorSchemeForStyle(*this, {GetColorSchemeBits()});
  20840 }
  20841 
  20842 ColorScheme Document::PreferredColorScheme(IgnoreRFP aIgnoreRFP) const {
  20843  if (ShouldResistFingerprinting(RFPTarget::CSSPrefersColorScheme) &&
  20844      aIgnoreRFP == IgnoreRFP::No) {
  20845    return ColorScheme::Light;
  20846  }
  20847 
  20848  if (nsPresContext* pc = GetPresContext()) {
  20849    if (auto scheme = pc->GetOverriddenOrEmbedderColorScheme()) {
  20850      return *scheme;
  20851    }
  20852  }
  20853 
  20854  return PreferenceSheet::PrefsFor(*this).mColorScheme;
  20855 }
  20856 
  20857 bool Document::HasRecentlyStartedForegroundLoads() {
  20858  if (!sLoadingForegroundTopLevelContentDocument) {
  20859    return false;
  20860  }
  20861 
  20862  for (size_t i = 0; i < sLoadingForegroundTopLevelContentDocument->Length();
  20863       ++i) {
  20864    Document* doc = sLoadingForegroundTopLevelContentDocument->ElementAt(i);
  20865    // A page loaded in foreground could be in background now.
  20866    if (!doc->IsInBackgroundWindow()) {
  20867      nsPIDOMWindowInner* win = doc->GetInnerWindow();
  20868      if (win) {
  20869        Performance* perf = win->GetPerformance();
  20870        if (perf &&
  20871            perf->Now() < StaticPrefs::page_load_deprioritization_period()) {
  20872          return true;
  20873        }
  20874      }
  20875    }
  20876  }
  20877 
  20878  // Didn't find any loading foreground documents, just clear the array.
  20879  delete sLoadingForegroundTopLevelContentDocument;
  20880  sLoadingForegroundTopLevelContentDocument = nullptr;
  20881 
  20882  mozilla::ipc::IdleSchedulerChild* idleScheduler =
  20883      mozilla::ipc::IdleSchedulerChild::GetMainThreadIdleScheduler();
  20884  if (idleScheduler) {
  20885    idleScheduler->SendPrioritizedOperationDone();
  20886  }
  20887  return false;
  20888 }
  20889 
  20890 void Document::AddPendingFrameStaticClone(nsFrameLoaderOwner* aElement,
  20891                                          nsFrameLoader* aStaticCloneOf) {
  20892  PendingFrameStaticClone* clone = mPendingFrameStaticClones.AppendElement();
  20893  clone->mElement = aElement;
  20894  clone->mStaticCloneOf = aStaticCloneOf;
  20895 }
  20896 
  20897 bool Document::ShouldAvoidNativeTheme() const {
  20898  return !IsInChromeDocShell() || XRE_IsContentProcess();
  20899 }
  20900 
  20901 bool Document::UseRegularPrincipal() const {
  20902  return EffectiveStoragePrincipal() == NodePrincipal();
  20903 }
  20904 
  20905 bool Document::HasThirdPartyChannel() {
  20906  nsCOMPtr<nsIChannel> channel = GetChannel();
  20907  if (channel) {
  20908    // We assume that the channel is a third-party by default.
  20909    bool thirdParty = true;
  20910 
  20911    nsCOMPtr<mozIThirdPartyUtil> thirdPartyUtil =
  20912        components::ThirdPartyUtil::Service();
  20913    if (!thirdPartyUtil) {
  20914      return thirdParty;
  20915    }
  20916 
  20917    // Check that if the channel is a third-party to its parent.
  20918    nsresult rv =
  20919        thirdPartyUtil->IsThirdPartyChannel(channel, nullptr, &thirdParty);
  20920    if (NS_FAILED(rv)) {
  20921      // Assume third-party in case of failure
  20922      thirdParty = true;
  20923    }
  20924 
  20925    return thirdParty;
  20926  }
  20927 
  20928  if (mParentDocument) {
  20929    return mParentDocument->HasThirdPartyChannel();
  20930  }
  20931 
  20932  return false;
  20933 }
  20934 
  20935 bool Document::IsLikelyContentInaccessibleTopLevelAboutBlank() const {
  20936  if (!mDocumentURI || !NS_IsAboutBlank(mDocumentURI)) {
  20937    return false;
  20938  }
  20939  // FIXME(emilio): This is not quite edge-case free. See bug 1860098.
  20940  //
  20941  // For stuff in frames, that makes our per-document telemetry probes not
  20942  // really reliable but doesn't affect the correctness of our page probes, so
  20943  // it's not too terrible.
  20944  BrowsingContext* bc = GetBrowsingContext();
  20945  return bc && bc->IsTop() && !bc->GetTopLevelCreatedByWebContent();
  20946 }
  20947 
  20948 bool Document::ShouldIncludeInTelemetry() const {
  20949  if (!IsContentDocument() && !IsResourceDoc()) {
  20950    return false;
  20951  }
  20952 
  20953  if (IsLikelyContentInaccessibleTopLevelAboutBlank()) {
  20954    return false;
  20955  }
  20956 
  20957  nsIPrincipal* prin = NodePrincipal();
  20958  // TODO(emilio): Should this use GetIsContentPrincipal() +
  20959  // GetPrecursorPrincipal() instead (accounting for add-ons separately)?
  20960  return !(prin->GetIsAddonOrExpandedAddonPrincipal() ||
  20961           prin->IsSystemPrincipal() || prin->SchemeIs("about") ||
  20962           prin->SchemeIs("chrome") || prin->SchemeIs("resource"));
  20963 }
  20964 
  20965 void Document::GetConnectedShadowRoots(
  20966    nsTArray<RefPtr<ShadowRoot>>& aOut) const {
  20967  AppendToArray(aOut, mComposedShadowRoots);
  20968 }
  20969 
  20970 void Document::AddMediaElementWithMSE() {
  20971  if (mMediaElementWithMSECount++ == 0) {
  20972    if (WindowGlobalChild* wgc = GetWindowGlobalChild()) {
  20973      wgc->BlockBFCacheFor(BFCacheStatus::CONTAINS_MSE_CONTENT);
  20974    }
  20975  }
  20976 }
  20977 
  20978 void Document::RemoveMediaElementWithMSE() {
  20979  MOZ_ASSERT(mMediaElementWithMSECount > 0);
  20980  if (--mMediaElementWithMSECount == 0) {
  20981    if (WindowGlobalChild* wgc = GetWindowGlobalChild()) {
  20982      wgc->UnblockBFCacheFor(BFCacheStatus::CONTAINS_MSE_CONTENT);
  20983    }
  20984  }
  20985 }
  20986 
  20987 void Document::UnregisterFromMemoryReportingForDataDocument() {
  20988  if (!mAddedToMemoryReportingAsDataDocument) {
  20989    return;
  20990  }
  20991  mAddedToMemoryReportingAsDataDocument = false;
  20992  nsIGlobalObject* global = GetScopeObject();
  20993  if (global) {
  20994    if (nsPIDOMWindowInner* win = global->GetAsInnerWindow()) {
  20995      nsGlobalWindowInner::Cast(win)->UnregisterDataDocumentForMemoryReporting(
  20996          this);
  20997    }
  20998  }
  20999 }
  21000 void Document::OOPChildLoadStarted(BrowserBridgeChild* aChild) {
  21001  MOZ_DIAGNOSTIC_ASSERT(!mOOPChildrenLoading.Contains(aChild));
  21002  mOOPChildrenLoading.AppendElement(aChild);
  21003  if (mOOPChildrenLoading.Length() == 1) {
  21004    // Let's block unload so that we're blocked from going into the BFCache
  21005    // until the child has actually notified us that it has done loading.
  21006    BlockOnload();
  21007  }
  21008 }
  21009 
  21010 void Document::OOPChildLoadDone(BrowserBridgeChild* aChild) {
  21011  // aChild will not be in the list if nsDocLoader::Stop() was called, since
  21012  // that clears mOOPChildrenLoading.  It also dispatches the 'load' event,
  21013  // so we don't need to call DocLoaderIsEmpty in that case.
  21014  if (mOOPChildrenLoading.RemoveElement(aChild)) {
  21015    if (mOOPChildrenLoading.IsEmpty()) {
  21016      UnblockOnload(false);
  21017    }
  21018    RefPtr<nsDocLoader> docLoader(mDocumentContainer);
  21019    if (docLoader) {
  21020      docLoader->OOPChildrenLoadingIsEmpty();
  21021    }
  21022  }
  21023 }
  21024 
  21025 void Document::ClearOOPChildrenLoading() {
  21026  nsTArray<const BrowserBridgeChild*> oopChildrenLoading;
  21027  mOOPChildrenLoading.SwapElements(oopChildrenLoading);
  21028  if (!oopChildrenLoading.IsEmpty()) {
  21029    UnblockOnload(false);
  21030  }
  21031 }
  21032 
  21033 bool Document::MayHaveDOMActivateListeners() const {
  21034  if (nsPIDOMWindowInner* inner = GetInnerWindow()) {
  21035    return inner->HasDOMActivateEventListeners();
  21036  }
  21037 
  21038  // If we can't get information from the window object, default to true.
  21039  return true;
  21040 }
  21041 
  21042 HighlightRegistry& Document::HighlightRegistry() {
  21043  if (!mHighlightRegistry) {
  21044    mHighlightRegistry = MakeRefPtr<class HighlightRegistry>(this);
  21045  }
  21046  return *mHighlightRegistry;
  21047 }
  21048 
  21049 FragmentDirective* Document::FragmentDirective() {
  21050  if (!mFragmentDirective) {
  21051    mFragmentDirective = MakeRefPtr<class FragmentDirective>(this);
  21052  }
  21053  return mFragmentDirective;
  21054 }
  21055 
  21056 RadioGroupContainer& Document::OwnedRadioGroupContainer() {
  21057  if (!mRadioGroupContainer) {
  21058    mRadioGroupContainer = MakeUnique<RadioGroupContainer>();
  21059  }
  21060  return *mRadioGroupContainer;
  21061 }
  21062 
  21063 void Document::UpdateHiddenByContentVisibilityForAnimations() {
  21064  mTimelinesController.UpdateHiddenByContentVisibility();
  21065 }
  21066 
  21067 void Document::SetAllowDeclarativeShadowRoots(
  21068    bool aAllowDeclarativeShadowRoots) {
  21069  mAllowDeclarativeShadowRoots = aAllowDeclarativeShadowRoots;
  21070 }
  21071 
  21072 bool Document::AllowsDeclarativeShadowRoots() const {
  21073  return mAllowDeclarativeShadowRoots;
  21074 }
  21075 
  21076 static already_AddRefed<Document> CreateHTMLDocument(GlobalObject& aGlobal,
  21077                                                     ErrorResult& aError) {
  21078  nsCOMPtr<nsIURI> uri;
  21079  aError = NS_NewURI(getter_AddRefs(uri), "about:blank");
  21080  if (aError.Failed()) {
  21081    return nullptr;
  21082  }
  21083 
  21084  nsCOMPtr<Document> doc;
  21085  aError =
  21086      NS_NewHTMLDocument(getter_AddRefs(doc), aGlobal.GetSubjectPrincipal(),
  21087                         aGlobal.GetSubjectPrincipal(), LoadedAsData::AsData);
  21088  if (aError.Failed()) {
  21089    return nullptr;
  21090  }
  21091 
  21092  doc->SetAllowDeclarativeShadowRoots(true);
  21093  doc->SetDocumentURI(uri);
  21094 
  21095  nsCOMPtr<nsIScriptGlobalObject> scriptHandlingObject =
  21096      do_QueryInterface(aGlobal.GetAsSupports());
  21097  doc->SetScriptHandlingObject(scriptHandlingObject);
  21098  doc->SetDocumentCharacterSet(UTF_8_ENCODING);
  21099 
  21100  return doc.forget();
  21101 }
  21102 
  21103 /* static */
  21104 already_AddRefed<Document> Document::ParseHTMLUnsafe(
  21105    GlobalObject& aGlobal, const TrustedHTMLOrString& aHTML,
  21106    const SetHTMLUnsafeOptions& aOptions, nsIPrincipal* aSubjectPrincipal,
  21107    ErrorResult& aError) {
  21108  // Step 1. Let compliantHTML be the result of invoking the Get Trusted Type
  21109  // compliant string algorithm with TrustedHTML, this’s relevant global object,
  21110  // html, "Document parseHTMLUnsafe", and "script".
  21111  nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(aGlobal.GetAsSupports());
  21112  constexpr nsLiteralString sink = u"Document parseHTMLUnsafe"_ns;
  21113  Maybe<nsAutoString> compliantStringHolder;
  21114  const nsAString* compliantString =
  21115      TrustedTypeUtils::GetTrustedTypesCompliantString(
  21116          aHTML, sink, kTrustedTypesOnlySinkGroup, *global, aSubjectPrincipal,
  21117          compliantStringHolder, aError);
  21118  if (aError.Failed()) {
  21119    return nullptr;
  21120  }
  21121 
  21122  // TODO: Always initialize the sanitizer.
  21123  bool sanitize = aOptions.mSanitizer.WasPassed();
  21124 
  21125  // Step 2. Let document be a new Document, whose content type is "text/html".
  21126  // Step 3. Set document’s allow declarative shadow roots to true.
  21127  RefPtr<Document> doc = CreateHTMLDocument(aGlobal, aError);
  21128  if (aError.Failed()) {
  21129    return nullptr;
  21130  }
  21131 
  21132  // Step 4. Parse HTML from a string given document and compliantHTML.
  21133  // TODO(bug 1960845): Investigate the behavior around <noscript> with
  21134  // parseHTML
  21135  aError = nsContentUtils::ParseDocumentHTML(
  21136      *compliantString, doc,
  21137      /* aScriptingEnabledForNoscriptParsing */ sanitize);
  21138  if (aError.Failed()) {
  21139    return nullptr;
  21140  }
  21141 
  21142  if (sanitize) {
  21143    // Step 5. Let sanitizer be the result of calling get a sanitizer instance
  21144    // from options with options and false.
  21145    nsCOMPtr<nsIGlobalObject> global =
  21146        do_QueryInterface(aGlobal.GetAsSupports());
  21147    RefPtr<Sanitizer> sanitizer = Sanitizer::GetInstance(
  21148        global, aOptions.mSanitizer.Value(), /* aSafe */ false, aError);
  21149    if (aError.Failed()) {
  21150      return nullptr;
  21151    }
  21152 
  21153    // Step 6. Call sanitize on document with sanitizer and false.
  21154    sanitizer->Sanitize(doc, /* aSafe */ false, aError);
  21155    if (aError.Failed()) {
  21156      return nullptr;
  21157    }
  21158  }
  21159 
  21160  // Step 7. Return document.
  21161  return doc.forget();
  21162 }
  21163 
  21164 // https://wicg.github.io/sanitizer-api/#document-parsehtml
  21165 /* static */
  21166 already_AddRefed<Document> Document::ParseHTML(GlobalObject& aGlobal,
  21167                                               const nsAString& aHTML,
  21168                                               const SetHTMLOptions& aOptions,
  21169                                               ErrorResult& aError) {
  21170  // Step 1. Let document be a new Document, whose content type is "text/html".
  21171  // Step 2. Set document’s allow declarative shadow roots to true.
  21172  RefPtr<Document> doc = CreateHTMLDocument(aGlobal, aError);
  21173  if (aError.Failed()) {
  21174    return nullptr;
  21175  }
  21176 
  21177  // Step 3. Parse HTML from a string given document and html.
  21178  // TODO(bug 1960845): Investigate the behavior around <noscript> with
  21179  // parseHTML
  21180  aError = nsContentUtils::ParseDocumentHTML(
  21181      aHTML, doc, /* aScriptingEnabledForNoscriptParsing */ true);
  21182  if (aError.Failed()) {
  21183    return nullptr;
  21184  }
  21185 
  21186  // Step 4. Let sanitizer be the result of calling get a sanitizer instance
  21187  // from options with options and true.
  21188  nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(aGlobal.GetAsSupports());
  21189  RefPtr<Sanitizer> sanitizer = Sanitizer::GetInstance(
  21190      global, aOptions.mSanitizer, /* aSafe */ true, aError);
  21191  if (aError.Failed()) {
  21192    return nullptr;
  21193  }
  21194 
  21195  // Step 5. Call sanitize on document with sanitizer and true.
  21196  sanitizer->Sanitize(doc, /* aSafe */ true, aError);
  21197  if (aError.Failed()) {
  21198    return nullptr;
  21199  }
  21200 
  21201  // Step 6. Return document.
  21202  return doc.forget();
  21203 }
  21204 
  21205 void Document::GetAllInProcessDocuments(
  21206    nsTArray<RefPtr<Document>>& aAllDocuments) {
  21207  for (Document* doc : AllDocumentsList()) {
  21208    aAllDocuments.AppendElement(doc);
  21209  }
  21210 }
  21211 
  21212 }  // namespace mozilla::dom