tor-browser

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

nsContentUtils.cpp (430205B)


      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 /* A namespace class for static layout utilities. */
      8 
      9 #include "nsContentUtils.h"
     10 
     11 #include <algorithm>
     12 #include <cstddef>
     13 #include <cstdint>
     14 #include <cstdlib>
     15 #include <cstring>
     16 #include <functional>
     17 #include <new>
     18 #include <utility>
     19 
     20 #include "BrowserChild.h"
     21 #include "CharacterDataBuffer.h"
     22 #include "DecoderTraits.h"
     23 #include "ErrorList.h"
     24 #include "HTMLSplitOnSpacesTokenizer.h"
     25 #include "ImageOps.h"
     26 #include "InProcessBrowserChildMessageManager.h"
     27 #include "MainThreadUtils.h"
     28 #include "PLDHashTable.h"
     29 #include "ReferrerInfo.h"
     30 #include "ScopedNSSTypes.h"
     31 #include "ThirdPartyUtil.h"
     32 #include "Units.h"
     33 #include "chrome/common/ipc_message.h"
     34 #include "gfxDrawable.h"
     35 #include "harfbuzz/hb.h"
     36 #include "imgICache.h"
     37 #include "imgIContainer.h"
     38 #include "imgILoader.h"
     39 #include "imgIRequest.h"
     40 #include "imgLoader.h"
     41 #include "js/Array.h"
     42 #include "js/ArrayBuffer.h"
     43 #include "js/BuildId.h"
     44 #include "js/GCAPI.h"
     45 #include "js/Id.h"
     46 #include "js/JSON.h"
     47 #include "js/PropertyAndElement.h"  // JS_DefineElement, JS_GetProperty
     48 #include "js/PropertyDescriptor.h"
     49 #include "js/Realm.h"
     50 #include "js/RegExp.h"
     51 #include "js/RegExpFlags.h"
     52 #include "js/RootingAPI.h"
     53 #include "js/TypeDecls.h"
     54 #include "js/Value.h"
     55 #include "js/Wrapper.h"
     56 #include "jsapi.h"
     57 #include "jsfriendapi.h"
     58 #include "mozAutoDocUpdate.h"
     59 #include "mozIDOMWindow.h"
     60 #include "mozilla/AlreadyAddRefed.h"
     61 #include "mozilla/ArrayIterator.h"
     62 #include "mozilla/AsyncEventDispatcher.h"
     63 #include "mozilla/AtomArray.h"
     64 #include "mozilla/Atomics.h"
     65 #include "mozilla/Attributes.h"
     66 #include "mozilla/AutoRestore.h"
     67 #include "mozilla/BackgroundHangMonitor.h"
     68 #include "mozilla/Base64.h"
     69 #include "mozilla/BasePrincipal.h"
     70 #include "mozilla/BasicEvents.h"
     71 #include "mozilla/BloomFilter.h"
     72 #include "mozilla/CORSMode.h"
     73 #include "mozilla/CallState.h"
     74 #include "mozilla/CheckedInt.h"
     75 #include "mozilla/ClearOnShutdown.h"
     76 #include "mozilla/Components.h"
     77 #include "mozilla/ContentBlockingAllowList.h"
     78 #include "mozilla/CycleCollectedJSContext.h"
     79 #include "mozilla/DOMEventTargetHelper.h"
     80 #include "mozilla/DebugOnly.h"
     81 #include "mozilla/ErrorResult.h"
     82 #include "mozilla/EventDispatcher.h"
     83 #include "mozilla/EventListenerManager.h"
     84 #include "mozilla/EventQueue.h"
     85 #include "mozilla/EventStateManager.h"
     86 #include "mozilla/FOGIPC.h"
     87 #include "mozilla/FlowMarkers.h"
     88 #include "mozilla/FlushType.h"
     89 #include "mozilla/HTMLEditor.h"
     90 #include "mozilla/HangAnnotations.h"
     91 #include "mozilla/IMEStateManager.h"
     92 #include "mozilla/InputEventOptions.h"
     93 #include "mozilla/Latin1.h"
     94 #include "mozilla/Likely.h"
     95 #include "mozilla/LoadInfo.h"
     96 #include "mozilla/Logging.h"
     97 #include "mozilla/ManualNAC.h"
     98 #include "mozilla/Maybe.h"
     99 #include "mozilla/MediaFeatureChange.h"
    100 #include "mozilla/MouseEvents.h"
    101 #include "mozilla/NullPrincipal.h"
    102 #include "mozilla/OriginAttributes.h"
    103 #include "mozilla/Preferences.h"
    104 #include "mozilla/PresShell.h"
    105 #include "mozilla/ProfilerRunnable.h"
    106 #include "mozilla/RangeBoundary.h"
    107 #include "mozilla/RefPtr.h"
    108 #include "mozilla/Result.h"
    109 #include "mozilla/ScrollContainerFrame.h"
    110 #include "mozilla/ScrollbarPreferences.h"
    111 #include "mozilla/ShutdownPhase.h"
    112 #include "mozilla/Span.h"
    113 #include "mozilla/StaticAnalysisFunctions.h"
    114 #include "mozilla/StaticPrefs_browser.h"
    115 #include "mozilla/StaticPrefs_dom.h"
    116 #include "mozilla/extensions/WebExtensionPolicy.h"
    117 #include "nsIOService.h"
    118 #include "nsMenuPopupFrame.h"
    119 #include "nsObjectLoadingContent.h"
    120 #ifdef FUZZING
    121 #  include "mozilla/StaticPrefs_fuzzing.h"
    122 #endif
    123 #include "mozilla/StaticPrefs_nglayout.h"
    124 #include "mozilla/StaticPrefs_privacy.h"
    125 #include "mozilla/StaticPrefs_test.h"
    126 #include "mozilla/StaticPrefs_ui.h"
    127 #include "mozilla/StaticPtr.h"
    128 #include "mozilla/TextControlState.h"
    129 #include "mozilla/TextEditor.h"
    130 #include "mozilla/TextEvents.h"
    131 #include "mozilla/Tokenizer.h"
    132 #include "mozilla/UniquePtr.h"
    133 #include "mozilla/ViewportUtils.h"
    134 #include "mozilla/dom/AncestorIterator.h"
    135 #include "mozilla/dom/AutoEntryScript.h"
    136 #include "mozilla/dom/AutoSuppressEventHandlingAndSuspend.h"
    137 #include "mozilla/dom/AutocompleteInfoBinding.h"
    138 #include "mozilla/dom/BindingDeclarations.h"
    139 #include "mozilla/dom/BindingUtils.h"
    140 #include "mozilla/dom/BlobImpl.h"
    141 #include "mozilla/dom/BlobURLProtocolHandler.h"
    142 #include "mozilla/dom/BorrowedAttrInfo.h"
    143 #include "mozilla/dom/BrowserBridgeParent.h"
    144 #include "mozilla/dom/BrowserParent.h"
    145 #include "mozilla/dom/BrowsingContext.h"
    146 #include "mozilla/dom/BrowsingContextGroup.h"
    147 #include "mozilla/dom/CacheExpirationTime.h"
    148 #include "mozilla/dom/CallbackFunction.h"
    149 #include "mozilla/dom/CallbackObject.h"
    150 #include "mozilla/dom/ChildIterator.h"
    151 #include "mozilla/dom/ChromeMessageBroadcaster.h"
    152 #include "mozilla/dom/ContentChild.h"
    153 #include "mozilla/dom/ContentFrameMessageManager.h"
    154 #include "mozilla/dom/ContentParent.h"
    155 #include "mozilla/dom/CustomElementRegistry.h"
    156 #include "mozilla/dom/CustomElementRegistryBinding.h"
    157 #include "mozilla/dom/CustomElementTypes.h"
    158 #include "mozilla/dom/DOMArena.h"
    159 #include "mozilla/dom/DOMException.h"
    160 #include "mozilla/dom/DOMExceptionBinding.h"
    161 #include "mozilla/dom/DOMSecurityMonitor.h"
    162 #include "mozilla/dom/DOMTypes.h"
    163 #include "mozilla/dom/DataTransfer.h"
    164 #include "mozilla/dom/DocGroup.h"
    165 #include "mozilla/dom/Document.h"
    166 #include "mozilla/dom/DocumentFragment.h"
    167 #include "mozilla/dom/DocumentInlines.h"
    168 #include "mozilla/dom/Element.h"
    169 #include "mozilla/dom/ElementBinding.h"
    170 #include "mozilla/dom/ElementInlines.h"
    171 #include "mozilla/dom/Event.h"
    172 #include "mozilla/dom/EventTarget.h"
    173 #include "mozilla/dom/FileBlobImpl.h"
    174 #include "mozilla/dom/FileSystemSecurity.h"
    175 #include "mozilla/dom/FilteredNodeIterator.h"
    176 #include "mozilla/dom/FormData.h"
    177 #include "mozilla/dom/FragmentOrElement.h"
    178 #include "mozilla/dom/FromParser.h"
    179 #include "mozilla/dom/FunctionBinding.h"
    180 #include "mozilla/dom/HTMLElement.h"
    181 #include "mozilla/dom/HTMLFormElement.h"
    182 #include "mozilla/dom/HTMLImageElement.h"
    183 #include "mozilla/dom/HTMLInputElement.h"
    184 #include "mozilla/dom/HTMLTemplateElement.h"
    185 #include "mozilla/dom/HTMLTextAreaElement.h"
    186 #include "mozilla/dom/IPCBlob.h"
    187 #include "mozilla/dom/IPCBlobUtils.h"
    188 #include "mozilla/dom/MessageBroadcaster.h"
    189 #include "mozilla/dom/MessageListenerManager.h"
    190 #include "mozilla/dom/MessagePort.h"
    191 #include "mozilla/dom/MimeType.h"
    192 #include "mozilla/dom/MouseEventBinding.h"
    193 #include "mozilla/dom/MutationObservers.h"
    194 #include "mozilla/dom/NameSpaceConstants.h"
    195 #include "mozilla/dom/NodeBinding.h"
    196 #include "mozilla/dom/NodeInfo.h"
    197 #include "mozilla/dom/PBrowser.h"
    198 #include "mozilla/dom/PContentChild.h"
    199 #include "mozilla/dom/PrototypeList.h"
    200 #include "mozilla/dom/ReferrerPolicyBinding.h"
    201 #include "mozilla/dom/Sanitizer.h"
    202 #include "mozilla/dom/ScriptSettings.h"
    203 #include "mozilla/dom/Selection.h"
    204 #include "mozilla/dom/ShadowRoot.h"
    205 #include "mozilla/dom/Text.h"
    206 #include "mozilla/dom/TrustedHTML.h"
    207 #include "mozilla/dom/TrustedTypeUtils.h"
    208 #include "mozilla/dom/TrustedTypesConstants.h"
    209 #include "mozilla/dom/UserActivation.h"
    210 #include "mozilla/dom/ViewTransition.h"
    211 #include "mozilla/dom/WindowBinding.h"
    212 #include "mozilla/dom/WindowContext.h"
    213 #include "mozilla/dom/WorkerCommon.h"
    214 #include "mozilla/dom/WorkerPrivate.h"
    215 #include "mozilla/dom/WorkerRunnable.h"
    216 #include "mozilla/dom/XULCommandEvent.h"
    217 #include "mozilla/fallible.h"
    218 #include "mozilla/gfx/2D.h"
    219 #include "mozilla/gfx/BaseMargin.h"
    220 #include "mozilla/gfx/BasePoint.h"
    221 #include "mozilla/gfx/BaseSize.h"
    222 #include "mozilla/gfx/DataSurfaceHelpers.h"
    223 #include "mozilla/gfx/Point.h"
    224 #include "mozilla/gfx/Rect.h"
    225 #include "mozilla/gfx/Types.h"
    226 #include "mozilla/glean/GleanPings.h"
    227 #include "mozilla/htmlaccel/htmlaccelEnabled.h"
    228 #ifdef MOZ_MAY_HAVE_HTMLACCEL
    229 #  include "mozilla/htmlaccel/htmlaccelNotInline.h"
    230 #endif
    231 #include "mozilla/intl/LocaleService.h"
    232 #include "mozilla/ipc/ProtocolUtils.h"
    233 #include "mozilla/net/UrlClassifierCommon.h"
    234 #include "mozilla/widget/IMEData.h"
    235 #include "nsAboutProtocolUtils.h"
    236 #include "nsArrayUtils.h"
    237 #include "nsAtomHashKeys.h"
    238 #include "nsAttrName.h"
    239 #include "nsAttrValue.h"
    240 #include "nsAttrValueInlines.h"
    241 #include "nsBaseHashtable.h"
    242 #include "nsCCUncollectableMarker.h"
    243 #include "nsCOMPtr.h"
    244 #include "nsCRT.h"
    245 #include "nsCRTGlue.h"
    246 #include "nsCanvasFrame.h"
    247 #include "nsCaseTreatment.h"
    248 #include "nsCharSeparatedTokenizer.h"
    249 #include "nsCharTraits.h"
    250 #include "nsCompatibility.h"
    251 #include "nsComponentManagerUtils.h"
    252 #include "nsContainerFrame.h"
    253 #include "nsContentCreatorFunctions.h"
    254 #include "nsContentDLF.h"
    255 #include "nsContentList.h"
    256 #include "nsContentListDeclarations.h"
    257 #include "nsContentPolicyUtils.h"
    258 #include "nsCoord.h"
    259 #include "nsCycleCollectionNoteChild.h"
    260 #include "nsDOMMutationObserver.h"
    261 #include "nsDOMString.h"
    262 #include "nsDebug.h"
    263 #include "nsDocShell.h"
    264 #include "nsDocShellCID.h"
    265 #include "nsError.h"
    266 #include "nsFocusManager.h"
    267 #include "nsFrameList.h"
    268 #include "nsFrameLoader.h"
    269 #include "nsFrameLoaderOwner.h"
    270 #include "nsGenericHTMLElement.h"
    271 #include "nsGkAtoms.h"
    272 #include "nsGlobalWindowInner.h"
    273 #include "nsGlobalWindowOuter.h"
    274 #include "nsHTMLDocument.h"
    275 #include "nsHTMLTags.h"
    276 #include "nsHashKeys.h"
    277 #include "nsHtml5StringParser.h"
    278 #include "nsIAboutModule.h"
    279 #include "nsIAnonymousContentCreator.h"
    280 #include "nsIAppShell.h"
    281 #include "nsIArray.h"
    282 #include "nsIAsyncVerifyRedirectCallback.h"
    283 #include "nsIBidiKeyboard.h"
    284 #include "nsIBrowser.h"
    285 #include "nsICacheInfoChannel.h"
    286 #include "nsICachingChannel.h"
    287 #include "nsICategoryManager.h"
    288 #include "nsIChannel.h"
    289 #include "nsIChannelEventSink.h"
    290 #include "nsIClassifiedChannel.h"
    291 #include "nsIConsoleService.h"
    292 #include "nsIContent.h"
    293 #include "nsIContentInlines.h"
    294 #include "nsIContentPolicy.h"
    295 #include "nsIContentSecurityPolicy.h"
    296 #include "nsIContentSink.h"
    297 #include "nsIDOMWindowUtils.h"
    298 #include "nsIDocShell.h"
    299 #include "nsIDocShellTreeItem.h"
    300 #include "nsIDocumentEncoder.h"
    301 #include "nsIDocumentLoaderFactory.h"
    302 #include "nsIDocumentViewer.h"
    303 #include "nsIDragService.h"
    304 #include "nsIDragSession.h"
    305 #include "nsIFile.h"
    306 #include "nsIFocusManager.h"
    307 #include "nsIFormControl.h"
    308 #include "nsIFragmentContentSink.h"
    309 #include "nsIFrame.h"
    310 #include "nsIGlobalObject.h"
    311 #include "nsIHttpChannel.h"
    312 #include "nsIHttpChannelInternal.h"
    313 #include "nsIIOService.h"
    314 #include "nsIImageLoadingContent.h"
    315 #include "nsIInputStream.h"
    316 #include "nsIInterfaceRequestor.h"
    317 #include "nsIInterfaceRequestorUtils.h"
    318 #include "nsILoadContext.h"
    319 #include "nsILoadGroup.h"
    320 #include "nsILoadInfo.h"
    321 #include "nsIMIMEService.h"
    322 #include "nsIMemoryReporter.h"
    323 #include "nsINetUtil.h"
    324 #include "nsINode.h"
    325 #include "nsIObjectLoadingContent.h"
    326 #include "nsIObserver.h"
    327 #include "nsIObserverService.h"
    328 #include "nsIParserUtils.h"
    329 #include "nsIPermissionManager.h"
    330 #include "nsIPrincipal.h"
    331 #include "nsIProperties.h"
    332 #include "nsIProtocolHandler.h"
    333 #include "nsIRequest.h"
    334 #include "nsIRunnable.h"
    335 #include "nsIScreen.h"
    336 #include "nsIScriptError.h"
    337 #include "nsIScriptGlobalObject.h"
    338 #include "nsIScriptObjectPrincipal.h"
    339 #include "nsIScriptSecurityManager.h"
    340 #include "nsISerialEventTarget.h"
    341 #include "nsIStreamConverter.h"
    342 #include "nsIStreamConverterService.h"
    343 #include "nsIStringBundle.h"
    344 #include "nsISupports.h"
    345 #include "nsISupportsPrimitives.h"
    346 #include "nsISupportsUtils.h"
    347 #include "nsITransferable.h"
    348 #include "nsIURI.h"
    349 #include "nsIURIMutator.h"
    350 #include "nsTHashMap.h"
    351 #if defined(MOZ_THUNDERBIRD) || defined(MOZ_SUITE)
    352 #  include "nsIURIWithSpecialOrigin.h"
    353 #endif
    354 #include "nsIUserIdleServiceInternal.h"
    355 #include "nsIWeakReferenceUtils.h"
    356 #include "nsIWebNavigation.h"
    357 #include "nsIWebNavigationInfo.h"
    358 #include "nsIWidget.h"
    359 #include "nsIWindowMediator.h"
    360 #include "nsIXPConnect.h"
    361 #include "nsJSPrincipals.h"
    362 #include "nsJSUtils.h"
    363 #include "nsLayoutUtils.h"
    364 #include "nsLiteralString.h"
    365 #include "nsMargin.h"
    366 #include "nsMimeTypes.h"
    367 #include "nsNameSpaceManager.h"
    368 #include "nsNetCID.h"
    369 #include "nsNetUtil.h"
    370 #include "nsNodeInfoManager.h"
    371 #include "nsPIDOMWindow.h"
    372 #include "nsPIDOMWindowInlines.h"
    373 #include "nsParser.h"
    374 #include "nsParserConstants.h"
    375 #include "nsPoint.h"
    376 #include "nsPointerHashKeys.h"
    377 #include "nsPresContext.h"
    378 #include "nsQueryFrame.h"
    379 #include "nsQueryObject.h"
    380 #include "nsRange.h"
    381 #include "nsRefPtrHashtable.h"
    382 #include "nsSandboxFlags.h"
    383 #include "nsScriptSecurityManager.h"
    384 #include "nsServiceManagerUtils.h"
    385 #include "nsStreamUtils.h"
    386 #include "nsString.h"
    387 #include "nsStringBundle.h"
    388 #include "nsStringFlags.h"
    389 #include "nsStringFwd.h"
    390 #include "nsStringIterator.h"
    391 #include "nsStringStream.h"
    392 #include "nsTArray.h"
    393 #include "nsTLiteralString.h"
    394 #include "nsTPromiseFlatString.h"
    395 #include "nsTStringRepr.h"
    396 #include "nsTextNode.h"
    397 #include "nsThreadManager.h"
    398 #include "nsThreadUtils.h"
    399 #include "nsTreeSanitizer.h"
    400 #include "nsUGenCategory.h"
    401 #include "nsURLHelper.h"
    402 #include "nsUnicodeProperties.h"
    403 #include "nsVariant.h"
    404 #include "nsWidgetsCID.h"
    405 #include "nsXPCOM.h"
    406 #include "nsXPCOMCID.h"
    407 #include "nsXULAppAPI.h"
    408 #include "nsXULElement.h"
    409 #include "nsXULPopupManager.h"
    410 #include "nscore.h"
    411 #include "prinrval.h"
    412 #include "xpcprivate.h"
    413 #include "xpcpublic.h"
    414 
    415 #if defined(XP_WIN)
    416 // Undefine LoadImage to prevent naming conflict with Windows.
    417 #  undef LoadImage
    418 #endif
    419 
    420 extern "C" int MOZ_XMLTranslateEntity(const char* ptr, const char* end,
    421                                      const char** next, char16_t* result);
    422 extern "C" int MOZ_XMLCheckQName(const char* ptr, const char* end, int ns_aware,
    423                                 const char** colon);
    424 
    425 using namespace mozilla::dom;
    426 using namespace mozilla::ipc;
    427 using namespace mozilla::gfx;
    428 using namespace mozilla::layers;
    429 using namespace mozilla::widget;
    430 using namespace mozilla;
    431 
    432 const char kLoadAsData[] = "loadAsData";
    433 
    434 nsIXPConnect* nsContentUtils::sXPConnect;
    435 nsIScriptSecurityManager* nsContentUtils::sSecurityManager;
    436 nsIPrincipal* nsContentUtils::sSystemPrincipal;
    437 nsIPrincipal* nsContentUtils::sNullSubjectPrincipal;
    438 nsIPrincipal* nsContentUtils::sFingerprintingProtectionPrincipal;
    439 nsIConsoleService* nsContentUtils::sConsoleService;
    440 
    441 static nsTHashMap<RefPtr<nsAtom>, EventNameMapping>* sAtomEventTable;
    442 static nsTHashMap<nsStringHashKey, EventNameMapping>* sStringEventTable;
    443 static nsTArray<RefPtr<nsAtom>>* sUserDefinedEvents;
    444 nsIStringBundleService* nsContentUtils::sStringBundleService;
    445 
    446 static StaticRefPtr<nsIStringBundle>
    447    sStringBundles[nsContentUtils::PropertiesFile_COUNT];
    448 
    449 nsIContentPolicy* nsContentUtils::sContentPolicyService;
    450 bool nsContentUtils::sTriedToGetContentPolicy = false;
    451 StaticRefPtr<nsIBidiKeyboard> nsContentUtils::sBidiKeyboard;
    452 uint32_t nsContentUtils::sScriptBlockerCount = 0;
    453 uint32_t nsContentUtils::sDOMNodeRemovedSuppressCount = 0;
    454 AutoTArray<nsCOMPtr<nsIRunnable>, 8>* nsContentUtils::sBlockedScriptRunners =
    455    nullptr;
    456 uint32_t nsContentUtils::sRunnersCountAtFirstBlocker = 0;
    457 nsIInterfaceRequestor* nsContentUtils::sSameOriginChecker = nullptr;
    458 
    459 bool nsContentUtils::sIsHandlingKeyBoardEvent = false;
    460 
    461 nsString* nsContentUtils::sShiftText = nullptr;
    462 nsString* nsContentUtils::sControlText = nullptr;
    463 nsString* nsContentUtils::sCommandOrWinText = nullptr;
    464 nsString* nsContentUtils::sAltText = nullptr;
    465 nsString* nsContentUtils::sModifierSeparator = nullptr;
    466 
    467 bool nsContentUtils::sInitialized = false;
    468 #ifndef RELEASE_OR_BETA
    469 bool nsContentUtils::sBypassCSSOMOriginCheck = false;
    470 #endif
    471 
    472 nsCString* nsContentUtils::sJSScriptBytecodeMimeType = nullptr;
    473 nsCString* nsContentUtils::sJSModuleBytecodeMimeType = nullptr;
    474 
    475 nsContentUtils::UserInteractionObserver*
    476    nsContentUtils::sUserInteractionObserver = nullptr;
    477 
    478 nsHtml5StringParser* nsContentUtils::sHTMLFragmentParser = nullptr;
    479 nsParser* nsContentUtils::sXMLFragmentParser = nullptr;
    480 nsIFragmentContentSink* nsContentUtils::sXMLFragmentSink = nullptr;
    481 bool nsContentUtils::sFragmentParsingActive = false;
    482 
    483 bool nsContentUtils::sMayHaveFormCheckboxStateChangeListeners = false;
    484 bool nsContentUtils::sMayHaveFormRadioStateChangeListeners = false;
    485 
    486 mozilla::LazyLogModule nsContentUtils::gResistFingerprintingLog(
    487    "nsResistFingerprinting");
    488 mozilla::LazyLogModule nsContentUtils::sDOMDumpLog("Dump");
    489 // Log when "input" and "beforeinput" events are dispatched or enqueued with
    490 // InputEvent:3,sync.
    491 mozilla::LazyLogModule gInputEventLog("InputEvent");
    492 
    493 int32_t nsContentUtils::sInnerOrOuterWindowCount = 0;
    494 uint32_t nsContentUtils::sInnerOrOuterWindowSerialCounter = 0;
    495 
    496 template Maybe<int32_t>
    497 nsContentUtils::ComparePoints<TreeKind::ShadowIncludingDOM>(
    498    const RangeBoundary& aFirstBoundary, const RangeBoundary& aSecondBoundary,
    499    NodeIndexCache* aIndexCache);
    500 template Maybe<int32_t> nsContentUtils::ComparePoints<TreeKind::Flat>(
    501    const RangeBoundary& aFirstBoundary, const RangeBoundary& aSecondBoundary,
    502    NodeIndexCache* aIndexCache);
    503 
    504 template Maybe<int32_t>
    505 nsContentUtils::ComparePoints<TreeKind::ShadowIncludingDOM>(
    506    const RangeBoundary& aFirstBoundary,
    507    const RawRangeBoundary& aSecondBoundary, NodeIndexCache* aIndexCache);
    508 template Maybe<int32_t> nsContentUtils::ComparePoints<TreeKind::Flat>(
    509    const RangeBoundary& aFirstBoundary,
    510    const RawRangeBoundary& aSecondBoundary, NodeIndexCache* aIndexCache);
    511 
    512 template Maybe<int32_t>
    513 nsContentUtils::ComparePoints<TreeKind::ShadowIncludingDOM>(
    514    const RawRangeBoundary& aFirstBoundary,
    515    const RangeBoundary& aSecondBoundary, NodeIndexCache* aIndexCache);
    516 template Maybe<int32_t> nsContentUtils::ComparePoints<TreeKind::Flat>(
    517    const RawRangeBoundary& aFirstBoundary,
    518    const RangeBoundary& aSecondBoundary, NodeIndexCache* aIndexCache);
    519 
    520 template Maybe<int32_t>
    521 nsContentUtils::ComparePoints<TreeKind::ShadowIncludingDOM>(
    522    const RawRangeBoundary& aFirstBoundary,
    523    const RawRangeBoundary& aSecondBoundary, NodeIndexCache* aIndexCache);
    524 template Maybe<int32_t> nsContentUtils::ComparePoints<TreeKind::Flat>(
    525    const RawRangeBoundary& aFirstBoundary,
    526    const RawRangeBoundary& aSecondBoundary, NodeIndexCache* aIndexCache);
    527 
    528 template Maybe<int32_t>
    529 nsContentUtils::ComparePoints<TreeKind::ShadowIncludingDOM>(
    530    const RangeBoundary& aFirstBoundary,
    531    const ConstRawRangeBoundary& aSecondBoundary, NodeIndexCache* aIndexCache);
    532 template Maybe<int32_t> nsContentUtils::ComparePoints<TreeKind::Flat>(
    533    const RangeBoundary& aFirstBoundary,
    534    const ConstRawRangeBoundary& aSecondBoundary, NodeIndexCache* aIndexCache);
    535 
    536 template Maybe<int32_t>
    537 nsContentUtils::ComparePoints<TreeKind::ShadowIncludingDOM>(
    538    const ConstRawRangeBoundary& aFirstBoundary,
    539    const RangeBoundary& aSecondBoundary, NodeIndexCache* aIndexCache);
    540 template Maybe<int32_t> nsContentUtils::ComparePoints<TreeKind::Flat>(
    541    const ConstRawRangeBoundary& aFirstBoundary,
    542    const RangeBoundary& aSecondBoundary, NodeIndexCache* aIndexCache);
    543 
    544 template Maybe<int32_t>
    545 nsContentUtils::ComparePoints<TreeKind::ShadowIncludingDOM>(
    546    const ConstRawRangeBoundary& aFirstBoundary,
    547    const RawRangeBoundary& aSecondBoundary, NodeIndexCache* aIndexCache);
    548 
    549 template Maybe<int32_t>
    550 nsContentUtils::ComparePoints<TreeKind::ShadowIncludingDOM>(
    551    const ConstRawRangeBoundary& aFirstBoundary,
    552    const ConstRawRangeBoundary& aSecondBoundary, NodeIndexCache* aIndexCache);
    553 template Maybe<int32_t> nsContentUtils::ComparePoints<TreeKind::Flat>(
    554    const ConstRawRangeBoundary& aFirstBoundary,
    555    const ConstRawRangeBoundary& aSecondBoundary, NodeIndexCache* aIndexCache);
    556 
    557 // Subset of
    558 // http://www.whatwg.org/specs/web-apps/current-work/#autofill-field-name
    559 enum AutocompleteUnsupportedFieldName : uint8_t {
    560 #define AUTOCOMPLETE_UNSUPPORTED_FIELD_NAME(name_, value_) \
    561  eAutocompleteUnsupportedFieldName_##name_,
    562 #include "AutocompleteFieldList.h"
    563 #undef AUTOCOMPLETE_UNSUPPORTED_FIELD_NAME
    564 };
    565 
    566 enum AutocompleteNoPersistFieldName : uint8_t {
    567 #define AUTOCOMPLETE_NO_PERSIST_FIELD_NAME(name_, value_) \
    568  eAutocompleteNoPersistFieldName_##name_,
    569 #include "AutocompleteFieldList.h"
    570 #undef AUTOCOMPLETE_NO_PERSIST_FIELD_NAME
    571 };
    572 
    573 enum AutocompleteUnsupportFieldContactHint : uint8_t {
    574 #define AUTOCOMPLETE_UNSUPPORTED_FIELD_CONTACT_HINT(name_, value_) \
    575  eAutocompleteUnsupportedFieldContactHint_##name_,
    576 #include "AutocompleteFieldList.h"
    577 #undef AUTOCOMPLETE_UNSUPPORTED_FIELD_CONTACT_HINT
    578 };
    579 
    580 enum AutocompleteFieldName : uint8_t {
    581 #define AUTOCOMPLETE_FIELD_NAME(name_, value_) eAutocompleteFieldName_##name_,
    582 #define AUTOCOMPLETE_CONTACT_FIELD_NAME(name_, value_) \
    583  AUTOCOMPLETE_FIELD_NAME(name_, value_)
    584 #include "AutocompleteFieldList.h"
    585 #undef AUTOCOMPLETE_FIELD_NAME
    586 #undef AUTOCOMPLETE_CONTACT_FIELD_NAME
    587 };
    588 
    589 enum AutocompleteFieldHint : uint8_t {
    590 #define AUTOCOMPLETE_FIELD_HINT(name_, value_) eAutocompleteFieldHint_##name_,
    591 #include "AutocompleteFieldList.h"
    592 #undef AUTOCOMPLETE_FIELD_HINT
    593 };
    594 
    595 enum AutocompleteFieldContactHint : uint8_t {
    596 #define AUTOCOMPLETE_FIELD_CONTACT_HINT(name_, value_) \
    597  eAutocompleteFieldContactHint_##name_,
    598 #include "AutocompleteFieldList.h"
    599 #undef AUTOCOMPLETE_FIELD_CONTACT_HINT
    600 };
    601 
    602 enum AutocompleteCredentialType : uint8_t {
    603 #define AUTOCOMPLETE_CREDENTIAL_TYPE(name_, value_) \
    604  eAutocompleteCredentialType_##name_,
    605 #include "AutocompleteFieldList.h"
    606 #undef AUTOCOMPLETE_CREDENTIAL_TYPE
    607 };
    608 
    609 enum AutocompleteCategory {
    610 #define AUTOCOMPLETE_CATEGORY(name_, value_) eAutocompleteCategory_##name_,
    611 #include "AutocompleteFieldList.h"
    612 #undef AUTOCOMPLETE_CATEGORY
    613 };
    614 
    615 static constexpr nsAttrValue::EnumTableEntry
    616    kAutocompleteUnsupportedFieldNameTable[]{
    617 #define AUTOCOMPLETE_UNSUPPORTED_FIELD_NAME(name_, value_) \
    618  {value_, eAutocompleteUnsupportedFieldName_##name_},
    619 #include "AutocompleteFieldList.h"
    620 #undef AUTOCOMPLETE_UNSUPPORTED_FIELD_NAME
    621    };
    622 
    623 static constexpr nsAttrValue::EnumTableEntry
    624    kAutocompleteNoPersistFieldNameTable[] = {
    625 #define AUTOCOMPLETE_NO_PERSIST_FIELD_NAME(name_, value_) \
    626  {value_, eAutocompleteNoPersistFieldName_##name_},
    627 #include "AutocompleteFieldList.h"
    628 #undef AUTOCOMPLETE_NO_PERSIST_FIELD_NAME
    629 };
    630 static constexpr nsAttrValue::EnumTableEntry
    631    kAutocompleteUnsupportedContactFieldHintTable[] = {
    632 #define AUTOCOMPLETE_UNSUPPORTED_FIELD_CONTACT_HINT(name_, value_) \
    633  {value_, eAutocompleteUnsupportedFieldContactHint_##name_},
    634 #include "AutocompleteFieldList.h"
    635 #undef AUTOCOMPLETE_UNSUPPORTED_FIELD_CONTACT_HINT
    636 };
    637 
    638 static constexpr nsAttrValue::EnumTableEntry kAutocompleteFieldNameTable[] = {
    639 #define AUTOCOMPLETE_FIELD_NAME(name_, value_) \
    640  {value_, eAutocompleteFieldName_##name_},
    641 #include "AutocompleteFieldList.h"
    642 #undef AUTOCOMPLETE_FIELD_NAME
    643 };
    644 
    645 static constexpr nsAttrValue::EnumTableEntry
    646    kAutocompleteContactFieldNameTable[] = {
    647 #define AUTOCOMPLETE_CONTACT_FIELD_NAME(name_, value_) \
    648  {value_, eAutocompleteFieldName_##name_},
    649 #include "AutocompleteFieldList.h"
    650 #undef AUTOCOMPLETE_CONTACT_FIELD_NAME
    651 };
    652 
    653 static constexpr nsAttrValue::EnumTableEntry kAutocompleteFieldHintTable[] = {
    654 #define AUTOCOMPLETE_FIELD_HINT(name_, value_) \
    655  {value_, eAutocompleteFieldHint_##name_},
    656 #include "AutocompleteFieldList.h"
    657 #undef AUTOCOMPLETE_FIELD_HINT
    658 };
    659 
    660 static constexpr nsAttrValue::EnumTableEntry
    661    kAutocompleteContactFieldHintTable[] = {
    662 #define AUTOCOMPLETE_FIELD_CONTACT_HINT(name_, value_) \
    663  {value_, eAutocompleteFieldContactHint_##name_},
    664 #include "AutocompleteFieldList.h"
    665 #undef AUTOCOMPLETE_FIELD_CONTACT_HINT
    666 };
    667 
    668 static constexpr nsAttrValue::EnumTableEntry
    669    kAutocompleteCredentialTypeTable[] = {
    670 #define AUTOCOMPLETE_CREDENTIAL_TYPE(name_, value_) \
    671  {value_, eAutocompleteCredentialType_##name_},
    672 #include "AutocompleteFieldList.h"
    673 #undef AUTOCOMPLETE_CREDENTIAL_TYPE
    674 };
    675 
    676 namespace {
    677 
    678 static PLDHashTable* sEventListenerManagersHash;
    679 
    680 // A global hashtable to for keeping the arena alive for cross docGroup node
    681 // adoption.
    682 static nsRefPtrHashtable<nsPtrHashKey<const nsINode>, mozilla::dom::DOMArena>*
    683    sDOMArenaHashtable;
    684 
    685 class DOMEventListenerManagersHashReporter final : public nsIMemoryReporter {
    686  MOZ_DEFINE_MALLOC_SIZE_OF(MallocSizeOf)
    687 
    688  ~DOMEventListenerManagersHashReporter() = default;
    689 
    690 public:
    691  NS_DECL_ISUPPORTS
    692 
    693  NS_IMETHOD CollectReports(nsIHandleReportCallback* aHandleReport,
    694                            nsISupports* aData, bool aAnonymize) override {
    695    // We don't measure the |EventListenerManager| objects pointed to by the
    696    // entries because those references are non-owning.
    697    int64_t amount =
    698        sEventListenerManagersHash
    699            ? sEventListenerManagersHash->ShallowSizeOfIncludingThis(
    700                  MallocSizeOf)
    701            : 0;
    702 
    703    MOZ_COLLECT_REPORT(
    704        "explicit/dom/event-listener-managers-hash", KIND_HEAP, UNITS_BYTES,
    705        amount, "Memory used by the event listener manager's hash table.");
    706 
    707    return NS_OK;
    708  }
    709 };
    710 
    711 NS_IMPL_ISUPPORTS(DOMEventListenerManagersHashReporter, nsIMemoryReporter)
    712 
    713 class EventListenerManagerMapEntry : public PLDHashEntryHdr {
    714 public:
    715  explicit EventListenerManagerMapEntry(const void* aKey) : mKey(aKey) {}
    716 
    717  ~EventListenerManagerMapEntry() {
    718    NS_ASSERTION(!mListenerManager, "caller must release and disconnect ELM");
    719  }
    720 
    721 protected:          // declared protected to silence clang warnings
    722  const void* mKey;  // must be first, to look like PLDHashEntryStub
    723 
    724 public:
    725  RefPtr<EventListenerManager> mListenerManager;
    726 };
    727 
    728 static void EventListenerManagerHashInitEntry(PLDHashEntryHdr* entry,
    729                                              const void* key) {
    730  // Initialize the entry with placement new
    731  new (entry) EventListenerManagerMapEntry(key);
    732 }
    733 
    734 static void EventListenerManagerHashClearEntry(PLDHashTable* table,
    735                                               PLDHashEntryHdr* entry) {
    736  EventListenerManagerMapEntry* lm =
    737      static_cast<EventListenerManagerMapEntry*>(entry);
    738 
    739  // Let the EventListenerManagerMapEntry clean itself up...
    740  lm->~EventListenerManagerMapEntry();
    741 }
    742 
    743 class SameOriginCheckerImpl final : public nsIChannelEventSink,
    744                                    public nsIInterfaceRequestor {
    745  ~SameOriginCheckerImpl() = default;
    746 
    747  NS_DECL_ISUPPORTS
    748  NS_DECL_NSICHANNELEVENTSINK
    749  NS_DECL_NSIINTERFACEREQUESTOR
    750 };
    751 
    752 }  // namespace
    753 
    754 void AutoSuppressEventHandling::SuppressDocument(Document* aDoc) {
    755  // Note: Document::SuppressEventHandling will also automatically suppress
    756  // event handling for any in-process sub-documents. However, since we need
    757  // to deal with cases where remote BrowsingContexts may be interleaved
    758  // with in-process ones, we still need to walk the entire tree ourselves.
    759  // This may be slightly redundant in some cases, but since event handling
    760  // suppressions maintain a count of current blockers, it does not cause
    761  // any problems.
    762  aDoc->SuppressEventHandling();
    763 }
    764 
    765 void AutoSuppressEventHandling::UnsuppressDocument(Document* aDoc) {
    766  aDoc->UnsuppressEventHandlingAndFireEvents(true);
    767 }
    768 
    769 AutoSuppressEventHandling::~AutoSuppressEventHandling() {
    770  UnsuppressDocuments();
    771 }
    772 
    773 void AutoSuppressEventHandlingAndSuspend::SuppressDocument(Document* aDoc) {
    774  AutoSuppressEventHandling::SuppressDocument(aDoc);
    775  if (nsCOMPtr<nsPIDOMWindowInner> win = aDoc->GetInnerWindow()) {
    776    win->Suspend();
    777    mWindows.AppendElement(win);
    778  }
    779 }
    780 
    781 AutoSuppressEventHandlingAndSuspend::~AutoSuppressEventHandlingAndSuspend() {
    782  for (const auto& win : mWindows) {
    783    win->Resume();
    784  }
    785 }
    786 
    787 static auto* GetParentNode(const nsINode* aNode) {
    788  return aNode->GetParentNode();
    789 }
    790 
    791 static auto* GetParentOrShadowHostNode(const nsINode* aNode) {
    792  return aNode->GetParentOrShadowHostNode();
    793 }
    794 
    795 static auto* GetFlattenedTreeParent(const nsIContent* aContent) {
    796  return aContent->GetFlattenedTreeParent();
    797 }
    798 
    799 static nsINode* GetFlattenedTreeParentNodeForSelection(const nsINode* aNode) {
    800  return aNode->GetFlattenedTreeParentNodeForSelection();
    801 }
    802 
    803 static auto* GetFlattenedTreeParentElementForStyle(const Element* aElement) {
    804  return aElement->GetFlattenedTreeParentElementForStyle();
    805 }
    806 
    807 static auto* GetParentBrowserParent(const BrowserParent* aBrowserParent) {
    808  return aBrowserParent->GetBrowserBridgeParent()
    809             ? aBrowserParent->GetBrowserBridgeParent()->Manager()
    810             : nullptr;
    811 }
    812 
    813 static bool AreNodesInSameSlot(const nsINode* aNode1, const nsINode* aNode2) {
    814  if (auto* content1 = nsIContent::FromNodeOrNull(aNode1)) {
    815    if (auto* slot = content1->GetAssignedSlot()) {
    816      if (auto* content2 = nsIContent::FromNodeOrNull(aNode2)) {
    817        return slot == content2->GetAssignedSlot();
    818      }
    819    }
    820  }
    821  return false;
    822 }
    823 
    824 template <TreeKind aKind,
    825          typename = std::enable_if_t<aKind == TreeKind::ShadowIncludingDOM ||
    826                                      aKind == TreeKind::Flat>>
    827 static nsINode* GetParentFuncForComparison(const nsINode* aNode) {
    828  MOZ_ASSERT(aNode);
    829  if constexpr (aKind == TreeKind::Flat) {
    830    if (aNode->IsContent() && aNode->AsContent()->GetAssignedSlot()) {
    831      return aNode->GetFlattenedTreeParentNodeForSelection();
    832    }
    833  }
    834  return aNode->GetParentOrShadowHostNode();
    835 }
    836 
    837 template <typename Node1, typename Node2, typename GetParentFunc>
    838 class MOZ_STACK_CLASS CommonAncestors final {
    839 public:
    840  CommonAncestors(Node1& aNode1, Node2& aNode2, GetParentFunc aGetParentFunc)
    841      : GetParent(aGetParentFunc) {
    842 #ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
    843    mAssertNoGC.emplace();
    844 #endif  // #ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
    845 
    846    AppendInclusiveAncestors(&aNode1, GetParent, mInclusiveAncestors1);
    847    AppendInclusiveAncestors(&aNode2, GetParent, mInclusiveAncestors2);
    848 
    849    // Find where the parent chain differs
    850    size_t depth1 = mInclusiveAncestors1.Length();
    851    size_t depth2 = mInclusiveAncestors2.Length();
    852    const size_t shorterLength = std::min(depth1, depth2);
    853    Node1** const inclusiveAncestors1 = mInclusiveAncestors1.Elements();
    854    Node2** const inclusiveAncestors2 = mInclusiveAncestors2.Elements();
    855    for ([[maybe_unused]] const size_t unused : IntegerRange(shorterLength)) {
    856      Node1* const inclusiveAncestor1 = inclusiveAncestors1[--depth1];
    857      Node2* const inclusiveAncestor2 = inclusiveAncestors2[--depth2];
    858      if (inclusiveAncestor1 != inclusiveAncestor2) {
    859        MOZ_ASSERT_IF(mClosestCommonAncestor,
    860                      inclusiveAncestor1 == GetClosestCommonAncestorChild1());
    861        MOZ_ASSERT_IF(mClosestCommonAncestor,
    862                      inclusiveAncestor2 == GetClosestCommonAncestorChild2());
    863        return;
    864      }
    865      mNumberOfCommonAncestors++;
    866      mClosestCommonAncestor = inclusiveAncestor1;
    867    }
    868    MOZ_ASSERT(mClosestCommonAncestor);
    869    MOZ_ASSERT(mNumberOfCommonAncestors);
    870    MOZ_ASSERT(!depth1 || !depth2);
    871    MOZ_ASSERT_IF(!depth1, !GetClosestCommonAncestorChild1());
    872    MOZ_ASSERT_IF(depth1, GetClosestCommonAncestorChild1());
    873    MOZ_ASSERT_IF(!depth2, !GetClosestCommonAncestorChild2());
    874    MOZ_ASSERT_IF(depth2, GetClosestCommonAncestorChild2());
    875  }
    876 
    877 #ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
    878  ~CommonAncestors() { MOZ_DIAGNOSTIC_ASSERT(!mMutationGuard.Mutated(0)); }
    879 #endif  // #ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
    880 
    881  [[nodiscard]] Node1* GetClosestCommonAncestor() const {
    882    return mClosestCommonAncestor;
    883  }
    884  [[nodiscard]] Node1* GetClosestCommonAncestorChild1() const {
    885    return GetClosestCommonAncestorChild(mInclusiveAncestors1);
    886  }
    887  [[nodiscard]] Node2* GetClosestCommonAncestorChild2() const {
    888    return GetClosestCommonAncestorChild(mInclusiveAncestors2);
    889  }
    890 
    891  template <TreeKind aKind>
    892  void WarnIfClosestCommonAncestorChildrenAreNotInChildList() const {
    893    WarnIfClosestCommonAncestorChildIsNotInChildList<aKind>(
    894        mInclusiveAncestors1);
    895    WarnIfClosestCommonAncestorChildIsNotInChildList<aKind>(
    896        mInclusiveAncestors2);
    897  }
    898 
    899 private:
    900  template <typename Node>
    901  static void AppendInclusiveAncestors(Node* aNode,
    902                                       GetParentFunc aGetParentFunc,
    903                                       nsTArray<Node*>& aArrayOfParents) {
    904    Node* node = aNode;
    905    while (node) {
    906      aArrayOfParents.AppendElement(node);
    907      node = aGetParentFunc(node);
    908    }
    909  }
    910 
    911  template <typename Node>
    912  Maybe<size_t> GetClosestCommonAncestorChildIndex(
    913      const nsTArray<Node*>& aInclusiveAncestors) const {
    914    if (!mClosestCommonAncestor ||
    915        aInclusiveAncestors.Length() <= mNumberOfCommonAncestors) {
    916      return Nothing();
    917    }
    918    return Some((aInclusiveAncestors.Length() - 1)  // last index
    919                - mNumberOfCommonAncestors);  // before closest common ancestor
    920  }
    921 
    922  template <typename Node>
    923  [[nodiscard]] Node* GetClosestCommonAncestorChild(
    924      const nsTArray<Node*>& aInclusiveAncestors) const {
    925    const Maybe<size_t> index =
    926        GetClosestCommonAncestorChildIndex(aInclusiveAncestors);
    927    if (index.isNothing()) {
    928      MOZ_ASSERT_IF(mClosestCommonAncestor,
    929                    aInclusiveAncestors.Length() == mNumberOfCommonAncestors);
    930      return nullptr;
    931    }
    932    Node* const child = aInclusiveAncestors[*index];
    933    MOZ_ASSERT(child);
    934    MOZ_ASSERT(GetParent(child) == mClosestCommonAncestor);
    935    return child;
    936  }
    937 
    938  template <TreeKind aKind, typename Node,
    939            typename = std::enable_if_t<aKind == TreeKind::ShadowIncludingDOM ||
    940                                        aKind == TreeKind::Flat>>
    941  void WarnIfClosestCommonAncestorChildIsNotInChildList(
    942      const nsTArray<Node*>& aInclusiveAncestors) const {
    943 #ifdef DEBUG
    944    if constexpr (std::is_base_of_v<nsINode, Node>) {
    945      Node* const child = GetClosestCommonAncestorChild(aInclusiveAncestors);
    946      if (!child) {
    947        return;
    948      }
    949 
    950      if (mClosestCommonAncestor->GetShadowRoot() == child) {
    951        return;
    952      }
    953 
    954      bool found = false;
    955      if constexpr (aKind == TreeKind::Flat) {
    956        if (auto* slot = HTMLSlotElement::FromNode(mClosestCommonAncestor)) {
    957          auto span = slot->AssignedNodes();
    958          found = span.IndexOf(child) != span.npos;
    959        }
    960      }
    961 
    962      if (!found) {
    963        found = mClosestCommonAncestor->ComputeIndexOf(child).isSome();
    964      }
    965      if (MOZ_LIKELY(found)) {
    966        return;
    967      }
    968      const Maybe<size_t> index =
    969          GetClosestCommonAncestorChildIndex(aInclusiveAncestors);
    970      NS_WARNING(
    971          fmt::format(
    972              FMT_STRING("The caller cannot compare the position of the child "
    973                         "of the common ancestor due to not in the child list "
    974                         "of the common ancestor:\n"
    975                         "  {}\n"      // common ancestor
    976                         "    + {}\n"  // common ancestor child
    977                         "{}"),  // child of common ancestor child if there is
    978              ToString(*mClosestCommonAncestor), ToString(*child),
    979              *index ? fmt::format(FMT_STRING("       + {}"),
    980                                   ToString(*aInclusiveAncestors[*index - 1]))
    981                     : "")
    982              .c_str());
    983    }
    984 #endif
    985  }
    986 
    987  AutoTArray<Node1*, 30> mInclusiveAncestors1;
    988  AutoTArray<Node2*, 30> mInclusiveAncestors2;
    989  Node1* mClosestCommonAncestor = nullptr;
    990  const GetParentFunc GetParent;
    991  uint32_t mNumberOfCommonAncestors = 0;
    992 
    993 #ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
    994  nsMutationGuard mMutationGuard;
    995  Maybe<JS::AutoAssertNoGC> mAssertNoGC;
    996 #endif
    997 };
    998 
    999 /**
   1000 * This class is used to determine whether or not the user is currently
   1001 * interacting with the browser. It listens to observer events to toggle the
   1002 * value of the sUserActive static.
   1003 *
   1004 * This class is an internal implementation detail.
   1005 * nsContentUtils::GetUserIsInteracting() should be used to access current
   1006 * user interaction status.
   1007 */
   1008 class nsContentUtils::UserInteractionObserver final
   1009    : public nsIObserver,
   1010      public BackgroundHangAnnotator {
   1011 public:
   1012  NS_DECL_ISUPPORTS
   1013  NS_DECL_NSIOBSERVER
   1014 
   1015  void Init();
   1016  void Shutdown();
   1017  void AnnotateHang(BackgroundHangAnnotations& aAnnotations) override;
   1018 
   1019  static Atomic<bool> sUserActive;
   1020 
   1021 private:
   1022  ~UserInteractionObserver() = default;
   1023 };
   1024 
   1025 static constexpr nsLiteralCString kRfpPrefs[] = {
   1026    "privacy.resistFingerprinting"_ns,
   1027    "privacy.resistFingerprinting.pbmode"_ns,
   1028    "privacy.fingerprintingProtection"_ns,
   1029    "privacy.fingerprintingProtection.pbmode"_ns,
   1030    "privacy.fingerprintingProtection.overrides"_ns,
   1031    "privacy.baselineFingerprintingProtection"_ns,
   1032    "privacy.baselineFingerprintingProtection.overrides"_ns,
   1033 };
   1034 
   1035 static void RecomputeResistFingerprintingAllDocs(const char*, void*) {
   1036  AutoTArray<RefPtr<Document>, 64> allDocuments;
   1037  Document::GetAllInProcessDocuments(allDocuments);
   1038  for (auto& doc : allDocuments) {
   1039    doc->RecomputeResistFingerprinting(
   1040        /* aForceRefreshRTPCallerType= */ true);
   1041    if (auto* pc = doc->GetPresContext()) {
   1042      pc->MediaFeatureValuesChanged(
   1043          {MediaFeatureChangeReason::PreferenceChange},
   1044          MediaFeatureChangePropagation::JustThisDocument);
   1045    }
   1046  }
   1047 }
   1048 
   1049 // static
   1050 nsresult nsContentUtils::Init() {
   1051  if (sInitialized) {
   1052    NS_WARNING("Init() called twice");
   1053 
   1054    return NS_OK;
   1055  }
   1056 
   1057  nsHTMLTags::AddRefTable();
   1058 
   1059  sXPConnect = nsXPConnect::XPConnect();
   1060  // We hold a strong ref to sXPConnect to ensure that it does not go away until
   1061  // nsLayoutStatics::Shutdown is happening.  Otherwise ~nsXPConnect can be
   1062  // triggered by xpcModuleDtor late in shutdown and cause crashes due to
   1063  // various stuff already being torn down by then.  Note that this means that
   1064  // we are effectively making sure that if we leak nsLayoutStatics then we also
   1065  // leak nsXPConnect.
   1066  NS_ADDREF(sXPConnect);
   1067 
   1068  sSecurityManager = nsScriptSecurityManager::GetScriptSecurityManager();
   1069  if (!sSecurityManager) return NS_ERROR_FAILURE;
   1070  NS_ADDREF(sSecurityManager);
   1071 
   1072  sSecurityManager->GetSystemPrincipal(&sSystemPrincipal);
   1073  MOZ_ASSERT(sSystemPrincipal);
   1074 
   1075  RefPtr<NullPrincipal> nullPrincipal =
   1076      NullPrincipal::CreateWithoutOriginAttributes();
   1077  if (!nullPrincipal) {
   1078    return NS_ERROR_FAILURE;
   1079  }
   1080 
   1081  nullPrincipal.forget(&sNullSubjectPrincipal);
   1082 
   1083  RefPtr<nsIPrincipal> fingerprintingProtectionPrincipal =
   1084      BasePrincipal::CreateContentPrincipal(
   1085          "about:fingerprintingprotection"_ns);
   1086  if (!fingerprintingProtectionPrincipal) {
   1087    return NS_ERROR_FAILURE;
   1088  }
   1089 
   1090  fingerprintingProtectionPrincipal.forget(&sFingerprintingProtectionPrincipal);
   1091 
   1092  if (!InitializeEventTable()) return NS_ERROR_FAILURE;
   1093 
   1094  if (!sEventListenerManagersHash) {
   1095    static const PLDHashTableOps hash_table_ops = {
   1096        PLDHashTable::HashVoidPtrKeyStub, PLDHashTable::MatchEntryStub,
   1097        PLDHashTable::MoveEntryStub, EventListenerManagerHashClearEntry,
   1098        EventListenerManagerHashInitEntry};
   1099 
   1100    sEventListenerManagersHash =
   1101        new PLDHashTable(&hash_table_ops, sizeof(EventListenerManagerMapEntry));
   1102 
   1103    RegisterStrongMemoryReporter(new DOMEventListenerManagersHashReporter());
   1104  }
   1105 
   1106  sBlockedScriptRunners = new AutoTArray<nsCOMPtr<nsIRunnable>, 8>;
   1107 
   1108 #ifndef RELEASE_OR_BETA
   1109  sBypassCSSOMOriginCheck = getenv("MOZ_BYPASS_CSSOM_ORIGIN_CHECK");
   1110 #endif
   1111 
   1112  Element::InitCCCallbacks();
   1113 
   1114  RefPtr<nsRFPService> rfpService = nsRFPService::GetOrCreate();
   1115  MOZ_ASSERT(rfpService);
   1116 
   1117  if (XRE_IsParentProcess()) {
   1118    AsyncPrecreateStringBundles();
   1119 
   1120 #if defined(MOZ_WIDGET_ANDROID)
   1121    // On Android, at-shutdown ping submission isn't reliable
   1122    // (( because, on Android, we usually get killed, not shut down )).
   1123    // To have a chance at submitting the ping, aim for idle after startup.
   1124    nsresult rv = NS_DispatchToCurrentThreadQueue(
   1125        NS_NewRunnableFunction(
   1126            "AndroidUseCounterPingSubmitter",
   1127            []() { glean_pings::UseCounters.Submit("idle_startup"_ns); }),
   1128        EventQueuePriority::Idle);
   1129    // This is mostly best-effort, so if it goes awry, just log.
   1130    (void)NS_WARN_IF(NS_FAILED(rv));
   1131 #endif  // defined(MOZ_WIDGET_ANDROID)
   1132 
   1133    RunOnShutdown(
   1134        [&] { glean_pings::UseCounters.Submit("app_shutdown_confirmed"_ns); },
   1135        ShutdownPhase::AppShutdownConfirmed);
   1136 
   1137    // On child process, this is initialized in ContentChild.
   1138    LookAndFeel::EnsureInit();
   1139  }
   1140 
   1141  RefPtr<UserInteractionObserver> uio = new UserInteractionObserver();
   1142  uio->Init();
   1143  uio.forget(&sUserInteractionObserver);
   1144 
   1145  for (const auto& pref : kRfpPrefs) {
   1146    Preferences::RegisterCallback(RecomputeResistFingerprintingAllDocs, pref);
   1147  }
   1148 
   1149  sInitialized = true;
   1150 
   1151  return NS_OK;
   1152 }
   1153 
   1154 bool nsContentUtils::InitJSBytecodeMimeType() {
   1155  MOZ_ASSERT(NS_IsMainThread());
   1156  MOZ_ASSERT(!sJSScriptBytecodeMimeType);
   1157  MOZ_ASSERT(!sJSModuleBytecodeMimeType);
   1158 
   1159  JS::BuildIdCharVector jsBuildId;
   1160  if (!JS::GetScriptTranscodingBuildId(&jsBuildId)) {
   1161    return false;
   1162  }
   1163 
   1164  nsDependentCSubstring jsBuildIdStr(jsBuildId.begin(), jsBuildId.length());
   1165  sJSScriptBytecodeMimeType =
   1166      new nsCString("javascript/moz-script-bytecode-"_ns + jsBuildIdStr);
   1167  sJSModuleBytecodeMimeType =
   1168      new nsCString("javascript/moz-module-bytecode-"_ns + jsBuildIdStr);
   1169  return true;
   1170 }
   1171 
   1172 void nsContentUtils::GetShiftText(nsAString& text) {
   1173  if (!sShiftText) InitializeModifierStrings();
   1174  text.Assign(*sShiftText);
   1175 }
   1176 
   1177 void nsContentUtils::GetControlText(nsAString& text) {
   1178  if (!sControlText) InitializeModifierStrings();
   1179  text.Assign(*sControlText);
   1180 }
   1181 
   1182 void nsContentUtils::GetCommandOrWinText(nsAString& text) {
   1183  if (!sCommandOrWinText) {
   1184    InitializeModifierStrings();
   1185  }
   1186  text.Assign(*sCommandOrWinText);
   1187 }
   1188 
   1189 void nsContentUtils::GetAltText(nsAString& text) {
   1190  if (!sAltText) InitializeModifierStrings();
   1191  text.Assign(*sAltText);
   1192 }
   1193 
   1194 void nsContentUtils::GetModifierSeparatorText(nsAString& text) {
   1195  if (!sModifierSeparator) InitializeModifierStrings();
   1196  text.Assign(*sModifierSeparator);
   1197 }
   1198 
   1199 void nsContentUtils::InitializeModifierStrings() {
   1200  // load the display strings for the keyboard accelerators
   1201  nsCOMPtr<nsIStringBundleService> bundleService =
   1202      mozilla::components::StringBundle::Service();
   1203  nsCOMPtr<nsIStringBundle> bundle;
   1204  DebugOnly<nsresult> rv = NS_OK;
   1205  if (bundleService) {
   1206    rv = bundleService->CreateBundle(
   1207        "chrome://global-platform/locale/platformKeys.properties",
   1208        getter_AddRefs(bundle));
   1209  }
   1210 
   1211  NS_ASSERTION(
   1212      NS_SUCCEEDED(rv) && bundle,
   1213      "chrome://global/locale/platformKeys.properties could not be loaded");
   1214  nsAutoString shiftModifier;
   1215  nsAutoString commandOrWinModifier;
   1216  nsAutoString altModifier;
   1217  nsAutoString controlModifier;
   1218  nsAutoString modifierSeparator;
   1219  if (bundle) {
   1220    // macs use symbols for each modifier key, so fetch each from the bundle,
   1221    // which also covers i18n
   1222    bundle->GetStringFromName("VK_SHIFT", shiftModifier);
   1223    bundle->GetStringFromName("VK_COMMAND_OR_WIN", commandOrWinModifier);
   1224    bundle->GetStringFromName("VK_ALT", altModifier);
   1225    bundle->GetStringFromName("VK_CONTROL", controlModifier);
   1226    bundle->GetStringFromName("MODIFIER_SEPARATOR", modifierSeparator);
   1227  }
   1228  // if any of these don't exist, we get  an empty string
   1229  sShiftText = new nsString(shiftModifier);
   1230  sCommandOrWinText = new nsString(commandOrWinModifier);
   1231  sAltText = new nsString(altModifier);
   1232  sControlText = new nsString(controlModifier);
   1233  sModifierSeparator = new nsString(modifierSeparator);
   1234 }
   1235 
   1236 mozilla::EventClassID nsContentUtils::GetEventClassIDFromMessage(
   1237    EventMessage aEventMessage) {
   1238  switch (aEventMessage) {
   1239 #define MESSAGE_TO_EVENT(name_, message_, type_, struct_) \
   1240  case message_:                                          \
   1241    return struct_;
   1242 #include "mozilla/EventNameList.h"
   1243 #undef MESSAGE_TO_EVENT
   1244    default:
   1245      MOZ_ASSERT_UNREACHABLE("Invalid event message?");
   1246      return eBasicEventClass;
   1247  }
   1248 }
   1249 
   1250 bool nsContentUtils::IsExternalProtocol(nsIURI* aURI) {
   1251  bool doesNotReturnData = false;
   1252  nsresult rv = NS_URIChainHasFlags(
   1253      aURI, nsIProtocolHandler::URI_DOES_NOT_RETURN_DATA, &doesNotReturnData);
   1254  return NS_SUCCEEDED(rv) && doesNotReturnData;
   1255 }
   1256 
   1257 /* static */
   1258 nsAtom* nsContentUtils::GetEventTypeFromMessage(EventMessage aEventMessage) {
   1259  switch (aEventMessage) {
   1260 #define MESSAGE_TO_EVENT(name_, message_, type_, struct_) \
   1261  case message_:                                          \
   1262    return nsGkAtoms::on##name_;
   1263 #include "mozilla/EventNameList.h"
   1264 #undef MESSAGE_TO_EVENT
   1265    default:
   1266      return nullptr;
   1267  }
   1268 }
   1269 
   1270 /* static */
   1271 already_AddRefed<nsAtom> nsContentUtils::GetEventType(
   1272    const WidgetEvent* aEvent) {
   1273  RefPtr<nsAtom> typeAtom =
   1274      aEvent->mMessage == eUnidentifiedEvent
   1275          ? aEvent->mSpecifiedEventType.get()
   1276          : nsContentUtils::GetEventTypeFromMessage(aEvent->mMessage);
   1277  return typeAtom.forget();
   1278 }
   1279 
   1280 bool nsContentUtils::InitializeEventTable() {
   1281  NS_ASSERTION(!sAtomEventTable, "EventTable already initialized!");
   1282  NS_ASSERTION(!sStringEventTable, "EventTable already initialized!");
   1283 
   1284  static const EventNameMapping eventArray[] = {
   1285 #define EVENT(name_, _message, _type, _class) \
   1286  {nsGkAtoms::on##name_, _type, _message, _class},
   1287 #define WINDOW_ONLY_EVENT EVENT
   1288 #define DOCUMENT_ONLY_EVENT EVENT
   1289 #define NON_IDL_EVENT EVENT
   1290 #include "mozilla/EventNameList.h"
   1291 #undef WINDOW_ONLY_EVENT
   1292 #undef NON_IDL_EVENT
   1293 #undef EVENT
   1294      {nullptr}};
   1295 
   1296  sAtomEventTable =
   1297      new nsTHashMap<RefPtr<nsAtom>, EventNameMapping>(std::size(eventArray));
   1298  sStringEventTable =
   1299      new nsTHashMap<nsStringHashKey, EventNameMapping>(std::size(eventArray));
   1300  sUserDefinedEvents = new nsTArray<RefPtr<nsAtom>>(64);
   1301 
   1302  // Subtract one from the length because of the trailing null
   1303  for (uint32_t i = 0; i < std::size(eventArray) - 1; ++i) {
   1304    MOZ_ASSERT(!sAtomEventTable->Contains(eventArray[i].mAtom),
   1305               "Double-defining event name; fix your EventNameList.h");
   1306    sAtomEventTable->InsertOrUpdate(eventArray[i].mAtom, eventArray[i]);
   1307    sStringEventTable->InsertOrUpdate(
   1308        Substring(nsDependentAtomString(eventArray[i].mAtom), 2),
   1309        eventArray[i]);
   1310  }
   1311 
   1312  return true;
   1313 }
   1314 
   1315 void nsContentUtils::InitializeTouchEventTable() {
   1316  static bool sEventTableInitialized = false;
   1317  if (!sEventTableInitialized && sAtomEventTable && sStringEventTable) {
   1318    sEventTableInitialized = true;
   1319    static const EventNameMapping touchEventArray[] = {
   1320 #define EVENT(name_, _message, _type, _class)
   1321 #define TOUCH_EVENT(name_, _message, _type, _class) \
   1322  {nsGkAtoms::on##name_, _type, _message, _class},
   1323 #include "mozilla/EventNameList.h"
   1324 #undef TOUCH_EVENT
   1325 #undef EVENT
   1326        {nullptr}};
   1327    // Subtract one from the length because of the trailing null
   1328    for (uint32_t i = 0; i < std::size(touchEventArray) - 1; ++i) {
   1329      sAtomEventTable->InsertOrUpdate(touchEventArray[i].mAtom,
   1330                                      touchEventArray[i]);
   1331      sStringEventTable->InsertOrUpdate(
   1332          Substring(nsDependentAtomString(touchEventArray[i].mAtom), 2),
   1333          touchEventArray[i]);
   1334    }
   1335  }
   1336 }
   1337 
   1338 static bool Is8bit(const nsAString& aString) {
   1339  static const char16_t EIGHT_BIT = char16_t(~0x00FF);
   1340 
   1341  for (nsAString::const_char_iterator start = aString.BeginReading(),
   1342                                      end = aString.EndReading();
   1343       start != end; ++start) {
   1344    if (*start & EIGHT_BIT) {
   1345      return false;
   1346    }
   1347  }
   1348 
   1349  return true;
   1350 }
   1351 
   1352 nsresult nsContentUtils::Btoa(const nsAString& aBinaryData,
   1353                              nsAString& aAsciiBase64String) {
   1354  if (!Is8bit(aBinaryData)) {
   1355    aAsciiBase64String.Truncate();
   1356    return NS_ERROR_DOM_INVALID_CHARACTER_ERR;
   1357  }
   1358 
   1359  return Base64Encode(aBinaryData, aAsciiBase64String);
   1360 }
   1361 
   1362 nsresult nsContentUtils::Atob(const nsAString& aAsciiBase64String,
   1363                              nsAString& aBinaryData) {
   1364  if (!Is8bit(aAsciiBase64String)) {
   1365    aBinaryData.Truncate();
   1366    return NS_ERROR_DOM_INVALID_CHARACTER_ERR;
   1367  }
   1368 
   1369  const char16_t* start = aAsciiBase64String.BeginReading();
   1370  const char16_t* cur = start;
   1371  const char16_t* end = aAsciiBase64String.EndReading();
   1372  bool hasWhitespace = false;
   1373 
   1374  while (cur < end) {
   1375    if (nsContentUtils::IsHTMLWhitespace(*cur)) {
   1376      hasWhitespace = true;
   1377      break;
   1378    }
   1379    cur++;
   1380  }
   1381 
   1382  nsresult rv;
   1383 
   1384  if (hasWhitespace) {
   1385    nsString trimmedString;
   1386 
   1387    if (!trimmedString.SetCapacity(aAsciiBase64String.Length(), fallible)) {
   1388      return NS_ERROR_DOM_INVALID_CHARACTER_ERR;
   1389    }
   1390 
   1391    trimmedString.Append(start, cur - start);
   1392 
   1393    while (cur < end) {
   1394      if (!nsContentUtils::IsHTMLWhitespace(*cur)) {
   1395        trimmedString.Append(*cur);
   1396      }
   1397      cur++;
   1398    }
   1399    rv = Base64Decode(trimmedString, aBinaryData);
   1400  } else {
   1401    rv = Base64Decode(aAsciiBase64String, aBinaryData);
   1402  }
   1403 
   1404  if (NS_FAILED(rv) && rv == NS_ERROR_INVALID_ARG) {
   1405    return NS_ERROR_DOM_INVALID_CHARACTER_ERR;
   1406  }
   1407  return rv;
   1408 }
   1409 
   1410 bool nsContentUtils::IsAutocompleteEnabled(mozilla::dom::Element* aElement) {
   1411  MOZ_ASSERT(aElement, "aElement should not be null!");
   1412 
   1413  nsAutoString autocomplete;
   1414 
   1415  if (auto* input = HTMLInputElement::FromNodeOrNull(aElement)) {
   1416    input->GetAutocomplete(autocomplete);
   1417  } else if (auto* textarea = HTMLTextAreaElement::FromNodeOrNull(aElement)) {
   1418    textarea->GetAutocomplete(autocomplete);
   1419  }
   1420 
   1421  if (autocomplete.IsEmpty()) {
   1422    auto* control = nsGenericHTMLFormControlElement::FromNode(aElement);
   1423    auto* form = control->GetForm();
   1424    if (!form) {
   1425      return true;
   1426    }
   1427 
   1428    form->GetAutocomplete(autocomplete);
   1429  }
   1430 
   1431  return !autocomplete.EqualsLiteral("off");
   1432 }
   1433 
   1434 nsContentUtils::AutocompleteAttrState
   1435 nsContentUtils::SerializeAutocompleteAttribute(
   1436    const nsAttrValue* aAttr, nsAString& aResult,
   1437    AutocompleteAttrState aCachedState) {
   1438  if (!aAttr ||
   1439      aCachedState == nsContentUtils::eAutocompleteAttrState_Invalid) {
   1440    return aCachedState;
   1441  }
   1442 
   1443  if (aCachedState == nsContentUtils::eAutocompleteAttrState_Valid) {
   1444    uint32_t atomCount = aAttr->GetAtomCount();
   1445    for (uint32_t i = 0; i < atomCount; i++) {
   1446      if (i != 0) {
   1447        aResult.Append(' ');
   1448      }
   1449      aResult.Append(nsDependentAtomString(aAttr->AtomAt(i)));
   1450    }
   1451    nsContentUtils::ASCIIToLower(aResult);
   1452    return aCachedState;
   1453  }
   1454 
   1455  aResult.Truncate();
   1456 
   1457  mozilla::dom::AutocompleteInfo info;
   1458  AutocompleteAttrState state =
   1459      InternalSerializeAutocompleteAttribute(aAttr, info);
   1460  if (state == eAutocompleteAttrState_Valid) {
   1461    // Concatenate the info fields.
   1462    aResult = info.mSection;
   1463 
   1464    if (!info.mAddressType.IsEmpty()) {
   1465      if (!aResult.IsEmpty()) {
   1466        aResult += ' ';
   1467      }
   1468      aResult += info.mAddressType;
   1469    }
   1470 
   1471    if (!info.mContactType.IsEmpty()) {
   1472      if (!aResult.IsEmpty()) {
   1473        aResult += ' ';
   1474      }
   1475      aResult += info.mContactType;
   1476    }
   1477 
   1478    if (!info.mFieldName.IsEmpty()) {
   1479      if (!aResult.IsEmpty()) {
   1480        aResult += ' ';
   1481      }
   1482      aResult += info.mFieldName;
   1483    }
   1484 
   1485    // The autocomplete attribute value "webauthn" is interpreted as both a
   1486    // field name and a credential type. The corresponding IDL-exposed autofill
   1487    // value is "webauthn", not "webauthn webauthn".
   1488    if (!info.mCredentialType.IsEmpty() &&
   1489        !(info.mCredentialType.Equals(u"webauthn"_ns) &&
   1490          info.mCredentialType.Equals(aResult))) {
   1491      if (!aResult.IsEmpty()) {
   1492        aResult += ' ';
   1493      }
   1494      aResult += info.mCredentialType;
   1495    }
   1496  }
   1497 
   1498  return state;
   1499 }
   1500 
   1501 nsContentUtils::AutocompleteAttrState
   1502 nsContentUtils::SerializeAutocompleteAttribute(
   1503    const nsAttrValue* aAttr, mozilla::dom::AutocompleteInfo& aInfo,
   1504    AutocompleteAttrState aCachedState, bool aGrantAllValidValue) {
   1505  if (!aAttr ||
   1506      aCachedState == nsContentUtils::eAutocompleteAttrState_Invalid) {
   1507    return aCachedState;
   1508  }
   1509 
   1510  return InternalSerializeAutocompleteAttribute(aAttr, aInfo,
   1511                                                aGrantAllValidValue);
   1512 }
   1513 
   1514 /**
   1515 * Helper to validate the @autocomplete tokens.
   1516 *
   1517 * @return {AutocompleteAttrState} The state of the attribute (invalid/valid).
   1518 */
   1519 nsContentUtils::AutocompleteAttrState
   1520 nsContentUtils::InternalSerializeAutocompleteAttribute(
   1521    const nsAttrValue* aAttrVal, mozilla::dom::AutocompleteInfo& aInfo,
   1522    bool aGrantAllValidValue) {
   1523  // No autocomplete attribute so we are done
   1524  if (!aAttrVal) {
   1525    return eAutocompleteAttrState_Invalid;
   1526  }
   1527 
   1528  uint32_t numTokens = aAttrVal->GetAtomCount();
   1529  if (!numTokens || numTokens > INT32_MAX) {
   1530    return eAutocompleteAttrState_Invalid;
   1531  }
   1532 
   1533  uint32_t index = numTokens - 1;
   1534  nsString tokenString = nsDependentAtomString(aAttrVal->AtomAt(index));
   1535  AutocompleteCategory category;
   1536  nsAttrValue enumValue;
   1537  nsAutoString credentialTypeStr;
   1538 
   1539  bool result = enumValue.ParseEnumValue(
   1540      tokenString, kAutocompleteCredentialTypeTable, false);
   1541  if (result) {
   1542    if (!enumValue.Equals(u"webauthn"_ns, eIgnoreCase) || numTokens > 5) {
   1543      return eAutocompleteAttrState_Invalid;
   1544    }
   1545    enumValue.ToString(credentialTypeStr);
   1546    ASCIIToLower(credentialTypeStr);
   1547    // category is Credential and the indexth token is "webauthn"
   1548    if (index == 0) {
   1549      aInfo.mFieldName.Assign(credentialTypeStr);
   1550      aInfo.mCredentialType.Assign(credentialTypeStr);
   1551      return eAutocompleteAttrState_Valid;
   1552    }
   1553 
   1554    --index;
   1555    tokenString = nsDependentAtomString(aAttrVal->AtomAt(index));
   1556 
   1557    // Only the Normal and Contact categories are allowed with webauthn
   1558    //  - disallow Credential
   1559    if (enumValue.ParseEnumValue(tokenString, kAutocompleteCredentialTypeTable,
   1560                                 false)) {
   1561      return eAutocompleteAttrState_Invalid;
   1562    }
   1563    //  - disallow Off and Automatic
   1564    if (enumValue.ParseEnumValue(tokenString, kAutocompleteFieldNameTable,
   1565                                 false)) {
   1566      if (enumValue.Equals(u"off"_ns, eIgnoreCase) ||
   1567          enumValue.Equals(u"on"_ns, eIgnoreCase)) {
   1568        return eAutocompleteAttrState_Invalid;
   1569      }
   1570    }
   1571 
   1572    // Proceed to process the remaining tokens as if "webauthn" was not present.
   1573    // We need to decrement numTokens to enforce the correct per-category limits
   1574    // on the maximum number of tokens.
   1575    --numTokens;
   1576  }
   1577 
   1578  bool unsupported = false;
   1579  if (!aGrantAllValidValue) {
   1580    unsupported = enumValue.ParseEnumValue(
   1581        tokenString, kAutocompleteUnsupportedFieldNameTable, false);
   1582    if (unsupported) {
   1583      return eAutocompleteAttrState_Invalid;
   1584    }
   1585  }
   1586 
   1587  nsAutoString fieldNameStr;
   1588  result =
   1589      enumValue.ParseEnumValue(tokenString, kAutocompleteFieldNameTable, false);
   1590 
   1591  if (result) {
   1592    // Off/Automatic/Normal categories.
   1593    if (enumValue.Equals(u"off"_ns, eIgnoreCase) ||
   1594        enumValue.Equals(u"on"_ns, eIgnoreCase)) {
   1595      if (numTokens > 1) {
   1596        return eAutocompleteAttrState_Invalid;
   1597      }
   1598      enumValue.ToString(fieldNameStr);
   1599      ASCIIToLower(fieldNameStr);
   1600      aInfo.mFieldName.Assign(fieldNameStr);
   1601      aInfo.mCredentialType.Assign(credentialTypeStr);
   1602      aInfo.mCanAutomaticallyPersist =
   1603          !enumValue.Equals(u"off"_ns, eIgnoreCase);
   1604      return eAutocompleteAttrState_Valid;
   1605    }
   1606 
   1607    // Only allow on/off if form autofill @autocomplete values aren't enabled
   1608    // and it doesn't grant all valid values.
   1609    if (!StaticPrefs::dom_forms_autocomplete_formautofill() &&
   1610        !aGrantAllValidValue) {
   1611      return eAutocompleteAttrState_Invalid;
   1612    }
   1613 
   1614    // Normal category
   1615    if (numTokens > 3) {
   1616      return eAutocompleteAttrState_Invalid;
   1617    }
   1618    category = eAutocompleteCategory_NORMAL;
   1619  } else {  // Check if the last token is of the contact category instead.
   1620    // Only allow on/off if form autofill @autocomplete values aren't enabled
   1621    // and it doesn't grant all valid values.
   1622    if (!StaticPrefs::dom_forms_autocomplete_formautofill() &&
   1623        !aGrantAllValidValue) {
   1624      return eAutocompleteAttrState_Invalid;
   1625    }
   1626 
   1627    result = enumValue.ParseEnumValue(
   1628        tokenString, kAutocompleteContactFieldNameTable, false);
   1629    if (!result || numTokens > 4) {
   1630      return eAutocompleteAttrState_Invalid;
   1631    }
   1632 
   1633    category = eAutocompleteCategory_CONTACT;
   1634  }
   1635 
   1636  enumValue.ToString(fieldNameStr);
   1637  ASCIIToLower(fieldNameStr);
   1638 
   1639  aInfo.mFieldName.Assign(fieldNameStr);
   1640  aInfo.mCredentialType.Assign(credentialTypeStr);
   1641  aInfo.mCanAutomaticallyPersist = !enumValue.ParseEnumValue(
   1642      tokenString, kAutocompleteNoPersistFieldNameTable, false);
   1643 
   1644  // We are done if this was the only token.
   1645  if (numTokens == 1) {
   1646    return eAutocompleteAttrState_Valid;
   1647  }
   1648 
   1649  --index;
   1650  tokenString = nsDependentAtomString(aAttrVal->AtomAt(index));
   1651 
   1652  if (category == eAutocompleteCategory_CONTACT) {
   1653    if (!aGrantAllValidValue) {
   1654      unsupported = enumValue.ParseEnumValue(
   1655          tokenString, kAutocompleteUnsupportedContactFieldHintTable, false);
   1656      if (unsupported) {
   1657        return eAutocompleteAttrState_Invalid;
   1658      }
   1659    }
   1660 
   1661    nsAttrValue contactFieldHint;
   1662    result = contactFieldHint.ParseEnumValue(
   1663        tokenString, kAutocompleteContactFieldHintTable, false);
   1664    if (result) {
   1665      nsAutoString contactFieldHintString;
   1666      contactFieldHint.ToString(contactFieldHintString);
   1667      ASCIIToLower(contactFieldHintString);
   1668      aInfo.mContactType.Assign(contactFieldHintString);
   1669      if (index == 0) {
   1670        return eAutocompleteAttrState_Valid;
   1671      }
   1672      --index;
   1673      tokenString = nsDependentAtomString(aAttrVal->AtomAt(index));
   1674    }
   1675  }
   1676 
   1677  // Check for billing/shipping tokens
   1678  nsAttrValue fieldHint;
   1679  if (fieldHint.ParseEnumValue(tokenString, kAutocompleteFieldHintTable,
   1680                               false)) {
   1681    nsString fieldHintString;
   1682    fieldHint.ToString(fieldHintString);
   1683    ASCIIToLower(fieldHintString);
   1684    aInfo.mAddressType.Assign(fieldHintString);
   1685    if (index == 0) {
   1686      return eAutocompleteAttrState_Valid;
   1687    }
   1688    --index;
   1689    tokenString = nsDependentAtomString(aAttrVal->AtomAt(index));
   1690  }
   1691 
   1692  // Check for section-* token
   1693  const nsDependentSubstring& section = Substring(tokenString, 0, 8);
   1694  if (section.LowerCaseEqualsASCII("section-")) {
   1695    ASCIIToLower(tokenString);
   1696    aInfo.mSection.Assign(tokenString);
   1697    if (index == 0) {
   1698      return eAutocompleteAttrState_Valid;
   1699    }
   1700  }
   1701 
   1702  // Clear the fields as the autocomplete attribute is invalid.
   1703  aInfo.mSection.Truncate();
   1704  aInfo.mAddressType.Truncate();
   1705  aInfo.mContactType.Truncate();
   1706  aInfo.mFieldName.Truncate();
   1707  aInfo.mCredentialType.Truncate();
   1708 
   1709  return eAutocompleteAttrState_Invalid;
   1710 }
   1711 
   1712 // Parse an integer according to HTML spec
   1713 template <class CharT>
   1714 int32_t nsContentUtils::ParseHTMLIntegerImpl(
   1715    const CharT* aStart, const CharT* aEnd,
   1716    ParseHTMLIntegerResultFlags* aResult) {
   1717  int result = eParseHTMLInteger_NoFlags;
   1718 
   1719  const CharT* iter = aStart;
   1720 
   1721  while (iter != aEnd && nsContentUtils::IsHTMLWhitespace(*iter)) {
   1722    result |= eParseHTMLInteger_NonStandard;
   1723    ++iter;
   1724  }
   1725 
   1726  if (iter == aEnd) {
   1727    result |= eParseHTMLInteger_Error | eParseHTMLInteger_ErrorNoValue;
   1728    *aResult = (ParseHTMLIntegerResultFlags)result;
   1729    return 0;
   1730  }
   1731 
   1732  int sign = 1;
   1733  if (*iter == CharT('-')) {
   1734    sign = -1;
   1735    result |= eParseHTMLInteger_Negative;
   1736    ++iter;
   1737  } else if (*iter == CharT('+')) {
   1738    result |= eParseHTMLInteger_NonStandard;
   1739    ++iter;
   1740  }
   1741 
   1742  bool foundValue = false;
   1743  CheckedInt32 value = 0;
   1744 
   1745  // Check for leading zeros first.
   1746  uint64_t leadingZeros = 0;
   1747  while (iter != aEnd) {
   1748    if (*iter != CharT('0')) {
   1749      break;
   1750    }
   1751 
   1752    ++leadingZeros;
   1753    foundValue = true;
   1754    ++iter;
   1755  }
   1756 
   1757  while (iter != aEnd) {
   1758    if (*iter >= CharT('0') && *iter <= CharT('9')) {
   1759      value = (value * 10) + (*iter - CharT('0')) * sign;
   1760      ++iter;
   1761      if (!value.isValid()) {
   1762        result |= eParseHTMLInteger_Error | eParseHTMLInteger_ErrorOverflow;
   1763        break;
   1764      }
   1765      foundValue = true;
   1766    } else {
   1767      break;
   1768    }
   1769  }
   1770 
   1771  if (!foundValue) {
   1772    result |= eParseHTMLInteger_Error | eParseHTMLInteger_ErrorNoValue;
   1773  }
   1774 
   1775  if (value.isValid() &&
   1776      ((leadingZeros > 1 || (leadingZeros == 1 && !(value == 0))) ||
   1777       (sign == -1 && value == 0))) {
   1778    result |= eParseHTMLInteger_NonStandard;
   1779  }
   1780 
   1781  if (iter != aEnd) {
   1782    result |= eParseHTMLInteger_DidNotConsumeAllInput;
   1783  }
   1784 
   1785  *aResult = (ParseHTMLIntegerResultFlags)result;
   1786  return value.isValid() ? value.value() : 0;
   1787 }
   1788 
   1789 // Parse an integer according to HTML spec
   1790 int32_t nsContentUtils::ParseHTMLInteger(const char16_t* aStart,
   1791                                         const char16_t* aEnd,
   1792                                         ParseHTMLIntegerResultFlags* aResult) {
   1793  return ParseHTMLIntegerImpl(aStart, aEnd, aResult);
   1794 }
   1795 
   1796 int32_t nsContentUtils::ParseHTMLInteger(const char* aStart, const char* aEnd,
   1797                                         ParseHTMLIntegerResultFlags* aResult) {
   1798  return ParseHTMLIntegerImpl(aStart, aEnd, aResult);
   1799 }
   1800 
   1801 Maybe<double> nsContentUtils::ParseHTMLFloatingPointNumber(
   1802    const nsAString& aString) {
   1803  // Check if it is a valid floating-point number first since the result of
   1804  // nsString.ToDouble() is more lenient than the spec,
   1805  // https://html.spec.whatwg.org/#valid-floating-point-number
   1806  nsAString::const_iterator iter, end;
   1807  aString.BeginReading(iter);
   1808  aString.EndReading(end);
   1809 
   1810  if (iter == end) {
   1811    return {};
   1812  }
   1813 
   1814  if (*iter == char16_t('-') && ++iter == end) {
   1815    return {};
   1816  }
   1817 
   1818  if (IsAsciiDigit(*iter)) {
   1819    for (; iter != end && IsAsciiDigit(*iter); ++iter);
   1820  } else if (*iter == char16_t('.')) {
   1821    // Do nothing, jumps to fraction part
   1822  } else {
   1823    return {};
   1824  }
   1825 
   1826  // Fraction
   1827  if (*iter == char16_t('.')) {
   1828    ++iter;
   1829    if (iter == end || !IsAsciiDigit(*iter)) {
   1830      // U+002E FULL STOP character (.) must be followed by one or more ASCII
   1831      // digits
   1832      return {};
   1833    }
   1834 
   1835    for (; iter != end && IsAsciiDigit(*iter); ++iter);
   1836  }
   1837 
   1838  if (iter != end && (*iter == char16_t('e') || *iter == char16_t('E'))) {
   1839    ++iter;
   1840    if (*iter == char16_t('-') || *iter == char16_t('+')) {
   1841      ++iter;
   1842    }
   1843 
   1844    if (iter == end || !IsAsciiDigit(*iter)) {
   1845      // Should have one or more ASCII digits
   1846      return {};
   1847    }
   1848 
   1849    for (; iter != end && IsAsciiDigit(*iter); ++iter);
   1850  }
   1851 
   1852  if (iter != end) {
   1853    return {};
   1854  }
   1855 
   1856  nsresult rv;
   1857  double result = PromiseFlatString(aString).ToDouble(&rv);
   1858  if (NS_FAILED(rv)) {
   1859    return {};
   1860  }
   1861  return Some(result);
   1862 }
   1863 
   1864 #define SKIP_WHITESPACE(iter, end_iter, end_res)                 \
   1865  while ((iter) != (end_iter) && nsCRT::IsAsciiSpace(*(iter))) { \
   1866    ++(iter);                                                    \
   1867  }                                                              \
   1868  if ((iter) == (end_iter)) {                                    \
   1869    return (end_res);                                            \
   1870  }
   1871 
   1872 #define SKIP_ATTR_NAME(iter, end_iter)                            \
   1873  while ((iter) != (end_iter) && !nsCRT::IsAsciiSpace(*(iter)) && \
   1874         *(iter) != '=') {                                        \
   1875    ++(iter);                                                     \
   1876  }
   1877 
   1878 bool nsContentUtils::GetPseudoAttributeValue(const nsString& aSource,
   1879                                             nsAtom* aName, nsAString& aValue) {
   1880  aValue.Truncate();
   1881 
   1882  const char16_t* start = aSource.get();
   1883  const char16_t* end = start + aSource.Length();
   1884  const char16_t* iter;
   1885 
   1886  while (start != end) {
   1887    SKIP_WHITESPACE(start, end, false)
   1888    iter = start;
   1889    SKIP_ATTR_NAME(iter, end)
   1890 
   1891    if (start == iter) {
   1892      return false;
   1893    }
   1894 
   1895    // Remember the attr name.
   1896    const nsDependentSubstring& attrName = Substring(start, iter);
   1897 
   1898    // Now check whether this is a valid name="value" pair.
   1899    start = iter;
   1900    SKIP_WHITESPACE(start, end, false)
   1901    if (*start != '=') {
   1902      // No '=', so this is not a name="value" pair.  We don't know
   1903      // what it is, and we have no way to handle it.
   1904      return false;
   1905    }
   1906 
   1907    // Have to skip the value.
   1908    ++start;
   1909    SKIP_WHITESPACE(start, end, false)
   1910    char16_t q = *start;
   1911    if (q != kQuote && q != kApostrophe) {
   1912      // Not a valid quoted value, so bail.
   1913      return false;
   1914    }
   1915 
   1916    ++start;  // Point to the first char of the value.
   1917    iter = start;
   1918 
   1919    while (iter != end && *iter != q) {
   1920      ++iter;
   1921    }
   1922 
   1923    if (iter == end) {
   1924      // Oops, unterminated quoted string.
   1925      return false;
   1926    }
   1927 
   1928    // At this point attrName holds the name of the "attribute" and
   1929    // the value is between start and iter.
   1930 
   1931    if (aName->Equals(attrName)) {
   1932      // We'll accumulate as many characters as possible (until we hit either
   1933      // the end of the string or the beginning of an entity). Chunks will be
   1934      // delimited by start and chunkEnd.
   1935      const char16_t* chunkEnd = start;
   1936      while (chunkEnd != iter) {
   1937        if (*chunkEnd == kLessThan) {
   1938          aValue.Truncate();
   1939 
   1940          return false;
   1941        }
   1942 
   1943        if (*chunkEnd == kAmpersand) {
   1944          aValue.Append(start, chunkEnd - start);
   1945 
   1946          const char16_t* afterEntity = nullptr;
   1947          char16_t result[2];
   1948          uint32_t count = MOZ_XMLTranslateEntity(
   1949              reinterpret_cast<const char*>(chunkEnd),
   1950              reinterpret_cast<const char*>(iter),
   1951              reinterpret_cast<const char**>(&afterEntity), result);
   1952          if (count == 0) {
   1953            aValue.Truncate();
   1954 
   1955            return false;
   1956          }
   1957 
   1958          aValue.Append(result, count);
   1959 
   1960          // Advance to after the entity and begin a new chunk.
   1961          start = chunkEnd = afterEntity;
   1962        } else {
   1963          ++chunkEnd;
   1964        }
   1965      }
   1966 
   1967      // Append remainder.
   1968      aValue.Append(start, iter - start);
   1969 
   1970      return true;
   1971    }
   1972 
   1973    // Resume scanning after the end of the attribute value (past the quote
   1974    // char).
   1975    start = iter + 1;
   1976  }
   1977 
   1978  return false;
   1979 }
   1980 
   1981 bool nsContentUtils::IsJavaScriptLanguage(const nsString& aName) {
   1982  // Create MIME type as "text/" + given input
   1983  nsAutoString mimeType(u"text/");
   1984  mimeType.Append(aName);
   1985 
   1986  return IsJavascriptMIMEType(mimeType);
   1987 }
   1988 
   1989 void nsContentUtils::SplitMimeType(const nsAString& aValue, nsString& aType,
   1990                                   nsString& aParams) {
   1991  aType.Truncate();
   1992  aParams.Truncate();
   1993  int32_t semiIndex = aValue.FindChar(char16_t(';'));
   1994  if (-1 != semiIndex) {
   1995    aType = Substring(aValue, 0, semiIndex);
   1996    aParams =
   1997        Substring(aValue, semiIndex + 1, aValue.Length() - (semiIndex + 1));
   1998    aParams.StripWhitespace();
   1999  } else {
   2000    aType = aValue;
   2001  }
   2002  aType.StripWhitespace();
   2003 }
   2004 
   2005 /**
   2006 * A helper function that parses a sandbox attribute (of an <iframe> or a CSP
   2007 * directive) and converts it to the set of flags used internally.
   2008 *
   2009 * @param aSandboxAttr  the sandbox attribute
   2010 * @return              the set of flags (SANDBOXED_NONE if aSandboxAttr is
   2011 *                      null)
   2012 */
   2013 uint32_t nsContentUtils::ParseSandboxAttributeToFlags(
   2014    const nsAttrValue* aSandboxAttr) {
   2015  if (!aSandboxAttr) {
   2016    return SANDBOXED_NONE;
   2017  }
   2018 
   2019  uint32_t out = SANDBOX_ALL_FLAGS;
   2020 
   2021 #define SANDBOX_KEYWORD(string, atom, flags)                  \
   2022  if (aSandboxAttr->Contains(nsGkAtoms::atom, eIgnoreCase)) { \
   2023    out &= ~(flags);                                          \
   2024  }
   2025 #include "IframeSandboxKeywordList.h"
   2026 #undef SANDBOX_KEYWORD
   2027 
   2028  return out;
   2029 }
   2030 
   2031 /**
   2032 * A helper function that checks if a string matches a valid sandbox flag.
   2033 *
   2034 * @param aFlag   the potential sandbox flag.
   2035 * @return        true if the flag is a sandbox flag.
   2036 */
   2037 bool nsContentUtils::IsValidSandboxFlag(const nsAString& aFlag) {
   2038 #define SANDBOX_KEYWORD(string, atom, flags)                                  \
   2039  if (EqualsIgnoreASCIICase(nsDependentAtomString(nsGkAtoms::atom), aFlag)) { \
   2040    return true;                                                              \
   2041  }
   2042 #include "IframeSandboxKeywordList.h"
   2043 #undef SANDBOX_KEYWORD
   2044  return false;
   2045 }
   2046 
   2047 /**
   2048 * A helper function that returns a string attribute corresponding to the
   2049 * sandbox flags.
   2050 *
   2051 * @param aFlags    the sandbox flags
   2052 * @param aString   the attribute corresponding to the flags (null if aFlags
   2053 *                  is zero)
   2054 */
   2055 void nsContentUtils::SandboxFlagsToString(uint32_t aFlags, nsAString& aString) {
   2056  if (!aFlags) {
   2057    SetDOMStringToNull(aString);
   2058    return;
   2059  }
   2060 
   2061  aString.Truncate();
   2062 
   2063 #define SANDBOX_KEYWORD(string, atom, flags)                \
   2064  if (!(aFlags & (flags))) {                                \
   2065    if (!aString.IsEmpty()) {                               \
   2066      aString.AppendLiteral(u" ");                          \
   2067    }                                                       \
   2068    aString.Append(nsDependentAtomString(nsGkAtoms::atom)); \
   2069  }
   2070 #include "IframeSandboxKeywordList.h"
   2071 #undef SANDBOX_KEYWORD
   2072 }
   2073 
   2074 nsIBidiKeyboard* nsContentUtils::GetBidiKeyboard() {
   2075  if (!sBidiKeyboard) {
   2076    sBidiKeyboard = nsIWidget::CreateBidiKeyboard();
   2077  }
   2078  return sBidiKeyboard;
   2079 }
   2080 
   2081 // static
   2082 bool nsContentUtils::IsAlphanumeric(uint32_t aChar) {
   2083  nsUGenCategory cat = mozilla::unicode::GetGenCategory(aChar);
   2084 
   2085  return (cat == nsUGenCategory::kLetter || cat == nsUGenCategory::kNumber);
   2086 }
   2087 
   2088 // static
   2089 bool nsContentUtils::IsAlphanumericOrSymbol(uint32_t aChar) {
   2090  nsUGenCategory cat = mozilla::unicode::GetGenCategory(aChar);
   2091 
   2092  return cat == nsUGenCategory::kLetter || cat == nsUGenCategory::kNumber ||
   2093         cat == nsUGenCategory::kSymbol;
   2094 }
   2095 
   2096 // static
   2097 bool nsContentUtils::IsHyphen(uint32_t aChar) {
   2098  // Characters treated as hyphens for the purpose of "emergency" breaking
   2099  // when the content would otherwise overflow.
   2100  return aChar == uint32_t('-') ||  // HYPHEN-MINUS
   2101         aChar == 0x2010 ||         // HYPHEN
   2102         aChar == 0x2012 ||         // FIGURE DASH
   2103         aChar == 0x2013 ||         // EN DASH
   2104         aChar == 0x058A;           // ARMENIAN HYPHEN
   2105 }
   2106 
   2107 /* static */
   2108 bool nsContentUtils::IsHTMLWhitespace(char16_t aChar) {
   2109  return aChar == char16_t(0x0009) || aChar == char16_t(0x000A) ||
   2110         aChar == char16_t(0x000C) || aChar == char16_t(0x000D) ||
   2111         aChar == char16_t(0x0020);
   2112 }
   2113 
   2114 /* static */
   2115 bool nsContentUtils::IsHTMLWhitespaceOrNBSP(char16_t aChar) {
   2116  return IsHTMLWhitespace(aChar) || aChar == char16_t(0xA0);
   2117 }
   2118 
   2119 /* static */
   2120 bool nsContentUtils::IsHTMLBlockLevelElement(nsIContent* aContent) {
   2121  return aContent->IsAnyOfHTMLElements(
   2122      nsGkAtoms::address, nsGkAtoms::article, nsGkAtoms::aside,
   2123      nsGkAtoms::blockquote, nsGkAtoms::center, nsGkAtoms::dir, nsGkAtoms::div,
   2124      nsGkAtoms::dl,  // XXX why not dt and dd?
   2125      nsGkAtoms::fieldset,
   2126      nsGkAtoms::figure,  // XXX shouldn't figcaption be on this list
   2127      nsGkAtoms::footer, nsGkAtoms::form, nsGkAtoms::h1, nsGkAtoms::h2,
   2128      nsGkAtoms::h3, nsGkAtoms::h4, nsGkAtoms::h5, nsGkAtoms::h6,
   2129      nsGkAtoms::header, nsGkAtoms::hgroup, nsGkAtoms::hr, nsGkAtoms::li,
   2130      nsGkAtoms::listing, nsGkAtoms::menu, nsGkAtoms::nav, nsGkAtoms::ol,
   2131      nsGkAtoms::p, nsGkAtoms::pre, nsGkAtoms::section, nsGkAtoms::table,
   2132      nsGkAtoms::ul, nsGkAtoms::xmp);
   2133 }
   2134 
   2135 // static
   2136 int32_t nsContentUtils::ParseLegacyFontSize(const nsAString& aValue) {
   2137  nsAString::const_iterator iter, end;
   2138  aValue.BeginReading(iter);
   2139  aValue.EndReading(end);
   2140 
   2141  while (iter != end && nsContentUtils::IsHTMLWhitespace(*iter)) {
   2142    ++iter;
   2143  }
   2144 
   2145  if (iter == end) {
   2146    return 0;
   2147  }
   2148 
   2149  bool relative = false;
   2150  bool negate = false;
   2151  if (*iter == char16_t('-')) {
   2152    relative = true;
   2153    negate = true;
   2154    ++iter;
   2155  } else if (*iter == char16_t('+')) {
   2156    relative = true;
   2157    ++iter;
   2158  }
   2159 
   2160  if (iter == end || *iter < char16_t('0') || *iter > char16_t('9')) {
   2161    return 0;
   2162  }
   2163 
   2164  // We don't have to worry about overflow, since we can bail out as soon as
   2165  // we're bigger than 7.
   2166  int32_t value = 0;
   2167  while (iter != end && *iter >= char16_t('0') && *iter <= char16_t('9')) {
   2168    value = 10 * value + (*iter - char16_t('0'));
   2169    if (value >= 7) {
   2170      break;
   2171    }
   2172    ++iter;
   2173  }
   2174 
   2175  if (relative) {
   2176    if (negate) {
   2177      value = 3 - value;
   2178    } else {
   2179      value = 3 + value;
   2180    }
   2181  }
   2182 
   2183  return std::clamp(value, 1, 7);
   2184 }
   2185 
   2186 /* static */
   2187 void nsContentUtils::GetOfflineAppManifest(Document* aDocument, nsIURI** aURI) {
   2188  MOZ_ASSERT(NS_IsMainThread());
   2189  MOZ_ASSERT(aDocument);
   2190  *aURI = nullptr;
   2191 
   2192  if (aDocument->GetController().isSome()) {
   2193    return;
   2194  }
   2195 
   2196  Element* docElement = aDocument->GetRootElement();
   2197  if (!docElement) {
   2198    return;
   2199  }
   2200 
   2201  nsAutoString manifestSpec;
   2202  docElement->GetAttr(nsGkAtoms::manifest, manifestSpec);
   2203 
   2204  // Manifest URIs can't have fragment identifiers.
   2205  if (manifestSpec.IsEmpty() || manifestSpec.Contains('#')) {
   2206    return;
   2207  }
   2208 
   2209  nsContentUtils::NewURIWithDocumentCharset(aURI, manifestSpec, aDocument,
   2210                                            aDocument->GetDocBaseURI());
   2211 }
   2212 
   2213 /* static */
   2214 bool nsContentUtils::OfflineAppAllowed(nsIURI* aURI) { return false; }
   2215 
   2216 /* static */
   2217 bool nsContentUtils::OfflineAppAllowed(nsIPrincipal* aPrincipal) {
   2218  return false;
   2219 }
   2220 // Static
   2221 bool nsContentUtils::IsErrorPage(nsIURI* aURI) {
   2222  if (!aURI) {
   2223    return false;
   2224  }
   2225 
   2226  if (!aURI->SchemeIs("about")) {
   2227    return false;
   2228  }
   2229 
   2230  nsAutoCString name;
   2231  nsresult rv = NS_GetAboutModuleName(aURI, name);
   2232  NS_ENSURE_SUCCESS(rv, false);
   2233 
   2234  return name.EqualsLiteral("certerror") || name.EqualsLiteral("neterror") ||
   2235         name.EqualsLiteral("blocked");
   2236 }
   2237 
   2238 // static
   2239 void nsContentUtils::Shutdown() {
   2240  sInitialized = false;
   2241 
   2242  nsHTMLTags::ReleaseTable();
   2243 
   2244  NS_IF_RELEASE(sContentPolicyService);
   2245  sTriedToGetContentPolicy = false;
   2246  for (StaticRefPtr<nsIStringBundle>& bundle : sStringBundles) {
   2247    bundle = nullptr;
   2248  }
   2249 
   2250  NS_IF_RELEASE(sStringBundleService);
   2251  NS_IF_RELEASE(sConsoleService);
   2252  NS_IF_RELEASE(sXPConnect);
   2253  NS_IF_RELEASE(sSecurityManager);
   2254  NS_IF_RELEASE(sSystemPrincipal);
   2255  NS_IF_RELEASE(sNullSubjectPrincipal);
   2256  NS_IF_RELEASE(sFingerprintingProtectionPrincipal);
   2257 
   2258  sBidiKeyboard = nullptr;
   2259 
   2260  delete sAtomEventTable;
   2261  sAtomEventTable = nullptr;
   2262  delete sStringEventTable;
   2263  sStringEventTable = nullptr;
   2264  delete sUserDefinedEvents;
   2265  sUserDefinedEvents = nullptr;
   2266 
   2267  if (sEventListenerManagersHash) {
   2268    NS_ASSERTION(sEventListenerManagersHash->EntryCount() == 0,
   2269                 "Event listener manager hash not empty at shutdown!");
   2270 
   2271    // See comment above.
   2272 
   2273    // However, we have to handle this table differently.  If it still
   2274    // has entries, we want to leak it too, so that we can keep it alive
   2275    // in case any elements are destroyed.  Because if they are, we need
   2276    // their event listener managers to be destroyed too, or otherwise
   2277    // it could leave dangling references in DOMClassInfo's preserved
   2278    // wrapper table.
   2279 
   2280    if (sEventListenerManagersHash->EntryCount() == 0) {
   2281      delete sEventListenerManagersHash;
   2282      sEventListenerManagersHash = nullptr;
   2283    }
   2284  }
   2285 
   2286  if (sDOMArenaHashtable) {
   2287    MOZ_ASSERT(sDOMArenaHashtable->Count() == 0);
   2288    MOZ_ASSERT(StaticPrefs::dom_arena_allocator_enabled_AtStartup());
   2289    delete sDOMArenaHashtable;
   2290    sDOMArenaHashtable = nullptr;
   2291  }
   2292 
   2293  NS_ASSERTION(!sBlockedScriptRunners || sBlockedScriptRunners->Length() == 0,
   2294               "How'd this happen?");
   2295  delete sBlockedScriptRunners;
   2296  sBlockedScriptRunners = nullptr;
   2297 
   2298  delete sShiftText;
   2299  sShiftText = nullptr;
   2300  delete sControlText;
   2301  sControlText = nullptr;
   2302  delete sCommandOrWinText;
   2303  sCommandOrWinText = nullptr;
   2304  delete sAltText;
   2305  sAltText = nullptr;
   2306  delete sModifierSeparator;
   2307  sModifierSeparator = nullptr;
   2308 
   2309  delete sJSScriptBytecodeMimeType;
   2310  sJSScriptBytecodeMimeType = nullptr;
   2311 
   2312  delete sJSModuleBytecodeMimeType;
   2313  sJSModuleBytecodeMimeType = nullptr;
   2314 
   2315  NS_IF_RELEASE(sSameOriginChecker);
   2316 
   2317  if (sUserInteractionObserver) {
   2318    sUserInteractionObserver->Shutdown();
   2319    NS_RELEASE(sUserInteractionObserver);
   2320  }
   2321 
   2322  for (const auto& pref : kRfpPrefs) {
   2323    Preferences::UnregisterCallback(RecomputeResistFingerprintingAllDocs, pref);
   2324  }
   2325 
   2326  TextControlState::Shutdown();
   2327 }
   2328 
   2329 /**
   2330 * Checks whether two nodes come from the same origin. aTrustedNode is
   2331 * considered 'safe' in that a user can operate on it.
   2332 */
   2333 // static
   2334 nsresult nsContentUtils::CheckSameOrigin(const nsINode* aTrustedNode,
   2335                                         const nsINode* unTrustedNode) {
   2336  MOZ_ASSERT(aTrustedNode);
   2337  MOZ_ASSERT(unTrustedNode);
   2338 
   2339  /*
   2340   * Get hold of each node's principal
   2341   */
   2342 
   2343  nsIPrincipal* trustedPrincipal = aTrustedNode->NodePrincipal();
   2344  nsIPrincipal* unTrustedPrincipal = unTrustedNode->NodePrincipal();
   2345 
   2346  if (trustedPrincipal == unTrustedPrincipal) {
   2347    return NS_OK;
   2348  }
   2349 
   2350  bool equal;
   2351  // XXXbz should we actually have a Subsumes() check here instead?  Or perhaps
   2352  // a separate method for that, with callers using one or the other?
   2353  if (NS_FAILED(trustedPrincipal->Equals(unTrustedPrincipal, &equal)) ||
   2354      !equal) {
   2355    return NS_ERROR_DOM_PROP_ACCESS_DENIED;
   2356  }
   2357 
   2358  return NS_OK;
   2359 }
   2360 
   2361 // static
   2362 bool nsContentUtils::CanCallerAccess(nsIPrincipal* aSubjectPrincipal,
   2363                                     nsIPrincipal* aPrincipal) {
   2364  bool subsumes;
   2365  nsresult rv = aSubjectPrincipal->Subsumes(aPrincipal, &subsumes);
   2366  NS_ENSURE_SUCCESS(rv, false);
   2367 
   2368  if (subsumes) {
   2369    return true;
   2370  }
   2371 
   2372  // The subject doesn't subsume aPrincipal. Allow access only if the subject
   2373  // is chrome.
   2374  return IsCallerChrome();
   2375 }
   2376 
   2377 // static
   2378 bool nsContentUtils::CanCallerAccess(const nsINode* aNode) {
   2379  nsIPrincipal* subject = SubjectPrincipal();
   2380  if (subject->IsSystemPrincipal()) {
   2381    return true;
   2382  }
   2383 
   2384  if (aNode->ChromeOnlyAccess()) {
   2385    return false;
   2386  }
   2387 
   2388  return CanCallerAccess(subject, aNode->NodePrincipal());
   2389 }
   2390 
   2391 // static
   2392 bool nsContentUtils::CanCallerAccess(nsPIDOMWindowInner* aWindow) {
   2393  nsCOMPtr<nsIScriptObjectPrincipal> scriptObject = do_QueryInterface(aWindow);
   2394  NS_ENSURE_TRUE(scriptObject, false);
   2395 
   2396  return CanCallerAccess(SubjectPrincipal(), scriptObject->GetPrincipal());
   2397 }
   2398 
   2399 // static
   2400 bool nsContentUtils::PrincipalHasPermission(nsIPrincipal& aPrincipal,
   2401                                            const nsAtom* aPerm) {
   2402  // Chrome gets access by default.
   2403  if (aPrincipal.IsSystemPrincipal()) {
   2404    return true;
   2405  }
   2406 
   2407  // Otherwise, only allow if caller is an addon with the permission.
   2408  return BasePrincipal::Cast(aPrincipal).AddonHasPermission(aPerm);
   2409 }
   2410 
   2411 // static
   2412 bool nsContentUtils::CallerHasPermission(JSContext* aCx, const nsAtom* aPerm) {
   2413  return PrincipalHasPermission(*SubjectPrincipal(aCx), aPerm);
   2414 }
   2415 
   2416 // static
   2417 nsIPrincipal* nsContentUtils::GetAttrTriggeringPrincipal(
   2418    nsIContent* aContent, const nsAString& aAttrValue,
   2419    nsIPrincipal* aSubjectPrincipal) {
   2420  nsIPrincipal* contentPrin = aContent ? aContent->NodePrincipal() : nullptr;
   2421 
   2422  // If the subject principal is the same as the content principal, or no
   2423  // explicit subject principal was provided, we don't need to do any further
   2424  // checks. Just return the content principal.
   2425  if (contentPrin == aSubjectPrincipal || !aSubjectPrincipal) {
   2426    return contentPrin;
   2427  }
   2428 
   2429  // Only use the subject principal if the URL string we are going to end up
   2430  // fetching is under the control of that principal, which is never the case
   2431  // for relative URLs.
   2432  if (aAttrValue.IsEmpty() ||
   2433      !IsAbsoluteURL(NS_ConvertUTF16toUTF8(aAttrValue))) {
   2434    return contentPrin;
   2435  }
   2436 
   2437  // Only use the subject principal as the attr triggering principal if it
   2438  // should override the CSP of the node's principal.
   2439  if (BasePrincipal::Cast(aSubjectPrincipal)->OverridesCSP(contentPrin)) {
   2440    return aSubjectPrincipal;
   2441  }
   2442 
   2443  return contentPrin;
   2444 }
   2445 
   2446 // static
   2447 bool nsContentUtils::IsAbsoluteURL(const nsACString& aURL) {
   2448  nsAutoCString scheme;
   2449  if (NS_FAILED(net_ExtractURLScheme(aURL, scheme))) {
   2450    // If we can't extract a scheme, it's not an absolute URL.
   2451    return false;
   2452  }
   2453 
   2454  // If it parses as an absolute StandardURL, it's definitely an absolute URL,
   2455  // so no need to check with the IO service.
   2456  if (net_IsAbsoluteURL(aURL)) {
   2457    return true;
   2458  }
   2459 
   2460  nsresult rv = NS_OK;
   2461  nsCOMPtr<nsIIOService> io = mozilla::components::IO::Service(&rv);
   2462  MOZ_DIAGNOSTIC_ASSERT(NS_SUCCEEDED(rv));
   2463  if (NS_FAILED(rv)) {
   2464    return false;
   2465  }
   2466 
   2467  uint32_t flags;
   2468  if (NS_SUCCEEDED(io->GetProtocolFlags(scheme.get(), &flags))) {
   2469    return flags & nsIProtocolHandler::URI_NORELATIVE;
   2470  }
   2471 
   2472  return false;
   2473 }
   2474 
   2475 // static
   2476 bool nsContentUtils::InProlog(nsINode* aNode) {
   2477  MOZ_ASSERT(aNode, "missing node to nsContentUtils::InProlog");
   2478 
   2479  nsINode* parent = aNode->GetParentNode();
   2480  if (!parent || !parent->IsDocument()) {
   2481    return false;
   2482  }
   2483 
   2484  const Document* doc = parent->AsDocument();
   2485  const nsIContent* root = doc->GetRootElement();
   2486  if (!root) {
   2487    return true;
   2488  }
   2489  const Maybe<uint32_t> indexOfNode = doc->ComputeIndexOf(aNode);
   2490  const Maybe<uint32_t> indexOfRoot = doc->ComputeIndexOf(root);
   2491  if (MOZ_LIKELY(indexOfNode.isSome() && indexOfRoot.isSome())) {
   2492    return *indexOfNode < *indexOfRoot;
   2493  }
   2494  // XXX Keep the odd traditional behavior for now.
   2495  return indexOfNode.isNothing() && indexOfRoot.isSome();
   2496 }
   2497 
   2498 bool nsContentUtils::IsCallerChrome() {
   2499  MOZ_ASSERT(NS_IsMainThread());
   2500  return SubjectPrincipal() == sSystemPrincipal;
   2501 }
   2502 
   2503 #ifdef FUZZING
   2504 bool nsContentUtils::IsFuzzingEnabled() {
   2505  return StaticPrefs::fuzzing_enabled();
   2506 }
   2507 #endif
   2508 
   2509 /* static */
   2510 bool nsContentUtils::IsCallerChromeOrElementTransformGettersEnabled(
   2511    JSContext* aCx, JSObject*) {
   2512  return ThreadsafeIsSystemCaller(aCx) ||
   2513         StaticPrefs::dom_element_transform_getters_enabled();
   2514 }
   2515 
   2516 // Older Should RFP Functions ----------------------------------
   2517 
   2518 /* static */
   2519 bool nsContentUtils::ShouldResistFingerprinting(bool aIsPrivateMode,
   2520                                                RFPTarget aTarget) {
   2521  return nsRFPService::IsRFPEnabledFor(aIsPrivateMode, aTarget, Nothing());
   2522 }
   2523 
   2524 /* static */
   2525 bool nsContentUtils::ShouldResistFingerprinting(nsIGlobalObject* aGlobalObject,
   2526                                                RFPTarget aTarget) {
   2527  if (!aGlobalObject) {
   2528    return ShouldResistFingerprinting("Null Object", aTarget);
   2529  }
   2530  return aGlobalObject->ShouldResistFingerprinting(aTarget);
   2531 }
   2532 
   2533 // Newer Should RFP Functions ----------------------------------
   2534 // Utilities ---------------------------------------------------
   2535 
   2536 inline void LogDomainAndList(const char* urlType, nsAutoCString& list,
   2537                             nsAutoCString& url, bool isExemptDomain) {
   2538  MOZ_LOG(nsContentUtils::ResistFingerprintingLog(), LogLevel::Debug,
   2539          ("%s \"%s\" is %s the exempt list \"%s\"", urlType,
   2540           PromiseFlatCString(url).get(), isExemptDomain ? "in" : "NOT in",
   2541           PromiseFlatCString(list).get()));
   2542 }
   2543 
   2544 inline already_AddRefed<nsICookieJarSettings> GetCookieJarSettings(
   2545    nsILoadInfo* aLoadInfo) {
   2546  nsCOMPtr<nsICookieJarSettings> cookieJarSettings;
   2547  nsresult rv =
   2548      aLoadInfo->GetCookieJarSettings(getter_AddRefs(cookieJarSettings));
   2549  if (rv == NS_ERROR_NOT_IMPLEMENTED) {
   2550    // The TRRLoadInfo in particular does not implement this method
   2551    // In that instance.  We will return false and let other code decide if
   2552    // we shouldRFP for this connection
   2553    return nullptr;
   2554  }
   2555  if (NS_WARN_IF(NS_FAILED(rv))) {
   2556    MOZ_LOG(nsContentUtils::ResistFingerprintingLog(), LogLevel::Info,
   2557            ("Called CookieJarSettingsSaysShouldResistFingerprinting but the "
   2558             "loadinfo's CookieJarSettings couldn't be retrieved"));
   2559    return nullptr;
   2560  }
   2561 
   2562  MOZ_ASSERT(cookieJarSettings);
   2563  return cookieJarSettings.forget();
   2564 }
   2565 
   2566 bool nsContentUtils::ETPSaysShouldNotResistFingerprinting(
   2567    nsICookieJarSettings* aCookieJarSettings, bool aIsPBM) {
   2568  // A positive return from this function should always be obeyed.
   2569  // A negative return means we should keep checking things.
   2570 
   2571  // We do not want this check to apply to RFP, only to FPP.
   2572  if (nsRFPService::IsRFPPrefEnabled(aIsPBM)) {
   2573    // In RFP, never use the ETP toggle to exempt.
   2574    // We can safely return false here even if we are not in PBM mode
   2575    // and RFP_pbmode is enabled because we will later see that and
   2576    // return false from the ShouldRFP function entirely.
   2577    return false;
   2578  }
   2579 
   2580  return ContentBlockingAllowList::Check(aCookieJarSettings);
   2581 }
   2582 
   2583 bool nsContentUtils::ETPSaysShouldNotResistFingerprinting(
   2584    nsIChannel* aChannel, nsILoadInfo* aLoadInfo) {
   2585  bool isPBM = NS_UsePrivateBrowsing(aChannel);
   2586 
   2587  nsCOMPtr<nsICookieJarSettings> cookieJarSettings =
   2588      GetCookieJarSettings(aLoadInfo);
   2589  if (!cookieJarSettings) {
   2590    return false;
   2591  }
   2592 
   2593  return ETPSaysShouldNotResistFingerprinting(cookieJarSettings, isPBM);
   2594 }
   2595 
   2596 inline bool CookieJarSettingsSaysShouldResistFingerprinting(
   2597    nsILoadInfo* aLoadInfo) {
   2598  // A positive return from this function should always be obeyed.
   2599  // A negative return means we should keep checking things.
   2600 
   2601  nsCOMPtr<nsICookieJarSettings> cookieJarSettings =
   2602      GetCookieJarSettings(aLoadInfo);
   2603  if (!cookieJarSettings) {
   2604    return false;
   2605  }
   2606  return cookieJarSettings->GetShouldResistFingerprinting();
   2607 }
   2608 
   2609 inline bool SchemeSaysShouldNotResistFingerprinting(nsIURI* aURI) {
   2610  return aURI->SchemeIs("chrome") || aURI->SchemeIs("resource") ||
   2611         aURI->SchemeIs("view-source") || aURI->SchemeIs("moz-extension") ||
   2612         (aURI->SchemeIs("about") && !NS_IsContentAccessibleAboutURI(aURI));
   2613 }
   2614 
   2615 inline bool SchemeSaysShouldNotResistFingerprinting(nsIPrincipal* aPrincipal) {
   2616  if (aPrincipal->SchemeIs("chrome") || aPrincipal->SchemeIs("resource") ||
   2617      aPrincipal->SchemeIs("view-source") ||
   2618      aPrincipal->SchemeIs("moz-extension")) {
   2619    return true;
   2620  }
   2621 
   2622  if (!aPrincipal->SchemeIs("about")) {
   2623    return false;
   2624  }
   2625 
   2626  bool isContentAccessibleAboutURI;
   2627  (void)aPrincipal->IsContentAccessibleAboutURI(&isContentAccessibleAboutURI);
   2628  return !isContentAccessibleAboutURI;
   2629 }
   2630 
   2631 inline bool PartionKeyIsAlsoExempted(
   2632    const mozilla::OriginAttributes& aOriginAttributes) {
   2633  // If we've gotten here we have (probably) passed the CookieJarSettings
   2634  // check that would tell us that if we _are_ a subdocument, then we are on
   2635  // an exempted top-level domain and we should see if we ourselves are
   2636  // exempted. But we may have gotten here because we directly called the
   2637  // _dangerous function and we haven't done that check, but we _were_
   2638  // instatiated from a state where we could have been partitioned.
   2639  // So perform this last-ditch check for that scenario.
   2640  // We arbitrarily use https as the scheme, but it doesn't matter.
   2641  nsresult rv = NS_ERROR_NOT_INITIALIZED;
   2642  nsCOMPtr<nsIURI> uri;
   2643  if (StaticPrefs::privacy_firstparty_isolate() &&
   2644      !aOriginAttributes.mFirstPartyDomain.IsEmpty()) {
   2645    rv = NS_NewURI(getter_AddRefs(uri),
   2646                   u"https://"_ns + aOriginAttributes.mFirstPartyDomain);
   2647  } else if (!aOriginAttributes.mPartitionKey.IsEmpty()) {
   2648    rv = NS_NewURI(getter_AddRefs(uri),
   2649                   u"https://"_ns + aOriginAttributes.mPartitionKey);
   2650  }
   2651 
   2652  if (!NS_FAILED(rv)) {
   2653    nsAutoCString list;
   2654    nsRFPService::GetExemptedDomainsLowercase(list);
   2655    bool isExemptPartitionKey = nsContentUtils::IsURIInList(uri, list);
   2656    if (MOZ_LOG_TEST(nsContentUtils::ResistFingerprintingLog(),
   2657                     mozilla::LogLevel::Debug)) {
   2658      nsAutoCString url;
   2659      uri->GetHost(url);
   2660      LogDomainAndList("Partition Key", list, url, isExemptPartitionKey);
   2661    }
   2662    return isExemptPartitionKey;
   2663  }
   2664  return true;
   2665 }
   2666 
   2667 // Functions ---------------------------------------------------
   2668 
   2669 /* static */
   2670 bool nsContentUtils::ShouldResistFingerprinting(const char* aJustification,
   2671                                                RFPTarget aTarget) {
   2672  // See comment in header file for information about usage
   2673  // We hardcode PBM to true to be the more restrictive option.
   2674  return nsContentUtils::ShouldResistFingerprinting(true, aTarget);
   2675 }
   2676 
   2677 namespace {
   2678 
   2679 // This function is only called within this file for Positive Return Checks
   2680 bool ShouldResistFingerprinting_(const char* aJustification,
   2681                                 bool aIsPrivateMode, RFPTarget aTarget) {
   2682  // See comment in header file for information about usage
   2683  return nsContentUtils::ShouldResistFingerprinting(aIsPrivateMode, aTarget);
   2684 }
   2685 
   2686 }  // namespace
   2687 
   2688 /* static */
   2689 bool nsContentUtils::ShouldResistFingerprinting(CallerType aCallerType,
   2690                                                nsIGlobalObject* aGlobalObject,
   2691                                                RFPTarget aTarget) {
   2692  if (aCallerType == CallerType::System) {
   2693    return false;
   2694  }
   2695  return ShouldResistFingerprinting(aGlobalObject, aTarget);
   2696 }
   2697 
   2698 /* static */
   2699 bool nsContentUtils::ShouldResistFingerprinting(nsIDocShell* aDocShell,
   2700                                                RFPTarget aTarget) {
   2701  if (!aDocShell) {
   2702    MOZ_LOG(nsContentUtils::ResistFingerprintingLog(), LogLevel::Info,
   2703            ("Called nsContentUtils::ShouldResistFingerprinting(nsIDocShell*) "
   2704             "with NULL docshell"));
   2705    return ShouldResistFingerprinting("Null Object", aTarget);
   2706  }
   2707  return ShouldResistFingerprinting(aDocShell->GetDocument(), aTarget);
   2708 }
   2709 
   2710 /* static */
   2711 bool nsContentUtils::ShouldResistFingerprinting(const Document* aDocument,
   2712                                                RFPTarget aTarget) {
   2713  if (!aDocument) {
   2714    MOZ_LOG(
   2715        nsContentUtils::ResistFingerprintingLog(), LogLevel::Info,
   2716        ("Called nsContentUtils::ShouldResistFingerprinting(const Document*) "
   2717         "with NULL document"));
   2718    return ShouldResistFingerprinting("Null Object", aTarget);
   2719  }
   2720  return aDocument->ShouldResistFingerprinting(aTarget);
   2721 }
   2722 
   2723 /* static */
   2724 bool nsContentUtils::ShouldResistFingerprinting(nsIChannel* aChannel,
   2725                                                RFPTarget aTarget) {
   2726  if (!aChannel) {
   2727    MOZ_LOG(nsContentUtils::ResistFingerprintingLog(), LogLevel::Info,
   2728            ("Called nsContentUtils::ShouldResistFingerprinting(nsIChannel* "
   2729             "aChannel) with NULL channel"));
   2730    return ShouldResistFingerprinting("Null Object", aTarget);
   2731  }
   2732 
   2733  bool isPBM = NS_UsePrivateBrowsing(aChannel);
   2734 
   2735  if (MOZ_LOG_TEST(nsContentUtils::ResistFingerprintingLog(),
   2736                   mozilla::LogLevel::Debug)) {
   2737    nsCOMPtr<nsIURI> channelURI;
   2738    (void)NS_GetFinalChannelURI(aChannel, getter_AddRefs(channelURI));
   2739    nsAutoCString channelSpec;
   2740    channelURI->GetSpec(channelSpec);
   2741    MOZ_LOG(nsContentUtils::ResistFingerprintingLog(), LogLevel::Debug,
   2742            ("Inside ShouldResistFingerprinting(nsIChannel*) for %s (PBM: %s)",
   2743             channelSpec.get(), isPBM ? "Yes" : "No"));
   2744  }
   2745 
   2746  nsCOMPtr<nsILoadInfo> loadInfo = aChannel->LoadInfo();
   2747  if (!loadInfo) {
   2748    MOZ_LOG(nsContentUtils::ResistFingerprintingLog(), LogLevel::Info,
   2749            ("Called nsContentUtils::ShouldResistFingerprinting(nsIChannel* "
   2750             "aChannel) but the channel's loadinfo was NULL"));
   2751    return ShouldResistFingerprinting("Null Object", aTarget);
   2752  }
   2753 
   2754  // With this check, we can ensure that the prefs and target say yes, so only
   2755  // an exemption would cause us to return false.
   2756  if (!ShouldResistFingerprinting_("Positive return check", isPBM, aTarget)) {
   2757    MOZ_LOG(nsContentUtils::ResistFingerprintingLog(), LogLevel::Debug,
   2758            ("Inside ShouldResistFingerprinting(nsIChannel*)"
   2759             " Positive return check said false (PBM: %s)",
   2760             isPBM ? "Yes" : "No"));
   2761    return false;
   2762  }
   2763 
   2764  if (ETPSaysShouldNotResistFingerprinting(aChannel, loadInfo)) {
   2765    MOZ_LOG(nsContentUtils::ResistFingerprintingLog(), LogLevel::Debug,
   2766            ("Inside ShouldResistFingerprinting(nsIChannel*)"
   2767             " ETPSaysShouldNotResistFingerprinting said false"));
   2768    return false;
   2769  }
   2770 
   2771  if (CookieJarSettingsSaysShouldResistFingerprinting(loadInfo)) {
   2772    MOZ_LOG(nsContentUtils::ResistFingerprintingLog(), LogLevel::Debug,
   2773            ("Inside ShouldResistFingerprinting(nsIChannel*)"
   2774             " CookieJarSettingsSaysShouldResistFingerprinting said true"));
   2775    return true;
   2776  }
   2777 
   2778  // Document types have no loading principal.  Subdocument types do have a
   2779  // loading principal, but it is the loading principal of the parent
   2780  // document; not the subdocument.
   2781  auto contentType = loadInfo->GetExternalContentPolicyType();
   2782  // Case 1: Document or Subdocument load
   2783  if (contentType == ExtContentPolicy::TYPE_DOCUMENT ||
   2784      contentType == ExtContentPolicy::TYPE_SUBDOCUMENT) {
   2785    nsCOMPtr<nsIURI> channelURI;
   2786    nsresult rv = NS_GetFinalChannelURI(aChannel, getter_AddRefs(channelURI));
   2787    MOZ_ASSERT(
   2788        NS_SUCCEEDED(rv),
   2789        "Failed to get URI in "
   2790        "nsContentUtils::ShouldResistFingerprinting(nsIChannel* aChannel)");
   2791    // this check is to ensure that we do not crash in non-debug builds.
   2792    if (NS_FAILED(rv)) {
   2793      return true;
   2794    }
   2795 
   2796    nsAutoCString loadingPrincipalSpec;
   2797    nsAutoCString channelSpec;
   2798    channelURI->GetSpec(channelSpec);
   2799 
   2800    if (contentType == ExtContentPolicy::TYPE_SUBDOCUMENT) {
   2801      MOZ_LOG_DEBUG_ONLY(
   2802          nsContentUtils::ResistFingerprintingLog(), LogLevel::Info,
   2803          ("Sub Document Type.  FinalChannelURI is %s, Loading Principal %s\n",
   2804           channelSpec.get(),
   2805           loadInfo->GetLoadingPrincipal()
   2806           ? loadInfo->GetLoadingPrincipal()->GetOrigin(loadingPrincipalSpec),
   2807           loadingPrincipalSpec.get() : "is NULL"));
   2808    } else {
   2809      MOZ_LOG_DEBUG_ONLY(
   2810          nsContentUtils::ResistFingerprintingLog(), LogLevel::Info,
   2811          ("Document Type.  FinalChannelURI is %s, Loading Principal %s\n",
   2812           channelSpec.get(),
   2813           loadInfo->GetLoadingPrincipal()
   2814           ? loadInfo->GetLoadingPrincipal()->GetOrigin(loadingPrincipalSpec),
   2815           loadingPrincipalSpec.get() : "is NULL"));
   2816    }
   2817 
   2818    return ShouldResistFingerprinting_dangerous(
   2819        channelURI, loadInfo->GetOriginAttributes(), "Internal Call", aTarget);
   2820  }
   2821 
   2822  // Case 2: Subresource Load
   2823  // Because this code is only used for subresource loads, this
   2824  // will check the parent's principal
   2825  nsIPrincipal* principal = loadInfo->GetLoadingPrincipal();
   2826 
   2827  MOZ_ASSERT_IF(principal && !principal->IsSystemPrincipal() &&
   2828                    !principal->GetIsAddonOrExpandedAddonPrincipal(),
   2829                BasePrincipal::Cast(principal)->OriginAttributesRef() ==
   2830                    loadInfo->GetOriginAttributes());
   2831  return ShouldResistFingerprinting_dangerous(principal, "Internal Call",
   2832                                              aTarget);
   2833 }
   2834 
   2835 /* static */
   2836 bool nsContentUtils::ShouldResistFingerprinting_dangerous(
   2837    nsIURI* aURI, const mozilla::OriginAttributes& aOriginAttributes,
   2838    const char* aJustification, RFPTarget aTarget) {
   2839  // With this check, we can ensure that the prefs and target say yes, so only
   2840  // an exemption would cause us to return false.
   2841  bool isPBM = aOriginAttributes.IsPrivateBrowsing();
   2842 
   2843  MOZ_LOG(nsContentUtils::ResistFingerprintingLog(), LogLevel::Debug,
   2844          ("Inside ShouldResistFingerprinting_dangerous(nsIURI*,"
   2845           " OriginAttributes) and the URI is %s  (PBM: %s)",
   2846           aURI->GetSpecOrDefault().get(), isPBM ? "Yes" : "No"));
   2847 
   2848  if (!ShouldResistFingerprinting_("Positive return check", isPBM, aTarget)) {
   2849    MOZ_LOG(nsContentUtils::ResistFingerprintingLog(), LogLevel::Debug,
   2850            ("Inside ShouldResistFingerprinting_dangerous(nsIURI*,"
   2851             " OriginAttributes) Positive return check said false (PBM: %s)",
   2852             isPBM ? "Yes" : "No"));
   2853    return false;
   2854  }
   2855 
   2856  MOZ_LOG(nsContentUtils::ResistFingerprintingLog(), LogLevel::Debug,
   2857          ("Inside ShouldResistFingerprinting_dangerous(nsIURI*,"
   2858           " OriginAttributes) and the URI is %s",
   2859           aURI->GetSpecOrDefault().get()));
   2860 
   2861  // Exclude internal schemes and web extensions
   2862  if (SchemeSaysShouldNotResistFingerprinting(aURI)) {
   2863    MOZ_LOG(nsContentUtils::ResistFingerprintingLog(), LogLevel::Debug,
   2864            ("Inside ShouldResistFingerprinting(nsIURI*)"
   2865             " SchemeSaysShouldNotResistFingerprinting said false"));
   2866    return false;
   2867  }
   2868 
   2869  bool isExemptDomain = false;
   2870  nsAutoCString list;
   2871  nsRFPService::GetExemptedDomainsLowercase(list);
   2872  isExemptDomain = IsURIInList(aURI, list);
   2873 
   2874  if (MOZ_LOG_TEST(nsContentUtils::ResistFingerprintingLog(),
   2875                   mozilla::LogLevel::Debug)) {
   2876    nsAutoCString url;
   2877    aURI->GetHost(url);
   2878    LogDomainAndList("URI", list, url, isExemptDomain);
   2879  }
   2880 
   2881  if (isExemptDomain) {
   2882    isExemptDomain &= PartionKeyIsAlsoExempted(aOriginAttributes);
   2883  }
   2884 
   2885  return !isExemptDomain;
   2886 }
   2887 
   2888 /* static */
   2889 bool nsContentUtils::ShouldResistFingerprinting_dangerous(
   2890    nsIPrincipal* aPrincipal, const char* aJustification, RFPTarget aTarget) {
   2891  if (!aPrincipal) {
   2892    MOZ_LOG(nsContentUtils::ResistFingerprintingLog(), LogLevel::Info,
   2893            ("Called nsContentUtils::ShouldResistFingerprinting(nsILoadInfo* "
   2894             "aChannel) but the loadinfo's loadingprincipal was NULL"));
   2895    return ShouldResistFingerprinting("Null object", aTarget);
   2896  }
   2897 
   2898  auto originAttributes =
   2899      BasePrincipal::Cast(aPrincipal)->OriginAttributesRef();
   2900  // With this check, we can ensure that the prefs and target say yes, so only
   2901  // an exemption would cause us to return false.
   2902  bool isPBM = originAttributes.IsPrivateBrowsing();
   2903 
   2904  if (MOZ_LOG_TEST(nsContentUtils::ResistFingerprintingLog(),
   2905                   mozilla::LogLevel::Debug)) {
   2906    nsAutoCString origin;
   2907    aPrincipal->GetOrigin(origin);
   2908    MOZ_LOG(
   2909        nsContentUtils::ResistFingerprintingLog(), LogLevel::Debug,
   2910        ("Inside ShouldResistFingerprinting(nsIPrincipal*) for %s (PBM: %s)",
   2911         origin.get(), isPBM ? "Yes" : "No"));
   2912  }
   2913 
   2914  if (!ShouldResistFingerprinting_("Positive return check", isPBM, aTarget)) {
   2915    MOZ_LOG(nsContentUtils::ResistFingerprintingLog(), LogLevel::Debug,
   2916            ("Inside ShouldResistFingerprinting(nsIPrincipal*) Positive return "
   2917             "check said false (PBM: %s)",
   2918             isPBM ? "Yes" : "No"));
   2919    return false;
   2920  }
   2921 
   2922  if (aPrincipal->IsSystemPrincipal()) {
   2923    MOZ_LOG(nsContentUtils::ResistFingerprintingLog(), LogLevel::Debug,
   2924            ("Inside ShouldResistFingerprinting(nsIPrincipal*) System "
   2925             "Principal said false"));
   2926    return false;
   2927  }
   2928 
   2929  // Exclude internal schemes and web extensions
   2930  if (SchemeSaysShouldNotResistFingerprinting(aPrincipal)) {
   2931    MOZ_LOG(nsContentUtils::ResistFingerprintingLog(), LogLevel::Debug,
   2932            ("Inside ShouldResistFingerprinting(nsIPrincipal*)"
   2933             " SchemeSaysShouldNotResistFingerprinting said false"));
   2934    return false;
   2935  }
   2936 
   2937  // Web extension principals are also excluded
   2938  if (BasePrincipal::Cast(aPrincipal)->AddonPolicyCore()) {
   2939    MOZ_LOG(nsContentUtils::ResistFingerprintingLog(), LogLevel::Debug,
   2940            ("Inside ShouldResistFingerprinting(nsIPrincipal*)"
   2941             " and AddonPolicy said false"));
   2942    return false;
   2943  }
   2944 
   2945  bool isExemptDomain = false;
   2946  nsAutoCString list;
   2947  nsRFPService::GetExemptedDomainsLowercase(list);
   2948  aPrincipal->IsURIInList(list, &isExemptDomain);
   2949 
   2950  if (MOZ_LOG_TEST(nsContentUtils::ResistFingerprintingLog(),
   2951                   mozilla::LogLevel::Debug)) {
   2952    nsAutoCString origin;
   2953    aPrincipal->GetOrigin(origin);
   2954    LogDomainAndList("URI", list, origin, isExemptDomain);
   2955  }
   2956 
   2957  if (isExemptDomain) {
   2958    isExemptDomain &= PartionKeyIsAlsoExempted(originAttributes);
   2959  }
   2960 
   2961  return !isExemptDomain;
   2962 }
   2963 
   2964 // --------------------------------------------------------------------
   2965 
   2966 /* static */
   2967 void nsContentUtils::CalcRoundedWindowSizeForResistingFingerprinting(
   2968    int32_t aChromeWidth, int32_t aChromeHeight, int32_t aScreenWidth,
   2969    int32_t aScreenHeight, int32_t aInputWidth, int32_t aInputHeight,
   2970    bool aSetOuterWidth, bool aSetOuterHeight, int32_t* aOutputWidth,
   2971    int32_t* aOutputHeight) {
   2972  MOZ_ASSERT(aOutputWidth);
   2973  MOZ_ASSERT(aOutputHeight);
   2974 
   2975  int32_t availContentWidth = 0;
   2976  int32_t availContentHeight = 0;
   2977 
   2978  availContentWidth = std::min(StaticPrefs::privacy_window_maxInnerWidth(),
   2979                               aScreenWidth - aChromeWidth);
   2980 #ifdef MOZ_WIDGET_GTK
   2981  // In the GTK window, it will not report outside system decorations
   2982  // when we get available window size, see Bug 581863. So, we leave a
   2983  // 40 pixels space for them when calculating the available content
   2984  // height. It is not necessary for the width since the content width
   2985  // is usually pretty much the same as the chrome width.
   2986  availContentHeight = std::min(StaticPrefs::privacy_window_maxInnerHeight(),
   2987                                (-40 + aScreenHeight) - aChromeHeight);
   2988 #else
   2989  availContentHeight = std::min(StaticPrefs::privacy_window_maxInnerHeight(),
   2990                                aScreenHeight - aChromeHeight);
   2991 #endif
   2992 
   2993  // Ideally, we'd like to round window size to 1000x1000, but the
   2994  // screen space could be too small to accommodate this size in some
   2995  // cases. If it happens, we would round the window size to the nearest
   2996  // 200x100.
   2997  availContentWidth = availContentWidth - (availContentWidth % 200);
   2998  availContentHeight = availContentHeight - (availContentHeight % 100);
   2999 
   3000  // If aIsOuter is true, we are setting the outer window. So we
   3001  // have to consider the chrome UI.
   3002  int32_t chromeOffsetWidth = aSetOuterWidth ? aChromeWidth : 0;
   3003  int32_t chromeOffsetHeight = aSetOuterHeight ? aChromeHeight : 0;
   3004  int32_t resultWidth = 0, resultHeight = 0;
   3005 
   3006  // if the original size is greater than the maximum available size, we set
   3007  // it to the maximum size. And if the original value is less than the
   3008  // minimum rounded size, we set it to the minimum 200x100.
   3009  if (aInputWidth > (availContentWidth + chromeOffsetWidth)) {
   3010    resultWidth = availContentWidth + chromeOffsetWidth;
   3011  } else if (aInputWidth < (200 + chromeOffsetWidth)) {
   3012    resultWidth = 200 + chromeOffsetWidth;
   3013  } else {
   3014    // Otherwise, we round the window to the nearest upper rounded 200x100.
   3015    resultWidth = NSToIntCeil((aInputWidth - chromeOffsetWidth) / 200.0) * 200 +
   3016                  chromeOffsetWidth;
   3017  }
   3018 
   3019  if (aInputHeight > (availContentHeight + chromeOffsetHeight)) {
   3020    resultHeight = availContentHeight + chromeOffsetHeight;
   3021  } else if (aInputHeight < (100 + chromeOffsetHeight)) {
   3022    resultHeight = 100 + chromeOffsetHeight;
   3023  } else {
   3024    resultHeight =
   3025        NSToIntCeil((aInputHeight - chromeOffsetHeight) / 100.0) * 100 +
   3026        chromeOffsetHeight;
   3027  }
   3028 
   3029  *aOutputWidth = resultWidth;
   3030  *aOutputHeight = resultHeight;
   3031 }
   3032 
   3033 bool nsContentUtils::ShouldRoundWindowSizeForResistingFingerprinting() {
   3034  return !(
   3035      Preferences::GetBool("privacy.resistFingerprinting.letterboxing",
   3036                           false) &&
   3037      // We want to round window size at least once in the browser's life time:
   3038      // AppWindow::ForceRoundedDimensions() will set this preference to true.
   3039      Preferences::GetBool(
   3040          "privacy.resistFingerprinting.letterboxing.didForceSize", false) &&
   3041      Preferences::GetBool(
   3042          "privacy.resistFingerprinting.letterboxing.rememberSize", false));
   3043 }
   3044 
   3045 bool nsContentUtils::ThreadsafeIsCallerChrome() {
   3046  return NS_IsMainThread() ? IsCallerChrome()
   3047                           : IsCurrentThreadRunningChromeWorker();
   3048 }
   3049 
   3050 bool nsContentUtils::IsCallerUAWidget() {
   3051  JSContext* cx = GetCurrentJSContext();
   3052  if (!cx) {
   3053    return false;
   3054  }
   3055 
   3056  JS::Realm* realm = JS::GetCurrentRealmOrNull(cx);
   3057  if (!realm) {
   3058    return false;
   3059  }
   3060 
   3061  return xpc::IsUAWidgetScope(realm);
   3062 }
   3063 
   3064 bool nsContentUtils::IsSystemCaller(JSContext* aCx) {
   3065  // Note that SubjectPrincipal() assumes we are in a compartment here.
   3066  return SubjectPrincipal(aCx) == sSystemPrincipal;
   3067 }
   3068 
   3069 bool nsContentUtils::ThreadsafeIsSystemCaller(JSContext* aCx) {
   3070  CycleCollectedJSContext* ccjscx = CycleCollectedJSContext::Get();
   3071  MOZ_ASSERT(ccjscx->Context() == aCx);
   3072 
   3073  return ccjscx->IsSystemCaller();
   3074 }
   3075 
   3076 // static
   3077 bool nsContentUtils::LookupBindingMember(
   3078    JSContext* aCx, nsIContent* aContent, JS::Handle<jsid> aId,
   3079    JS::MutableHandle<JS::PropertyDescriptor> aDesc) {
   3080  return true;
   3081 }
   3082 
   3083 nsINode* nsContentUtils::GetNearestInProcessCrossDocParentNode(
   3084    nsINode* aChild) {
   3085  if (aChild->IsDocument()) {
   3086    for (BrowsingContext* bc = aChild->AsDocument()->GetBrowsingContext(); bc;
   3087         bc = bc->GetParent()) {
   3088      if (bc->GetEmbedderElement()) {
   3089        return bc->GetEmbedderElement();
   3090      }
   3091    }
   3092    return nullptr;
   3093  }
   3094 
   3095  nsINode* parent = aChild->GetParentNode();
   3096  if (parent && parent->IsContent() && aChild->IsContent()) {
   3097    parent = aChild->AsContent()->GetFlattenedTreeParent();
   3098  }
   3099 
   3100  return parent;
   3101 }
   3102 
   3103 bool nsContentUtils::ContentIsHostIncludingDescendantOf(
   3104    const nsINode* aPossibleDescendant, const nsINode* aPossibleAncestor) {
   3105  MOZ_ASSERT(aPossibleDescendant, "The possible descendant is null!");
   3106  MOZ_ASSERT(aPossibleAncestor, "The possible ancestor is null!");
   3107 
   3108  while (true) {
   3109    if (aPossibleDescendant == aPossibleAncestor) {
   3110      return true;
   3111    }
   3112    if (nsINode* parent = aPossibleDescendant->GetParentNode()) {
   3113      aPossibleDescendant = parent;
   3114      continue;
   3115    }
   3116    if (auto* df = DocumentFragment::FromNode(aPossibleDescendant)) {
   3117      if (nsINode* host = df->GetHost()) {
   3118        aPossibleDescendant = host;
   3119        continue;
   3120      }
   3121    }
   3122    break;
   3123  }
   3124  return false;
   3125 }
   3126 
   3127 // static
   3128 bool nsContentUtils::ContentIsCrossDocDescendantOf(nsINode* aPossibleDescendant,
   3129                                                   nsINode* aPossibleAncestor) {
   3130  MOZ_ASSERT(aPossibleDescendant, "The possible descendant is null!");
   3131  MOZ_ASSERT(aPossibleAncestor, "The possible ancestor is null!");
   3132 
   3133  do {
   3134    if (aPossibleDescendant == aPossibleAncestor) {
   3135      return true;
   3136    }
   3137 
   3138    aPossibleDescendant =
   3139        GetNearestInProcessCrossDocParentNode(aPossibleDescendant);
   3140  } while (aPossibleDescendant);
   3141 
   3142  return false;
   3143 }
   3144 
   3145 // static
   3146 bool nsContentUtils::ContentIsFlattenedTreeDescendantOf(
   3147    const nsINode* aPossibleDescendant, const nsINode* aPossibleAncestor) {
   3148  MOZ_ASSERT(aPossibleDescendant, "The possible descendant is null!");
   3149  MOZ_ASSERT(aPossibleAncestor, "The possible ancestor is null!");
   3150 
   3151  do {
   3152    if (aPossibleDescendant == aPossibleAncestor) {
   3153      return true;
   3154    }
   3155    aPossibleDescendant = aPossibleDescendant->GetFlattenedTreeParentNode();
   3156  } while (aPossibleDescendant);
   3157 
   3158  return false;
   3159 }
   3160 
   3161 // static
   3162 bool nsContentUtils::ContentIsFlattenedTreeDescendantOfForStyle(
   3163    const nsINode* aPossibleDescendant, const nsINode* aPossibleAncestor) {
   3164  MOZ_ASSERT(aPossibleDescendant, "The possible descendant is null!");
   3165  MOZ_ASSERT(aPossibleAncestor, "The possible ancestor is null!");
   3166 
   3167  do {
   3168    if (aPossibleDescendant == aPossibleAncestor) {
   3169      return true;
   3170    }
   3171    aPossibleDescendant =
   3172        aPossibleDescendant->GetFlattenedTreeParentNodeForStyle();
   3173  } while (aPossibleDescendant);
   3174 
   3175  return false;
   3176 }
   3177 
   3178 // static
   3179 nsINode* nsContentUtils::Retarget(nsINode* aTargetA, const nsINode* aTargetB) {
   3180  while (true && aTargetA) {
   3181    // If A's root is not a shadow root...
   3182    nsINode* root = aTargetA->SubtreeRoot();
   3183    if (!root->IsShadowRoot()) {
   3184      // ...then return A.
   3185      return aTargetA;
   3186    }
   3187 
   3188    // or A's root is a shadow-including inclusive ancestor of B...
   3189    if (aTargetB && aTargetB->IsShadowIncludingInclusiveDescendantOf(root)) {
   3190      // ...then return A.
   3191      return aTargetA;
   3192    }
   3193 
   3194    aTargetA = ShadowRoot::FromNode(root)->GetHost();
   3195  }
   3196 
   3197  return nullptr;
   3198 }
   3199 
   3200 // static
   3201 Element* nsContentUtils::GetAnElementForTiming(Element* aTarget,
   3202                                               const Document* aDocument,
   3203                                               nsIGlobalObject* aGlobal) {
   3204  if (!aTarget->IsInComposedDoc()) {
   3205    return nullptr;
   3206  }
   3207 
   3208  if (!aDocument) {
   3209    nsCOMPtr<nsPIDOMWindowInner> inner = do_QueryInterface(aGlobal);
   3210    if (!inner) {
   3211      return nullptr;
   3212    }
   3213    aDocument = inner->GetExtantDoc();
   3214  }
   3215 
   3216  MOZ_ASSERT(aDocument);
   3217 
   3218  if (aTarget->GetUncomposedDocOrConnectedShadowRoot() != aDocument ||
   3219      !aDocument->IsCurrentActiveDocument()) {
   3220    return nullptr;
   3221  }
   3222 
   3223  return aTarget;
   3224 }
   3225 
   3226 // static
   3227 nsresult nsContentUtils::GetInclusiveAncestors(nsINode* aNode,
   3228                                               nsTArray<nsINode*>& aArray) {
   3229  while (aNode) {
   3230    aArray.AppendElement(aNode);
   3231    aNode = aNode->GetParentNode();
   3232  }
   3233  return NS_OK;
   3234 }
   3235 
   3236 // static
   3237 template <typename GetParentFunc, typename ComputeChildIndexFunc>
   3238 nsresult static GetInclusiveAncestorsAndOffsetsHelper(
   3239    nsINode* aNode, uint32_t aOffset, nsTArray<nsIContent*>& aAncestorNodes,
   3240    nsTArray<Maybe<uint32_t>>& aAncestorOffsets, GetParentFunc aGetParentFunc,
   3241    ComputeChildIndexFunc aComputeChildIndexFunc) {
   3242  NS_ENSURE_ARG_POINTER(aNode);
   3243 
   3244  if (!aNode->IsContent()) {
   3245    return NS_ERROR_FAILURE;
   3246  }
   3247  nsIContent* content = aNode->AsContent();
   3248 
   3249  if (!aAncestorNodes.IsEmpty()) {
   3250    NS_WARNING("aAncestorNodes is not empty");
   3251    aAncestorNodes.Clear();
   3252  }
   3253 
   3254  if (!aAncestorOffsets.IsEmpty()) {
   3255    NS_WARNING("aAncestorOffsets is not empty");
   3256    aAncestorOffsets.Clear();
   3257  }
   3258 
   3259  // insert the node itself
   3260  aAncestorNodes.AppendElement(content);
   3261  aAncestorOffsets.AppendElement(Some(aOffset));
   3262 
   3263  // insert all the ancestors
   3264  nsIContent* child = content;
   3265  nsIContent* parent = aGetParentFunc(child);
   3266  while (parent) {
   3267    aAncestorNodes.AppendElement(parent->AsContent());
   3268    aAncestorOffsets.AppendElement(aComputeChildIndexFunc(parent, child));
   3269    child = parent;
   3270    parent = aGetParentFunc(child);
   3271  }
   3272 
   3273  return NS_OK;
   3274 }
   3275 
   3276 nsresult nsContentUtils::GetInclusiveAncestorsAndOffsets(
   3277    nsINode* aNode, uint32_t aOffset, nsTArray<nsIContent*>& aAncestorNodes,
   3278    nsTArray<Maybe<uint32_t>>& aAncestorOffsets) {
   3279  return GetInclusiveAncestorsAndOffsetsHelper(
   3280      aNode, aOffset, aAncestorNodes, aAncestorOffsets,
   3281      [](nsIContent* aContent) { return aContent->GetParent(); },
   3282      [](nsIContent* aParent, nsIContent* aChild) {
   3283        return aParent->ComputeIndexOf(aChild);
   3284      });
   3285 }
   3286 
   3287 nsresult nsContentUtils::GetFlattenedTreeAncestorsAndOffsets(
   3288    nsINode* aNode, uint32_t aOffset, nsTArray<nsIContent*>& aAncestorNodes,
   3289    nsTArray<Maybe<uint32_t>>& aAncestorOffsets) {
   3290  return GetInclusiveAncestorsAndOffsetsHelper(
   3291      aNode, aOffset, aAncestorNodes, aAncestorOffsets,
   3292      [](nsIContent* aContent) -> nsIContent* {
   3293        return nsIContent::FromNodeOrNull(
   3294            GetParentFuncForComparison<TreeKind::Flat>(aContent));
   3295      },
   3296      [](nsIContent* aParent, nsIContent* aChild) {
   3297        return aParent->ComputeFlatTreeIndexOf(aChild);
   3298      });
   3299 }
   3300 
   3301 /* static */
   3302 nsINode* nsContentUtils::GetCommonAncestorHelper(nsINode* aNode1,
   3303                                                 nsINode* aNode2) {
   3304  MOZ_ASSERT(aNode1);
   3305  MOZ_ASSERT(aNode2);
   3306  return CommonAncestors(*aNode1, *aNode2, GetParentNode)
   3307      .GetClosestCommonAncestor();
   3308 }
   3309 
   3310 /* static */
   3311 nsINode* nsContentUtils::GetClosestCommonShadowIncludingInclusiveAncestor(
   3312    nsINode* aNode1, nsINode* aNode2) {
   3313  if (aNode1 == aNode2) {
   3314    return aNode1;
   3315  }
   3316 
   3317  MOZ_ASSERT(aNode1);
   3318  MOZ_ASSERT(aNode2);
   3319  return CommonAncestors(*aNode1, *aNode2, GetParentOrShadowHostNode)
   3320      .GetClosestCommonAncestor();
   3321 }
   3322 
   3323 /* static */
   3324 nsIContent* nsContentUtils::GetCommonFlattenedTreeAncestorHelper(
   3325    nsIContent* aContent1, nsIContent* aContent2) {
   3326  MOZ_ASSERT(aContent1);
   3327  MOZ_ASSERT(aContent2);
   3328  return CommonAncestors(*aContent1, *aContent2, GetFlattenedTreeParent)
   3329      .GetClosestCommonAncestor();
   3330 }
   3331 
   3332 /* static */
   3333 nsINode* nsContentUtils::GetCommonFlattenedTreeAncestorForSelection(
   3334    nsINode* aNode1, nsINode* aNode2) {
   3335  if (aNode1 == aNode2) {
   3336    return aNode1;
   3337  }
   3338  MOZ_ASSERT(aNode1);
   3339  MOZ_ASSERT(aNode2);
   3340  return CommonAncestors(*aNode1, *aNode2,
   3341                         GetFlattenedTreeParentNodeForSelection)
   3342      .GetClosestCommonAncestor();
   3343 }
   3344 
   3345 /* static */
   3346 Element* nsContentUtils::GetCommonFlattenedTreeAncestorForStyle(
   3347    Element* aElement1, Element* aElement2) {
   3348  MOZ_ASSERT(aElement1);
   3349  MOZ_ASSERT(aElement2);
   3350  return CommonAncestors(*aElement1, *aElement2,
   3351                         GetFlattenedTreeParentElementForStyle)
   3352      .GetClosestCommonAncestor();
   3353 }
   3354 
   3355 /* static */
   3356 template <TreeKind aKind, typename Dummy>
   3357 Maybe<int32_t> nsContentUtils::CompareChildNodes(
   3358    const nsINode* aChild1, const nsINode* aChild2,
   3359    NodeIndexCache* aIndexCache /* = nullptr */) {
   3360  if (MOZ_UNLIKELY(
   3361          (aChild1 && (NS_WARN_IF(aChild1->IsRootOfNativeAnonymousSubtree()) ||
   3362                       NS_WARN_IF(aChild1->IsDocumentFragment()))) ||
   3363          (aChild2 && (NS_WARN_IF(aChild2->IsRootOfNativeAnonymousSubtree()) ||
   3364                       NS_WARN_IF(aChild2->IsDocumentFragment()))))) {
   3365    return Nothing();
   3366  }
   3367  if (MOZ_UNLIKELY(aChild1 == aChild2)) {
   3368    return Some(0);
   3369  }
   3370  MOZ_ASSERT(aChild1 || aChild2);
   3371  if (!aChild1) {  // i.e., end of parent vs aChild2
   3372    MOZ_ASSERT(aChild2->GetParentOrShadowHostNode());
   3373    return Some(1);
   3374  }
   3375  if (!aChild2) {  // i.e., aChild1 vs. end of parent
   3376    MOZ_ASSERT(aChild1->GetParentOrShadowHostNode());
   3377    return Some(-1);
   3378  }
   3379 
   3380  if constexpr (aKind == TreeKind::Flat) {
   3381    if (AreNodesInSameSlot(aChild1, aChild2)) {
   3382      // They differ at slot, so use their position in slot
   3383      const auto* slot = aChild1->AsContent()->GetAssignedSlot();
   3384      MOZ_ASSERT(slot);
   3385 
   3386      constexpr auto NoIndex = size_t(-1);
   3387      auto child1Index = NoIndex;
   3388      auto child2Index = NoIndex;
   3389      size_t index = 0;
   3390      for (nsINode* node : slot->AssignedNodes()) {
   3391        if (node == aChild1) {
   3392          child1Index = index;
   3393          if (child2Index != NoIndex) {
   3394            break;
   3395          }
   3396        } else if (node == aChild2) {
   3397          child2Index = index;
   3398          if (child1Index != NoIndex) {
   3399            break;
   3400          }
   3401        }
   3402        index++;
   3403      }
   3404 
   3405      MOZ_ASSERT(child1Index != NoIndex);
   3406      MOZ_ASSERT(child2Index != NoIndex);
   3407 
   3408      return Some(child1Index < child2Index ? -1 : 1);
   3409    }
   3410  }
   3411 
   3412  MOZ_ASSERT(aChild1->GetParentOrShadowHostNode());
   3413  const nsINode& commonParentNode = *aChild1->GetParentOrShadowHostNode();
   3414  MOZ_ASSERT(aChild2->GetParentOrShadowHostNode() == &commonParentNode);
   3415  if (aChild1->GetNextSibling() == aChild2) {
   3416    return Some(-1);
   3417  }
   3418  if (aChild2->GetNextSibling() == aChild1) {
   3419    return Some(1);
   3420  }
   3421  MOZ_ASSERT(commonParentNode.GetFirstChild());
   3422  const nsIContent& firstChild = *commonParentNode.GetFirstChild();
   3423  if (aChild1 == &firstChild) {
   3424    return Some(-1);
   3425  }
   3426  if (aChild2 == &firstChild) {
   3427    return Some(1);
   3428  }
   3429  MOZ_ASSERT(commonParentNode.GetLastChild());
   3430  const nsIContent& lastChild = *commonParentNode.GetLastChild();
   3431  MOZ_ASSERT(&firstChild != &lastChild);
   3432  if (aChild1 == &lastChild) {
   3433    return Some(1);
   3434  }
   3435  if (aChild2 == &lastChild) {
   3436    return Some(-1);
   3437  }
   3438 
   3439  // nsINode::ComputeIndexOf() computes the index with scanning siblings from
   3440  // the first child in the worst case.  However, if the node caches the
   3441  // computed result, the scan range may be narrowed in fortunate cases.
   3442  // Therefore, if the node uses the cache, we should use it because the callers
   3443  // may need to compute the index again.  In such cases, the cache saves the
   3444  // computation cost.
   3445  if (commonParentNode.MaybeCachesComputedIndex()) {
   3446    Maybe<int32_t> child1Index;
   3447    Maybe<int32_t> child2Index;
   3448    if (aIndexCache) {
   3449      aIndexCache->ComputeIndicesOf<aKind>(&commonParentNode, aChild1, aChild2,
   3450                                           child1Index, child2Index);
   3451    } else {
   3452      child1Index = commonParentNode.ComputeIndexOf(aChild1);
   3453      child2Index = commonParentNode.ComputeIndexOf(aChild2);
   3454    }
   3455    if (MOZ_LIKELY(child1Index.isSome() && child2Index.isSome())) {
   3456      MOZ_ASSERT(*child1Index != *child2Index);
   3457      return Some(*child1Index < *child2Index ? -1 : 1);
   3458    }
   3459    // XXX Keep the odd traditional behavior for now.
   3460    return Some(child1Index.isNothing() && child2Index.isSome() ? -1 : 1);
   3461  }
   3462 
   3463  // On the other hand, it does not use the cache, let's scan siblings starting
   3464  // from aChild1. Then, if we find aChild2, the position is aChild1 < aChild2.
   3465  // Otherwise, aChild2 < aChild1.  This narrows the scanning range than using
   3466  // ComputeIndexOf(), i.e., even in the worst case, this is faster than a call
   3467  // of ComputeIndexOf().
   3468  for (const nsIContent* followingSiblingOfChild1 = aChild1->GetNextSibling();
   3469       followingSiblingOfChild1;
   3470       followingSiblingOfChild1 = followingSiblingOfChild1->GetNextSibling()) {
   3471    if (followingSiblingOfChild1 == aChild2) {
   3472      return Some(-1);
   3473    }
   3474  }
   3475  MOZ_ASSERT(aChild2->ComputeIndexInParentNode().isSome());
   3476  return Some(1);
   3477 }
   3478 
   3479 /* static */
   3480 template <TreeKind aKind, typename Dummy>
   3481 Maybe<int32_t> nsContentUtils::CompareClosestCommonAncestorChildren(
   3482    const nsINode& aParent, const nsINode* aChild1, const nsINode* aChild2,
   3483    nsContentUtils::NodeIndexCache* aIndexCache) {
   3484  MOZ_ASSERT_IF(aChild1, GetParentOrShadowHostNode(aChild1));
   3485  MOZ_ASSERT_IF(aChild2, GetParentOrShadowHostNode(aChild2));
   3486 
   3487  if (aChild1 && aChild2) {
   3488    if (MOZ_UNLIKELY(aChild1->IsShadowRoot())) {
   3489      // Shadow roots come before light DOM per
   3490      // https://dom.spec.whatwg.org/#concept-shadow-including-tree-order
   3491      MOZ_ASSERT(!aChild2->IsShadowRoot(), "Two shadow roots?");
   3492      return Some(-1);
   3493    }
   3494    if (MOZ_UNLIKELY(aChild2->IsShadowRoot())) {
   3495      return Some(1);
   3496    }
   3497  }
   3498  // FIXME: bug 1946001, bug 1946003 and bug 1946008.
   3499  if (MOZ_UNLIKELY((aChild1 && (aChild1->IsRootOfNativeAnonymousSubtree() ||
   3500                                aChild1->IsDocumentFragment())) ||
   3501                   (aChild2 && (aChild2->IsRootOfNativeAnonymousSubtree() ||
   3502                                aChild2->IsDocumentFragment())))) {
   3503    // XXX Keep the odd traditional behavior for now.  I think that we should
   3504    // return Nothing in this case.
   3505    return Some(1);
   3506  }
   3507  const Maybe<int32_t> comp =
   3508      nsContentUtils::CompareChildNodes<aKind>(aChild1, aChild2, aIndexCache);
   3509  if (MOZ_UNLIKELY(comp.isNothing())) {
   3510    NS_ASSERTION(comp.isSome(),
   3511                 "nsContentUtils::CompareChildNodes() must return Some here. "
   3512                 "It should've already checked before we call it.");
   3513    // XXX Keep the odd traditional behavior for now.  I think that we should
   3514    // return Nothing in this case.
   3515    return Some(1);
   3516  }
   3517  MOZ_ASSERT_IF(!*comp, aChild1 == aChild2);
   3518  MOZ_ASSERT_IF(*comp < 0 && !AreNodesInSameSlot(aChild1, aChild2),
   3519                (aChild1 ? *aChild1->ComputeIndexInParentNode()
   3520                         : aParent.GetChildCount()) <
   3521                    (aChild2 ? *aChild2->ComputeIndexInParentNode()
   3522                             : aParent.GetChildCount()));
   3523  MOZ_ASSERT_IF(*comp > 0 && !AreNodesInSameSlot(aChild1, aChild2),
   3524                (aChild2 ? *aChild2->ComputeIndexInParentNode()
   3525                         : aParent.GetChildCount()) <
   3526                    (aChild1 ? *aChild1->ComputeIndexInParentNode()
   3527                             : aParent.GetChildCount()));
   3528  return comp;
   3529 }
   3530 
   3531 /* static */
   3532 template <TreeKind aKind, typename Dummy>
   3533 Maybe<int32_t> nsContentUtils::CompareChildOffsetAndChildNode(
   3534    uint32_t aOffset1, const nsINode& aChild2,
   3535    NodeIndexCache* aIndexCache /* = nullptr */) {
   3536  if (NS_WARN_IF(aChild2.IsRootOfNativeAnonymousSubtree()) ||
   3537      NS_WARN_IF(aChild2.IsDocumentFragment())) {
   3538    return Nothing();
   3539  }
   3540  const nsINode* parentNode = GetParentFuncForComparison<aKind>(&aChild2);
   3541  MOZ_ASSERT(parentNode);
   3542 
   3543  const uint32_t parentNodeChildCount = [parentNode]() -> uint32_t {
   3544    if constexpr (aKind == TreeKind::Flat) {
   3545      if (const HTMLSlotElement* slot = HTMLSlotElement::FromNode(parentNode)) {
   3546        return slot->AssignedNodes().Length();
   3547      }
   3548    }
   3549    return parentNode->GetChildCount();
   3550  }();
   3551 
   3552  if (aOffset1 >= parentNodeChildCount) {
   3553    return Some(1);
   3554  }
   3555 
   3556 #ifdef DEBUG
   3557  bool isFlatAndSlotted = false;
   3558  if constexpr (aKind == TreeKind::Flat) {
   3559    if (const HTMLSlotElement* slot = HTMLSlotElement::FromNode(parentNode)) {
   3560      MOZ_ASSERT(!slot->AssignedNodes().IsEmpty());
   3561      isFlatAndSlotted = true;
   3562    }
   3563  }
   3564  if (!isFlatAndSlotted) {
   3565    MOZ_ASSERT(parentNode->GetFirstChild());
   3566  }
   3567 #endif
   3568 
   3569  const nsIContent& firstChild = [parentNode]() -> const nsIContent& {
   3570    if constexpr (aKind == TreeKind::Flat) {
   3571      if (const HTMLSlotElement* slot = HTMLSlotElement::FromNode(parentNode)) {
   3572        MOZ_ASSERT(slot->AssignedNodes()[0]->IsContent());
   3573        return *(slot->AssignedNodes()[0]->AsContent());
   3574      }
   3575    }
   3576    return *parentNode->GetFirstChild();
   3577  }();
   3578 
   3579  if (&aChild2 == &firstChild) {
   3580    return Some(!aOffset1 ? 0 : 1);
   3581  }
   3582 
   3583  MOZ_ASSERT_IF(!isFlatAndSlotted, parentNode->GetLastChild());
   3584  const nsIContent& lastChild = [parentNode]() -> const nsIContent& {
   3585    if constexpr (aKind == TreeKind::Flat) {
   3586      if (const HTMLSlotElement* slot = HTMLSlotElement::FromNode(parentNode)) {
   3587        auto assigned = slot->AssignedNodes();
   3588        return *assigned[assigned.Length() - 1]->AsContent();
   3589      }
   3590    }
   3591 
   3592    return *parentNode->GetLastChild();
   3593  }();
   3594 
   3595  MOZ_ASSERT(&firstChild != &lastChild);
   3596  if (&aChild2 == &lastChild) {
   3597    return Some(aOffset1 == parentNodeChildCount - 1 ? 0 : -1);
   3598  }
   3599  const Maybe<int32_t> child2Index =
   3600      aIndexCache ? aIndexCache->ComputeIndexOf<aKind>(parentNode, &aChild2)
   3601                  : GetIndexInParent<aKind>(parentNode, &aChild2);
   3602  if (NS_WARN_IF(child2Index.isNothing())) {
   3603    return Some(1);
   3604  }
   3605  return Some(aOffset1 == uint32_t(*child2Index)
   3606                  ? 0
   3607                  : (aOffset1 < uint32_t(*child2Index) ? -1 : 1));
   3608 }
   3609 
   3610 /* static */
   3611 template <TreeKind aKind, typename Dummy>
   3612 Maybe<int32_t> nsContentUtils::CompareChildNodeAndChildOffset(
   3613    const nsINode& aChild1, uint32_t aOffset2,
   3614    NodeIndexCache* aIndexCache /* = nullptr */) {
   3615  Maybe<int32_t> comp =
   3616      CompareChildOffsetAndChildNode<aKind>(aOffset2, aChild1);
   3617  if (comp.isNothing()) {
   3618    return comp;
   3619  }
   3620  return Some(*comp * -1);
   3621 }
   3622 
   3623 /* static */
   3624 template <TreeKind aKind, typename Dummy>
   3625 Maybe<int32_t> nsContentUtils::ComparePointsWithIndices(
   3626    const nsINode* aParent1, uint32_t aOffset1, const nsINode* aParent2,
   3627    uint32_t aOffset2, NodeIndexCache* aIndexCache) {
   3628  MOZ_ASSERT(aParent1);
   3629  MOZ_ASSERT(aParent2);
   3630 
   3631  if (aParent1 == aParent2) {
   3632    return Some(aOffset1 < aOffset2 ? -1 : (aOffset1 > aOffset2 ? 1 : 0));
   3633  }
   3634 
   3635  const CommonAncestors commonAncestors(*aParent1, *aParent2,
   3636                                        GetParentFuncForComparison<aKind>);
   3637 
   3638  if (MOZ_UNLIKELY(!commonAncestors.GetClosestCommonAncestor())) {
   3639    return Nothing();
   3640  }
   3641 
   3642  const nsINode* closestCommonAncestorChild1 =
   3643      commonAncestors.GetClosestCommonAncestorChild1();
   3644  const nsINode* closestCommonAncestorChild2 =
   3645      commonAncestors.GetClosestCommonAncestorChild2();
   3646  MOZ_ASSERT(closestCommonAncestorChild1 != closestCommonAncestorChild2);
   3647  commonAncestors
   3648      .template WarnIfClosestCommonAncestorChildrenAreNotInChildList<aKind>();
   3649  if (closestCommonAncestorChild1 && closestCommonAncestorChild2) {
   3650    return CompareClosestCommonAncestorChildren<aKind>(
   3651        *commonAncestors.GetClosestCommonAncestor(),
   3652        closestCommonAncestorChild1, closestCommonAncestorChild2, aIndexCache);
   3653  }
   3654 
   3655  if (closestCommonAncestorChild2) {
   3656    MOZ_ASSERT(GetParentFuncForComparison<aKind>(closestCommonAncestorChild2) ==
   3657               aParent1);
   3658    if (aParent1->GetShadowRoot() == closestCommonAncestorChild2) {
   3659      // Comparing a shadow host with its shadow root.
   3660      // We consider: [aParent1, 0] < closestCommonAncestorChild2 < [aParent1,
   3661      // 1]
   3662      return aOffset1 > 0 ? Some(1) : Some(-1);
   3663    }
   3664 
   3665    // FIXME: bug 1946001, bug 1946003 and bug 1946008.
   3666    if (MOZ_UNLIKELY(
   3667            closestCommonAncestorChild2->IsRootOfNativeAnonymousSubtree() ||
   3668            closestCommonAncestorChild2->IsDocumentFragment())) {
   3669      // XXX Keep the odd traditional behavior for now.
   3670      return Some(1);
   3671    }
   3672    const Maybe<int32_t> comp =
   3673        nsContentUtils::CompareChildOffsetAndChildNode<aKind>(
   3674            aOffset1, *closestCommonAncestorChild2, aIndexCache);
   3675    if (NS_WARN_IF(comp.isNothing())) {
   3676      NS_ASSERTION(
   3677          comp.isSome(),
   3678          "nsContentUtils::CompareChildOffsetAndChildNode() must return Some "
   3679          "here. It should've already checked before we call it.");
   3680      // XXX Keep the odd traditional behavior for now.
   3681      return Some(1);
   3682    }
   3683    // If the child of the closest common ancestor is at aOffset1 of aParent1,
   3684    // it means that aOffset2 of aParent2 refers a descendant of the child.
   3685    // So, aOffset2 of aParent2 refers a descendant of aOffset1 of aParent1.
   3686    if (!*comp) {
   3687      return Some(-1);
   3688    }
   3689    return comp;
   3690  }
   3691 
   3692  if (aParent2->GetShadowRoot() == closestCommonAncestorChild1) {
   3693    // Comparing a shadow host with its shadow root.
   3694    // We consider: [aParent2, 0] < closestCommonAncestorChild1 < [aParent2, 1]
   3695    return aOffset2 > 0 ? Some(-1) : Some(1);
   3696  }
   3697 
   3698  MOZ_ASSERT(closestCommonAncestorChild1);
   3699  MOZ_ASSERT(GetParentFuncForComparison<aKind>(closestCommonAncestorChild1) ==
   3700             aParent2);
   3701  // FIXME: bug 1946001, bug 1946003 and bug 1946008.
   3702  if (MOZ_UNLIKELY(
   3703          closestCommonAncestorChild1->IsRootOfNativeAnonymousSubtree() ||
   3704          closestCommonAncestorChild1->IsDocumentFragment())) {
   3705    // XXX Keep the odd traditional behavior for now.
   3706    return Some(-1);
   3707  }
   3708  const Maybe<int32_t> comp =
   3709      nsContentUtils::CompareChildNodeAndChildOffset<aKind>(
   3710          *closestCommonAncestorChild1, aOffset2, aIndexCache);
   3711  if (NS_WARN_IF(comp.isNothing())) {
   3712    NS_ASSERTION(comp.isSome(),
   3713                 "nsContentUtils::CompareChildOffsetAndChildNode() must return "
   3714                 "Some here. It should've already checked before we call it.");
   3715    // XXX Keep the odd traditional behavior for now.
   3716    return Some(-1);
   3717  }
   3718  // If the child of the closet common ancestor is at aOffset2 of aParent2, it
   3719  // means that aOffset1 of aParent1 refers a descendant of the child.
   3720  // So, aOffset1 of aParent1 refers a descendant of aOffset2 of aParent2.
   3721  if (!*comp) {
   3722    return Some(1);
   3723  }
   3724  return comp;
   3725 }
   3726 
   3727 /* static */
   3728 BrowserParent* nsContentUtils::GetCommonBrowserParentAncestor(
   3729    BrowserParent* aBrowserParent1, BrowserParent* aBrowserParent2) {
   3730  MOZ_ASSERT(aBrowserParent1);
   3731  MOZ_ASSERT(aBrowserParent2);
   3732  return CommonAncestors(*aBrowserParent1, *aBrowserParent2,
   3733                         GetParentBrowserParent)
   3734      .GetClosestCommonAncestor();
   3735 }
   3736 
   3737 /* static */
   3738 Element* nsContentUtils::GetTargetElement(Document* aDocument,
   3739                                          const nsAString& aAnchorName) {
   3740  MOZ_ASSERT(aDocument);
   3741 
   3742  if (aAnchorName.IsEmpty()) {
   3743    return nullptr;
   3744  }
   3745  // 1. If there is an element in the document tree that has an ID equal to
   3746  //    fragment, then return the first such element in tree order.
   3747  if (Element* el = aDocument->GetElementById(aAnchorName)) {
   3748    return el;
   3749  }
   3750 
   3751  // 2. If there is an a element in the document tree that has a name
   3752  // attribute whose value is equal to fragment, then return the first such
   3753  // element in tree order.
   3754  //
   3755  // FIXME(emilio): Why the different code-paths for HTML and non-HTML docs?
   3756  if (aDocument->IsHTMLDocument()) {
   3757    nsCOMPtr<nsINodeList> list = aDocument->GetElementsByName(aAnchorName);
   3758    // Loop through the named nodes looking for the first anchor
   3759    uint32_t length = list->Length();
   3760    for (uint32_t i = 0; i < length; i++) {
   3761      nsIContent* node = list->Item(i);
   3762      if (node->IsHTMLElement(nsGkAtoms::a)) {
   3763        return node->AsElement();
   3764      }
   3765    }
   3766  } else {
   3767    constexpr auto nameSpace = u"http://www.w3.org/1999/xhtml"_ns;
   3768    // Get the list of anchor elements
   3769    nsCOMPtr<nsINodeList> list =
   3770        aDocument->GetElementsByTagNameNS(nameSpace, u"a"_ns);
   3771    // Loop through the anchors looking for the first one with the given name.
   3772    for (uint32_t i = 0; true; i++) {
   3773      nsIContent* node = list->Item(i);
   3774      if (!node) {  // End of list
   3775        break;
   3776      }
   3777 
   3778      // Compare the name attribute
   3779      if (node->AsElement()->AttrValueIs(kNameSpaceID_None, nsGkAtoms::name,
   3780                                         aAnchorName, eCaseMatters)) {
   3781        return node->AsElement();
   3782      }
   3783    }
   3784  }
   3785 
   3786  // 3. Return null.
   3787  return nullptr;
   3788 }
   3789 
   3790 /* static */
   3791 template <TreeKind aKind, typename PT1, typename RT1, typename PT2,
   3792          typename RT2, typename Dummy>
   3793 Maybe<int32_t> nsContentUtils::ComparePoints(
   3794    const RangeBoundaryBase<PT1, RT1>& aBoundary1,
   3795    const RangeBoundaryBase<PT2, RT2>& aBoundary2,
   3796    NodeIndexCache* aIndexCache /* = nullptr */) {
   3797  if (!aBoundary1.IsSet() || !aBoundary2.IsSet()) {
   3798    return Nothing{};
   3799  }
   3800  MOZ_ASSERT(aBoundary1.GetTreeKind() == aBoundary2.GetTreeKind());
   3801 
   3802  const auto kValidOrInvalidOffsets1 =
   3803      RangeBoundaryBase<PT1, RT1>::OffsetFilter::kValidOrInvalidOffsets;
   3804  const auto kValidOrInvalidOffsets2 =
   3805      RangeBoundaryBase<PT2, RT2>::OffsetFilter::kValidOrInvalidOffsets;
   3806 
   3807  // RangeBoundaryBase instances may be initialized only with a child node or an
   3808  // offset in the container.  If both instances have computed offset, we can
   3809  // use ComparePointsWithIndices() which works with offsets.
   3810  if (aBoundary1.HasOffset() && aBoundary2.HasOffset()) {
   3811    return ComparePointsWithIndices<aKind>(
   3812        aBoundary1.GetContainer(), *aBoundary1.Offset(kValidOrInvalidOffsets1),
   3813        aBoundary2.GetContainer(), *aBoundary2.Offset(kValidOrInvalidOffsets2),
   3814        aIndexCache);
   3815  }
   3816 
   3817  // Otherwise, i.e., at least one RangeBoundaryBase stores the child node.
   3818  // In the most cases, RangeBoundaryBase has it, so, the worst scenario here
   3819  // is, one of the boundaries comes from a StaticRange or is initialized with
   3820  // offset and RangeBoundaryIsMutationObserved::No.  However, for making it
   3821  // faster in the most cases, we should compare the child nodes without
   3822  // offsets if possible.
   3823 
   3824  // If we're comparing children in the same container, we don't need to compute
   3825  // common ancestors.  So, we can skip it and just compare the children.
   3826  if (aBoundary1.GetContainer() == aBoundary2.GetContainer()) {
   3827    const nsIContent* const child1 = aBoundary1.GetChildAtOffset();
   3828    const nsIContent* const child2 = aBoundary2.GetChildAtOffset();
   3829    return CompareClosestCommonAncestorChildren<aKind>(
   3830        *aBoundary1.GetContainer(), child1, child2, aIndexCache);
   3831  }
   3832 
   3833  // Otherwise, we need to compare the common ancestor children which is the
   3834  // most distant different inclusive ancestors of the containers.  So, the
   3835  // following implementation is similar to ComparePointsWithIndices(), but we
   3836  // don't have offset, so, we cannot use offset when we compare the boundaries
   3837  // whose one is a descendant of the other.
   3838  const CommonAncestors commonAncestors(*aBoundary1.GetContainer(),
   3839                                        *aBoundary2.GetContainer(),
   3840                                        GetParentFuncForComparison<aKind>);
   3841 
   3842  if (MOZ_UNLIKELY(!commonAncestors.GetClosestCommonAncestor())) {
   3843    return Nothing();
   3844  }
   3845  MOZ_ASSERT(commonAncestors.GetClosestCommonAncestor());
   3846 
   3847  const nsINode* closestCommonAncestorChild1 =
   3848      commonAncestors.GetClosestCommonAncestorChild1();
   3849  const nsINode* closestCommonAncestorChild2 =
   3850      commonAncestors.GetClosestCommonAncestorChild2();
   3851  commonAncestors
   3852      .template WarnIfClosestCommonAncestorChildrenAreNotInChildList<aKind>();
   3853  MOZ_ASSERT(closestCommonAncestorChild1 != closestCommonAncestorChild2);
   3854  if (closestCommonAncestorChild1 && closestCommonAncestorChild2) {
   3855    return CompareClosestCommonAncestorChildren<aKind>(
   3856        *commonAncestors.GetClosestCommonAncestor(),
   3857        closestCommonAncestorChild1, closestCommonAncestorChild2, aIndexCache);
   3858  }
   3859 
   3860  if (closestCommonAncestorChild2) {
   3861    MOZ_ASSERT(closestCommonAncestorChild2->GetParentOrShadowHostNode() ==
   3862               aBoundary1.GetContainer());
   3863    // FIXME: bug 1946001, bug 1946003 and bug 1946008.
   3864    if (MOZ_UNLIKELY(
   3865            closestCommonAncestorChild2->IsRootOfNativeAnonymousSubtree() ||
   3866            closestCommonAncestorChild2->IsDocumentFragment())) {
   3867      // XXX Keep the odd traditional behavior for now.
   3868      return Some(1);
   3869    }
   3870    const Maybe<int32_t> comp = nsContentUtils::CompareChildNodes<aKind>(
   3871        aBoundary1.GetChildAtOffset(), closestCommonAncestorChild2,
   3872        aIndexCache);
   3873    if (NS_WARN_IF(comp.isNothing())) {
   3874      NS_ASSERTION(
   3875          comp.isSome(),
   3876          "nsContentUtils::CompareChildOffsetAndChildNode() must return Some "
   3877          "here. It should've already checked before we call it.");
   3878      // XXX Keep the odd traditional behavior for now.
   3879      return Some(1);
   3880    }
   3881    // If the child of the closest common ancestor is at aBoundary1, it means
   3882    // that aBoundary2 refers a descendant of the child. So, aBoundary2 refers a
   3883    // descendant of aBoundary1.
   3884    if (!*comp) {
   3885      MOZ_ASSERT(*closestCommonAncestorChild2->ComputeIndexInParentNode() ==
   3886                 *aBoundary1.Offset(kValidOrInvalidOffsets1));
   3887      return Some(-1);
   3888    }
   3889    MOZ_ASSERT_IF(*comp < 0,
   3890                  *aBoundary1.Offset(kValidOrInvalidOffsets1) <
   3891                      *closestCommonAncestorChild2->ComputeIndexInParentNode());
   3892    MOZ_ASSERT_IF(*comp > 0,
   3893                  *closestCommonAncestorChild2->ComputeIndexInParentNode() <
   3894                      *aBoundary1.Offset(kValidOrInvalidOffsets1));
   3895    return comp;
   3896  }
   3897 
   3898  MOZ_ASSERT(closestCommonAncestorChild1);
   3899  MOZ_ASSERT(closestCommonAncestorChild1->GetParentOrShadowHostNode() ==
   3900             aBoundary2.GetContainer());
   3901  // FIXME: bug 1946001, bug 1946003 and bug 1946008.
   3902  if (MOZ_UNLIKELY(
   3903          closestCommonAncestorChild1->IsRootOfNativeAnonymousSubtree() ||
   3904          closestCommonAncestorChild1->IsDocumentFragment())) {
   3905    // XXX Keep the odd traditional behavior for now.
   3906    return Some(-1);
   3907  }
   3908  const Maybe<int32_t> comp = nsContentUtils::CompareChildNodes<aKind>(
   3909      closestCommonAncestorChild1, aBoundary2.GetChildAtOffset(), aIndexCache);
   3910  if (NS_WARN_IF(comp.isNothing())) {
   3911    NS_ASSERTION(comp.isSome(),
   3912                 "nsContentUtils::CompareChildOffsetAndChildNode() must return "
   3913                 "Some here. It should've already checked before we call it.");
   3914    // XXX Keep the odd traditional behavior for now.
   3915    return Some(-1);
   3916  }
   3917  // If the child of the closet common ancestor is at aBoundary2, it means that
   3918  // aBoundary1 refers a descendant of the child. So, aBoundary1 refers a
   3919  // descendant of aBoundary2.
   3920  if (!*comp) {
   3921    MOZ_ASSERT(*closestCommonAncestorChild1->ComputeIndexInParentNode() ==
   3922               *aBoundary2.Offset(kValidOrInvalidOffsets2));
   3923    return Some(1);
   3924  }
   3925  MOZ_ASSERT_IF(*comp < 0,
   3926                *closestCommonAncestorChild1->ComputeIndexInParentNode() <
   3927                    *aBoundary2.Offset(kValidOrInvalidOffsets2));
   3928  MOZ_ASSERT_IF(*comp > 0,
   3929                *aBoundary2.Offset(kValidOrInvalidOffsets2) <
   3930                    *closestCommonAncestorChild1->ComputeIndexInParentNode());
   3931  return comp;
   3932 }
   3933 
   3934 inline bool IsCharInSet(const char* aSet, const char16_t aChar) {
   3935  char16_t ch;
   3936  while ((ch = *aSet)) {
   3937    if (aChar == char16_t(ch)) {
   3938      return true;
   3939    }
   3940    ++aSet;
   3941  }
   3942  return false;
   3943 }
   3944 
   3945 /**
   3946 * This method strips leading/trailing chars, in given set, from string.
   3947 */
   3948 
   3949 // static
   3950 const nsDependentSubstring nsContentUtils::TrimCharsInSet(
   3951    const char* aSet, const nsAString& aValue) {
   3952  nsAString::const_iterator valueCurrent, valueEnd;
   3953 
   3954  aValue.BeginReading(valueCurrent);
   3955  aValue.EndReading(valueEnd);
   3956 
   3957  // Skip characters in the beginning
   3958  while (valueCurrent != valueEnd) {
   3959    if (!IsCharInSet(aSet, *valueCurrent)) {
   3960      break;
   3961    }
   3962    ++valueCurrent;
   3963  }
   3964 
   3965  if (valueCurrent != valueEnd) {
   3966    for (;;) {
   3967      --valueEnd;
   3968      if (!IsCharInSet(aSet, *valueEnd)) {
   3969        break;
   3970      }
   3971    }
   3972    ++valueEnd;  // Step beyond the last character we want in the value.
   3973  }
   3974 
   3975  // valueEnd should point to the char after the last to copy
   3976  return Substring(valueCurrent, valueEnd);
   3977 }
   3978 
   3979 /**
   3980 * This method strips leading and trailing whitespace from a string.
   3981 */
   3982 
   3983 // static
   3984 template <bool IsWhitespace(char16_t)>
   3985 const nsDependentSubstring nsContentUtils::TrimWhitespace(const nsAString& aStr,
   3986                                                          bool aTrimTrailing) {
   3987  nsAString::const_iterator start, end;
   3988 
   3989  aStr.BeginReading(start);
   3990  aStr.EndReading(end);
   3991 
   3992  // Skip whitespace characters in the beginning
   3993  while (start != end && IsWhitespace(*start)) {
   3994    ++start;
   3995  }
   3996 
   3997  if (aTrimTrailing) {
   3998    // Skip whitespace characters in the end.
   3999    while (end != start) {
   4000      --end;
   4001 
   4002      if (!IsWhitespace(*end)) {
   4003        // Step back to the last non-whitespace character.
   4004        ++end;
   4005 
   4006        break;
   4007      }
   4008    }
   4009  }
   4010 
   4011  // Return a substring for the string w/o leading and/or trailing
   4012  // whitespace
   4013 
   4014  return Substring(start, end);
   4015 }
   4016 
   4017 // Declaring the templates we are going to use avoid linking issues without
   4018 // inlining the method. Considering there is not so much spaces checking
   4019 // methods we can consider this to be better than inlining.
   4020 template const nsDependentSubstring
   4021 nsContentUtils::TrimWhitespace<nsCRT::IsAsciiSpace>(const nsAString&, bool);
   4022 template const nsDependentSubstring nsContentUtils::TrimWhitespace<
   4023    nsContentUtils::IsHTMLWhitespace>(const nsAString&, bool);
   4024 template const nsDependentSubstring nsContentUtils::TrimWhitespace<
   4025    nsContentUtils::IsHTMLWhitespaceOrNBSP>(const nsAString&, bool);
   4026 
   4027 static inline void KeyAppendSep(nsACString& aKey) {
   4028  if (!aKey.IsEmpty()) {
   4029    aKey.Append('>');
   4030  }
   4031 }
   4032 
   4033 static inline void KeyAppendString(const nsAString& aString, nsACString& aKey) {
   4034  KeyAppendSep(aKey);
   4035 
   4036  // Could escape separator here if collisions happen.  > is not a legal char
   4037  // for a name or type attribute, so we should be safe avoiding that extra
   4038  // work.
   4039 
   4040  AppendUTF16toUTF8(aString, aKey);
   4041 }
   4042 
   4043 static inline void KeyAppendString(const nsACString& aString,
   4044                                   nsACString& aKey) {
   4045  KeyAppendSep(aKey);
   4046 
   4047  // Could escape separator here if collisions happen.  > is not a legal char
   4048  // for a name or type attribute, so we should be safe avoiding that extra
   4049  // work.
   4050 
   4051  aKey.Append(aString);
   4052 }
   4053 
   4054 static inline void KeyAppendInt(int32_t aInt, nsACString& aKey) {
   4055  KeyAppendSep(aKey);
   4056 
   4057  aKey.AppendInt(aInt);
   4058 }
   4059 
   4060 static inline bool IsAutocompleteOff(const nsIContent* aContent) {
   4061  return aContent->IsElement() &&
   4062         aContent->AsElement()->AttrValueIs(kNameSpaceID_None,
   4063                                            nsGkAtoms::autocomplete, u"off"_ns,
   4064                                            eIgnoreCase);
   4065 }
   4066 
   4067 /*static*/
   4068 void nsContentUtils::GenerateStateKey(nsIContent* aContent, Document* aDocument,
   4069                                      nsACString& aKey) {
   4070  MOZ_ASSERT(aContent);
   4071 
   4072  aKey.Truncate();
   4073 
   4074  uint32_t partID = aDocument ? aDocument->GetPartID() : 0;
   4075 
   4076  // Don't capture state for anonymous content
   4077  if (aContent->IsInNativeAnonymousSubtree()) {
   4078    return;
   4079  }
   4080 
   4081  if (IsAutocompleteOff(aContent)) {
   4082    return;
   4083  }
   4084 
   4085  RefPtr<Document> doc = aContent->GetUncomposedDoc();
   4086 
   4087  KeyAppendInt(partID, aKey);  // first append a partID
   4088  bool generatedUniqueKey = false;
   4089 
   4090  if (doc && doc->IsHTMLOrXHTML()) {
   4091    nsHTMLDocument* htmlDoc = doc->AsHTMLDocument();
   4092 
   4093    // If we have a form control and can calculate form information, use that
   4094    // as the key - it is more reliable than just recording position in the
   4095    // DOM.
   4096    // XXXbz Is it, really?  We have bugs on this, I think...
   4097    // Important to have a unique key, and tag/type/name may not be.
   4098    //
   4099    // The format of the key depends on whether the control has a form,
   4100    // and whether the element was parser inserted:
   4101    //
   4102    // [Has Form, Parser Inserted]:
   4103    //   fp>type>FormNum>IndOfControlInForm>FormName>name
   4104    //
   4105    // [No Form, Parser Inserted]:
   4106    //   dp>type>ControlNum>name
   4107    //
   4108    // [Has Form, Not Parser Inserted]:
   4109    //   fn>type>IndOfFormInDoc>IndOfControlInForm>FormName>name
   4110    //
   4111    // [No Form, Not Parser Inserted]:
   4112    //   dn>type>IndOfControlInDoc>name
   4113    //
   4114    // XXX We don't need to use index if name is there
   4115    // XXXbz We don't?  Why not?  I don't follow.
   4116    //
   4117    if (const auto* control = nsIFormControl::FromNode(aContent)) {
   4118      // Get the control number if this was a parser inserted element from the
   4119      // network.
   4120      int32_t controlNumber =
   4121          control->GetParserInsertedControlNumberForStateKey();
   4122      bool parserInserted = controlNumber != -1;
   4123 
   4124      RefPtr<nsContentList> htmlForms;
   4125      RefPtr<nsContentList> htmlFormControls;
   4126      if (!parserInserted) {
   4127        // Getting these lists is expensive, as we need to keep them up to date
   4128        // as the document loads, so we avoid it if we don't need them.
   4129        htmlDoc->GetFormsAndFormControls(getter_AddRefs(htmlForms),
   4130                                         getter_AddRefs(htmlFormControls));
   4131      }
   4132 
   4133      // Append the control type
   4134      KeyAppendInt(int32_t(control->ControlType()), aKey);
   4135 
   4136      // If in a form, add form name / index of form / index in form
   4137      HTMLFormElement* formElement = control->GetForm();
   4138      if (formElement) {
   4139        if (IsAutocompleteOff(formElement)) {
   4140          aKey.Truncate();
   4141          return;
   4142        }
   4143 
   4144        // Append the form number, if this is a parser inserted control, or
   4145        // the index of the form in the document otherwise.
   4146        bool appendedForm = false;
   4147        if (parserInserted) {
   4148          MOZ_ASSERT(formElement->GetFormNumberForStateKey() != -1,
   4149                     "when generating a state key for a parser inserted form "
   4150                     "control we should have a parser inserted <form> element");
   4151          KeyAppendString("fp"_ns, aKey);
   4152          KeyAppendInt(formElement->GetFormNumberForStateKey(), aKey);
   4153          appendedForm = true;
   4154        } else {
   4155          KeyAppendString("fn"_ns, aKey);
   4156          int32_t index = htmlForms->IndexOf(formElement, false);
   4157          if (index <= -1) {
   4158            //
   4159            // XXX HACK this uses some state that was dumped into the document
   4160            // specifically to fix bug 138892.  What we are trying to do is
   4161            // *guess* which form this control's state is found in, with the
   4162            // highly likely guess that the highest form parsed so far is the
   4163            // one. This code should not be on trunk, only branch.
   4164            //
   4165            index = htmlDoc->GetNumFormsSynchronous() - 1;
   4166          }
   4167          if (index > -1) {
   4168            KeyAppendInt(index, aKey);
   4169            appendedForm = true;
   4170          }
   4171        }
   4172 
   4173        if (appendedForm) {
   4174          // Append the index of the control in the form
   4175          int32_t index = formElement->IndexOfContent(aContent);
   4176 
   4177          if (index > -1) {
   4178            KeyAppendInt(index, aKey);
   4179            generatedUniqueKey = true;
   4180          }
   4181        }
   4182 
   4183        // Append the form name
   4184        nsAutoString formName;
   4185        formElement->GetAttr(nsGkAtoms::name, formName);
   4186        KeyAppendString(formName, aKey);
   4187      } else {
   4188        // Not in a form.  Append the control number, if this is a parser
   4189        // inserted control, or the index of the control in the document
   4190        // otherwise.
   4191        if (parserInserted) {
   4192          KeyAppendString("dp"_ns, aKey);
   4193          KeyAppendInt(control->GetParserInsertedControlNumberForStateKey(),
   4194                       aKey);
   4195          generatedUniqueKey = true;
   4196        } else {
   4197          KeyAppendString("dn"_ns, aKey);
   4198          int32_t index = htmlFormControls->IndexOf(aContent, true);
   4199          if (index > -1) {
   4200            KeyAppendInt(index, aKey);
   4201            generatedUniqueKey = true;
   4202          }
   4203        }
   4204 
   4205        // Append the control name
   4206        nsAutoString name;
   4207        aContent->AsElement()->GetAttr(nsGkAtoms::name, name);
   4208        KeyAppendString(name, aKey);
   4209      }
   4210    }
   4211  }
   4212 
   4213  if (!generatedUniqueKey) {
   4214    // Either we didn't have a form control or we aren't in an HTML document so
   4215    // we can't figure out form info.  Append the tag name if it's an element
   4216    // to avoid restoring state for one type of element on another type.
   4217    if (aContent->IsElement()) {
   4218      KeyAppendString(nsDependentAtomString(aContent->NodeInfo()->NameAtom()),
   4219                      aKey);
   4220    } else {
   4221      // Append a character that is not "d" or "f" to disambiguate from
   4222      // the case when we were a form control in an HTML document.
   4223      KeyAppendString("o"_ns, aKey);
   4224    }
   4225 
   4226    // Now start at aContent and append the indices of it and all its ancestors
   4227    // in their containers.  That should at least pin down its position in the
   4228    // DOM...
   4229    nsINode* parent = aContent->GetParentNode();
   4230    nsINode* content = aContent;
   4231    while (parent) {
   4232      if (content->IsShadowRoot()) {
   4233        KeyAppendString("s"_ns, aKey);
   4234      } else {
   4235        KeyAppendInt(parent->ComputeIndexOf_Deprecated(content), aKey);
   4236      }
   4237      content = parent;
   4238      parent = content->GetParentOrShadowHostNode();
   4239    }
   4240  }
   4241 }
   4242 
   4243 // static
   4244 nsIPrincipal* nsContentUtils::SubjectPrincipal(JSContext* aCx) {
   4245  MOZ_ASSERT(NS_IsMainThread());
   4246 
   4247  // As opposed to SubjectPrincipal(), we do in fact assume that
   4248  // we're in a realm here; anyone who calls this function in
   4249  // situations where that's not the case is doing it wrong.
   4250  JS::Realm* realm = js::GetContextRealm(aCx);
   4251  MOZ_ASSERT(realm);
   4252 
   4253  JSPrincipals* principals = JS::GetRealmPrincipals(realm);
   4254  return nsJSPrincipals::get(principals);
   4255 }
   4256 
   4257 // static
   4258 nsIPrincipal* nsContentUtils::SubjectPrincipal() {
   4259  MOZ_ASSERT(IsInitialized());
   4260  MOZ_ASSERT(NS_IsMainThread());
   4261  JSContext* cx = GetCurrentJSContext();
   4262  if (!cx) {
   4263    MOZ_CRASH(
   4264        "Accessing the Subject Principal without an AutoJSAPI on the stack is "
   4265        "forbidden");
   4266  }
   4267 
   4268  JS::Realm* realm = js::GetContextRealm(cx);
   4269 
   4270  // When an AutoJSAPI is instantiated, we are in a null realm until the
   4271  // first JSAutoRealm, which is kind of a purgatory as far as permissions
   4272  // go. It would be nice to just hard-abort if somebody does a security check
   4273  // in this purgatory zone, but that would be too fragile, since it could be
   4274  // triggered by random IsCallerChrome() checks 20-levels deep.
   4275  //
   4276  // So we want to return _something_ here - and definitely not the System
   4277  // Principal, since that would make an AutoJSAPI a very dangerous thing to
   4278  // instantiate.
   4279  //
   4280  // The natural thing to return is a null principal. Ideally, we'd return a
   4281  // different null principal each time, to avoid any unexpected interactions
   4282  // when the principal accidentally gets inherited somewhere. But
   4283  // SubjectPrincipal doesn't return strong references, so there's no way to
   4284  // sanely manage the lifetime of multiple null principals.
   4285  //
   4286  // So we use a singleton null principal. To avoid it being accidentally
   4287  // inherited and becoming a "real" subject or object principal, we do a
   4288  // release-mode assert during realm creation against using this principal on
   4289  // an actual global.
   4290  if (!realm) {
   4291    return sNullSubjectPrincipal;
   4292  }
   4293 
   4294  return SubjectPrincipal(cx);
   4295 }
   4296 
   4297 // static
   4298 nsIPrincipal* nsContentUtils::ObjectPrincipal(JSObject* aObj) {
   4299 #ifdef DEBUG
   4300  JS::AssertObjectBelongsToCurrentThread(aObj);
   4301 #endif
   4302 
   4303  MOZ_DIAGNOSTIC_ASSERT(!js::IsCrossCompartmentWrapper(aObj));
   4304 
   4305  JS::Realm* realm = js::GetNonCCWObjectRealm(aObj);
   4306  JSPrincipals* principals = JS::GetRealmPrincipals(realm);
   4307  return nsJSPrincipals::get(principals);
   4308 }
   4309 
   4310 // static
   4311 nsresult nsContentUtils::NewURIWithDocumentCharset(nsIURI** aResult,
   4312                                                   const nsAString& aSpec,
   4313                                                   Document* aDocument,
   4314                                                   nsIURI* aBaseURI) {
   4315  if (aDocument) {
   4316    return NS_NewURI(aResult, aSpec, aDocument->GetDocumentCharacterSet(),
   4317                     aBaseURI);
   4318  }
   4319  return NS_NewURI(aResult, aSpec, nullptr, aBaseURI);
   4320 }
   4321 
   4322 // static
   4323 bool nsContentUtils::ContainsChar(nsAtom* aAtom, char aChar) {
   4324  const uint32_t len = aAtom->GetLength();
   4325  if (!len) {
   4326    return false;
   4327  }
   4328  const char16_t* name = aAtom->GetUTF16String();
   4329  uint32_t i = 0;
   4330  while (i < len) {
   4331    if (name[i] == aChar) {
   4332      return true;
   4333    }
   4334    i++;
   4335  }
   4336  return false;
   4337 }
   4338 
   4339 // static
   4340 bool nsContentUtils::IsNameWithDash(nsAtom* aName) {
   4341  // A valid custom element name is a sequence of characters name which
   4342  // must match the PotentialCustomElementName production:
   4343  // PotentialCustomElementName ::= [a-z] (PCENChar)* '-' (PCENChar)*
   4344  const char16_t* name = aName->GetUTF16String();
   4345  uint32_t len = aName->GetLength();
   4346  bool hasDash = false;
   4347 
   4348  // A string name is a valid custom element name if all of the following are
   4349  // true:
   4350 
   4351  // name's 0th code point is an ASCII lower alpha;
   4352  if (!len || name[0] < 'a' || name[0] > 'z') {
   4353    return false;
   4354  }
   4355 
   4356  if (StaticPrefs::dom_custom_elements_relaxed_names_enabled()) {
   4357    uint32_t i = 1;
   4358    while (i < len) {
   4359      // name does not contain any ASCII upper alphas
   4360      if (name[i] >= 'A' && name[i] <= 'Z') {
   4361        return false;
   4362      }
   4363 
   4364      // name is a valid element local name;
   4365      //
   4366      //   // https://dom.spec.whatwg.org/#valid-element-local-name
   4367      //   Step 2.1.
   4368      //   If name contains ASCII whitespace, U+0000 NULL, U+002F (/), or U+003E
   4369      //   (>), then return false.
   4370      //
   4371      //   ASCII whitespace is U+0009 TAB, U+000A LF, U+000C FF, U+000D CR, or
   4372      //   U+0020 SPACE.
   4373      if (name[i] == 0x0000 ||  // null
   4374          name[i] == 0x0009 ||  // tab
   4375          name[i] == 0x000A ||  // newline
   4376          name[i] == 0x000C ||  // form feed
   4377          name[i] == 0x000D ||  // carriage return
   4378          name[i] == 0x0020 ||  // space
   4379          name[i] == 0x002F ||  // /
   4380          name[i] == 0x003E) {  // >
   4381        return false;
   4382      }
   4383 
   4384      if (name[i] == '-') {
   4385        hasDash = true;
   4386      }
   4387 
   4388      i++;
   4389    }
   4390    return hasDash;
   4391  }
   4392 
   4393  uint32_t i = 1;
   4394  while (i < len) {
   4395    if (i + 1 < len && NS_IS_SURROGATE_PAIR(name[i], name[i + 1])) {
   4396      // Merged two 16-bit surrogate pairs into code point.
   4397      char32_t code = SURROGATE_TO_UCS4(name[i], name[i + 1]);
   4398 
   4399      if (code < 0x10000 || code > 0xEFFFF) {
   4400        return false;
   4401      }
   4402 
   4403      i += 2;
   4404    } else {
   4405      if (name[i] == '-') {
   4406        hasDash = true;
   4407      }
   4408 
   4409      if (name[i] != '-' && name[i] != '.' && name[i] != '_' &&
   4410          name[i] != 0xB7 && (name[i] < '0' || name[i] > '9') &&
   4411          (name[i] < 'a' || name[i] > 'z') &&
   4412          (name[i] < 0xC0 || name[i] > 0xD6) &&
   4413          (name[i] < 0xF8 || name[i] > 0x37D) &&
   4414          (name[i] < 0x37F || name[i] > 0x1FFF) &&
   4415          (name[i] < 0x200C || name[i] > 0x200D) &&
   4416          (name[i] < 0x203F || name[i] > 0x2040) &&
   4417          (name[i] < 0x2070 || name[i] > 0x218F) &&
   4418          (name[i] < 0x2C00 || name[i] > 0x2FEF) &&
   4419          (name[i] < 0x3001 || name[i] > 0xD7FF) &&
   4420          (name[i] < 0xF900 || name[i] > 0xFDCF) &&
   4421          (name[i] < 0xFDF0 || name[i] > 0xFFFD)) {
   4422        return false;
   4423      }
   4424 
   4425      i++;
   4426    }
   4427  }
   4428 
   4429  return hasDash;
   4430 }
   4431 
   4432 // static
   4433 bool nsContentUtils::IsCustomElementName(nsAtom* aName, uint32_t aNameSpaceID) {
   4434  // Allow non-dashed names in XUL for XBL to Custom Element migrations.
   4435  if (aNameSpaceID == kNameSpaceID_XUL) {
   4436    return true;
   4437  }
   4438 
   4439  bool hasDash = IsNameWithDash(aName);
   4440  if (!hasDash) {
   4441    return false;
   4442  }
   4443 
   4444  // The custom element name must not be one of the following values:
   4445  //  annotation-xml
   4446  //  color-profile
   4447  //  font-face
   4448  //  font-face-src
   4449  //  font-face-uri
   4450  //  font-face-format
   4451  //  font-face-name
   4452  //  missing-glyph
   4453  return aName != nsGkAtoms::annotation_xml &&
   4454         aName != nsGkAtoms::color_profile && aName != nsGkAtoms::font_face &&
   4455         aName != nsGkAtoms::font_face_src &&
   4456         aName != nsGkAtoms::font_face_uri &&
   4457         aName != nsGkAtoms::font_face_format &&
   4458         aName != nsGkAtoms::font_face_name && aName != nsGkAtoms::missingGlyph;
   4459 }
   4460 
   4461 // static
   4462 nsresult nsContentUtils::CheckQName(const nsAString& aQualifiedName,
   4463                                    bool aNamespaceAware,
   4464                                    const char16_t** aColon) {
   4465  const char* colon = nullptr;
   4466  const char16_t* begin = aQualifiedName.BeginReading();
   4467  const char16_t* end = aQualifiedName.EndReading();
   4468 
   4469  int result = MOZ_XMLCheckQName(reinterpret_cast<const char*>(begin),
   4470                                 reinterpret_cast<const char*>(end),
   4471                                 aNamespaceAware, &colon);
   4472 
   4473  if (!result) {
   4474    if (aColon) {
   4475      *aColon = reinterpret_cast<const char16_t*>(colon);
   4476    }
   4477 
   4478    return NS_OK;
   4479  }
   4480 
   4481  return NS_ERROR_DOM_INVALID_CHARACTER_ERR;
   4482 }
   4483 
   4484 // static
   4485 nsresult nsContentUtils::SplitQName(const nsIContent* aNamespaceResolver,
   4486                                    const nsString& aQName, int32_t* aNamespace,
   4487                                    nsAtom** aLocalName) {
   4488  const char16_t* colon;
   4489  nsresult rv = nsContentUtils::CheckQName(aQName, true, &colon);
   4490  NS_ENSURE_SUCCESS(rv, rv);
   4491 
   4492  if (colon) {
   4493    const char16_t* end;
   4494    aQName.EndReading(end);
   4495    nsAutoString nameSpace;
   4496    rv = aNamespaceResolver->LookupNamespaceURIInternal(
   4497        Substring(aQName.get(), colon), nameSpace);
   4498    NS_ENSURE_SUCCESS(rv, rv);
   4499 
   4500    *aNamespace = nsNameSpaceManager::GetInstance()->GetNameSpaceID(
   4501        nameSpace, nsContentUtils::IsChromeDoc(aNamespaceResolver->OwnerDoc()));
   4502    if (*aNamespace == kNameSpaceID_Unknown) return NS_ERROR_FAILURE;
   4503 
   4504    *aLocalName = NS_AtomizeMainThread(Substring(colon + 1, end)).take();
   4505  } else {
   4506    *aNamespace = kNameSpaceID_None;
   4507    *aLocalName = NS_AtomizeMainThread(aQName).take();
   4508  }
   4509  NS_ENSURE_TRUE(aLocalName, NS_ERROR_OUT_OF_MEMORY);
   4510  return NS_OK;
   4511 }
   4512 
   4513 // static
   4514 nsresult nsContentUtils::GetNodeInfoFromQName(
   4515    const nsAString& aNamespaceURI, const nsAString& aQualifiedName,
   4516    nsNodeInfoManager* aNodeInfoManager, uint16_t aNodeType,
   4517    mozilla::dom::NodeInfo** aNodeInfo) {
   4518  const nsString& qName = PromiseFlatString(aQualifiedName);
   4519  const char16_t* colon;
   4520  nsresult rv = nsContentUtils::CheckQName(qName, true, &colon);
   4521  NS_ENSURE_SUCCESS(rv, rv);
   4522 
   4523  int32_t nsID;
   4524  nsNameSpaceManager::GetInstance()->RegisterNameSpace(aNamespaceURI, nsID);
   4525  if (colon) {
   4526    const char16_t* end;
   4527    qName.EndReading(end);
   4528 
   4529    RefPtr<nsAtom> prefix = NS_AtomizeMainThread(Substring(qName.get(), colon));
   4530 
   4531    rv = aNodeInfoManager->GetNodeInfo(Substring(colon + 1, end), prefix, nsID,
   4532                                       aNodeType, aNodeInfo);
   4533  } else {
   4534    rv = aNodeInfoManager->GetNodeInfo(aQualifiedName, nullptr, nsID, aNodeType,
   4535                                       aNodeInfo);
   4536  }
   4537  NS_ENSURE_SUCCESS(rv, rv);
   4538 
   4539  return nsContentUtils::IsValidNodeName((*aNodeInfo)->NameAtom(),
   4540                                         (*aNodeInfo)->GetPrefixAtom(),
   4541                                         (*aNodeInfo)->NamespaceID())
   4542             ? NS_OK
   4543             : NS_ERROR_DOM_NAMESPACE_ERR;
   4544 }
   4545 
   4546 // static
   4547 void nsContentUtils::SplitExpatName(const char16_t* aExpatName,
   4548                                    nsAtom** aPrefix, nsAtom** aLocalName,
   4549                                    int32_t* aNameSpaceID) {
   4550  /**
   4551   *  Expat can send the following:
   4552   *    localName
   4553   *    namespaceURI<separator>localName
   4554   *    namespaceURI<separator>localName<separator>prefix
   4555   *
   4556   *  and we use 0xFFFF for the <separator>.
   4557   *
   4558   */
   4559 
   4560  const char16_t* uriEnd = nullptr;
   4561  const char16_t* nameEnd = nullptr;
   4562  const char16_t* pos;
   4563  for (pos = aExpatName; *pos; ++pos) {
   4564    if (*pos == 0xFFFF) {
   4565      if (uriEnd) {
   4566        nameEnd = pos;
   4567      } else {
   4568        uriEnd = pos;
   4569      }
   4570    }
   4571  }
   4572 
   4573  const char16_t* nameStart;
   4574  if (uriEnd) {
   4575    nsNameSpaceManager::GetInstance()->RegisterNameSpace(
   4576        nsDependentSubstring(aExpatName, uriEnd), *aNameSpaceID);
   4577 
   4578    nameStart = (uriEnd + 1);
   4579    if (nameEnd) {
   4580      const char16_t* prefixStart = nameEnd + 1;
   4581      *aPrefix = NS_AtomizeMainThread(Substring(prefixStart, pos)).take();
   4582    } else {
   4583      nameEnd = pos;
   4584      *aPrefix = nullptr;
   4585    }
   4586  } else {
   4587    *aNameSpaceID = kNameSpaceID_None;
   4588    nameStart = aExpatName;
   4589    nameEnd = pos;
   4590    *aPrefix = nullptr;
   4591  }
   4592  *aLocalName = NS_AtomizeMainThread(Substring(nameStart, nameEnd)).take();
   4593 }
   4594 
   4595 // static
   4596 PresShell* nsContentUtils::GetPresShellForContent(const nsIContent* aContent) {
   4597  Document* doc = aContent->GetComposedDoc();
   4598  if (!doc) {
   4599    return nullptr;
   4600  }
   4601  return doc->GetPresShell();
   4602 }
   4603 
   4604 // static
   4605 nsPresContext* nsContentUtils::GetContextForContent(
   4606    const nsIContent* aContent) {
   4607  PresShell* presShell = GetPresShellForContent(aContent);
   4608  if (!presShell) {
   4609    return nullptr;
   4610  }
   4611  return presShell->GetPresContext();
   4612 }
   4613 
   4614 // static
   4615 bool nsContentUtils::IsInPrivateBrowsing(nsILoadGroup* aLoadGroup) {
   4616  if (!aLoadGroup) {
   4617    return false;
   4618  }
   4619  // See duplicated code in Document::Reset/ResetToURI
   4620  bool isPrivate = false;
   4621  nsCOMPtr<nsIInterfaceRequestor> callbacks;
   4622  aLoadGroup->GetNotificationCallbacks(getter_AddRefs(callbacks));
   4623  if (callbacks) {
   4624    nsCOMPtr<nsILoadContext> loadContext = do_GetInterface(callbacks);
   4625    isPrivate = loadContext && loadContext->UsePrivateBrowsing();
   4626  }
   4627  return isPrivate;
   4628 }
   4629 
   4630 // FIXME(emilio): This is (effectively) almost but not quite the same as
   4631 // Document::ShouldLoadImages(), which one is right?
   4632 bool nsContentUtils::DocumentInactiveForImageLoads(Document* aDocument) {
   4633  if (!aDocument) {
   4634    return false;
   4635  }
   4636  if (IsChromeDoc(aDocument) || aDocument->IsResourceDoc() ||
   4637      aDocument->IsStaticDocument()) {
   4638    return false;
   4639  }
   4640  nsCOMPtr<nsPIDOMWindowInner> win =
   4641      do_QueryInterface(aDocument->GetScopeObject());
   4642  return !win || !win->GetDocShell();
   4643 }
   4644 
   4645 imgLoader* nsContentUtils::GetImgLoaderForDocument(Document* aDoc) {
   4646  NS_ENSURE_TRUE(!DocumentInactiveForImageLoads(aDoc), nullptr);
   4647 
   4648  if (!aDoc) {
   4649    return imgLoader::NormalLoader();
   4650  }
   4651  const bool isPrivate = aDoc->IsInPrivateBrowsing();
   4652  return isPrivate ? imgLoader::PrivateBrowsingLoader()
   4653                   : imgLoader::NormalLoader();
   4654 }
   4655 
   4656 // static
   4657 imgLoader* nsContentUtils::GetImgLoaderForChannel(nsIChannel* aChannel,
   4658                                                  Document* aContext) {
   4659  NS_ENSURE_TRUE(!DocumentInactiveForImageLoads(aContext), nullptr);
   4660 
   4661  if (!aChannel) {
   4662    return imgLoader::NormalLoader();
   4663  }
   4664  return NS_UsePrivateBrowsing(aChannel) ? imgLoader::PrivateBrowsingLoader()
   4665                                         : imgLoader::NormalLoader();
   4666 }
   4667 
   4668 // static
   4669 int32_t nsContentUtils::CORSModeToLoadImageFlags(mozilla::CORSMode aMode) {
   4670  switch (aMode) {
   4671    case CORS_ANONYMOUS:
   4672      return imgILoader::LOAD_CORS_ANONYMOUS;
   4673    case CORS_USE_CREDENTIALS:
   4674      return imgILoader::LOAD_CORS_USE_CREDENTIALS;
   4675    default:
   4676      return 0;
   4677  }
   4678 }
   4679 
   4680 // static
   4681 nsresult nsContentUtils::LoadImage(
   4682    nsIURI* aURI, nsINode* aContext, Document* aLoadingDocument,
   4683    nsIPrincipal* aLoadingPrincipal, uint64_t aRequestContextID,
   4684    nsIReferrerInfo* aReferrerInfo, imgINotificationObserver* aObserver,
   4685    int32_t aLoadFlags, const nsAString& initiatorType,
   4686    imgRequestProxy** aRequest, nsContentPolicyType aContentPolicyType,
   4687    bool aUseUrgentStartForChannel, bool aLinkPreload,
   4688    uint64_t aEarlyHintPreloaderId,
   4689    mozilla::dom::FetchPriority aFetchPriority) {
   4690  MOZ_ASSERT(aURI, "Must have a URI");
   4691  MOZ_ASSERT(aContext, "Must have a context");
   4692  MOZ_ASSERT(aLoadingDocument, "Must have a document");
   4693  MOZ_ASSERT(aLoadingPrincipal, "Must have a principal");
   4694  MOZ_ASSERT(aRequest, "Null out param");
   4695 
   4696  imgLoader* imgLoader = GetImgLoaderForDocument(aLoadingDocument);
   4697  if (!imgLoader) {
   4698    // nothing we can do here
   4699    return NS_ERROR_FAILURE;
   4700  }
   4701 
   4702  nsCOMPtr<nsILoadGroup> loadGroup = aLoadingDocument->GetDocumentLoadGroup();
   4703 
   4704  nsIURI* documentURI = aLoadingDocument->GetDocumentURI();
   4705 
   4706  NS_ASSERTION(loadGroup || aLoadingDocument->IsSVGGlyphsDocument(),
   4707               "Could not get loadgroup; onload may fire too early");
   4708 
   4709  // XXXbz using "documentURI" for the initialDocumentURI is not quite
   4710  // right, but the best we can do here...
   4711  return imgLoader->LoadImage(aURI,               /* uri to load */
   4712                              documentURI,        /* initialDocumentURI */
   4713                              aReferrerInfo,      /* referrerInfo */
   4714                              aLoadingPrincipal,  /* loading principal */
   4715                              aRequestContextID,  /* request context ID */
   4716                              loadGroup,          /* loadgroup */
   4717                              aObserver,          /* imgINotificationObserver */
   4718                              aContext,           /* loading context */
   4719                              aLoadingDocument,   /* uniquification key */
   4720                              aLoadFlags,         /* load flags */
   4721                              nullptr,            /* cache key */
   4722                              aContentPolicyType, /* content policy type */
   4723                              initiatorType,      /* the load initiator */
   4724                              aUseUrgentStartForChannel, /* urgent-start flag */
   4725                              aLinkPreload, /* <link preload> initiator */
   4726                              aEarlyHintPreloaderId, aFetchPriority, aRequest);
   4727 }
   4728 
   4729 // static
   4730 already_AddRefed<imgIContainer> nsContentUtils::GetImageFromContent(
   4731    nsIImageLoadingContent* aContent, imgIRequest** aRequest) {
   4732  if (aRequest) {
   4733    *aRequest = nullptr;
   4734  }
   4735 
   4736  NS_ENSURE_TRUE(aContent, nullptr);
   4737 
   4738  nsCOMPtr<imgIRequest> imgRequest;
   4739  aContent->GetRequest(nsIImageLoadingContent::CURRENT_REQUEST,
   4740                       getter_AddRefs(imgRequest));
   4741  if (!imgRequest) {
   4742    return nullptr;
   4743  }
   4744 
   4745  nsCOMPtr<imgIContainer> imgContainer;
   4746  imgRequest->GetImage(getter_AddRefs(imgContainer));
   4747 
   4748  if (!imgContainer) {
   4749    return nullptr;
   4750  }
   4751 
   4752  if (aRequest) {
   4753    // If the consumer wants the request, verify it has actually loaded
   4754    // successfully.
   4755    uint32_t imgStatus;
   4756    imgRequest->GetImageStatus(&imgStatus);
   4757    if (imgStatus & imgIRequest::STATUS_FRAME_COMPLETE &&
   4758        !(imgStatus & imgIRequest::STATUS_ERROR)) {
   4759      imgRequest.swap(*aRequest);
   4760    }
   4761  }
   4762 
   4763  return imgContainer.forget();
   4764 }
   4765 
   4766 static bool IsLinkWithURI(const nsIContent& aContent) {
   4767  const auto* element = Element::FromNode(aContent);
   4768  if (!element || !element->IsLink()) {
   4769    return false;
   4770  }
   4771  nsCOMPtr<nsIURI> absURI = element->GetHrefURI();
   4772  return !!absURI;
   4773 }
   4774 
   4775 static bool HasImageRequest(nsIContent& aContent) {
   4776  nsCOMPtr<nsIImageLoadingContent> imageContent(do_QueryInterface(&aContent));
   4777  if (!imageContent) {
   4778    return false;
   4779  }
   4780 
   4781  nsCOMPtr<imgIRequest> imgRequest;
   4782  imageContent->GetRequest(nsIImageLoadingContent::CURRENT_REQUEST,
   4783                           getter_AddRefs(imgRequest));
   4784 
   4785  // XXXbz It may be draggable even if the request resulted in an error.  Why?
   4786  // Not sure; that's what the old nsContentAreaDragDrop/nsFrame code did.
   4787  return !!imgRequest;
   4788 }
   4789 
   4790 static Maybe<bool> DraggableOverride(const nsIContent& aContent) {
   4791  if (auto* el = nsGenericHTMLElement::FromNode(aContent)) {
   4792    if (el->Draggable()) {
   4793      return Some(true);
   4794    }
   4795 
   4796    if (el->AttrValueIs(kNameSpaceID_None, nsGkAtoms::draggable,
   4797                        nsGkAtoms::_false, eIgnoreCase)) {
   4798      return Some(false);
   4799    }
   4800  }
   4801  if (aContent.IsSVGElement()) {
   4802    return Some(false);
   4803  }
   4804  return Nothing();
   4805 }
   4806 
   4807 // static
   4808 bool nsContentUtils::ContentIsDraggable(nsIContent* aContent) {
   4809  MOZ_ASSERT(aContent);
   4810 
   4811  if (auto draggable = DraggableOverride(*aContent)) {
   4812    return *draggable;
   4813  }
   4814 
   4815  // special handling for content area image and link dragging
   4816  return HasImageRequest(*aContent) || IsLinkWithURI(*aContent);
   4817 }
   4818 
   4819 // static
   4820 bool nsContentUtils::IsDraggableImage(nsIContent* aContent) {
   4821  MOZ_ASSERT(aContent);
   4822  return HasImageRequest(*aContent) &&
   4823         DraggableOverride(*aContent).valueOr(true);
   4824 }
   4825 
   4826 // static
   4827 bool nsContentUtils::IsDraggableLink(const nsIContent* aContent) {
   4828  MOZ_ASSERT(aContent);
   4829  return IsLinkWithURI(*aContent) && DraggableOverride(*aContent).valueOr(true);
   4830 }
   4831 
   4832 // static
   4833 nsresult nsContentUtils::QNameChanged(mozilla::dom::NodeInfo* aNodeInfo,
   4834                                      nsAtom* aName,
   4835                                      mozilla::dom::NodeInfo** aResult) {
   4836  nsNodeInfoManager* niMgr = aNodeInfo->NodeInfoManager();
   4837 
   4838  *aResult = niMgr
   4839                 ->GetNodeInfo(aName, nullptr, aNodeInfo->NamespaceID(),
   4840                               aNodeInfo->NodeType(), aNodeInfo->GetExtraName())
   4841                 .take();
   4842  return NS_OK;
   4843 }
   4844 
   4845 static bool TestSitePerm(nsIPrincipal* aPrincipal, const nsACString& aType,
   4846                         uint32_t aPerm, bool aExactHostMatch) {
   4847  if (!aPrincipal) {
   4848    // We always deny (i.e. don't allow) the permission if we don't have a
   4849    // principal.
   4850    return aPerm != nsIPermissionManager::ALLOW_ACTION;
   4851  }
   4852 
   4853  nsCOMPtr<nsIPermissionManager> permMgr =
   4854      components::PermissionManager::Service();
   4855  NS_ENSURE_TRUE(permMgr, false);
   4856 
   4857  uint32_t perm;
   4858  nsresult rv;
   4859  if (aExactHostMatch) {
   4860    rv = permMgr->TestExactPermissionFromPrincipal(aPrincipal, aType, &perm);
   4861  } else {
   4862    rv = permMgr->TestPermissionFromPrincipal(aPrincipal, aType, &perm);
   4863  }
   4864  NS_ENSURE_SUCCESS(rv, false);
   4865 
   4866  return perm == aPerm;
   4867 }
   4868 
   4869 bool nsContentUtils::IsSitePermAllow(nsIPrincipal* aPrincipal,
   4870                                     const nsACString& aType) {
   4871  return TestSitePerm(aPrincipal, aType, nsIPermissionManager::ALLOW_ACTION,
   4872                      false);
   4873 }
   4874 
   4875 bool nsContentUtils::IsSitePermDeny(nsIPrincipal* aPrincipal,
   4876                                    const nsACString& aType) {
   4877  return TestSitePerm(aPrincipal, aType, nsIPermissionManager::DENY_ACTION,
   4878                      false);
   4879 }
   4880 
   4881 bool nsContentUtils::IsExactSitePermAllow(nsIPrincipal* aPrincipal,
   4882                                          const nsACString& aType) {
   4883  return TestSitePerm(aPrincipal, aType, nsIPermissionManager::ALLOW_ACTION,
   4884                      true);
   4885 }
   4886 
   4887 bool nsContentUtils::IsExactSitePermDeny(nsIPrincipal* aPrincipal,
   4888                                         const nsACString& aType) {
   4889  return TestSitePerm(aPrincipal, aType, nsIPermissionManager::DENY_ACTION,
   4890                      true);
   4891 }
   4892 
   4893 bool nsContentUtils::HasSitePerm(nsIPrincipal* aPrincipal,
   4894                                 const nsACString& aType) {
   4895  if (!aPrincipal) {
   4896    return false;
   4897  }
   4898 
   4899  nsCOMPtr<nsIPermissionManager> permMgr =
   4900      components::PermissionManager::Service();
   4901  NS_ENSURE_TRUE(permMgr, false);
   4902 
   4903  uint32_t perm;
   4904  nsresult rv = permMgr->TestPermissionFromPrincipal(aPrincipal, aType, &perm);
   4905  NS_ENSURE_SUCCESS(rv, false);
   4906 
   4907  return perm != nsIPermissionManager::UNKNOWN_ACTION;
   4908 }
   4909 
   4910 static const char* gEventNames[] = {"event"};
   4911 static const char* gSVGEventNames[] = {"evt"};
   4912 // for b/w compat, the first name to onerror is still 'event', even though it
   4913 // is actually the error message
   4914 static const char* gOnErrorNames[] = {"event", "source", "lineno", "colno",
   4915                                      "error"};
   4916 
   4917 // static
   4918 void nsContentUtils::GetEventArgNames(int32_t aNameSpaceID, nsAtom* aEventName,
   4919                                      bool aIsForWindow, uint32_t* aArgCount,
   4920                                      const char*** aArgArray) {
   4921 #define SET_EVENT_ARG_NAMES(names)               \
   4922  *aArgCount = sizeof(names) / sizeof(names[0]); \
   4923  *aArgArray = names;
   4924 
   4925  // JSEventHandler is what does the arg magic for onerror, and it does
   4926  // not seem to take the namespace into account.  So we let onerror in all
   4927  // namespaces get the 3 arg names.
   4928  if (aEventName == nsGkAtoms::onerror && aIsForWindow) {
   4929    SET_EVENT_ARG_NAMES(gOnErrorNames);
   4930  } else if (aNameSpaceID == kNameSpaceID_SVG) {
   4931    SET_EVENT_ARG_NAMES(gSVGEventNames);
   4932  } else {
   4933    SET_EVENT_ARG_NAMES(gEventNames);
   4934  }
   4935 }
   4936 
   4937 // Note: The list of content bundles in nsStringBundle.cpp should be updated
   4938 // whenever entries are added or removed from this list.
   4939 static const char* gPropertiesFiles[nsContentUtils::PropertiesFile_COUNT] = {
   4940    // Must line up with the enum values in |PropertiesFile| enum.
   4941    "chrome://global/locale/css.properties",
   4942    "chrome://global/locale/xul.properties",
   4943    "chrome://global/locale/layout_errors.properties",
   4944    "chrome://global/locale/layout/HtmlForm.properties",
   4945    "chrome://global/locale/printing.properties",
   4946    "chrome://global/locale/dom/dom.properties",
   4947    "chrome://global/locale/layout/htmlparser.properties",
   4948    "chrome://global/locale/svg/svg.properties",
   4949    "chrome://branding/locale/brand.properties",
   4950    "chrome://global/locale/commonDialogs.properties",
   4951    "chrome://global/locale/mathml/mathml.properties",
   4952    "chrome://global/locale/security/security.properties",
   4953    "chrome://necko/locale/necko.properties",
   4954    "resource://gre/res/locale/layout/HtmlForm.properties",
   4955    "resource://gre/res/locale/dom/dom.properties",
   4956    "resource://gre/res/locale/necko/necko.properties",
   4957 };
   4958 
   4959 /* static */
   4960 nsresult nsContentUtils::EnsureStringBundle(PropertiesFile aFile) {
   4961  MOZ_DIAGNOSTIC_ASSERT(NS_IsMainThread(),
   4962                        "Should not create bundles off main thread.");
   4963  if (!sStringBundles[aFile]) {
   4964    if (!sStringBundleService) {
   4965      nsresult rv =
   4966          CallGetService(NS_STRINGBUNDLE_CONTRACTID, &sStringBundleService);
   4967      NS_ENSURE_SUCCESS(rv, rv);
   4968    }
   4969    RefPtr<nsIStringBundle> bundle;
   4970    MOZ_TRY(sStringBundleService->CreateBundle(gPropertiesFiles[aFile],
   4971                                               getter_AddRefs(bundle)));
   4972    sStringBundles[aFile] = bundle.forget();
   4973  }
   4974  return NS_OK;
   4975 }
   4976 
   4977 /* static */
   4978 void nsContentUtils::AsyncPrecreateStringBundles() {
   4979  // We only ever want to pre-create bundles in the parent process.
   4980  //
   4981  // All nsContentUtils bundles are shared between the parent and child
   4982  // precesses, and the shared memory regions that back them *must* be created
   4983  // in the parent, and then sent to all children.
   4984  //
   4985  // If we attempt to create a bundle in the child before its memory region is
   4986  // available, we need to create a temporary non-shared bundle, and later
   4987  // replace that with the shared memory copy. So attempting to pre-load in the
   4988  // child is wasteful and unnecessary.
   4989  MOZ_ASSERT(XRE_IsParentProcess());
   4990 
   4991  for (uint32_t bundleIndex = 0; bundleIndex < PropertiesFile_COUNT;
   4992       ++bundleIndex) {
   4993    nsresult rv = NS_DispatchToCurrentThreadQueue(
   4994        NS_NewRunnableFunction("AsyncPrecreateStringBundles",
   4995                               [bundleIndex]() {
   4996                                 PropertiesFile file =
   4997                                     static_cast<PropertiesFile>(bundleIndex);
   4998                                 EnsureStringBundle(file);
   4999                                 nsIStringBundle* bundle = sStringBundles[file];
   5000                                 bundle->AsyncPreload();
   5001                               }),
   5002        EventQueuePriority::Idle);
   5003    (void)NS_WARN_IF(NS_FAILED(rv));
   5004  }
   5005 }
   5006 
   5007 static nsContentUtils::PropertiesFile GetMaybeSpoofedPropertiesFile(
   5008    nsContentUtils::PropertiesFile aFile, const char* aKey,
   5009    Document* aDocument) {
   5010  // When we spoof English, use en-US properties in strings that are accessible
   5011  // by content.
   5012  bool spoofLocale = nsContentUtils::ShouldResistFingerprinting(
   5013      aDocument, RFPTarget::JSLocale);
   5014  if (spoofLocale) {
   5015    switch (aFile) {
   5016      case nsContentUtils::eFORMS_PROPERTIES:
   5017        return nsContentUtils::eFORMS_PROPERTIES_en_US;
   5018      case nsContentUtils::eDOM_PROPERTIES:
   5019        return nsContentUtils::eDOM_PROPERTIES_en_US;
   5020      default:
   5021        break;
   5022    }
   5023  }
   5024  return aFile;
   5025 }
   5026 
   5027 /* static */
   5028 nsresult nsContentUtils::GetMaybeLocalizedString(PropertiesFile aFile,
   5029                                                 const char* aKey,
   5030                                                 Document* aDocument,
   5031                                                 nsAString& aResult) {
   5032  return GetLocalizedString(
   5033      GetMaybeSpoofedPropertiesFile(aFile, aKey, aDocument), aKey, aResult);
   5034 }
   5035 
   5036 /* static */
   5037 nsresult nsContentUtils::GetLocalizedString(PropertiesFile aFile,
   5038                                            const char* aKey,
   5039                                            nsAString& aResult) {
   5040  return FormatLocalizedString(aFile, aKey, {}, aResult);
   5041 }
   5042 
   5043 /* static */
   5044 nsresult nsContentUtils::FormatMaybeLocalizedString(
   5045    PropertiesFile aFile, const char* aKey, Document* aDocument,
   5046    const nsTArray<nsString>& aParams, nsAString& aResult) {
   5047  return FormatLocalizedString(
   5048      GetMaybeSpoofedPropertiesFile(aFile, aKey, aDocument), aKey, aParams,
   5049      aResult);
   5050 }
   5051 
   5052 class FormatLocalizedStringRunnable final : public WorkerMainThreadRunnable {
   5053 public:
   5054  FormatLocalizedStringRunnable(WorkerPrivate* aWorkerPrivate,
   5055                                nsContentUtils::PropertiesFile aFile,
   5056                                const char* aKey,
   5057                                const nsTArray<nsString>& aParams,
   5058                                nsAString& aLocalizedString)
   5059      : WorkerMainThreadRunnable(aWorkerPrivate,
   5060                                 "FormatLocalizedStringRunnable"_ns),
   5061        mFile(aFile),
   5062        mKey(aKey),
   5063        mParams(aParams),
   5064        mLocalizedString(aLocalizedString) {
   5065    MOZ_ASSERT(aWorkerPrivate);
   5066    aWorkerPrivate->AssertIsOnWorkerThread();
   5067  }
   5068 
   5069  bool MainThreadRun() override {
   5070    AssertIsOnMainThread();
   5071 
   5072    mResult = nsContentUtils::FormatLocalizedString(mFile, mKey, mParams,
   5073                                                    mLocalizedString);
   5074    (void)NS_WARN_IF(NS_FAILED(mResult));
   5075    return true;
   5076  }
   5077 
   5078  nsresult GetResult() const { return mResult; }
   5079 
   5080 private:
   5081  const nsContentUtils::PropertiesFile mFile;
   5082  const char* mKey;
   5083  const nsTArray<nsString>& mParams;
   5084  nsresult mResult = NS_ERROR_FAILURE;
   5085  nsAString& mLocalizedString;
   5086 };
   5087 
   5088 /* static */
   5089 nsresult nsContentUtils::FormatLocalizedString(
   5090    PropertiesFile aFile, const char* aKey, const nsTArray<nsString>& aParams,
   5091    nsAString& aResult) {
   5092  if (!NS_IsMainThread()) {
   5093    // nsIStringBundle is thread-safe but its creation is not, and in particular
   5094    // we don't create and store nsIStringBundle objects in a thread-safe way.
   5095    //
   5096    // TODO(emilio): Maybe if we already have the right bundle created we could
   5097    // just call into it, but we should make sure that Shutdown() doesn't get
   5098    // called on the main thread when that happens which is a bit tricky to
   5099    // prove?
   5100    WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
   5101    if (NS_WARN_IF(!workerPrivate)) {
   5102      return NS_ERROR_UNEXPECTED;
   5103    }
   5104 
   5105    auto runnable = MakeRefPtr<FormatLocalizedStringRunnable>(
   5106        workerPrivate, aFile, aKey, aParams, aResult);
   5107 
   5108    runnable->Dispatch(workerPrivate, Canceling, IgnoreErrors());
   5109    return runnable->GetResult();
   5110  }
   5111 
   5112  MOZ_TRY(EnsureStringBundle(aFile));
   5113  nsIStringBundle* bundle = sStringBundles[aFile];
   5114  if (aParams.IsEmpty()) {
   5115    return bundle->GetStringFromName(aKey, aResult);
   5116  }
   5117  return bundle->FormatStringFromName(aKey, aParams, aResult);
   5118 }
   5119 
   5120 /* static */
   5121 void nsContentUtils::LogSimpleConsoleError(const nsAString& aErrorText,
   5122                                           const nsACString& aCategory,
   5123                                           bool aFromPrivateWindow,
   5124                                           bool aFromChromeContext,
   5125                                           uint32_t aErrorFlags) {
   5126  nsCOMPtr<nsIScriptError> scriptError =
   5127      do_CreateInstance(NS_SCRIPTERROR_CONTRACTID);
   5128  if (scriptError) {
   5129    nsCOMPtr<nsIConsoleService> console =
   5130        do_GetService(NS_CONSOLESERVICE_CONTRACTID);
   5131    if (console && NS_SUCCEEDED(scriptError->Init(
   5132                       aErrorText, ""_ns, 0, 0, aErrorFlags, aCategory,
   5133                       aFromPrivateWindow, aFromChromeContext))) {
   5134      console->LogMessage(scriptError);
   5135    }
   5136  }
   5137 }
   5138 
   5139 /* static */
   5140 nsresult nsContentUtils::ReportToConsole(
   5141    uint32_t aErrorFlags, const nsACString& aCategory,
   5142    const Document* aDocument, PropertiesFile aFile, const char* aMessageName,
   5143    const nsTArray<nsString>& aParams, const SourceLocation& aLoc) {
   5144  nsresult rv;
   5145  nsAutoString errorText;
   5146  if (!aParams.IsEmpty()) {
   5147    rv = FormatLocalizedString(aFile, aMessageName, aParams, errorText);
   5148  } else {
   5149    rv = GetLocalizedString(aFile, aMessageName, errorText);
   5150  }
   5151  NS_ENSURE_SUCCESS(rv, rv);
   5152  return ReportToConsoleNonLocalized(errorText, aErrorFlags, aCategory,
   5153                                     aDocument, aLoc);
   5154 }
   5155 
   5156 /* static */
   5157 void nsContentUtils::ReportEmptyGetElementByIdArg(const Document* aDoc) {
   5158  ReportToConsole(nsIScriptError::warningFlag, "DOM"_ns, aDoc,
   5159                  nsContentUtils::eDOM_PROPERTIES, "EmptyGetElementByIdParam");
   5160 }
   5161 
   5162 /* static */
   5163 nsresult nsContentUtils::ReportToConsoleNonLocalized(
   5164    const nsAString& aErrorText, uint32_t aErrorFlags,
   5165    const nsACString& aCategory, const Document* aDocument,
   5166    const SourceLocation& aLoc) {
   5167  uint64_t innerWindowID = aDocument ? aDocument->InnerWindowID() : 0;
   5168  if (aLoc || !aDocument || !aDocument->GetDocumentURI()) {
   5169    return ReportToConsoleByWindowID(aErrorText, aErrorFlags, aCategory,
   5170                                     innerWindowID, aLoc);
   5171  }
   5172  return ReportToConsoleByWindowID(aErrorText, aErrorFlags, aCategory,
   5173                                   innerWindowID,
   5174                                   SourceLocation(aDocument->GetDocumentURI()));
   5175 }
   5176 
   5177 /* static */
   5178 nsresult nsContentUtils::ReportToConsoleByWindowID(
   5179    const nsAString& aErrorText, uint32_t aErrorFlags,
   5180    const nsACString& aCategory, uint64_t aInnerWindowID,
   5181    const SourceLocation& aLocation) {
   5182  nsresult rv;
   5183  if (!sConsoleService) {  // only need to bother null-checking here
   5184    rv = CallGetService(NS_CONSOLESERVICE_CONTRACTID, &sConsoleService);
   5185    NS_ENSURE_SUCCESS(rv, rv);
   5186  }
   5187 
   5188  nsCOMPtr<nsIScriptError> errorObject =
   5189      do_CreateInstance(NS_SCRIPTERROR_CONTRACTID, &rv);
   5190  NS_ENSURE_SUCCESS(rv, rv);
   5191 
   5192  if (aLocation.mResource.is<nsCOMPtr<nsIURI>>()) {
   5193    nsIURI* uri = aLocation.mResource.as<nsCOMPtr<nsIURI>>();
   5194    rv = errorObject->InitWithSourceURI(aErrorText, uri, aLocation.mLine,
   5195                                        aLocation.mColumn, aErrorFlags,
   5196                                        aCategory, aInnerWindowID);
   5197  } else {
   5198    rv = errorObject->InitWithWindowID(
   5199        aErrorText, aLocation.mResource.as<nsCString>(), aLocation.mLine,
   5200        aLocation.mColumn, aErrorFlags, aCategory, aInnerWindowID);
   5201  }
   5202  NS_ENSURE_SUCCESS(rv, rv);
   5203 
   5204  return sConsoleService->LogMessage(errorObject);
   5205 }
   5206 
   5207 void nsContentUtils::LogMessageToConsole(const char* aMsg) {
   5208  if (!sConsoleService) {  // only need to bother null-checking here
   5209    CallGetService(NS_CONSOLESERVICE_CONTRACTID, &sConsoleService);
   5210    if (!sConsoleService) {
   5211      return;
   5212    }
   5213  }
   5214  sConsoleService->LogStringMessage(NS_ConvertUTF8toUTF16(aMsg).get());
   5215 }
   5216 
   5217 bool nsContentUtils::IsChromeDoc(const Document* aDocument) {
   5218  return aDocument && aDocument->NodePrincipal() == sSystemPrincipal;
   5219 }
   5220 
   5221 bool nsContentUtils::IsAddonDoc(const Document* aDocument) {
   5222  return aDocument &&
   5223         aDocument->NodePrincipal()->GetIsAddonOrExpandedAddonPrincipal();
   5224 }
   5225 
   5226 bool nsContentUtils::IsChildOfSameType(Document* aDoc) {
   5227  if (BrowsingContext* bc = aDoc->GetBrowsingContext()) {
   5228    return bc->GetParent();
   5229  }
   5230  return false;
   5231 }
   5232 
   5233 static bool IsJSONType(const nsACString& aContentType) {
   5234  return aContentType.EqualsLiteral(TEXT_JSON) ||
   5235         aContentType.EqualsLiteral(APPLICATION_JSON);
   5236 }
   5237 
   5238 static bool IsNonPlainTextType(const nsACString& aContentType) {
   5239  // MIME type suffixes which should not be plain text.
   5240  static constexpr std::string_view kNonPlainTextTypes[] = {
   5241      "html",
   5242      "xml",
   5243      "xsl",
   5244      "calendar",
   5245      "x-calendar",
   5246      "x-vcalendar",
   5247      "vcalendar",
   5248      "vcard",
   5249      "x-vcard",
   5250      "directory",
   5251      "ldif",
   5252      "qif",
   5253      "x-qif",
   5254      "x-csv",
   5255      "x-vcf",
   5256      "rtf",
   5257      "comma-separated-values",
   5258      "csv",
   5259      "tab-separated-values",
   5260      "tsv",
   5261      "ofx",
   5262      "vnd.sun.j2me.app-descriptor",
   5263      "x-ms-iqy",
   5264      "x-ms-odc",
   5265      "x-ms-rqy",
   5266      "x-ms-contact"};
   5267 
   5268  // Trim off the "text/" prefix for comparison.
   5269  MOZ_ASSERT(StringBeginsWith(aContentType, "text/"_ns));
   5270  std::string_view suffix = aContentType;
   5271  suffix.remove_prefix(5);
   5272 
   5273  for (std::string_view type : kNonPlainTextTypes) {
   5274    if (type == suffix) {
   5275      return true;
   5276    }
   5277  }
   5278  return false;
   5279 }
   5280 
   5281 bool nsContentUtils::IsPlainTextType(const nsACString& aContentType) {
   5282  // All `text/*`, any JSON type and any JavaScript type are considered "plain
   5283  // text" types for the purposes of how to render them as a document.
   5284  return (StringBeginsWith(aContentType, "text/"_ns) &&
   5285          !IsNonPlainTextType(aContentType)) ||
   5286         IsJSONType(aContentType) || IsJavascriptMIMEType(aContentType);
   5287 }
   5288 
   5289 bool nsContentUtils::IsUtf8OnlyPlainTextType(const nsACString& aContentType) {
   5290  // NOTE: This must be a subset of the list in IsPlainTextType().
   5291  return IsJSONType(aContentType) ||
   5292         aContentType.EqualsLiteral(TEXT_CACHE_MANIFEST) ||
   5293         aContentType.EqualsLiteral(TEXT_VTT);
   5294 }
   5295 
   5296 bool nsContentUtils::IsInChromeDocshell(const Document* aDocument) {
   5297  return aDocument && aDocument->IsInChromeDocShell();
   5298 }
   5299 
   5300 // static
   5301 nsIContentPolicy* nsContentUtils::GetContentPolicy() {
   5302  if (!sTriedToGetContentPolicy) {
   5303    CallGetService(NS_CONTENTPOLICY_CONTRACTID, &sContentPolicyService);
   5304    // It's OK to not have a content policy service
   5305    sTriedToGetContentPolicy = true;
   5306  }
   5307 
   5308  return sContentPolicyService;
   5309 }
   5310 
   5311 // static
   5312 bool nsContentUtils::IsEventAttributeName(nsAtom* aName, int32_t aType) {
   5313  const char16_t* name = aName->GetUTF16String();
   5314  if (name[0] != 'o' || name[1] != 'n') {
   5315    return false;
   5316  }
   5317 
   5318  EventNameMapping mapping;
   5319  return (sAtomEventTable->Get(aName, &mapping) && mapping.mType & aType);
   5320 }
   5321 
   5322 // static
   5323 EventMessage nsContentUtils::GetEventMessage(nsAtom* aName) {
   5324  MOZ_ASSERT(NS_IsMainThread(), "sAtomEventTable is not threadsafe");
   5325  if (aName) {
   5326    EventNameMapping mapping;
   5327    if (sAtomEventTable->Get(aName, &mapping)) {
   5328      return mapping.mMessage;
   5329    }
   5330  }
   5331 
   5332  return eUnidentifiedEvent;
   5333 }
   5334 
   5335 // static
   5336 void nsContentUtils::ForEachEventAttributeName(
   5337    int32_t aType, const FunctionRef<void(nsAtom*)> aFunc) {
   5338  for (auto iter = sAtomEventTable->ConstIter(); !iter.Done(); iter.Next()) {
   5339    if (iter.Data().mType & aType) {
   5340      aFunc(iter.Key());
   5341    }
   5342  }
   5343 }
   5344 
   5345 // static
   5346 mozilla::EventClassID nsContentUtils::GetEventClassID(const nsAString& aName) {
   5347  EventNameMapping mapping;
   5348  if (sStringEventTable->Get(aName, &mapping)) return mapping.mEventClassID;
   5349 
   5350  return eBasicEventClass;
   5351 }
   5352 
   5353 nsAtom* nsContentUtils::GetEventMessageAndAtom(
   5354    const nsAString& aName, mozilla::EventClassID aEventClassID,
   5355    EventMessage* aEventMessage) {
   5356  MOZ_ASSERT(NS_IsMainThread(), "Our hashtables are not threadsafe");
   5357  EventNameMapping mapping;
   5358  if (sStringEventTable->Get(aName, &mapping)) {
   5359    *aEventMessage = mapping.mEventClassID == aEventClassID
   5360                         ? mapping.mMessage
   5361                         : eUnidentifiedEvent;
   5362    return mapping.mAtom;
   5363  }
   5364 
   5365  // If we have cached lots of user defined event names, clear some of them.
   5366  if (sUserDefinedEvents->Length() > 127) {
   5367    while (sUserDefinedEvents->Length() > 64) {
   5368      nsAtom* first = sUserDefinedEvents->ElementAt(0);
   5369      sStringEventTable->Remove(Substring(nsDependentAtomString(first), 2));
   5370      sUserDefinedEvents->RemoveElementAt(0);
   5371    }
   5372  }
   5373 
   5374  *aEventMessage = eUnidentifiedEvent;
   5375  RefPtr<nsAtom> atom = NS_AtomizeMainThread(u"on"_ns + aName);
   5376  sUserDefinedEvents->AppendElement(atom);
   5377  mapping.mAtom = atom;
   5378  mapping.mMessage = eUnidentifiedEvent;
   5379  mapping.mType = EventNameType_None;
   5380  mapping.mEventClassID = eBasicEventClass;
   5381  sStringEventTable->InsertOrUpdate(aName, mapping);
   5382  return mapping.mAtom;
   5383 }
   5384 
   5385 // static
   5386 EventMessage nsContentUtils::GetEventMessageAndAtomForListener(
   5387    const nsAString& aName, nsAtom** aOnName) {
   5388  MOZ_ASSERT(NS_IsMainThread(), "Our hashtables are not threadsafe");
   5389 
   5390  // Check sStringEventTable for a matching entry. This will only fail for
   5391  // user-defined event types.
   5392  EventNameMapping mapping;
   5393  if (sStringEventTable->Get(aName, &mapping)) {
   5394    RefPtr<nsAtom> atom = mapping.mAtom;
   5395    atom.forget(aOnName);
   5396    return mapping.mMessage;
   5397  }
   5398 
   5399  // sStringEventTable did not contain an entry for this event type string.
   5400  // Call GetEventMessageAndAtom, which will create an event type atom and
   5401  // cache it in sStringEventTable for future calls.
   5402  EventMessage msg = eUnidentifiedEvent;
   5403  RefPtr<nsAtom> atom = GetEventMessageAndAtom(aName, eBasicEventClass, &msg);
   5404  atom.forget(aOnName);
   5405  return msg;
   5406 }
   5407 
   5408 static already_AddRefed<Event> GetEventWithTarget(
   5409    Document* aDoc, EventTarget* aTarget, const nsAString& aEventName,
   5410    CanBubble aCanBubble, Cancelable aCancelable, Composed aComposed,
   5411    Trusted aTrusted, ErrorResult& aErrorResult) {
   5412  RefPtr<Event> event =
   5413      aDoc->CreateEvent(u"Events"_ns, CallerType::System, aErrorResult);
   5414  if (aErrorResult.Failed()) {
   5415    return nullptr;
   5416  }
   5417 
   5418  event->InitEvent(aEventName, aCanBubble, aCancelable, aComposed);
   5419  event->SetTrusted(aTrusted == Trusted::eYes);
   5420 
   5421  event->SetTarget(aTarget);
   5422 
   5423  return event.forget();
   5424 }
   5425 
   5426 // static
   5427 nsresult nsContentUtils::DispatchTrustedEvent(
   5428    Document* aDoc, EventTarget* aTarget, const nsAString& aEventName,
   5429    CanBubble aCanBubble, Cancelable aCancelable, Composed aComposed,
   5430    bool* aDefaultAction, SystemGroupOnly aSystemGroupOnly) {
   5431  MOZ_ASSERT(!aEventName.EqualsLiteral("input") &&
   5432                 !aEventName.EqualsLiteral("beforeinput"),
   5433             "Use DispatchInputEvent() instead");
   5434  return DispatchEvent(aDoc, aTarget, aEventName, aCanBubble, aCancelable,
   5435                       aComposed, Trusted::eYes, aDefaultAction,
   5436                       ChromeOnlyDispatch::eNo, aSystemGroupOnly);
   5437 }
   5438 
   5439 // static
   5440 nsresult nsContentUtils::DispatchUntrustedEvent(
   5441    Document* aDoc, EventTarget* aTarget, const nsAString& aEventName,
   5442    CanBubble aCanBubble, Cancelable aCancelable, bool* aDefaultAction) {
   5443  return DispatchEvent(aDoc, aTarget, aEventName, aCanBubble, aCancelable,
   5444                       Composed::eDefault, Trusted::eNo, aDefaultAction);
   5445 }
   5446 
   5447 // static
   5448 nsresult nsContentUtils::DispatchEvent(
   5449    Document* aDoc, EventTarget* aTarget, const nsAString& aEventName,
   5450    CanBubble aCanBubble, Cancelable aCancelable, Composed aComposed,
   5451    Trusted aTrusted, bool* aDefaultAction,
   5452    ChromeOnlyDispatch aOnlyChromeDispatch, SystemGroupOnly aSystemGroupOnly) {
   5453  if (!aDoc || !aTarget) {
   5454    return NS_ERROR_INVALID_ARG;
   5455  }
   5456 
   5457  ErrorResult err;
   5458  RefPtr<Event> event =
   5459      GetEventWithTarget(aDoc, aTarget, aEventName, aCanBubble, aCancelable,
   5460                         aComposed, aTrusted, err);
   5461  if (err.Failed()) {
   5462    return err.StealNSResult();
   5463  }
   5464  event->WidgetEventPtr()->mFlags.mOnlyChromeDispatch =
   5465      aOnlyChromeDispatch == ChromeOnlyDispatch::eYes;
   5466  event->WidgetEventPtr()->mFlags.mOnlySystemGroupDispatch =
   5467      aSystemGroupOnly == SystemGroupOnly::eYes;
   5468 
   5469  bool doDefault = aTarget->DispatchEvent(*event, CallerType::System, err);
   5470  if (aDefaultAction) {
   5471    *aDefaultAction = doDefault;
   5472  }
   5473  return err.StealNSResult();
   5474 }
   5475 
   5476 // static
   5477 nsresult nsContentUtils::DispatchEvent(Document* aDoc, EventTarget* aTarget,
   5478                                       WidgetEvent& aEvent,
   5479                                       EventMessage aEventMessage,
   5480                                       CanBubble aCanBubble,
   5481                                       Cancelable aCancelable, Trusted aTrusted,
   5482                                       bool* aDefaultAction,
   5483                                       ChromeOnlyDispatch aOnlyChromeDispatch) {
   5484  MOZ_ASSERT_IF(aOnlyChromeDispatch == ChromeOnlyDispatch::eYes,
   5485                aTrusted == Trusted::eYes);
   5486 
   5487  aEvent.mSpecifiedEventType = GetEventTypeFromMessage(aEventMessage);
   5488  aEvent.SetDefaultComposed();
   5489  aEvent.SetDefaultComposedInNativeAnonymousContent();
   5490 
   5491  aEvent.mFlags.mBubbles = aCanBubble == CanBubble::eYes;
   5492  aEvent.mFlags.mCancelable = aCancelable == Cancelable::eYes;
   5493  aEvent.mFlags.mOnlyChromeDispatch =
   5494      aOnlyChromeDispatch == ChromeOnlyDispatch::eYes;
   5495 
   5496  aEvent.mTarget = aTarget;
   5497 
   5498  nsEventStatus status = nsEventStatus_eIgnore;
   5499  nsresult rv = EventDispatcher::DispatchDOMEvent(aTarget, &aEvent, nullptr,
   5500                                                  nullptr, &status);
   5501  if (aDefaultAction) {
   5502    *aDefaultAction = (status != nsEventStatus_eConsumeNoDefault);
   5503  }
   5504  return rv;
   5505 }
   5506 
   5507 // static
   5508 nsresult nsContentUtils::DispatchInputEvent(Element* aEventTarget) {
   5509  return DispatchInputEvent(aEventTarget, mozilla::eEditorInput,
   5510                            mozilla::EditorInputType::eUnknown, nullptr,
   5511                            InputEventOptions());
   5512 }
   5513 
   5514 // static
   5515 nsresult nsContentUtils::DispatchInputEvent(
   5516    Element* aEventTargetElement, EventMessage aEventMessage,
   5517    EditorInputType aEditorInputType, EditorBase* aEditorBase,
   5518    InputEventOptions&& aOptions, nsEventStatus* aEventStatus /* = nullptr */) {
   5519  MOZ_ASSERT(aEventMessage == eEditorInput ||
   5520             aEventMessage == eEditorBeforeInput);
   5521 
   5522  if (NS_WARN_IF(!aEventTargetElement)) {
   5523    return NS_ERROR_INVALID_ARG;
   5524  }
   5525 
   5526  // If this is called from editor, the instance should be set to aEditorBase.
   5527  // Otherwise, we need to look for an editor for aEventTargetElement.
   5528  // However, we don't need to do it for HTMLEditor since nobody shouldn't
   5529  // dispatch "beforeinput" nor "input" event for HTMLEditor except HTMLEditor
   5530  // itself.
   5531  bool useInputEvent = false;
   5532  if (aEditorBase) {
   5533    useInputEvent = true;
   5534  } else if (const HTMLTextAreaElement* const textAreaElement =
   5535                 HTMLTextAreaElement::FromNode(aEventTargetElement)) {
   5536    aEditorBase = textAreaElement->GetExtantTextEditor();
   5537    useInputEvent = true;
   5538  } else if (const HTMLInputElement* const inputElement =
   5539                 HTMLInputElement::FromNode(aEventTargetElement)) {
   5540    if (inputElement->IsInputEventTarget()) {
   5541      aEditorBase = inputElement->GetExtantTextEditor();
   5542      useInputEvent = true;
   5543    }
   5544  }
   5545 #ifdef DEBUG
   5546  else {
   5547    MOZ_ASSERT(!aEventTargetElement->IsTextControlElement(),
   5548               "The event target may have editor, but we've not known it yet.");
   5549  }
   5550 #endif  // #ifdef DEBUG
   5551 
   5552  if (!useInputEvent) {
   5553    MOZ_ASSERT(aEventMessage == eEditorInput);
   5554    MOZ_ASSERT(aEditorInputType == EditorInputType::eUnknown);
   5555    MOZ_ASSERT(!aOptions.mNeverCancelable);
   5556    // Dispatch "input" event with Event instance.
   5557    WidgetEvent widgetEvent(true, eUnidentifiedEvent);
   5558    widgetEvent.mSpecifiedEventType = nsGkAtoms::oninput;
   5559    widgetEvent.mFlags.mCancelable = false;
   5560    widgetEvent.mFlags.mComposed = true;
   5561    MOZ_LOG(gInputEventLog, LogLevel::Info,
   5562            ("Dispatching %s, safe?=%s, aEditorBase=%p, aEventTargetElement=%s",
   5563             ToChar(widgetEvent.mMessage),
   5564             YesOrNo(nsContentUtils::IsSafeToRunScript()), aEditorBase,
   5565             ToString(RefPtr{aEventTargetElement}).c_str()));
   5566    return AsyncEventDispatcher::RunDOMEventWhenSafe(*aEventTargetElement,
   5567                                                     widgetEvent, aEventStatus);
   5568  }
   5569 
   5570  MOZ_ASSERT_IF(aEventMessage != eEditorBeforeInput,
   5571                !aOptions.mNeverCancelable);
   5572  MOZ_ASSERT_IF(
   5573      aEventMessage == eEditorBeforeInput && aOptions.mNeverCancelable,
   5574      aEditorInputType == EditorInputType::eInsertReplacementText);
   5575 
   5576  nsCOMPtr<nsIWidget> widget;
   5577  if (aEditorBase) {
   5578    widget = aEditorBase->GetWidget();
   5579    if (NS_WARN_IF(!widget)) {
   5580      return NS_ERROR_FAILURE;
   5581    }
   5582  } else {
   5583    Document* document = aEventTargetElement->OwnerDoc();
   5584    if (NS_WARN_IF(!document)) {
   5585      return NS_ERROR_FAILURE;
   5586    }
   5587    // If we're running xpcshell tests, we fail to get presShell here.
   5588    // Even in such case, we need to dispatch "input" event without widget.
   5589    PresShell* presShell = document->GetPresShell();
   5590    if (presShell) {
   5591      nsPresContext* presContext = presShell->GetPresContext();
   5592      if (NS_WARN_IF(!presContext)) {
   5593        return NS_ERROR_FAILURE;
   5594      }
   5595      widget = presContext->GetRootWidget();
   5596      if (NS_WARN_IF(!widget)) {
   5597        return NS_ERROR_FAILURE;
   5598      }
   5599    }
   5600  }
   5601 
   5602  // Dispatch "input" event with InputEvent instance.
   5603  InternalEditorInputEvent inputEvent(true, aEventMessage, widget);
   5604 
   5605  inputEvent.mFlags.mCancelable =
   5606      !aOptions.mNeverCancelable && aEventMessage == eEditorBeforeInput &&
   5607      IsCancelableBeforeInputEvent(aEditorInputType);
   5608  MOZ_ASSERT(!inputEvent.mFlags.mCancelable || aEventStatus);
   5609 
   5610  // If there is an editor, set isComposing to true when it has composition.
   5611  // Note that EditorBase::IsIMEComposing() may return false even when we
   5612  // need to set it to true.
   5613  // Otherwise, i.e., editor hasn't been created for the element yet,
   5614  // we should set isComposing to false since the element can never has
   5615  // composition without editor.
   5616  inputEvent.mIsComposing = aEditorBase && aEditorBase->GetComposition();
   5617 
   5618  if (!aEditorBase || aEditorBase->IsTextEditor()) {
   5619    if (IsDataAvailableOnTextEditor(aEditorInputType)) {
   5620      inputEvent.mData = std::move(aOptions.mData);
   5621      MOZ_ASSERT(!inputEvent.mData.IsVoid(),
   5622                 "inputEvent.mData shouldn't be void");
   5623    }
   5624 #ifdef DEBUG
   5625    else {
   5626      MOZ_ASSERT(inputEvent.mData.IsVoid(), "inputEvent.mData should be void");
   5627    }
   5628 #endif  // #ifdef DEBUG
   5629    MOZ_ASSERT(
   5630        aOptions.mTargetRanges.IsEmpty(),
   5631        "Target ranges for <input> and <textarea> should always be empty");
   5632  } else {
   5633    MOZ_ASSERT(aEditorBase->IsHTMLEditor());
   5634    if (IsDataAvailableOnHTMLEditor(aEditorInputType)) {
   5635      inputEvent.mData = std::move(aOptions.mData);
   5636      MOZ_ASSERT(!inputEvent.mData.IsVoid(),
   5637                 "inputEvent.mData shouldn't be void");
   5638    } else {
   5639      MOZ_ASSERT(inputEvent.mData.IsVoid(), "inputEvent.mData should be void");
   5640      if (IsDataTransferAvailableOnHTMLEditor(aEditorInputType)) {
   5641        inputEvent.mDataTransfer = std::move(aOptions.mDataTransfer);
   5642        MOZ_ASSERT(inputEvent.mDataTransfer,
   5643                   "inputEvent.mDataTransfer shouldn't be nullptr");
   5644        MOZ_ASSERT(inputEvent.mDataTransfer->IsReadOnly(),
   5645                   "inputEvent.mDataTransfer should be read only");
   5646      }
   5647 #ifdef DEBUG
   5648      else {
   5649        MOZ_ASSERT(!inputEvent.mDataTransfer,
   5650                   "inputEvent.mDataTransfer should be nullptr");
   5651      }
   5652 #endif  // #ifdef DEBUG
   5653    }
   5654    if (aEventMessage == eEditorBeforeInput &&
   5655        MayHaveTargetRangesOnHTMLEditor(aEditorInputType)) {
   5656      inputEvent.mTargetRanges = std::move(aOptions.mTargetRanges);
   5657    }
   5658 #ifdef DEBUG
   5659    else {
   5660      MOZ_ASSERT(aOptions.mTargetRanges.IsEmpty(),
   5661                 "Target ranges shouldn't be set for the dispatching event");
   5662    }
   5663 #endif  // #ifdef DEBUG
   5664  }
   5665 
   5666  inputEvent.mInputType = aEditorInputType;
   5667 
   5668  // If we cannot dispatch an event right now, we cannot make it cancelable.
   5669  if (!nsContentUtils::IsSafeToRunScript()) {
   5670    NS_ASSERTION(
   5671        !inputEvent.mFlags.mCancelable,
   5672        "Cancelable beforeinput event dispatcher should run when it's safe");
   5673    inputEvent.mFlags.mCancelable = false;
   5674  }
   5675  MOZ_LOG(gInputEventLog, LogLevel::Info,
   5676          ("Dispatching %s, safe?=%s, inputType=%s, aEditorBase=%p, "
   5677           "aEventTargetElement=%s",
   5678           ToChar(inputEvent.mMessage),
   5679           YesOrNo(nsContentUtils::IsSafeToRunScript()),
   5680           ToString(inputEvent.mInputType).c_str(), aEditorBase,
   5681           ToString(RefPtr{aEventTargetElement}).c_str()));
   5682  return AsyncEventDispatcher::RunDOMEventWhenSafe(*aEventTargetElement,
   5683                                                   inputEvent, aEventStatus);
   5684 }
   5685 
   5686 nsresult nsContentUtils::DispatchChromeEvent(
   5687    Document* aDoc, EventTarget* aTarget, const nsAString& aEventName,
   5688    CanBubble aCanBubble, Cancelable aCancelable, bool* aDefaultAction) {
   5689  if (!aDoc || !aTarget) {
   5690    return NS_ERROR_INVALID_ARG;
   5691  }
   5692 
   5693  if (!aDoc->GetWindow()) {
   5694    return NS_ERROR_INVALID_ARG;
   5695  }
   5696 
   5697  EventTarget* piTarget = aDoc->GetWindow()->GetParentTarget();
   5698  if (!piTarget) {
   5699    return NS_ERROR_INVALID_ARG;
   5700  }
   5701 
   5702  ErrorResult err;
   5703  RefPtr<Event> event =
   5704      GetEventWithTarget(aDoc, aTarget, aEventName, aCanBubble, aCancelable,
   5705                         Composed::eDefault, Trusted::eYes, err);
   5706  if (err.Failed()) {
   5707    return err.StealNSResult();
   5708  }
   5709 
   5710  bool defaultActionEnabled =
   5711      piTarget->DispatchEvent(*event, CallerType::System, err);
   5712  if (aDefaultAction) {
   5713    *aDefaultAction = defaultActionEnabled;
   5714  }
   5715  return err.StealNSResult();
   5716 }
   5717 
   5718 void nsContentUtils::RequestFrameFocus(Element& aFrameElement, bool aCanRaise,
   5719                                       CallerType aCallerType) {
   5720  RefPtr<Element> target = &aFrameElement;
   5721  bool defaultAction = true;
   5722  if (aCanRaise) {
   5723    DispatchEventOnlyToChrome(target->OwnerDoc(), target,
   5724                              u"framefocusrequested"_ns, CanBubble::eYes,
   5725                              Cancelable::eYes, &defaultAction);
   5726  }
   5727  if (!defaultAction) {
   5728    return;
   5729  }
   5730 
   5731  RefPtr<nsFocusManager> fm = nsFocusManager::GetFocusManager();
   5732  if (!fm) {
   5733    return;
   5734  }
   5735 
   5736  uint32_t flags = nsIFocusManager::FLAG_NOSCROLL;
   5737  if (aCanRaise) {
   5738    flags |= nsIFocusManager::FLAG_RAISE;
   5739  }
   5740 
   5741  if (aCallerType == CallerType::NonSystem) {
   5742    flags |= nsIFocusManager::FLAG_NONSYSTEMCALLER;
   5743  }
   5744 
   5745  fm->SetFocus(target, flags);
   5746 }
   5747 
   5748 nsresult nsContentUtils::DispatchEventOnlyToChrome(
   5749    Document* aDoc, EventTarget* aTarget, const nsAString& aEventName,
   5750    CanBubble aCanBubble, Cancelable aCancelable, Composed aComposed,
   5751    bool* aDefaultAction) {
   5752  return DispatchEvent(aDoc, aTarget, aEventName, aCanBubble, aCancelable,
   5753                       aComposed, Trusted::eYes, aDefaultAction,
   5754                       ChromeOnlyDispatch::eYes);
   5755 }
   5756 
   5757 /* static */
   5758 Element* nsContentUtils::MatchElementId(nsIContent* aContent,
   5759                                        const nsAtom* aId) {
   5760  for (nsIContent* cur = aContent; cur; cur = cur->GetNextNode(aContent)) {
   5761    if (aId == cur->GetID()) {
   5762      return cur->AsElement();
   5763    }
   5764  }
   5765 
   5766  return nullptr;
   5767 }
   5768 
   5769 /* static */
   5770 Element* nsContentUtils::MatchElementId(nsIContent* aContent,
   5771                                        const nsAString& aId) {
   5772  MOZ_ASSERT(!aId.IsEmpty(), "Will match random elements");
   5773 
   5774  // ID attrs are generally stored as atoms, so just atomize this up front
   5775  RefPtr<nsAtom> id(NS_Atomize(aId));
   5776  if (!id) {
   5777    // OOM, so just bail
   5778    return nullptr;
   5779  }
   5780 
   5781  return MatchElementId(aContent, id);
   5782 }
   5783 
   5784 /* static */
   5785 void nsContentUtils::RegisterShutdownObserver(nsIObserver* aObserver) {
   5786  nsCOMPtr<nsIObserverService> observerService =
   5787      mozilla::services::GetObserverService();
   5788  if (observerService) {
   5789    observerService->AddObserver(aObserver, NS_XPCOM_SHUTDOWN_OBSERVER_ID,
   5790                                 false);
   5791  }
   5792 }
   5793 
   5794 /* static */
   5795 void nsContentUtils::UnregisterShutdownObserver(nsIObserver* aObserver) {
   5796  nsCOMPtr<nsIObserverService> observerService =
   5797      mozilla::services::GetObserverService();
   5798  if (observerService) {
   5799    observerService->RemoveObserver(aObserver, NS_XPCOM_SHUTDOWN_OBSERVER_ID);
   5800  }
   5801 }
   5802 
   5803 /* static */
   5804 bool nsContentUtils::HasNonEmptyAttr(const nsIContent* aContent,
   5805                                     int32_t aNameSpaceID, nsAtom* aName) {
   5806  static AttrArray::AttrValuesArray strings[] = {nsGkAtoms::_empty, nullptr};
   5807  return aContent->IsElement() &&
   5808         aContent->AsElement()->FindAttrValueIn(aNameSpaceID, aName, strings,
   5809                                                eCaseMatters) ==
   5810             AttrArray::ATTR_VALUE_NO_MATCH;
   5811 }
   5812 
   5813 void nsContentUtils::NotifyDevToolsOfNodeRemoval(nsINode& aRemovingNode) {
   5814  // Having an explicit check here since it's an easy mistake to fall into,
   5815  // and there might be existing code with problems. We'd rather be safe
   5816  // than fire a chrome only event in all corner cases. We also rely on it for
   5817  // nsAutoScriptBlockerSuppressNodeRemoved.
   5818  if (!IsSafeToRunScript()) {
   5819    // This checks that IsSafeToRunScript is true since we don't want to fire
   5820    // events when that is false. We can't rely on EventDispatcher to assert
   5821    // this in this situation since most of the time DevTools is not observing
   5822    // the mutations, in which case we won't even attempt to dispatch events.
   5823    // However this also allows for two exceptions. First off, we don't assert
   5824    // if the mutation happens to native anonymous content since we never fire
   5825    // mutation events on such content anyway.
   5826    // Second, we don't assert if sDOMNodeRemovedSuppressCount is true since
   5827    // that is a know case when we'd normally fire a mutation event, but can't
   5828    // make that safe and so we suppress it at this time. Ideally this should
   5829    // go away eventually.
   5830    if (!aRemovingNode.IsInNativeAnonymousSubtree() &&
   5831        !sDOMNodeRemovedSuppressCount) {
   5832      NS_ERROR(
   5833          "Want to fire \"devtoolschildremoved\" event, but it's not safe");
   5834      WarnScriptWasIgnored(aRemovingNode.OwnerDoc());
   5835    }
   5836    return;
   5837  }
   5838 
   5839  if (MOZ_UNLIKELY(aRemovingNode.DevToolsShouldBeNotifiedOfThisRemoval())) {
   5840    const RefPtr<Document> doc = aRemovingNode.OwnerDoc();
   5841    DispatchChromeEvent(doc, &aRemovingNode, u"devtoolschildremoved"_ns,
   5842                        CanBubble::eNo, Cancelable::eNo);
   5843  }
   5844 }
   5845 
   5846 void nsContentUtils::UnmarkGrayJSListenersInCCGenerationDocuments() {
   5847  if (!sEventListenerManagersHash) {
   5848    return;
   5849  }
   5850 
   5851  for (auto i = sEventListenerManagersHash->Iter(); !i.Done(); i.Next()) {
   5852    auto entry = static_cast<EventListenerManagerMapEntry*>(i.Get());
   5853    nsINode* n = static_cast<nsINode*>(entry->mListenerManager->GetTarget());
   5854    if (n && n->IsInComposedDoc() &&
   5855        nsCCUncollectableMarker::InGeneration(
   5856            n->OwnerDoc()->GetMarkedCCGeneration())) {
   5857      entry->mListenerManager->MarkForCC();
   5858    }
   5859  }
   5860 }
   5861 
   5862 /* static */
   5863 void nsContentUtils::TraverseListenerManager(
   5864    nsINode* aNode, nsCycleCollectionTraversalCallback& cb) {
   5865  if (!sEventListenerManagersHash) {
   5866    // We're already shut down, just return.
   5867    return;
   5868  }
   5869 
   5870  auto entry = static_cast<EventListenerManagerMapEntry*>(
   5871      sEventListenerManagersHash->Search(aNode));
   5872  if (entry) {
   5873    CycleCollectionNoteChild(cb, entry->mListenerManager.get(),
   5874                             "[via hash] mListenerManager");
   5875  }
   5876 }
   5877 
   5878 EventListenerManager* nsContentUtils::GetListenerManagerForNode(
   5879    nsINode* aNode) {
   5880  if (!sEventListenerManagersHash) {
   5881    // We're already shut down, don't bother creating an event listener
   5882    // manager.
   5883 
   5884    return nullptr;
   5885  }
   5886 
   5887  auto entry = static_cast<EventListenerManagerMapEntry*>(
   5888      sEventListenerManagersHash->Add(aNode, fallible));
   5889 
   5890  if (!entry) {
   5891    return nullptr;
   5892  }
   5893 
   5894  if (!entry->mListenerManager) {
   5895    entry->mListenerManager = new EventListenerManager(aNode);
   5896 
   5897    aNode->SetFlags(NODE_HAS_LISTENERMANAGER);
   5898  }
   5899 
   5900  return entry->mListenerManager;
   5901 }
   5902 
   5903 EventListenerManager* nsContentUtils::GetExistingListenerManagerForNode(
   5904    const nsINode* aNode) {
   5905  if (!aNode->HasFlag(NODE_HAS_LISTENERMANAGER)) {
   5906    return nullptr;
   5907  }
   5908 
   5909  if (!sEventListenerManagersHash) {
   5910    // We're already shut down, don't bother creating an event listener
   5911    // manager.
   5912 
   5913    return nullptr;
   5914  }
   5915 
   5916  auto entry = static_cast<EventListenerManagerMapEntry*>(
   5917      sEventListenerManagersHash->Search(aNode));
   5918  if (entry) {
   5919    return entry->mListenerManager;
   5920  }
   5921 
   5922  return nullptr;
   5923 }
   5924 
   5925 void nsContentUtils::AddEntryToDOMArenaTable(nsINode* aNode,
   5926                                             DOMArena* aDOMArena) {
   5927  MOZ_ASSERT(StaticPrefs::dom_arena_allocator_enabled_AtStartup());
   5928  MOZ_ASSERT_IF(sDOMArenaHashtable, !sDOMArenaHashtable->Contains(aNode));
   5929  MOZ_ASSERT(!aNode->HasFlag(NODE_KEEPS_DOMARENA));
   5930  if (!sDOMArenaHashtable) {
   5931    sDOMArenaHashtable =
   5932        new nsRefPtrHashtable<nsPtrHashKey<const nsINode>, dom::DOMArena>();
   5933  }
   5934  aNode->SetFlags(NODE_KEEPS_DOMARENA);
   5935  sDOMArenaHashtable->InsertOrUpdate(aNode, RefPtr<DOMArena>(aDOMArena));
   5936 }
   5937 
   5938 already_AddRefed<DOMArena> nsContentUtils::TakeEntryFromDOMArenaTable(
   5939    const nsINode* aNode) {
   5940  MOZ_ASSERT(sDOMArenaHashtable->Contains(aNode));
   5941  MOZ_ASSERT(StaticPrefs::dom_arena_allocator_enabled_AtStartup());
   5942  RefPtr<DOMArena> arena;
   5943  sDOMArenaHashtable->Remove(aNode, getter_AddRefs(arena));
   5944  return arena.forget();
   5945 }
   5946 
   5947 /* static */
   5948 void nsContentUtils::RemoveListenerManager(nsINode* aNode) {
   5949  if (sEventListenerManagersHash) {
   5950    auto entry = static_cast<EventListenerManagerMapEntry*>(
   5951        sEventListenerManagersHash->Search(aNode));
   5952    if (entry) {
   5953      RefPtr<EventListenerManager> listenerManager;
   5954      listenerManager.swap(entry->mListenerManager);
   5955      // Remove the entry and *then* do operations that could cause further
   5956      // modification of sEventListenerManagersHash.  See bug 334177.
   5957      sEventListenerManagersHash->RawRemove(entry);
   5958      if (listenerManager) {
   5959        listenerManager->Disconnect();
   5960      }
   5961    }
   5962  }
   5963 }
   5964 
   5965 /* static */
   5966 bool nsContentUtils::IsValidNodeName(nsAtom* aLocalName, nsAtom* aPrefix,
   5967                                     int32_t aNamespaceID) {
   5968  if (aNamespaceID == kNameSpaceID_Unknown) {
   5969    return false;
   5970  }
   5971 
   5972  if (!aPrefix) {
   5973    // If the prefix is null, then either the QName must be xmlns or the
   5974    // namespace must not be XMLNS.
   5975    return (aLocalName == nsGkAtoms::xmlns) ==
   5976           (aNamespaceID == kNameSpaceID_XMLNS);
   5977  }
   5978 
   5979  // If the prefix is non-null then the namespace must not be null.
   5980  if (aNamespaceID == kNameSpaceID_None) {
   5981    return false;
   5982  }
   5983 
   5984  // If the namespace is the XMLNS namespace then the prefix must be xmlns,
   5985  // but the localname must not be xmlns.
   5986  if (aNamespaceID == kNameSpaceID_XMLNS) {
   5987    return aPrefix == nsGkAtoms::xmlns && aLocalName != nsGkAtoms::xmlns;
   5988  }
   5989 
   5990  // If the namespace is not the XMLNS namespace then the prefix must not be
   5991  // xmlns.
   5992  // If the namespace is the XML namespace then the prefix can be anything.
   5993  // If the namespace is not the XML namespace then the prefix must not be xml.
   5994  return aPrefix != nsGkAtoms::xmlns &&
   5995         (aNamespaceID == kNameSpaceID_XML || aPrefix != nsGkAtoms::xml);
   5996 }
   5997 
   5998 already_AddRefed<DocumentFragment> nsContentUtils::CreateContextualFragment(
   5999    nsINode* aContextNode, const nsAString& aFragment,
   6000    bool aPreventScriptExecution, ErrorResult& aRv) {
   6001  if (!aContextNode) {
   6002    aRv.Throw(NS_ERROR_INVALID_ARG);
   6003    return nullptr;
   6004  }
   6005 
   6006  // If we don't have a document here, we can't get the right security context
   6007  // for compiling event handlers... so just bail out.
   6008  RefPtr<Document> document = aContextNode->OwnerDoc();
   6009  bool isHTML = document->IsHTMLDocument();
   6010 
   6011  if (isHTML) {
   6012    RefPtr<DocumentFragment> frag = new (document->NodeInfoManager())
   6013        DocumentFragment(document->NodeInfoManager());
   6014 
   6015    Element* element = aContextNode->GetAsElementOrParentElement();
   6016    if (element && !element->IsHTMLElement(nsGkAtoms::html)) {
   6017      aRv = ParseFragmentHTML(
   6018          aFragment, frag, element->NodeInfo()->NameAtom(),
   6019          element->GetNameSpaceID(),
   6020          (document->GetCompatibilityMode() == eCompatibility_NavQuirks),
   6021          aPreventScriptExecution);
   6022    } else {
   6023      aRv = ParseFragmentHTML(
   6024          aFragment, frag, nsGkAtoms::body, kNameSpaceID_XHTML,
   6025          (document->GetCompatibilityMode() == eCompatibility_NavQuirks),
   6026          aPreventScriptExecution);
   6027    }
   6028 
   6029    return frag.forget();
   6030  }
   6031 
   6032  AutoTArray<nsString, 32> tagStack;
   6033  nsAutoString uriStr, nameStr;
   6034  for (Element* element : aContextNode->InclusiveAncestorsOfType<Element>()) {
   6035    nsString& tagName = *tagStack.AppendElement();
   6036    // It mostly doesn't actually matter what tag name we use here: XML doesn't
   6037    // have parsing that depends on the open tag stack, apart from namespace
   6038    // declarations.  So this whole tagStack bit is just there to get the right
   6039    // namespace declarations to the XML parser.  That said, the parser _is_
   6040    // going to create elements with the tag names we provide here, so we need
   6041    // to make sure they are not names that can trigger custom element
   6042    // constructors.  Just make up a name that is never going to be a valid
   6043    // custom element name.
   6044    //
   6045    // The principled way to do this would probably be to add a new FromParser
   6046    // value and make sure we use it when creating the context elements, then
   6047    // make sure we teach all FromParser consumers (and in particular the custom
   6048    // element code) about it as needed.  But right now the XML parser never
   6049    // actually uses FromParser values other than NOT_FROM_PARSER, and changing
   6050    // that is pretty complicated.
   6051    tagName.AssignLiteral("notacustomelement");
   6052 
   6053    // see if we need to add xmlns declarations
   6054    uint32_t count = element->GetAttrCount();
   6055    bool setDefaultNamespace = false;
   6056    if (count > 0) {
   6057      uint32_t index;
   6058 
   6059      for (index = 0; index < count; index++) {
   6060        const BorrowedAttrInfo info = element->GetAttrInfoAt(index);
   6061        const nsAttrName* name = info.mName;
   6062        if (name->NamespaceEquals(kNameSpaceID_XMLNS)) {
   6063          info.mValue->ToString(uriStr);
   6064 
   6065          // really want something like nsXMLContentSerializer::SerializeAttr
   6066          tagName.AppendLiteral(" xmlns");  // space important
   6067          if (name->GetPrefix()) {
   6068            tagName.Append(char16_t(':'));
   6069            name->LocalName()->ToString(nameStr);
   6070            tagName.Append(nameStr);
   6071          } else {
   6072            setDefaultNamespace = true;
   6073          }
   6074          tagName.AppendLiteral(R"(=")");
   6075          tagName.Append(uriStr);
   6076          tagName.Append('"');
   6077        }
   6078      }
   6079    }
   6080 
   6081    if (!setDefaultNamespace) {
   6082      mozilla::dom::NodeInfo* info = element->NodeInfo();
   6083      if (!info->GetPrefixAtom() && info->NamespaceID() != kNameSpaceID_None) {
   6084        // We have no namespace prefix, but have a namespace ID.  Push
   6085        // default namespace attr in, so that our kids will be in our
   6086        // namespace.
   6087        info->GetNamespaceURI(uriStr);
   6088        tagName.AppendLiteral(R"( xmlns=")");
   6089        tagName.Append(uriStr);
   6090        tagName.Append('"');
   6091      }
   6092    }
   6093  }
   6094 
   6095  RefPtr<DocumentFragment> frag;
   6096  aRv = ParseFragmentXML(aFragment, document, tagStack, aPreventScriptExecution,
   6097                         -1, getter_AddRefs(frag));
   6098  return frag.forget();
   6099 }
   6100 
   6101 /* static */
   6102 void nsContentUtils::DropFragmentParsers() {
   6103  NS_IF_RELEASE(sHTMLFragmentParser);
   6104  NS_IF_RELEASE(sXMLFragmentParser);
   6105  NS_IF_RELEASE(sXMLFragmentSink);
   6106 }
   6107 
   6108 /* static */
   6109 void nsContentUtils::XPCOMShutdown() { nsContentUtils::DropFragmentParsers(); }
   6110 
   6111 /* Helper function to compuate Sanitization Flags for ParseFramentHTML/XML */
   6112 uint32_t computeSanitizationFlags(nsIPrincipal* aPrincipal, int32_t aFlags) {
   6113  uint32_t sanitizationFlags = 0;
   6114  if (aPrincipal->IsSystemPrincipal()) {
   6115    if (aFlags < 0) {
   6116      // if this is a chrome-privileged document and no explicit flags
   6117      // were passed, then use this sanitization flags.
   6118      sanitizationFlags = nsIParserUtils::SanitizerAllowStyle |
   6119                          nsIParserUtils::SanitizerAllowComments |
   6120                          nsIParserUtils::SanitizerDropForms |
   6121                          nsIParserUtils::SanitizerLogRemovals;
   6122    } else {
   6123      // if the caller explicitly passes flags, then we use those
   6124      // flags but additionally drop forms.
   6125      sanitizationFlags = aFlags | nsIParserUtils::SanitizerDropForms;
   6126    }
   6127  } else if (aFlags >= 0) {
   6128    // aFlags by default is -1 and is only ever non equal to -1 if the
   6129    // caller of ParseFragmentHTML/ParseFragmentXML is
   6130    // ParserUtils::ParseFragment(). Only in that case we should use
   6131    // the sanitization flags passed within aFlags.
   6132    sanitizationFlags = aFlags;
   6133  }
   6134  return sanitizationFlags;
   6135 }
   6136 
   6137 // https://wicg.github.io/sanitizer-api/#set-and-filter-html
   6138 static void SetAndFilterHTML(
   6139    FragmentOrElement* aTarget, Element* aContext, const nsAString& aHTML,
   6140    const OwningSanitizerOrSanitizerConfigOrSanitizerPresets& aSanitizerOptions,
   6141    const bool aSafe, ErrorResult& aError) {
   6142  const RefPtr<Document> doc = aTarget->OwnerDoc();
   6143 
   6144  // Step 1. If safe and contextElement’s local name is "script" and
   6145  // contextElement’s namespace is the HTML namespace or the SVG namespace, then
   6146  // return.
   6147  if (aSafe && (aContext->IsHTMLElement(nsGkAtoms::script) ||
   6148                aContext->IsSVGElement(nsGkAtoms::script))) {
   6149    nsContentUtils::ReportToConsole(nsIScriptError::warningFlag, "DOM"_ns, doc,
   6150                                    nsContentUtils::eDOM_PROPERTIES,
   6151                                    "SetHTMLScript");
   6152    return;
   6153  }
   6154 
   6155  // Step 2. Let sanitizer be the result of calling get a sanitizer instance
   6156  // from options with options and safe.
   6157  nsCOMPtr<nsIGlobalObject> global = aTarget->GetOwnerGlobal();
   6158  if (!global) {
   6159    aError.ThrowInvalidStateError("Missing owner global.");
   6160    return;
   6161  }
   6162  RefPtr<Sanitizer> sanitizer =
   6163      Sanitizer::GetInstance(global, aSanitizerOptions, aSafe, aError);
   6164  if (aError.Failed()) {
   6165    return;
   6166  }
   6167 
   6168  aTarget->NotifyDevToolsOfRemovalsOfChildren();
   6169 
   6170  // Needed when innerHTML is used in combination with contenteditable
   6171  mozAutoDocUpdate updateBatch(doc, true);
   6172 
   6173  // Remove childnodes.
   6174  nsAutoMutationBatch mb(aTarget, true, false);
   6175  aTarget->RemoveAllChildren(true);
   6176  mb.RemovalDone();
   6177 
   6178  nsAutoScriptLoaderDisabler sld(doc);
   6179 
   6180  // Step 3. Let newChildren be the result of the HTML fragment parsing
   6181  // algorithm steps given contextElement, html, and true.
   6182  // Step 4. Let fragment be a new DocumentFragment whose node document is
   6183  // contextElement’s node document.
   6184  // Step 5. For each node in newChildren, append node to fragment.
   6185 
   6186  // We MUST NOT cause any requests during parsing, so we'll
   6187  // create an inert Document and parse into a new DocumentFragment.
   6188 
   6189  RefPtr<Document> inertDoc = nsContentUtils::CreateInertHTMLDocument(doc);
   6190  if (!inertDoc) {
   6191    aError = NS_ERROR_FAILURE;
   6192    return;
   6193  }
   6194 
   6195  RefPtr<DocumentFragment> fragment = new (inertDoc->NodeInfoManager())
   6196      DocumentFragment(inertDoc->NodeInfoManager());
   6197 
   6198  nsAtom* contextLocalName = aContext->NodeInfo()->NameAtom();
   6199  int32_t contextNameSpaceID = aContext->GetNameSpaceID();
   6200  aError = nsContentUtils::ParseFragmentHTML(aHTML, fragment, contextLocalName,
   6201                                             contextNameSpaceID, false, true);
   6202  if (aError.Failed()) {
   6203    return;
   6204  }
   6205 
   6206  // Suppress assertion about node removal mutation events that can't have
   6207  // listeners anyway, because no one has had the chance to register
   6208  // mutation listeners on the fragment that comes from the parser.
   6209  nsAutoScriptBlockerSuppressNodeRemoved scriptBlocker;
   6210 
   6211  // Step 6. Run sanitize on fragment using sanitizer and safe.
   6212  sanitizer->Sanitize(fragment, aSafe, aError);
   6213  if (aError.Failed()) {
   6214    return;
   6215  }
   6216 
   6217  // Step 7. Replace all with fragment within target.
   6218  aTarget->AppendChild(*fragment, aError);
   6219  if (aError.Failed()) {
   6220    return;
   6221  }
   6222 
   6223  mb.NodesAdded();
   6224 }
   6225 
   6226 /* static */
   6227 void nsContentUtils::SetHTML(FragmentOrElement* aTarget, Element* aContext,
   6228                             const nsAString& aHTML,
   6229                             const SetHTMLOptions& aOptions,
   6230                             ErrorResult& aError) {
   6231  SetAndFilterHTML(aTarget, aContext, aHTML, aOptions.mSanitizer,
   6232                   /* aSafe */ true, aError);
   6233 }
   6234 
   6235 /* static */
   6236 void nsContentUtils::SetHTMLUnsafe(
   6237    FragmentOrElement* aTarget, Element* aContext,
   6238    const TrustedHTMLOrString& aSource, const SetHTMLUnsafeOptions& aOptions,
   6239    bool aIsShadowRoot, nsIPrincipal* aSubjectPrincipal, ErrorResult& aError) {
   6240  constexpr nsLiteralString elementSink = u"Element setHTMLUnsafe"_ns;
   6241  constexpr nsLiteralString shadowRootSink = u"ShadowRoot setHTMLUnsafe"_ns;
   6242  Maybe<nsAutoString> compliantStringHolder;
   6243  const nsAString* compliantString =
   6244      TrustedTypeUtils::GetTrustedTypesCompliantString(
   6245          aSource, aIsShadowRoot ? shadowRootSink : elementSink,
   6246          kTrustedTypesOnlySinkGroup, *aContext, aSubjectPrincipal,
   6247          compliantStringHolder, aError);
   6248  if (aError.Failed()) {
   6249    return;
   6250  }
   6251 
   6252  // Fallback to the more optimized code below without a sanitizer.
   6253  if (aOptions.mSanitizer.WasPassed()) {
   6254    return SetAndFilterHTML(aTarget, aContext, *compliantString,
   6255                            aOptions.mSanitizer.Value(), /* aSafe */ false,
   6256                            aError);
   6257  }
   6258 
   6259  RefPtr<DocumentFragment> fragment;
   6260  {
   6261    MOZ_ASSERT(!sFragmentParsingActive,
   6262               "Re-entrant fragment parsing attempted.");
   6263    mozilla::AutoRestore<bool> guard(sFragmentParsingActive);
   6264    sFragmentParsingActive = true;
   6265    if (!sHTMLFragmentParser) {
   6266      NS_ADDREF(sHTMLFragmentParser = new nsHtml5StringParser());
   6267      // Now sHTMLFragmentParser owns the object
   6268    }
   6269 
   6270    nsAtom* contextLocalName = aContext->NodeInfo()->NameAtom();
   6271    int32_t contextNameSpaceID = aContext->GetNameSpaceID();
   6272 
   6273    RefPtr<Document> doc = aTarget->OwnerDoc();
   6274    fragment = doc->CreateDocumentFragment();
   6275 
   6276    nsresult rv = sHTMLFragmentParser->ParseFragment(
   6277        *compliantString, fragment, contextLocalName, contextNameSpaceID,
   6278        fragment->OwnerDoc()->GetCompatibilityMode() ==
   6279            eCompatibility_NavQuirks,
   6280        true, true);
   6281    if (NS_FAILED(rv)) {
   6282      NS_WARNING("Failed to parse fragment for SetHTMLUnsafe");
   6283    }
   6284  }
   6285 
   6286  aTarget->ReplaceChildren(fragment, IgnoreErrors());
   6287 }
   6288 
   6289 /* static */
   6290 nsresult nsContentUtils::ParseFragmentHTML(
   6291    const nsAString& aSourceBuffer, nsIContent* aTargetNode,
   6292    nsAtom* aContextLocalName, int32_t aContextNamespace, bool aQuirks,
   6293    bool aPreventScriptExecution, int32_t aFlags) {
   6294  if (nsContentUtils::sFragmentParsingActive) {
   6295    MOZ_ASSERT_UNREACHABLE("Re-entrant fragment parsing attempted.");
   6296    return NS_ERROR_DOM_INVALID_STATE_ERR;
   6297  }
   6298  mozilla::AutoRestore<bool> guard(nsContentUtils::sFragmentParsingActive);
   6299  nsContentUtils::sFragmentParsingActive = true;
   6300  if (!sHTMLFragmentParser) {
   6301    NS_ADDREF(sHTMLFragmentParser = new nsHtml5StringParser());
   6302    // Now sHTMLFragmentParser owns the object
   6303  }
   6304 
   6305  nsCOMPtr<nsIPrincipal> nodePrincipal = aTargetNode->NodePrincipal();
   6306 
   6307 #ifdef DEBUG
   6308  // aFlags should always be -1 unless the caller of ParseFragmentHTML
   6309  // is ParserUtils::ParseFragment() which is the only caller that intends
   6310  // sanitization. For all other callers we need to ensure to call
   6311  // AuditParsingOfHTMLXMLFragments.
   6312  if (aFlags < 0) {
   6313    DOMSecurityMonitor::AuditParsingOfHTMLXMLFragments(nodePrincipal,
   6314                                                       aSourceBuffer);
   6315  }
   6316 #endif
   6317 
   6318  nsIContent* target = aTargetNode;
   6319 
   6320  RefPtr<Document> doc = aTargetNode->OwnerDoc();
   6321  RefPtr<DocumentFragment> fragment;
   6322  // We sanitize if the fragment occurs in a system privileged
   6323  // context, an about: page, or if there are explicit sanitization flags.
   6324  // Please note that about:blank and about:srcdoc inherit the security
   6325  // context from the embedding context and hence are not loaded using
   6326  // an about: scheme principal.
   6327  bool shouldSanitize = nodePrincipal->IsSystemPrincipal() ||
   6328                        nodePrincipal->SchemeIs("about") || aFlags >= 0;
   6329  if (shouldSanitize) {
   6330    if (!doc->IsLoadedAsData()) {
   6331      doc = nsContentUtils::CreateInertHTMLDocument(doc);
   6332      if (!doc) {
   6333        return NS_ERROR_FAILURE;
   6334      }
   6335    }
   6336    fragment =
   6337        new (doc->NodeInfoManager()) DocumentFragment(doc->NodeInfoManager());
   6338    target = fragment;
   6339  }
   6340 
   6341  nsresult rv = sHTMLFragmentParser->ParseFragment(
   6342      aSourceBuffer, target, aContextLocalName, aContextNamespace, aQuirks,
   6343      aPreventScriptExecution, false);
   6344  NS_ENSURE_SUCCESS(rv, rv);
   6345 
   6346  if (fragment) {
   6347    uint32_t sanitizationFlags =
   6348        computeSanitizationFlags(nodePrincipal, aFlags);
   6349    // Don't fire mutation events for nodes removed by the sanitizer.
   6350    nsAutoScriptBlockerSuppressNodeRemoved scriptBlocker;
   6351    nsTreeSanitizer sanitizer(sanitizationFlags);
   6352    sanitizer.Sanitize(fragment);
   6353 
   6354    ErrorResult error;
   6355    aTargetNode->AppendChild(*fragment, error);
   6356    rv = error.StealNSResult();
   6357  }
   6358 
   6359  return rv;
   6360 }
   6361 
   6362 /* static */
   6363 nsresult nsContentUtils::ParseDocumentHTML(
   6364    const nsAString& aSourceBuffer, Document* aTargetDocument,
   6365    bool aScriptingEnabledForNoscriptParsing) {
   6366  if (nsContentUtils::sFragmentParsingActive) {
   6367    MOZ_ASSERT_UNREACHABLE("Re-entrant fragment parsing attempted.");
   6368    return NS_ERROR_DOM_INVALID_STATE_ERR;
   6369  }
   6370  mozilla::AutoRestore<bool> guard(nsContentUtils::sFragmentParsingActive);
   6371  nsContentUtils::sFragmentParsingActive = true;
   6372  if (!sHTMLFragmentParser) {
   6373    NS_ADDREF(sHTMLFragmentParser = new nsHtml5StringParser());
   6374    // Now sHTMLFragmentParser owns the object
   6375  }
   6376  nsresult rv = sHTMLFragmentParser->ParseDocument(
   6377      aSourceBuffer, aTargetDocument, aScriptingEnabledForNoscriptParsing);
   6378  return rv;
   6379 }
   6380 
   6381 /* static */
   6382 nsresult nsContentUtils::ParseFragmentXML(const nsAString& aSourceBuffer,
   6383                                          Document* aDocument,
   6384                                          nsTArray<nsString>& aTagStack,
   6385                                          bool aPreventScriptExecution,
   6386                                          int32_t aFlags,
   6387                                          DocumentFragment** aReturn) {
   6388  if (nsContentUtils::sFragmentParsingActive) {
   6389    MOZ_ASSERT_UNREACHABLE("Re-entrant fragment parsing attempted.");
   6390    return NS_ERROR_DOM_INVALID_STATE_ERR;
   6391  }
   6392  mozilla::AutoRestore<bool> guard(nsContentUtils::sFragmentParsingActive);
   6393  nsContentUtils::sFragmentParsingActive = true;
   6394  if (!sXMLFragmentParser) {
   6395    RefPtr<nsParser> parser = new nsParser();
   6396    parser.forget(&sXMLFragmentParser);
   6397    // sXMLFragmentParser now owns the parser
   6398  }
   6399  if (!sXMLFragmentSink) {
   6400    NS_NewXMLFragmentContentSink(&sXMLFragmentSink);
   6401    // sXMLFragmentSink now owns the sink
   6402  }
   6403  nsCOMPtr<nsIContentSink> contentsink = do_QueryInterface(sXMLFragmentSink);
   6404  MOZ_ASSERT(contentsink, "Sink doesn't QI to nsIContentSink!");
   6405  sXMLFragmentParser->SetContentSink(contentsink);
   6406 
   6407  RefPtr<Document> doc;
   6408  nsCOMPtr<nsIPrincipal> nodePrincipal = aDocument->NodePrincipal();
   6409 
   6410 #ifdef DEBUG
   6411  // aFlags should always be -1 unless the caller of ParseFragmentXML
   6412  // is ParserUtils::ParseFragment() which is the only caller that intends
   6413  // sanitization. For all other callers we need to ensure to call
   6414  // AuditParsingOfHTMLXMLFragments.
   6415  if (aFlags < 0) {
   6416    DOMSecurityMonitor::AuditParsingOfHTMLXMLFragments(nodePrincipal,
   6417                                                       aSourceBuffer);
   6418  }
   6419 #endif
   6420 
   6421  // We sanitize if the fragment occurs in a system privileged
   6422  // context, an about: page, or if there are explicit sanitization flags.
   6423  // Please note that about:blank and about:srcdoc inherit the security
   6424  // context from the embedding context and hence are not loaded using
   6425  // an about: scheme principal.
   6426  bool shouldSanitize = nodePrincipal->IsSystemPrincipal() ||
   6427                        nodePrincipal->SchemeIs("about") || aFlags >= 0;
   6428  if (shouldSanitize && !aDocument->IsLoadedAsData()) {
   6429    doc = nsContentUtils::CreateInertXMLDocument(aDocument);
   6430  } else {
   6431    doc = aDocument;
   6432  }
   6433 
   6434  sXMLFragmentSink->SetTargetDocument(doc);
   6435  sXMLFragmentSink->SetPreventScriptExecution(aPreventScriptExecution);
   6436 
   6437  nsresult rv = sXMLFragmentParser->ParseFragment(aSourceBuffer, aTagStack);
   6438  if (NS_FAILED(rv)) {
   6439    // Drop the fragment parser and sink that might be in an inconsistent state
   6440    NS_IF_RELEASE(sXMLFragmentParser);
   6441    NS_IF_RELEASE(sXMLFragmentSink);
   6442    return rv;
   6443  }
   6444 
   6445  rv = sXMLFragmentSink->FinishFragmentParsing(aReturn);
   6446 
   6447  sXMLFragmentParser->Reset();
   6448  NS_ENSURE_SUCCESS(rv, rv);
   6449 
   6450  if (shouldSanitize) {
   6451    uint32_t sanitizationFlags =
   6452        computeSanitizationFlags(nodePrincipal, aFlags);
   6453    // Don't fire mutation events for nodes removed by the sanitizer.
   6454    nsAutoScriptBlockerSuppressNodeRemoved scriptBlocker;
   6455    nsTreeSanitizer sanitizer(sanitizationFlags);
   6456    sanitizer.Sanitize(*aReturn);
   6457  }
   6458 
   6459  return rv;
   6460 }
   6461 
   6462 /* static */
   6463 nsresult nsContentUtils::ConvertToPlainText(const nsAString& aSourceBuffer,
   6464                                            nsAString& aResultBuffer,
   6465                                            uint32_t aFlags,
   6466                                            uint32_t aWrapCol) {
   6467  RefPtr<Document> document = nsContentUtils::CreateInertHTMLDocument(nullptr);
   6468  if (!document) {
   6469    return NS_ERROR_FAILURE;
   6470  }
   6471 
   6472  nsresult rv = nsContentUtils::ParseDocumentHTML(
   6473      aSourceBuffer, document,
   6474      !(aFlags & nsIDocumentEncoder::OutputNoScriptContent));
   6475  NS_ENSURE_SUCCESS(rv, rv);
   6476 
   6477  nsCOMPtr<nsIDocumentEncoder> encoder = do_createDocumentEncoder("text/plain");
   6478 
   6479  rv = encoder->Init(document, u"text/plain"_ns, aFlags);
   6480  NS_ENSURE_SUCCESS(rv, rv);
   6481 
   6482  encoder->SetWrapColumn(aWrapCol);
   6483 
   6484  return encoder->EncodeToString(aResultBuffer);
   6485 }
   6486 
   6487 static already_AddRefed<Document> CreateInertDocument(const Document* aTemplate,
   6488                                                      DocumentFlavor aFlavor) {
   6489  if (aTemplate) {
   6490    bool hasHad = true;
   6491    nsIScriptGlobalObject* sgo = aTemplate->GetScriptHandlingObject(hasHad);
   6492    NS_ENSURE_TRUE(sgo || !hasHad, nullptr);
   6493 
   6494    nsCOMPtr<Document> doc;
   6495    nsresult rv = NS_NewDOMDocument(
   6496        getter_AddRefs(doc), u""_ns, u""_ns, nullptr,
   6497        aTemplate->GetDocumentURI(), aTemplate->GetDocBaseURI(),
   6498        aTemplate->NodePrincipal(), LoadedAsData::AsData, sgo, aFlavor);
   6499    if (NS_FAILED(rv)) {
   6500      return nullptr;
   6501    }
   6502    return doc.forget();
   6503  }
   6504  nsCOMPtr<nsIURI> uri;
   6505  NS_NewURI(getter_AddRefs(uri), "about:blank"_ns);
   6506  if (!uri) {
   6507    return nullptr;
   6508  }
   6509 
   6510  RefPtr<NullPrincipal> nullPrincipal =
   6511      NullPrincipal::CreateWithoutOriginAttributes();
   6512  if (!nullPrincipal) {
   6513    return nullptr;
   6514  }
   6515 
   6516  nsCOMPtr<Document> doc;
   6517  nsresult rv =
   6518      NS_NewDOMDocument(getter_AddRefs(doc), u""_ns, u""_ns, nullptr, uri, uri,
   6519                        nullPrincipal, LoadedAsData::AsData, nullptr, aFlavor);
   6520  if (NS_FAILED(rv)) {
   6521    return nullptr;
   6522  }
   6523  return doc.forget();
   6524 }
   6525 
   6526 /* static */
   6527 already_AddRefed<Document> nsContentUtils::CreateInertXMLDocument(
   6528    const Document* aTemplate) {
   6529  return CreateInertDocument(aTemplate, DocumentFlavor::XML);
   6530 }
   6531 
   6532 /* static */
   6533 already_AddRefed<Document> nsContentUtils::CreateInertHTMLDocument(
   6534    const Document* aTemplate) {
   6535  return CreateInertDocument(aTemplate, DocumentFlavor::HTML);
   6536 }
   6537 
   6538 /* static */
   6539 nsresult nsContentUtils::SetNodeTextContent(
   6540    nsIContent* aContent, const nsAString& aValue, bool aTryReuse,
   6541    MutationEffectOnScript aMutationEffectOnScript) {
   6542  // Optimize the common case of there being no observers
   6543  if (MOZ_UNLIKELY(
   6544          aContent->MaybeNeedsToNotifyDevToolsOfNodeRemovalsInOwnerDoc())) {
   6545    if (aTryReuse) {
   6546      bool skipFirstText = true;
   6547      for (nsCOMPtr<nsINode> child = aContent->GetFirstChild();
   6548           child && child->GetParentNode() == aContent;
   6549           child = child->GetNextSibling()) {
   6550        if (skipFirstText && child->IsText()) {
   6551          skipFirstText = false;
   6552          continue;
   6553        }
   6554        nsContentUtils::NotifyDevToolsOfNodeRemoval(*child);
   6555      }
   6556    } else {
   6557      aContent->NotifyDevToolsOfRemovalsOfChildren();
   6558    }
   6559  }
   6560 
   6561  // Might as well stick a batch around this since we're performing several
   6562  // mutations.
   6563  mozAutoDocUpdate updateBatch(aContent->GetComposedDoc(), true);
   6564  nsAutoMutationBatch mb;
   6565 
   6566  if (aTryReuse && !aValue.IsEmpty()) {
   6567    // Let's remove nodes until we find a eTEXT.
   6568    while (aContent->HasChildren()) {
   6569      nsIContent* child = aContent->GetFirstChild();
   6570      if (child->IsText()) {
   6571        break;
   6572      }
   6573      aContent->RemoveChildNode(child, true, nullptr, nullptr,
   6574                                aMutationEffectOnScript);
   6575    }
   6576 
   6577    // If we have a node, it must be a eTEXT and we reuse it.
   6578    if (aContent->HasChildren()) {
   6579      nsIContent* child = aContent->GetFirstChild();
   6580      nsresult rv = child->AsText()->SetText(aValue, true);
   6581      NS_ENSURE_SUCCESS(rv, rv);
   6582 
   6583      // All the following nodes, if they exist, must be deleted.
   6584      while (nsIContent* lastChild = aContent->GetLastChild()) {
   6585        if (lastChild == child) {
   6586          break;
   6587        }
   6588        aContent->RemoveChildNode(lastChild, true, nullptr, nullptr,
   6589                                  aMutationEffectOnScript);
   6590      }
   6591    }
   6592 
   6593    if (aContent->HasChildren()) {
   6594      return NS_OK;
   6595    }
   6596  } else {
   6597    mb.Init(aContent, true, false);
   6598    aContent->RemoveAllChildren(true);
   6599  }
   6600  mb.RemovalDone();
   6601 
   6602  if (aValue.IsEmpty()) {
   6603    return NS_OK;
   6604  }
   6605 
   6606  RefPtr<nsTextNode> textContent = new (aContent->NodeInfo()->NodeInfoManager())
   6607      nsTextNode(aContent->NodeInfo()->NodeInfoManager());
   6608 
   6609  textContent->SetText(aValue, true);
   6610 
   6611  ErrorResult rv;
   6612  aContent->AppendChildTo(textContent, true, rv, aMutationEffectOnScript);
   6613  mb.NodesAdded();
   6614  return rv.StealNSResult();
   6615 }
   6616 
   6617 static bool AppendNodeTextContentsRecurse(const nsINode* aNode,
   6618                                          nsAString& aResult,
   6619                                          const fallible_t& aFallible) {
   6620  for (nsIContent* child = aNode->GetFirstChild(); child;
   6621       child = child->GetNextSibling()) {
   6622    if (child->IsElement()) {
   6623      bool ok = AppendNodeTextContentsRecurse(child, aResult, aFallible);
   6624      if (!ok) {
   6625        return false;
   6626      }
   6627    } else if (Text* text = child->GetAsText()) {
   6628      bool ok = text->AppendTextTo(aResult, aFallible);
   6629      if (!ok) {
   6630        return false;
   6631      }
   6632    }
   6633  }
   6634 
   6635  return true;
   6636 }
   6637 
   6638 /* static */
   6639 bool nsContentUtils::AppendNodeTextContent(const nsINode* aNode, bool aDeep,
   6640                                           nsAString& aResult,
   6641                                           const fallible_t& aFallible) {
   6642  if (const Text* text = aNode->GetAsText()) {
   6643    return text->AppendTextTo(aResult, aFallible);
   6644  }
   6645  if (aDeep) {
   6646    return AppendNodeTextContentsRecurse(aNode, aResult, aFallible);
   6647  }
   6648 
   6649  for (nsIContent* child = aNode->GetFirstChild(); child;
   6650       child = child->GetNextSibling()) {
   6651    if (Text* text = child->GetAsText()) {
   6652      bool ok = text->AppendTextTo(aResult, fallible);
   6653      if (!ok) {
   6654        return false;
   6655      }
   6656    }
   6657  }
   6658  return true;
   6659 }
   6660 
   6661 bool nsContentUtils::HasNonEmptyTextContent(
   6662    nsINode* aNode, TextContentDiscoverMode aDiscoverMode) {
   6663  for (nsIContent* child = aNode->GetFirstChild(); child;
   6664       child = child->GetNextSibling()) {
   6665    if (child->IsText() && child->TextLength() > 0) {
   6666      return true;
   6667    }
   6668 
   6669    if (aDiscoverMode == eRecurseIntoChildren &&
   6670        HasNonEmptyTextContent(child, aDiscoverMode)) {
   6671      return true;
   6672    }
   6673  }
   6674 
   6675  return false;
   6676 }
   6677 
   6678 /* static */
   6679 bool nsContentUtils::IsInSameAnonymousTree(const nsINode* aNode,
   6680                                           const nsINode* aOtherNode) {
   6681  MOZ_ASSERT(aNode, "Must have a node to work with");
   6682  MOZ_ASSERT(aOtherNode, "Must have a content to work with");
   6683 
   6684  const bool anon = aNode->IsInNativeAnonymousSubtree();
   6685  if (anon != aOtherNode->IsInNativeAnonymousSubtree()) {
   6686    return false;
   6687  }
   6688 
   6689  if (anon) {
   6690    return aOtherNode->GetClosestNativeAnonymousSubtreeRoot() ==
   6691           aNode->GetClosestNativeAnonymousSubtreeRoot();
   6692  }
   6693 
   6694  // FIXME: This doesn't deal with disconnected nodes whatsoever, but it didn't
   6695  // use to either. Maybe that's fine.
   6696  return aNode->GetContainingShadow() == aOtherNode->GetContainingShadow();
   6697 }
   6698 
   6699 /* static */
   6700 bool nsContentUtils::IsInInteractiveHTMLContent(const Element* aElement,
   6701                                                const Element* aStop) {
   6702  const Element* element = aElement;
   6703  while (element && element != aStop) {
   6704    if (element->IsInteractiveHTMLContent()) {
   6705      return true;
   6706    }
   6707    element = element->GetFlattenedTreeParentElement();
   6708  }
   6709  return false;
   6710 }
   6711 
   6712 /* static */
   6713 void nsContentUtils::NotifyInstalledMenuKeyboardListener(bool aInstalling) {
   6714  IMEStateManager::OnInstalledMenuKeyboardListener(aInstalling);
   6715 }
   6716 
   6717 /* static */
   6718 bool nsContentUtils::SchemeIs(nsIURI* aURI, const char* aScheme) {
   6719  nsCOMPtr<nsIURI> baseURI = NS_GetInnermostURI(aURI);
   6720  NS_ENSURE_TRUE(baseURI, false);
   6721  return baseURI->SchemeIs(aScheme);
   6722 }
   6723 
   6724 bool nsContentUtils::IsExpandedPrincipal(nsIPrincipal* aPrincipal) {
   6725  return aPrincipal && aPrincipal->GetIsExpandedPrincipal();
   6726 }
   6727 
   6728 bool nsContentUtils::IsSystemOrExpandedPrincipal(nsIPrincipal* aPrincipal) {
   6729  return (aPrincipal && aPrincipal->IsSystemPrincipal()) ||
   6730         IsExpandedPrincipal(aPrincipal);
   6731 }
   6732 
   6733 nsIPrincipal* nsContentUtils::GetSystemPrincipal() {
   6734  MOZ_ASSERT(IsInitialized());
   6735  return sSystemPrincipal;
   6736 }
   6737 
   6738 bool nsContentUtils::CombineResourcePrincipals(
   6739    nsCOMPtr<nsIPrincipal>* aResourcePrincipal, nsIPrincipal* aExtraPrincipal) {
   6740  if (!aExtraPrincipal) {
   6741    return false;
   6742  }
   6743  if (!*aResourcePrincipal) {
   6744    *aResourcePrincipal = aExtraPrincipal;
   6745    return true;
   6746  }
   6747  if (*aResourcePrincipal == aExtraPrincipal) {
   6748    return false;
   6749  }
   6750  bool subsumes;
   6751  if (NS_SUCCEEDED(
   6752          (*aResourcePrincipal)->Subsumes(aExtraPrincipal, &subsumes)) &&
   6753      subsumes) {
   6754    return false;
   6755  }
   6756  *aResourcePrincipal = sSystemPrincipal;
   6757  return true;
   6758 }
   6759 
   6760 /* static */
   6761 void nsContentUtils::TriggerLinkClick(
   6762    nsIContent* aContent, nsIURI* aLinkURI, const nsString& aTargetSpec,
   6763    UserNavigationInvolvement aUserInvolvement) {
   6764  MOZ_ASSERT(aLinkURI, "No link URI");
   6765 
   6766  if (aContent->IsEditable() || !aContent->OwnerDoc()->LinkHandlingEnabled()) {
   6767    return;
   6768  }
   6769 
   6770  RefPtr<nsDocShell> docShell =
   6771      nsDocShell::Cast(aContent->OwnerDoc()->GetDocShell());
   6772  if (!docShell) {
   6773    return;
   6774  }
   6775 
   6776  // Check that this page is allowed to load this URI.
   6777  nsresult proceed = NS_OK;
   6778 
   6779  if (sSecurityManager) {
   6780    uint32_t flag = static_cast<uint32_t>(nsIScriptSecurityManager::STANDARD);
   6781    proceed = sSecurityManager->CheckLoadURIWithPrincipal(
   6782        aContent->NodePrincipal(), aLinkURI, flag,
   6783        aContent->OwnerDoc()->InnerWindowID());
   6784  }
   6785 
   6786  // Only pass off the click event if the script security manager says it's ok.
   6787  // We need to rest aTargetSpec for forced downloads.
   6788  if (NS_SUCCEEDED(proceed)) {
   6789    // A link/area element with a download attribute is allowed to set
   6790    // a pseudo Content-Disposition header.
   6791    // For security reasons we only allow websites to declare same-origin
   6792    // resources as downloadable. If this check fails we will just do the normal
   6793    // thing (i.e. navigate to the resource).
   6794    nsAutoString fileName;
   6795    if ((!aContent->IsHTMLElement(nsGkAtoms::a) &&
   6796         !aContent->IsHTMLElement(nsGkAtoms::area) &&
   6797         !aContent->IsSVGElement(nsGkAtoms::a)) ||
   6798        !aContent->AsElement()->GetAttr(nsGkAtoms::download, fileName) ||
   6799        NS_FAILED(aContent->NodePrincipal()->CheckMayLoad(aLinkURI, true))) {
   6800      fileName.SetIsVoid(true);  // No actionable download attribute was found.
   6801    }
   6802 
   6803    nsCOMPtr<nsIPrincipal> triggeringPrincipal = aContent->NodePrincipal();
   6804    nsCOMPtr<nsIPolicyContainer> policyContainer =
   6805        aContent->GetPolicyContainer();
   6806 
   6807    // Sanitize fileNames containing null characters by replacing them with
   6808    // underscores.
   6809    if (!fileName.IsVoid()) {
   6810      fileName.ReplaceChar(char16_t(0), '_');
   6811    }
   6812 
   6813    docShell->OnLinkClick(
   6814        aContent, aLinkURI, fileName.IsVoid() ? aTargetSpec : u""_ns, fileName,
   6815        nullptr, nullptr, UserActivation::IsHandlingUserInput(),
   6816        aUserInvolvement, triggeringPrincipal, policyContainer);
   6817  }
   6818 }
   6819 
   6820 /* static */
   6821 void nsContentUtils::TriggerLinkMouseOver(nsIContent* aContent,
   6822                                          nsIURI* aLinkURI,
   6823                                          const nsString& aTargetSpec) {
   6824  MOZ_ASSERT(aLinkURI, "No link URI");
   6825 
   6826  if (aContent->IsEditable() || !aContent->OwnerDoc()->LinkHandlingEnabled()) {
   6827    return;
   6828  }
   6829 
   6830  RefPtr<nsDocShell> docShell =
   6831      nsDocShell::Cast(aContent->OwnerDoc()->GetDocShell());
   6832  if (!docShell) {
   6833    return;
   6834  }
   6835 
   6836  docShell->OnOverLink(aContent, aLinkURI, aTargetSpec);
   6837 }
   6838 
   6839 /* static */
   6840 void nsContentUtils::GetLinkLocation(Element* aElement,
   6841                                     nsString& aLocationString) {
   6842  nsCOMPtr<nsIURI> hrefURI = aElement->GetHrefURI();
   6843  if (hrefURI) {
   6844    nsAutoCString specUTF8;
   6845    nsresult rv = hrefURI->GetSpec(specUTF8);
   6846    if (NS_SUCCEEDED(rv)) CopyUTF8toUTF16(specUTF8, aLocationString);
   6847  }
   6848 }
   6849 
   6850 /* static */
   6851 nsIWidget* nsContentUtils::GetTopLevelWidget(nsIWidget* aWidget) {
   6852  if (!aWidget) return nullptr;
   6853 
   6854  return aWidget->GetTopLevelWidget();
   6855 }
   6856 
   6857 /* static */
   6858 const nsDependentString nsContentUtils::GetLocalizedEllipsis() {
   6859  static char16_t sBuf[4] = {0, 0, 0, 0};
   6860  if (!sBuf[0]) {
   6861    if (!nsContentUtils::ShouldResistFingerprinting("No context",
   6862                                                    RFPTarget::JSLocale)) {
   6863      nsAutoString tmp;
   6864      intl::LocaleService::GetInstance()->GetEllipsis(tmp);
   6865      uint32_t len =
   6866          std::min(uint32_t(tmp.Length()), uint32_t(std::size(sBuf) - 1));
   6867      CopyUnicodeTo(tmp, 0, sBuf, len);
   6868    }
   6869    if (!sBuf[0]) {
   6870      sBuf[0] = char16_t(0x2026);
   6871    }
   6872  }
   6873  return nsDependentString(sBuf);
   6874 }
   6875 
   6876 /* static */
   6877 void nsContentUtils::AddScriptBlocker() {
   6878  MOZ_ASSERT(NS_IsMainThread());
   6879  if (!sScriptBlockerCount) {
   6880    MOZ_ASSERT(sRunnersCountAtFirstBlocker == 0,
   6881               "Should not already have a count");
   6882    sRunnersCountAtFirstBlocker =
   6883        sBlockedScriptRunners ? sBlockedScriptRunners->Length() : 0;
   6884  }
   6885  ++sScriptBlockerCount;
   6886 }
   6887 
   6888 #ifdef DEBUG
   6889 static bool sRemovingScriptBlockers = false;
   6890 #endif
   6891 
   6892 /* static */
   6893 void nsContentUtils::RemoveScriptBlocker() {
   6894  MOZ_ASSERT(NS_IsMainThread());
   6895  MOZ_ASSERT(!sRemovingScriptBlockers);
   6896  NS_ASSERTION(sScriptBlockerCount != 0, "Negative script blockers");
   6897  --sScriptBlockerCount;
   6898  if (sScriptBlockerCount) {
   6899    return;
   6900  }
   6901 
   6902  if (!sBlockedScriptRunners) {
   6903    return;
   6904  }
   6905 
   6906  uint32_t firstBlocker = sRunnersCountAtFirstBlocker;
   6907  uint32_t lastBlocker = sBlockedScriptRunners->Length();
   6908  uint32_t originalFirstBlocker = firstBlocker;
   6909  uint32_t blockersCount = lastBlocker - firstBlocker;
   6910  sRunnersCountAtFirstBlocker = 0;
   6911  NS_ASSERTION(firstBlocker <= lastBlocker, "bad sRunnersCountAtFirstBlocker");
   6912 
   6913  while (firstBlocker < lastBlocker) {
   6914    nsCOMPtr<nsIRunnable> runnable;
   6915    runnable.swap((*sBlockedScriptRunners)[firstBlocker]);
   6916    ++firstBlocker;
   6917 
   6918    // Calling the runnable can reenter us
   6919    {
   6920      AUTO_PROFILE_FOLLOWING_RUNNABLE(runnable);
   6921      runnable->Run();
   6922    }
   6923    // So can dropping the reference to the runnable
   6924    runnable = nullptr;
   6925 
   6926    NS_ASSERTION(sRunnersCountAtFirstBlocker == 0, "Bad count");
   6927    NS_ASSERTION(!sScriptBlockerCount, "This is really bad");
   6928  }
   6929 #ifdef DEBUG
   6930  AutoRestore<bool> removingScriptBlockers(sRemovingScriptBlockers);
   6931  sRemovingScriptBlockers = true;
   6932 #endif
   6933  sBlockedScriptRunners->RemoveElementsAt(originalFirstBlocker, blockersCount);
   6934 }
   6935 
   6936 /* static */
   6937 already_AddRefed<nsPIDOMWindowOuter>
   6938 nsContentUtils::GetMostRecentNonPBWindow() {
   6939  nsCOMPtr<nsIWindowMediator> wm = do_GetService(NS_WINDOWMEDIATOR_CONTRACTID);
   6940 
   6941  nsCOMPtr<mozIDOMWindowProxy> window;
   6942  wm->GetMostRecentNonPBWindow(u"navigator:browser", getter_AddRefs(window));
   6943  nsCOMPtr<nsPIDOMWindowOuter> pwindow;
   6944  pwindow = do_QueryInterface(window);
   6945 
   6946  return pwindow.forget();
   6947 }
   6948 
   6949 /* static */
   6950 already_AddRefed<nsPIDOMWindowOuter> nsContentUtils::GetMostRecentWindowBy(
   6951    WindowMediatorFilter aFilter) {
   6952  nsCOMPtr<nsIWindowMediator> wm = do_GetService(NS_WINDOWMEDIATOR_CONTRACTID);
   6953 
   6954  nsCOMPtr<mozIDOMWindowProxy> window;
   6955  wm->GetMostRecentWindowBy(u"navigator:browser", static_cast<uint8_t>(aFilter),
   6956                            getter_AddRefs(window));
   6957  nsCOMPtr<nsPIDOMWindowOuter> pwindow;
   6958  pwindow = do_QueryInterface(window);
   6959 
   6960  return pwindow.forget();
   6961 }
   6962 
   6963 /* static */
   6964 void nsContentUtils::WarnScriptWasIgnored(Document* aDocument) {
   6965  nsAutoString msg;
   6966  bool privateBrowsing = false;
   6967  bool chromeContext = false;
   6968 
   6969  if (aDocument) {
   6970    nsCOMPtr<nsIURI> uri = aDocument->GetDocumentURI();
   6971    if (uri) {
   6972      msg.Append(NS_ConvertUTF8toUTF16(uri->GetSpecOrDefault()));
   6973      msg.AppendLiteral(" : ");
   6974    }
   6975    privateBrowsing =
   6976        aDocument->NodePrincipal()->OriginAttributesRef().IsPrivateBrowsing();
   6977    chromeContext = aDocument->NodePrincipal()->IsSystemPrincipal();
   6978  }
   6979 
   6980  msg.AppendLiteral(
   6981      "Unable to run script because scripts are blocked internally.");
   6982  LogSimpleConsoleError(msg, "DOM"_ns, privateBrowsing, chromeContext);
   6983 }
   6984 
   6985 /* static */
   6986 void nsContentUtils::AddScriptRunner(already_AddRefed<nsIRunnable> aRunnable) {
   6987  nsCOMPtr<nsIRunnable> runnable = aRunnable;
   6988  if (!runnable) {
   6989    return;
   6990  }
   6991 
   6992  if (sScriptBlockerCount) {
   6993    PROFILER_MARKER("nsContentUtils::AddScriptRunner", OTHER, {}, FlowMarker,
   6994                    Flow::FromPointer(runnable.get()));
   6995    sBlockedScriptRunners->AppendElement(runnable.forget());
   6996    return;
   6997  }
   6998 
   6999  AUTO_PROFILE_FOLLOWING_RUNNABLE(runnable);
   7000  runnable->Run();
   7001 }
   7002 
   7003 /* static */
   7004 void nsContentUtils::AddScriptRunner(nsIRunnable* aRunnable) {
   7005  nsCOMPtr<nsIRunnable> runnable = aRunnable;
   7006  AddScriptRunner(runnable.forget());
   7007 }
   7008 
   7009 /* static */ bool nsContentUtils::IsSafeToRunScript() {
   7010  MOZ_ASSERT(NS_IsMainThread(),
   7011             "This static variable only makes sense on the main thread!");
   7012  return sScriptBlockerCount == 0;
   7013 }
   7014 
   7015 /* static */
   7016 void nsContentUtils::RunInStableState(already_AddRefed<nsIRunnable> aRunnable) {
   7017  MOZ_ASSERT(CycleCollectedJSContext::Get(), "Must be on a script thread!");
   7018  CycleCollectedJSContext::Get()->RunInStableState(std::move(aRunnable));
   7019 }
   7020 
   7021 /* static */
   7022 void nsContentUtils::AddPendingIDBTransaction(
   7023    already_AddRefed<nsIRunnable> aTransaction) {
   7024  MOZ_ASSERT(CycleCollectedJSContext::Get(), "Must be on a script thread!");
   7025  CycleCollectedJSContext::Get()->AddPendingIDBTransaction(
   7026      std::move(aTransaction));
   7027 }
   7028 
   7029 /* static */
   7030 bool nsContentUtils::IsInStableOrMetaStableState() {
   7031  MOZ_ASSERT(CycleCollectedJSContext::Get(), "Must be on a script thread!");
   7032  return CycleCollectedJSContext::Get()->IsInStableOrMetaStableState();
   7033 }
   7034 
   7035 /* static */
   7036 void nsContentUtils::HidePopupsInDocument(Document* aDocument) {
   7037  RefPtr<nsXULPopupManager> pm = nsXULPopupManager::GetInstance();
   7038  if (!pm || !aDocument) {
   7039    return;
   7040  }
   7041  nsCOMPtr<nsIDocShellTreeItem> docShellToHide = aDocument->GetDocShell();
   7042  if (docShellToHide) {
   7043    pm->HidePopupsInDocShell(docShellToHide);
   7044  }
   7045 }
   7046 
   7047 /* static */
   7048 already_AddRefed<nsIDragSession> nsContentUtils::GetDragSession(
   7049    nsIWidget* aWidget) {
   7050  nsCOMPtr<nsIDragSession> dragSession;
   7051  nsCOMPtr<nsIDragService> dragService =
   7052      do_GetService("@mozilla.org/widget/dragservice;1");
   7053  if (dragService) {
   7054    dragSession = dragService->GetCurrentSession(aWidget);
   7055  }
   7056  return dragSession.forget();
   7057 }
   7058 
   7059 /* static */
   7060 already_AddRefed<nsIDragSession> nsContentUtils::GetDragSession(
   7061    nsPresContext* aPC) {
   7062  NS_ENSURE_TRUE(aPC, nullptr);
   7063  auto* widget = aPC->GetRootWidget();
   7064  if (!widget) {
   7065    return nullptr;
   7066  }
   7067  return GetDragSession(widget);
   7068 }
   7069 
   7070 /* static */
   7071 nsresult nsContentUtils::SetDataTransferInEvent(WidgetDragEvent* aDragEvent) {
   7072  if (aDragEvent->mDataTransfer || !aDragEvent->IsTrusted()) {
   7073    return NS_OK;
   7074  }
   7075 
   7076  // For dragstart events, the data transfer object is
   7077  // created before the event fires, so it should already be set. For other
   7078  // drag events, get the object from the drag session.
   7079  NS_ASSERTION(aDragEvent->mMessage != eDragStart,
   7080               "draggesture event created without a dataTransfer");
   7081 
   7082  nsCOMPtr<nsIDragSession> dragSession = GetDragSession(aDragEvent->mWidget);
   7083  NS_ENSURE_TRUE(dragSession, NS_OK);  // no drag in progress
   7084 
   7085  RefPtr<DataTransfer> initialDataTransfer = dragSession->GetDataTransfer();
   7086  if (!initialDataTransfer) {
   7087    // A dataTransfer won't exist when a drag was started by some other
   7088    // means, for instance calling the drag service directly, or a drag
   7089    // from another application. In either case, a new dataTransfer should
   7090    // be created that reflects the data.
   7091    initialDataTransfer = new DataTransfer(
   7092        aDragEvent->mTarget, aDragEvent->mMessage, true, Nothing());
   7093 
   7094    // now set it in the drag session so we don't need to create it again
   7095    dragSession->SetDataTransfer(initialDataTransfer);
   7096  }
   7097 
   7098  bool isCrossDomainSubFrameDrop = false;
   7099  if (aDragEvent->mMessage == eDrop) {
   7100    isCrossDomainSubFrameDrop = CheckForSubFrameDrop(dragSession, aDragEvent);
   7101  }
   7102 
   7103  // each event should use a clone of the original dataTransfer.
   7104  initialDataTransfer->Clone(
   7105      aDragEvent->mTarget, aDragEvent->mMessage, aDragEvent->mUserCancelled,
   7106      isCrossDomainSubFrameDrop, getter_AddRefs(aDragEvent->mDataTransfer));
   7107  if (NS_WARN_IF(!aDragEvent->mDataTransfer)) {
   7108    return NS_ERROR_OUT_OF_MEMORY;
   7109  }
   7110 
   7111  // for the dragenter and dragover events, initialize the drop effect
   7112  // from the drop action, which platform specific widget code sets before
   7113  // the event is fired based on the keyboard state.
   7114  if (aDragEvent->mMessage == eDragEnter || aDragEvent->mMessage == eDragOver) {
   7115    uint32_t action;
   7116    dragSession->GetDragAction(&action);
   7117    uint32_t effectAllowed = aDragEvent->mDataTransfer->EffectAllowedInt();
   7118    aDragEvent->mDataTransfer->SetDropEffectInt(
   7119        FilterDropEffect(action, effectAllowed));
   7120  } else if (aDragEvent->mMessage == eDrop ||
   7121             aDragEvent->mMessage == eDragEnd) {
   7122    // For the drop and dragend events, set the drop effect based on the
   7123    // last value that the dropEffect had. This will have been set in
   7124    // EventStateManager::PostHandleEvent for the last dragenter or
   7125    // dragover event.
   7126    aDragEvent->mDataTransfer->SetDropEffectInt(
   7127        initialDataTransfer->DropEffectInt());
   7128  }
   7129 
   7130  return NS_OK;
   7131 }
   7132 
   7133 /* static */
   7134 uint32_t nsContentUtils::FilterDropEffect(uint32_t aAction,
   7135                                          uint32_t aEffectAllowed) {
   7136  // It is possible for the drag action to include more than one action, but
   7137  // the widget code which sets the action from the keyboard state should only
   7138  // be including one. If multiple actions were set, we just consider them in
   7139  //  the following order:
   7140  //   copy, link, move
   7141  if (aAction & nsIDragService::DRAGDROP_ACTION_COPY)
   7142    aAction = nsIDragService::DRAGDROP_ACTION_COPY;
   7143  else if (aAction & nsIDragService::DRAGDROP_ACTION_LINK)
   7144    aAction = nsIDragService::DRAGDROP_ACTION_LINK;
   7145  else if (aAction & nsIDragService::DRAGDROP_ACTION_MOVE)
   7146    aAction = nsIDragService::DRAGDROP_ACTION_MOVE;
   7147 
   7148  // Filter the action based on the effectAllowed. If the effectAllowed
   7149  // doesn't include the action, then that action cannot be done, so adjust
   7150  // the action to something that is allowed. For a copy, adjust to move or
   7151  // link. For a move, adjust to copy or link. For a link, adjust to move or
   7152  // link. Otherwise, use none.
   7153  if (aAction & aEffectAllowed ||
   7154      aEffectAllowed == nsIDragService::DRAGDROP_ACTION_UNINITIALIZED)
   7155    return aAction;
   7156  if (aEffectAllowed & nsIDragService::DRAGDROP_ACTION_MOVE)
   7157    return nsIDragService::DRAGDROP_ACTION_MOVE;
   7158  if (aEffectAllowed & nsIDragService::DRAGDROP_ACTION_COPY)
   7159    return nsIDragService::DRAGDROP_ACTION_COPY;
   7160  if (aEffectAllowed & nsIDragService::DRAGDROP_ACTION_LINK)
   7161    return nsIDragService::DRAGDROP_ACTION_LINK;
   7162  return nsIDragService::DRAGDROP_ACTION_NONE;
   7163 }
   7164 
   7165 /* static */
   7166 bool nsContentUtils::CheckForSubFrameDrop(nsIDragSession* aDragSession,
   7167                                          WidgetDragEvent* aDropEvent) {
   7168  nsCOMPtr<nsIContent> target =
   7169      nsIContent::FromEventTargetOrNull(aDropEvent->mOriginalTarget);
   7170  if (!target) {
   7171    return true;
   7172  }
   7173 
   7174  // Always allow dropping onto chrome shells.
   7175  BrowsingContext* targetBC = target->OwnerDoc()->GetBrowsingContext();
   7176  if (targetBC->IsChrome()) {
   7177    return false;
   7178  }
   7179 
   7180  WindowContext* targetWC = target->OwnerDoc()->GetWindowContext();
   7181 
   7182  // If there is no source browsing context, then this is a drag from another
   7183  // application, which should be allowed.
   7184  RefPtr<WindowContext> sourceWC;
   7185  aDragSession->GetSourceWindowContext(getter_AddRefs(sourceWC));
   7186  if (sourceWC) {
   7187    // Get each successive parent of the source document and compare it to
   7188    // the drop document. If they match, then this is a drag from a child frame.
   7189    for (sourceWC = sourceWC->GetParentWindowContext(); sourceWC;
   7190         sourceWC = sourceWC->GetParentWindowContext()) {
   7191      // If the source and the target match, then the drag started in a
   7192      // descendant frame. If the source is discarded, err on the side of
   7193      // caution and treat it as a subframe drag.
   7194      if (sourceWC == targetWC || sourceWC->IsDiscarded()) {
   7195        return true;
   7196      }
   7197    }
   7198  }
   7199 
   7200  return false;
   7201 }
   7202 
   7203 /* static */
   7204 bool nsContentUtils::URIIsLocalFile(nsIURI* aURI) {
   7205  bool isFile;
   7206  nsCOMPtr<nsINetUtil> util = mozilla::components::IO::Service();
   7207 
   7208  // Important: we do NOT test the entire URI chain here!
   7209  return util &&
   7210         NS_SUCCEEDED(util->ProtocolHasFlags(
   7211             aURI, nsIProtocolHandler::URI_IS_LOCAL_FILE, &isFile)) &&
   7212         isFile;
   7213 }
   7214 
   7215 /* static */
   7216 JSContext* nsContentUtils::GetCurrentJSContext() {
   7217  MOZ_ASSERT(IsInitialized());
   7218  if (!IsJSAPIActive()) {
   7219    return nullptr;
   7220  }
   7221  return danger::GetJSContext();
   7222 }
   7223 
   7224 template <typename StringType, typename CharType>
   7225 void _ASCIIToLowerInSitu(StringType& aStr) {
   7226  CharType* iter = aStr.BeginWriting();
   7227  CharType* end = aStr.EndWriting();
   7228  MOZ_ASSERT(iter && end);
   7229 
   7230  while (iter != end) {
   7231    CharType c = *iter;
   7232    if (c >= 'A' && c <= 'Z') {
   7233      *iter = c + ('a' - 'A');
   7234    }
   7235    ++iter;
   7236  }
   7237 }
   7238 
   7239 /* static */
   7240 void nsContentUtils::ASCIIToLower(nsAString& aStr) {
   7241  return _ASCIIToLowerInSitu<nsAString, char16_t>(aStr);
   7242 }
   7243 
   7244 /* static */
   7245 void nsContentUtils::ASCIIToLower(nsACString& aStr) {
   7246  return _ASCIIToLowerInSitu<nsACString, char>(aStr);
   7247 }
   7248 
   7249 template <typename StringType, typename CharType>
   7250 void _ASCIIToLowerCopy(const StringType& aSource, StringType& aDest) {
   7251  uint32_t len = aSource.Length();
   7252  aDest.SetLength(len);
   7253  MOZ_ASSERT(aDest.Length() == len);
   7254 
   7255  CharType* dest = aDest.BeginWriting();
   7256  MOZ_ASSERT(dest);
   7257 
   7258  const CharType* iter = aSource.BeginReading();
   7259  const CharType* end = aSource.EndReading();
   7260  while (iter != end) {
   7261    CharType c = *iter;
   7262    *dest = (c >= 'A' && c <= 'Z') ? c + ('a' - 'A') : c;
   7263    ++iter;
   7264    ++dest;
   7265  }
   7266 }
   7267 
   7268 /* static */
   7269 void nsContentUtils::ASCIIToLower(const nsAString& aSource, nsAString& aDest) {
   7270  return _ASCIIToLowerCopy<nsAString, char16_t>(aSource, aDest);
   7271 }
   7272 
   7273 /* static */
   7274 void nsContentUtils::ASCIIToLower(const nsACString& aSource,
   7275                                  nsACString& aDest) {
   7276  return _ASCIIToLowerCopy<nsACString, char>(aSource, aDest);
   7277 }
   7278 
   7279 template <typename StringType, typename CharType>
   7280 void _ASCIIToUpperInSitu(StringType& aStr) {
   7281  CharType* iter = aStr.BeginWriting();
   7282  CharType* end = aStr.EndWriting();
   7283  MOZ_ASSERT(iter && end);
   7284 
   7285  while (iter != end) {
   7286    CharType c = *iter;
   7287    if (c >= 'a' && c <= 'z') {
   7288      *iter = c + ('A' - 'a');
   7289    }
   7290    ++iter;
   7291  }
   7292 }
   7293 
   7294 /* static */
   7295 void nsContentUtils::ASCIIToUpper(nsAString& aStr) {
   7296  return _ASCIIToUpperInSitu<nsAString, char16_t>(aStr);
   7297 }
   7298 
   7299 /* static */
   7300 void nsContentUtils::ASCIIToUpper(nsACString& aStr) {
   7301  return _ASCIIToUpperInSitu<nsACString, char>(aStr);
   7302 }
   7303 
   7304 template <typename StringType, typename CharType>
   7305 void _ASCIIToUpperCopy(const StringType& aSource, StringType& aDest) {
   7306  uint32_t len = aSource.Length();
   7307  aDest.SetLength(len);
   7308  MOZ_ASSERT(aDest.Length() == len);
   7309 
   7310  CharType* dest = aDest.BeginWriting();
   7311  MOZ_ASSERT(dest);
   7312 
   7313  const CharType* iter = aSource.BeginReading();
   7314  const CharType* end = aSource.EndReading();
   7315  while (iter != end) {
   7316    CharType c = *iter;
   7317    *dest = (c >= 'a' && c <= 'z') ? c + ('A' - 'a') : c;
   7318    ++iter;
   7319    ++dest;
   7320  }
   7321 }
   7322 
   7323 /* static */
   7324 void nsContentUtils::ASCIIToUpper(const nsAString& aSource, nsAString& aDest) {
   7325  return _ASCIIToUpperCopy<nsAString, char16_t>(aSource, aDest);
   7326 }
   7327 
   7328 /* static */
   7329 void nsContentUtils::ASCIIToUpper(const nsACString& aSource,
   7330                                  nsACString& aDest) {
   7331  return _ASCIIToUpperCopy<nsACString, char>(aSource, aDest);
   7332 }
   7333 
   7334 /* static */
   7335 bool nsContentUtils::EqualsIgnoreASCIICase(nsAtom* aAtom1, nsAtom* aAtom2) {
   7336  if (aAtom1 == aAtom2) {
   7337    return true;
   7338  }
   7339 
   7340  // If both are ascii lowercase already, we know that the slow comparison
   7341  // below is going to return false.
   7342  if (aAtom1->IsAsciiLowercase() && aAtom2->IsAsciiLowercase()) {
   7343    return false;
   7344  }
   7345 
   7346  return EqualsIgnoreASCIICase(nsDependentAtomString(aAtom1),
   7347                               nsDependentAtomString(aAtom2));
   7348 }
   7349 
   7350 /* static */
   7351 bool nsContentUtils::EqualsIgnoreASCIICase(const nsAString& aStr1,
   7352                                           const nsAString& aStr2) {
   7353  uint32_t len = aStr1.Length();
   7354  if (len != aStr2.Length()) {
   7355    return false;
   7356  }
   7357 
   7358  const char16_t* str1 = aStr1.BeginReading();
   7359  const char16_t* str2 = aStr2.BeginReading();
   7360  const char16_t* end = str1 + len;
   7361 
   7362  while (str1 < end) {
   7363    char16_t c1 = *str1++;
   7364    char16_t c2 = *str2++;
   7365 
   7366    // First check if any bits other than the 0x0020 differs
   7367    if ((c1 ^ c2) & 0xffdf) {
   7368      return false;
   7369    }
   7370 
   7371    // We know they can only differ in the 0x0020 bit.
   7372    // Likely the two chars are the same, so check that first
   7373    if (c1 != c2) {
   7374      // They do differ, but since it's only in the 0x0020 bit, check if it's
   7375      // the same ascii char, but just differing in case
   7376      char16_t c1Upper = c1 & 0xffdf;
   7377      if (!('A' <= c1Upper && c1Upper <= 'Z')) {
   7378        return false;
   7379      }
   7380    }
   7381  }
   7382 
   7383  return true;
   7384 }
   7385 
   7386 /* static */
   7387 bool nsContentUtils::StringContainsASCIIUpper(const nsAString& aStr) {
   7388  const char16_t* iter = aStr.BeginReading();
   7389  const char16_t* end = aStr.EndReading();
   7390  while (iter != end) {
   7391    char16_t c = *iter;
   7392    if (c >= 'A' && c <= 'Z') {
   7393      return true;
   7394    }
   7395    ++iter;
   7396  }
   7397 
   7398  return false;
   7399 }
   7400 
   7401 /* static */
   7402 nsIInterfaceRequestor* nsContentUtils::SameOriginChecker() {
   7403  if (!sSameOriginChecker) {
   7404    sSameOriginChecker = new SameOriginCheckerImpl();
   7405    NS_ADDREF(sSameOriginChecker);
   7406  }
   7407  return sSameOriginChecker;
   7408 }
   7409 
   7410 /* static */
   7411 nsresult nsContentUtils::CheckSameOrigin(nsIChannel* aOldChannel,
   7412                                         nsIChannel* aNewChannel) {
   7413  if (!nsContentUtils::GetSecurityManager()) return NS_ERROR_NOT_AVAILABLE;
   7414 
   7415  nsCOMPtr<nsIPrincipal> oldPrincipal;
   7416  nsContentUtils::GetSecurityManager()->GetChannelResultPrincipal(
   7417      aOldChannel, getter_AddRefs(oldPrincipal));
   7418 
   7419  nsCOMPtr<nsIURI> newURI;
   7420  aNewChannel->GetURI(getter_AddRefs(newURI));
   7421  nsCOMPtr<nsIURI> newOriginalURI;
   7422  aNewChannel->GetOriginalURI(getter_AddRefs(newOriginalURI));
   7423 
   7424  NS_ENSURE_STATE(oldPrincipal && newURI && newOriginalURI);
   7425 
   7426  nsresult rv = oldPrincipal->CheckMayLoad(newURI, false);
   7427  if (NS_SUCCEEDED(rv) && newOriginalURI != newURI) {
   7428    rv = oldPrincipal->CheckMayLoad(newOriginalURI, false);
   7429  }
   7430 
   7431  return rv;
   7432 }
   7433 
   7434 NS_IMPL_ISUPPORTS(SameOriginCheckerImpl, nsIChannelEventSink,
   7435                  nsIInterfaceRequestor)
   7436 
   7437 NS_IMETHODIMP
   7438 SameOriginCheckerImpl::AsyncOnChannelRedirect(
   7439    nsIChannel* aOldChannel, nsIChannel* aNewChannel, uint32_t aFlags,
   7440    nsIAsyncVerifyRedirectCallback* cb) {
   7441  MOZ_ASSERT(aNewChannel, "Redirecting to null channel?");
   7442 
   7443  nsresult rv = nsContentUtils::CheckSameOrigin(aOldChannel, aNewChannel);
   7444  if (NS_SUCCEEDED(rv)) {
   7445    cb->OnRedirectVerifyCallback(NS_OK);
   7446  }
   7447 
   7448  return rv;
   7449 }
   7450 
   7451 NS_IMETHODIMP
   7452 SameOriginCheckerImpl::GetInterface(const nsIID& aIID, void** aResult) {
   7453  return QueryInterface(aIID, aResult);
   7454 }
   7455 
   7456 /* static */
   7457 nsresult nsContentUtils::GetWebExposedOriginSerialization(nsIURI* aURI,
   7458                                                          nsACString& aOrigin) {
   7459  nsresult rv;
   7460  MOZ_ASSERT(aURI, "missing uri");
   7461 
   7462  // For Blob URI, the path is the URL of the owning page.
   7463  if (aURI->SchemeIs(BLOBURI_SCHEME)) {
   7464    nsAutoCString path;
   7465    rv = aURI->GetPathQueryRef(path);
   7466    NS_ENSURE_SUCCESS(rv, rv);
   7467 
   7468    nsCOMPtr<nsIURI> uri;
   7469    rv = NS_NewURI(getter_AddRefs(uri), path);
   7470    if (NS_FAILED(rv)) {
   7471      aOrigin.AssignLiteral("null");
   7472      return NS_OK;
   7473    }
   7474 
   7475    if (
   7476        // Schemes in spec. https://url.spec.whatwg.org/#origin
   7477        !net::SchemeIsHttpOrHttps(uri) && !uri->SchemeIs("file") &&
   7478        !uri->SchemeIs("resource") &&
   7479        // Our own schemes.
   7480        !uri->SchemeIs("moz-extension")) {
   7481      aOrigin.AssignLiteral("null");
   7482      return NS_OK;
   7483    }
   7484 
   7485    return GetWebExposedOriginSerialization(uri, aOrigin);
   7486  }
   7487 
   7488  nsAutoCString scheme;
   7489  aURI->GetScheme(scheme);
   7490 
   7491  // If the protocol doesn't have URI_HAS_WEB_EXPOSED_ORIGIN, then
   7492  // return "null" as the origin serialization.
   7493  // We make an exception for "ftp" since we don't have a protocol handler
   7494  // for this scheme
   7495  uint32_t flags = 0;
   7496  nsCOMPtr<nsIIOService> io = mozilla::components::IO::Service(&rv);
   7497  if (!scheme.Equals("ftp") && NS_SUCCEEDED(rv) &&
   7498      NS_SUCCEEDED(io->GetProtocolFlags(scheme.get(), &flags))) {
   7499    if (!(flags & nsIProtocolHandler::URI_HAS_WEB_EXPOSED_ORIGIN)) {
   7500      aOrigin.AssignLiteral("null");
   7501      return NS_OK;
   7502    }
   7503  }
   7504 
   7505  aOrigin.Truncate();
   7506 
   7507  nsCOMPtr<nsIURI> uri = NS_GetInnermostURI(aURI);
   7508  NS_ENSURE_TRUE(uri, NS_ERROR_UNEXPECTED);
   7509 
   7510  nsAutoCString host;
   7511  rv = uri->GetAsciiHost(host);
   7512 
   7513  if (NS_SUCCEEDED(rv) && !host.IsEmpty()) {
   7514    nsAutoCString userPass;
   7515    uri->GetUserPass(userPass);
   7516 
   7517    nsAutoCString prePath;
   7518    if (!userPass.IsEmpty()) {
   7519      rv = NS_MutateURI(uri).SetUserPass(""_ns).Finalize(uri);
   7520      NS_ENSURE_SUCCESS(rv, rv);
   7521    }
   7522 
   7523    rv = uri->GetPrePath(prePath);
   7524    NS_ENSURE_SUCCESS(rv, rv);
   7525 
   7526    aOrigin = prePath;
   7527  } else {
   7528    aOrigin.AssignLiteral("null");
   7529  }
   7530 
   7531  return NS_OK;
   7532 }
   7533 
   7534 /* static */
   7535 nsresult nsContentUtils::GetWebExposedOriginSerialization(
   7536    nsIPrincipal* aPrincipal, nsAString& aOrigin) {
   7537  MOZ_ASSERT(aPrincipal, "missing principal");
   7538 
   7539  aOrigin.Truncate();
   7540  nsAutoCString webExposedOriginSerialization;
   7541 
   7542  nsresult rv = aPrincipal->GetWebExposedOriginSerialization(
   7543      webExposedOriginSerialization);
   7544  if (NS_FAILED(rv)) {
   7545    webExposedOriginSerialization.AssignLiteral("null");
   7546  }
   7547 
   7548  CopyUTF8toUTF16(webExposedOriginSerialization, aOrigin);
   7549  return NS_OK;
   7550 }
   7551 
   7552 /* static */
   7553 nsresult nsContentUtils::GetWebExposedOriginSerialization(nsIURI* aURI,
   7554                                                          nsAString& aOrigin) {
   7555  MOZ_ASSERT(aURI, "missing uri");
   7556  nsresult rv;
   7557 
   7558 #if defined(MOZ_THUNDERBIRD) || defined(MOZ_SUITE)
   7559  // Check if either URI has a special origin.
   7560  nsCOMPtr<nsIURIWithSpecialOrigin> uriWithSpecialOrigin =
   7561      do_QueryInterface(aURI);
   7562  if (uriWithSpecialOrigin) {
   7563    nsCOMPtr<nsIURI> origin;
   7564    rv = uriWithSpecialOrigin->GetOrigin(getter_AddRefs(origin));
   7565    NS_ENSURE_SUCCESS(rv, rv);
   7566 
   7567    return GetWebExposedOriginSerialization(origin, aOrigin);
   7568  }
   7569 #endif
   7570 
   7571  nsAutoCString webExposedOriginSerialization;
   7572  rv = GetWebExposedOriginSerialization(aURI, webExposedOriginSerialization);
   7573  NS_ENSURE_SUCCESS(rv, rv);
   7574 
   7575  CopyUTF8toUTF16(webExposedOriginSerialization, aOrigin);
   7576  return NS_OK;
   7577 }
   7578 
   7579 /* static */
   7580 bool nsContentUtils::CheckMayLoad(nsIPrincipal* aPrincipal,
   7581                                  nsIChannel* aChannel,
   7582                                  bool aAllowIfInheritsPrincipal) {
   7583  nsCOMPtr<nsIURI> channelURI;
   7584  nsresult rv = NS_GetFinalChannelURI(aChannel, getter_AddRefs(channelURI));
   7585  NS_ENSURE_SUCCESS(rv, false);
   7586 
   7587  return NS_SUCCEEDED(
   7588      aPrincipal->CheckMayLoad(channelURI, aAllowIfInheritsPrincipal));
   7589 }
   7590 
   7591 /* static */
   7592 bool nsContentUtils::CanAccessNativeAnon() {
   7593  return LegacyIsCallerChromeOrNativeCode();
   7594 }
   7595 
   7596 /* static */
   7597 nsresult nsContentUtils::DispatchXULCommand(nsIContent* aTarget, bool aTrusted,
   7598                                            Event* aSourceEvent,
   7599                                            PresShell* aPresShell, bool aCtrl,
   7600                                            bool aAlt, bool aShift, bool aMeta,
   7601                                            uint16_t aInputSource,
   7602                                            int16_t aButton) {
   7603  NS_ENSURE_STATE(aTarget);
   7604  Document* doc = aTarget->OwnerDoc();
   7605  nsPresContext* presContext = doc->GetPresContext();
   7606 
   7607  RefPtr<XULCommandEvent> xulCommand =
   7608      new XULCommandEvent(doc, presContext, nullptr);
   7609  xulCommand->InitCommandEvent(u"command"_ns, true, true,
   7610                               nsGlobalWindowInner::Cast(doc->GetInnerWindow()),
   7611                               0, aCtrl, aAlt, aShift, aMeta, aButton,
   7612                               aSourceEvent, aInputSource, IgnoreErrors());
   7613 
   7614  if (aPresShell) {
   7615    nsEventStatus status = nsEventStatus_eIgnore;
   7616    return aPresShell->HandleDOMEventWithTarget(aTarget, xulCommand, &status);
   7617  }
   7618 
   7619  ErrorResult rv;
   7620  aTarget->DispatchEvent(*xulCommand, rv);
   7621  return rv.StealNSResult();
   7622 }
   7623 
   7624 // static
   7625 nsresult nsContentUtils::WrapNative(JSContext* cx, nsISupports* native,
   7626                                    nsWrapperCache* cache, const nsIID* aIID,
   7627                                    JS::MutableHandle<JS::Value> vp,
   7628                                    bool aAllowWrapping) {
   7629  MOZ_ASSERT(cx == GetCurrentJSContext());
   7630 
   7631  if (!native) {
   7632    vp.setNull();
   7633 
   7634    return NS_OK;
   7635  }
   7636 
   7637  JSObject* wrapper = xpc_FastGetCachedWrapper(cx, cache, vp);
   7638  if (wrapper) {
   7639    return NS_OK;
   7640  }
   7641 
   7642  NS_ENSURE_TRUE(sXPConnect, NS_ERROR_UNEXPECTED);
   7643 
   7644  if (!NS_IsMainThread()) {
   7645    MOZ_CRASH();
   7646  }
   7647 
   7648  JS::Rooted<JSObject*> scope(cx, JS::CurrentGlobalOrNull(cx));
   7649  nsresult rv = sXPConnect->WrapNativeToJSVal(cx, scope, native, cache, aIID,
   7650                                              aAllowWrapping, vp);
   7651  return rv;
   7652 }
   7653 
   7654 void nsContentUtils::StripNullChars(const nsAString& aInStr,
   7655                                    nsAString& aOutStr) {
   7656  // In common cases where we don't have nulls in the
   7657  // string we can simple simply bypass the checking code.
   7658  int32_t firstNullPos = aInStr.FindChar('\0');
   7659  if (firstNullPos == kNotFound) {
   7660    aOutStr.Assign(aInStr);
   7661    return;
   7662  }
   7663 
   7664  aOutStr.SetCapacity(aInStr.Length() - 1);
   7665  nsAString::const_iterator start, end;
   7666  aInStr.BeginReading(start);
   7667  aInStr.EndReading(end);
   7668  while (start != end) {
   7669    if (*start != '\0') aOutStr.Append(*start);
   7670    ++start;
   7671  }
   7672 }
   7673 
   7674 struct ClassMatchingInfo {
   7675  AtomArray mClasses;
   7676  nsCaseTreatment mCaseTreatment;
   7677 };
   7678 
   7679 // static
   7680 bool nsContentUtils::MatchClassNames(Element* aElement, int32_t aNamespaceID,
   7681                                     nsAtom* aAtom, void* aData) {
   7682  // We can't match if there are no class names
   7683  const nsAttrValue* classAttr = aElement->GetClasses();
   7684  if (!classAttr) {
   7685    return false;
   7686  }
   7687 
   7688  // need to match *all* of the classes
   7689  ClassMatchingInfo* info = static_cast<ClassMatchingInfo*>(aData);
   7690  uint32_t length = info->mClasses.Length();
   7691  if (!length) {
   7692    // If we actually had no classes, don't match.
   7693    return false;
   7694  }
   7695  uint32_t i;
   7696  for (i = 0; i < length; ++i) {
   7697    if (!classAttr->Contains(info->mClasses[i], info->mCaseTreatment)) {
   7698      return false;
   7699    }
   7700  }
   7701 
   7702  return true;
   7703 }
   7704 
   7705 // static
   7706 void nsContentUtils::DestroyClassNameArray(void* aData) {
   7707  ClassMatchingInfo* info = static_cast<ClassMatchingInfo*>(aData);
   7708  delete info;
   7709 }
   7710 
   7711 // static
   7712 void* nsContentUtils::AllocClassMatchingInfo(nsINode* aRootNode,
   7713                                             const nsString* aClasses) {
   7714  nsAttrValue attrValue;
   7715  attrValue.ParseAtomArray(*aClasses);
   7716  // nsAttrValue::Equals is sensitive to order, so we'll send an array
   7717  auto* info = new ClassMatchingInfo;
   7718  if (attrValue.Type() == nsAttrValue::eAtomArray) {
   7719    info->mClasses = attrValue.GetAtomArrayValue()->mArray.Clone();
   7720  } else if (attrValue.Type() == nsAttrValue::eAtom) {
   7721    info->mClasses.AppendElement(attrValue.GetAtomValue());
   7722  }
   7723 
   7724  info->mCaseTreatment =
   7725      aRootNode->OwnerDoc()->GetCompatibilityMode() == eCompatibility_NavQuirks
   7726          ? eIgnoreCase
   7727          : eCaseMatters;
   7728  return info;
   7729 }
   7730 
   7731 void nsContentUtils::FlushLayoutForTree(nsPIDOMWindowOuter* aWindow) {
   7732  if (!aWindow) {
   7733    return;
   7734  }
   7735 
   7736  // Note that because FlushPendingNotifications flushes parents, this
   7737  // is O(N^2) in docshell tree depth.  However, the docshell tree is
   7738  // usually pretty shallow.
   7739 
   7740  if (RefPtr<Document> doc = aWindow->GetDoc()) {
   7741    doc->FlushPendingNotifications(FlushType::Layout);
   7742  }
   7743 
   7744  if (nsCOMPtr<nsIDocShell> docShell = aWindow->GetDocShell()) {
   7745    int32_t i = 0, i_end;
   7746    docShell->GetInProcessChildCount(&i_end);
   7747    for (; i < i_end; ++i) {
   7748      nsCOMPtr<nsIDocShellTreeItem> item;
   7749      if (docShell->GetInProcessChildAt(i, getter_AddRefs(item)) == NS_OK &&
   7750          item) {
   7751        if (nsCOMPtr<nsPIDOMWindowOuter> win = item->GetWindow()) {
   7752          FlushLayoutForTree(win);
   7753        }
   7754      }
   7755    }
   7756  }
   7757 }
   7758 
   7759 void nsContentUtils::RemoveNewlines(nsString& aString) { aString.StripCRLF(); }
   7760 
   7761 void nsContentUtils::PlatformToDOMLineBreaks(nsString& aString) {
   7762  if (!PlatformToDOMLineBreaks(aString, fallible)) {
   7763    aString.AllocFailed(aString.Length());
   7764  }
   7765 }
   7766 
   7767 bool nsContentUtils::PlatformToDOMLineBreaks(nsString& aString,
   7768                                             const fallible_t& aFallible) {
   7769  if (aString.FindChar(char16_t('\r')) != -1) {
   7770    // Windows linebreaks: Map CRLF to LF:
   7771    if (!aString.ReplaceSubstring(u"\r\n", u"\n", aFallible)) {
   7772      return false;
   7773    }
   7774 
   7775    // Mac linebreaks: Map any remaining CR to LF:
   7776    if (!aString.ReplaceSubstring(u"\r", u"\n", aFallible)) {
   7777      return false;
   7778    }
   7779  }
   7780 
   7781  return true;
   7782 }
   7783 
   7784 already_AddRefed<nsContentList> nsContentUtils::GetElementsByClassName(
   7785    nsINode* aRootNode, const nsAString& aClasses) {
   7786  MOZ_ASSERT(aRootNode, "Must have root node");
   7787 
   7788  return GetFuncStringContentList<nsCacheableFuncStringHTMLCollection>(
   7789      aRootNode, MatchClassNames, DestroyClassNameArray, AllocClassMatchingInfo,
   7790      aClasses);
   7791 }
   7792 
   7793 PresShell* nsContentUtils::FindPresShellForDocument(const Document* aDocument) {
   7794  const Document* doc = aDocument;
   7795  Document* displayDoc = doc->GetDisplayDocument();
   7796  if (displayDoc) {
   7797    doc = displayDoc;
   7798  }
   7799 
   7800  PresShell* presShell = doc->GetPresShell();
   7801  if (presShell) {
   7802    return presShell;
   7803  }
   7804 
   7805  nsCOMPtr<nsIDocShellTreeItem> docShellTreeItem = doc->GetDocShell();
   7806  while (docShellTreeItem) {
   7807    // We may be in a display:none subdocument, or we may not have a presshell
   7808    // created yet.
   7809    // Walk the docshell tree to find the nearest container that has a
   7810    // presshell, and return that.
   7811    nsCOMPtr<nsIDocShell> docShell = do_QueryInterface(docShellTreeItem);
   7812    if (PresShell* presShell = docShell->GetPresShell()) {
   7813      return presShell;
   7814    }
   7815    nsCOMPtr<nsIDocShellTreeItem> parent;
   7816    docShellTreeItem->GetInProcessParent(getter_AddRefs(parent));
   7817    docShellTreeItem = parent;
   7818  }
   7819 
   7820  return nullptr;
   7821 }
   7822 
   7823 /* static */
   7824 nsPresContext* nsContentUtils::FindPresContextForDocument(
   7825    const Document* aDocument) {
   7826  if (PresShell* presShell = FindPresShellForDocument(aDocument)) {
   7827    return presShell->GetPresContext();
   7828  }
   7829  return nullptr;
   7830 }
   7831 
   7832 nsIWidget* nsContentUtils::WidgetForDocument(const Document* aDocument) {
   7833  PresShell* presShell = FindPresShellForDocument(aDocument);
   7834  return presShell ? presShell->GetNearestWidget() : nullptr;
   7835 }
   7836 
   7837 nsIWidget* nsContentUtils::WidgetForContent(const nsIContent* aContent) {
   7838  nsIFrame* frame = aContent->GetPrimaryFrame();
   7839  if (!frame) {
   7840    return nullptr;
   7841  }
   7842  return frame->GetNearestWidget();
   7843 }
   7844 
   7845 WindowRenderer* nsContentUtils::WindowRendererForContent(
   7846    const nsIContent* aContent) {
   7847  nsIWidget* widget = nsContentUtils::WidgetForContent(aContent);
   7848  if (widget) {
   7849    return widget->GetWindowRenderer();
   7850  }
   7851 
   7852  return nullptr;
   7853 }
   7854 
   7855 WindowRenderer* nsContentUtils::WindowRendererForDocument(
   7856    const Document* aDoc) {
   7857  if (nsIWidget* widget = nsContentUtils::WidgetForDocument(aDoc)) {
   7858    return widget->GetWindowRenderer();
   7859  }
   7860  return nullptr;
   7861 }
   7862 
   7863 bool nsContentUtils::AllowXULXBLForPrincipal(nsIPrincipal* aPrincipal) {
   7864  if (!aPrincipal) {
   7865    return false;
   7866  }
   7867 
   7868  if (aPrincipal->IsSystemPrincipal()) {
   7869    return true;
   7870  }
   7871 
   7872  return xpc::IsInAutomation() && IsSitePermAllow(aPrincipal, "allowXULXBL"_ns);
   7873 }
   7874 
   7875 bool nsContentUtils::IsPDFJSEnabled() {
   7876  nsCOMPtr<nsIStreamConverter> conv = do_CreateInstance(
   7877      "@mozilla.org/streamconv;1?from=application/pdf&to=text/html");
   7878  return conv;
   7879 }
   7880 
   7881 bool nsContentUtils::IsPDFJS(nsIPrincipal* aPrincipal) {
   7882  if (!aPrincipal || !aPrincipal->SchemeIs("resource")) {
   7883    return false;
   7884  }
   7885  nsAutoCString spec;
   7886  nsresult rv = aPrincipal->GetAsciiSpec(spec);
   7887  NS_ENSURE_SUCCESS(rv, false);
   7888  return spec.EqualsLiteral("resource://pdf.js/web/viewer.html");
   7889 }
   7890 
   7891 bool nsContentUtils::IsSystemOrPDFJS(JSContext* aCx, JSObject*) {
   7892  nsIPrincipal* principal = SubjectPrincipal(aCx);
   7893  return principal && (principal->IsSystemPrincipal() || IsPDFJS(principal));
   7894 }
   7895 
   7896 bool nsContentUtils::IsSecureContextOrWebExtension(JSContext* aCx,
   7897                                                   JSObject* aGlobal) {
   7898  nsIPrincipal* principal = SubjectPrincipal(aCx);
   7899  return mozilla::dom::IsSecureContextOrObjectIsFromSecureContext(aCx,
   7900                                                                  aGlobal) ||
   7901         (principal && principal->GetIsAddonOrExpandedAddonPrincipal());
   7902 }
   7903 
   7904 already_AddRefed<nsIDocumentLoaderFactory>
   7905 nsContentUtils::FindInternalDocumentViewer(const nsACString& aType,
   7906                                           DocumentViewerType* aLoaderType) {
   7907  if (aLoaderType) {
   7908    *aLoaderType = TYPE_UNSUPPORTED;
   7909  }
   7910 
   7911  // one helper factory, please
   7912  nsCOMPtr<nsICategoryManager> catMan(
   7913      do_GetService(NS_CATEGORYMANAGER_CONTRACTID));
   7914  if (!catMan) return nullptr;
   7915 
   7916  nsCOMPtr<nsIDocumentLoaderFactory> docFactory;
   7917 
   7918  nsCString contractID;
   7919  nsresult rv =
   7920      catMan->GetCategoryEntry("Gecko-Content-Viewers", aType, contractID);
   7921  if (NS_SUCCEEDED(rv)) {
   7922    docFactory = do_GetService(contractID.get());
   7923    if (docFactory && aLoaderType) {
   7924      if (contractID.EqualsLiteral(CONTENT_DLF_CONTRACTID))
   7925        *aLoaderType = TYPE_CONTENT;
   7926      else if (contractID.EqualsLiteral(PLUGIN_DLF_CONTRACTID))
   7927        *aLoaderType = TYPE_FALLBACK;
   7928      else
   7929        *aLoaderType = TYPE_UNKNOWN;
   7930    }
   7931    return docFactory.forget();
   7932  }
   7933 
   7934  // If the type wasn't registered in `Gecko-Content-Viewers`, check if it's
   7935  // another type which we may dynamically support, such as `text/*` types or
   7936  // video document types. These types are all backed by the nsContentDLF.
   7937  if (IsPlainTextType(aType) ||
   7938      DecoderTraits::IsSupportedInVideoDocument(aType)) {
   7939    docFactory = do_GetService(CONTENT_DLF_CONTRACTID);
   7940    if (docFactory && aLoaderType) {
   7941      *aLoaderType = TYPE_CONTENT;
   7942    }
   7943    return docFactory.forget();
   7944  }
   7945 
   7946  return nullptr;
   7947 }
   7948 
   7949 static void ReportPatternCompileFailure(nsAString& aPattern,
   7950                                        const JS::RegExpFlags& aFlags,
   7951                                        const Document* aDocument,
   7952                                        JS::MutableHandle<JS::Value> error,
   7953                                        JSContext* cx) {
   7954  AutoTArray<nsString, 3> strings;
   7955 
   7956  strings.AppendElement(aPattern);
   7957 
   7958  std::stringstream flag_ss;
   7959  flag_ss << aFlags;
   7960  nsString* flagstr = strings.AppendElement();
   7961  AppendUTF8toUTF16(flag_ss.str(), *flagstr);
   7962 
   7963  JS::AutoSaveExceptionState savedExc(cx);
   7964  JS::Rooted<JSObject*> exnObj(cx, &error.toObject());
   7965  JS::Rooted<JS::Value> messageVal(cx);
   7966  if (!JS_GetProperty(cx, exnObj, "message", &messageVal)) {
   7967    return;
   7968  }
   7969  JS::Rooted<JSString*> messageStr(cx, messageVal.toString());
   7970  MOZ_ASSERT(messageStr);
   7971  if (!AssignJSString(cx, *strings.AppendElement(), messageStr)) {
   7972    return;
   7973  }
   7974 
   7975  nsContentUtils::ReportToConsole(nsIScriptError::errorFlag, "DOM"_ns,
   7976                                  aDocument, nsContentUtils::eDOM_PROPERTIES,
   7977                                  "PatternAttributeCompileFailurev2", strings);
   7978  savedExc.drop();
   7979 }
   7980 
   7981 // static
   7982 Maybe<bool> nsContentUtils::IsPatternMatching(const nsAString& aValue,
   7983                                              nsString&& aPattern,
   7984                                              const Document* aDocument,
   7985                                              bool aHasMultiple,
   7986                                              JS::RegExpFlags aFlags) {
   7987  NS_ASSERTION(aDocument, "aDocument should be a valid pointer (not null)");
   7988 
   7989  // The fact that we're using a JS regexp under the hood should not be visible
   7990  // to things like window onerror handlers, so we don't initialize our JSAPI
   7991  // with the document's window (which may not exist anyway).
   7992  AutoJSAPI jsapi;
   7993  jsapi.Init();
   7994  JSContext* cx = jsapi.cx();
   7995  AutoDisableJSInterruptCallback disabler(cx);
   7996 
   7997  // We can use the junk scope here, because we're just using it for regexp
   7998  // evaluation, not actual script execution, and we disable statics so that the
   7999  // evaluation does not interact with the execution global.
   8000  JSAutoRealm ar(cx, xpc::PrivilegedJunkScope());
   8001 
   8002  // Check if the pattern by itself is valid first, and not that it only becomes
   8003  // valid once we add ^(?: and )$.
   8004  JS::Rooted<JS::Value> error(cx);
   8005  if (!JS::CheckRegExpSyntax(cx, aPattern.BeginReading(), aPattern.Length(),
   8006                             aFlags, &error)) {
   8007    return Nothing();
   8008  }
   8009 
   8010  if (!error.isUndefined()) {
   8011    ReportPatternCompileFailure(aPattern, aFlags, aDocument, &error, cx);
   8012    return Some(true);
   8013  }
   8014 
   8015  // The pattern has to match the entire value.
   8016  aPattern.InsertLiteral(u"^(?:", 0);
   8017  aPattern.AppendLiteral(")$");
   8018 
   8019  JS::Rooted<JSObject*> re(
   8020      cx, JS::NewUCRegExpObject(cx, aPattern.BeginReading(), aPattern.Length(),
   8021                                aFlags));
   8022  if (!re) {
   8023    return Nothing();
   8024  }
   8025 
   8026  JS::Rooted<JS::Value> rval(cx, JS::NullValue());
   8027  if (!aHasMultiple) {
   8028    size_t idx = 0;
   8029    if (!JS::ExecuteRegExpNoStatics(cx, re, aValue.BeginReading(),
   8030                                    aValue.Length(), &idx, true, &rval)) {
   8031      return Nothing();
   8032    }
   8033    return Some(!rval.isNull());
   8034  }
   8035 
   8036  HTMLSplitOnSpacesTokenizer tokenizer(aValue, ',');
   8037  while (tokenizer.hasMoreTokens()) {
   8038    const nsAString& value = tokenizer.nextToken();
   8039    size_t idx = 0;
   8040    if (!JS::ExecuteRegExpNoStatics(cx, re, value.BeginReading(),
   8041                                    value.Length(), &idx, true, &rval)) {
   8042      return Nothing();
   8043    }
   8044    if (rval.isNull()) {
   8045      return Some(false);
   8046    }
   8047  }
   8048  return Some(true);
   8049 }
   8050 
   8051 // static
   8052 nsresult nsContentUtils::URIInheritsSecurityContext(nsIURI* aURI,
   8053                                                    bool* aResult) {
   8054  // Note: about:blank URIs do NOT inherit the security context from the
   8055  // current document, which is what this function tests for...
   8056  return NS_URIChainHasFlags(
   8057      aURI, nsIProtocolHandler::URI_INHERITS_SECURITY_CONTEXT, aResult);
   8058 }
   8059 
   8060 // static
   8061 bool nsContentUtils::ChannelShouldInheritPrincipal(
   8062    nsIPrincipal* aLoadingPrincipal, nsIURI* aURI, bool aInheritForAboutBlank,
   8063    bool aForceInherit) {
   8064  MOZ_ASSERT(aLoadingPrincipal,
   8065             "Can not check inheritance without a principal");
   8066 
   8067  // Only tell the channel to inherit if it can't provide its own security
   8068  // context.
   8069  //
   8070  // XXX: If this is ever changed, check all callers for what owners
   8071  //      they're passing in.  In particular, see the code and
   8072  //      comments in nsDocShell::LoadURI where we fall back on
   8073  //      inheriting the owner if called from chrome.  That would be
   8074  //      very wrong if this code changed anything but channels that
   8075  //      can't provide their own security context!
   8076  //
   8077  // If aForceInherit is true, we will inherit, even for a channel that
   8078  // can provide its own security context. This is used for srcdoc loads.
   8079  bool inherit = aForceInherit;
   8080  if (!inherit) {
   8081    bool uriInherits;
   8082    // We expect URIInheritsSecurityContext to return success for an
   8083    // about:blank URI, so don't call NS_IsAboutBlank() if this call fails.
   8084    // This condition needs to match the one in nsDocShell::InternalLoad where
   8085    // we're checking for things that will use the owner.
   8086    inherit =
   8087        (NS_SUCCEEDED(URIInheritsSecurityContext(aURI, &uriInherits)) &&
   8088         (uriInherits || (aInheritForAboutBlank &&
   8089                          NS_IsAboutBlankAllowQueryAndFragment(aURI)))) ||
   8090        //
   8091        // file: uri special-casing
   8092        //
   8093        // If this is a file: load opened from another file: then it may need
   8094        // to inherit the owner from the referrer so they can script each other.
   8095        // If we don't set the owner explicitly then each file: gets an owner
   8096        // based on its own codebase later.
   8097        //
   8098        (URIIsLocalFile(aURI) &&
   8099         NS_SUCCEEDED(aLoadingPrincipal->CheckMayLoad(aURI, false)) &&
   8100         // One more check here.  CheckMayLoad will always return true for the
   8101         // system principal, but we do NOT want to inherit in that case.
   8102         !aLoadingPrincipal->IsSystemPrincipal());
   8103  }
   8104  return inherit;
   8105 }
   8106 
   8107 /* static */
   8108 bool nsContentUtils::IsCutCopyAllowed(Document* aDocument,
   8109                                      nsIPrincipal& aSubjectPrincipal) {
   8110  if (StaticPrefs::dom_allow_cut_copy() && aDocument &&
   8111      aDocument->HasValidTransientUserGestureActivation()) {
   8112    return true;
   8113  }
   8114 
   8115  return PrincipalHasPermission(aSubjectPrincipal, nsGkAtoms::clipboardWrite);
   8116 }
   8117 
   8118 /* static */
   8119 bool nsContentUtils::HaveEqualPrincipals(Document* aDoc1, Document* aDoc2) {
   8120  if (!aDoc1 || !aDoc2) {
   8121    return false;
   8122  }
   8123  bool principalsEqual = false;
   8124  aDoc1->NodePrincipal()->Equals(aDoc2->NodePrincipal(), &principalsEqual);
   8125  return principalsEqual;
   8126 }
   8127 
   8128 /* static */
   8129 const Document* nsContentUtils::GetInProcessSubtreeRootDocument(
   8130    const Document* aDoc) {
   8131  if (!aDoc) {
   8132    return nullptr;
   8133  }
   8134  const Document* doc = aDoc;
   8135  while (doc->GetInProcessParentDocument()) {
   8136    doc = doc->GetInProcessParentDocument();
   8137  }
   8138  return doc;
   8139 }
   8140 
   8141 // static
   8142 int32_t nsContentUtils::GetAdjustedOffsetInTextControl(nsIFrame* aOffsetFrame,
   8143                                                       int32_t aOffset) {
   8144  // The structure of the anonymous frames within a text control frame is
   8145  // an optional block frame, followed by an optional br frame.
   8146 
   8147  // If the offset frame has a child, then this frame is the block which
   8148  // has the text frames (containing the content) as its children. This will
   8149  // be the case if we click to the right of any of the text frames, or at the
   8150  // bottom of the text area.
   8151  nsIFrame* firstChild = aOffsetFrame->PrincipalChildList().FirstChild();
   8152  if (firstChild) {
   8153    // In this case, the passed-in offset is incorrect, and we want the length
   8154    // of the entire content in the text control frame.
   8155    return firstChild->GetContent()->Length();
   8156  }
   8157 
   8158  if (aOffsetFrame->GetPrevSibling() && !aOffsetFrame->GetNextSibling()) {
   8159    // In this case, we're actually within the last frame, which is a br
   8160    // frame. Our offset should therefore be the length of the first child of
   8161    // our parent.
   8162    int32_t aOutOffset = aOffsetFrame->GetParent()
   8163                             ->PrincipalChildList()
   8164                             .FirstChild()
   8165                             ->GetContent()
   8166                             ->Length();
   8167    return aOutOffset;
   8168  }
   8169 
   8170  // Otherwise, we're within one of the text frames, in which case our offset
   8171  // has already been correctly calculated.
   8172  return aOffset;
   8173 }
   8174 
   8175 // static
   8176 bool nsContentUtils::IsPointInSelection(
   8177    const mozilla::dom::Selection& aSelection, const nsINode& aNode,
   8178    const uint32_t aOffset, const bool aAllowCrossShadowBoundary) {
   8179  const bool selectionIsCollapsed =
   8180      !aAllowCrossShadowBoundary
   8181          ? aSelection.IsCollapsed()
   8182          : aSelection.AreNormalAndCrossShadowBoundaryRangesCollapsed();
   8183  if (selectionIsCollapsed) {
   8184    return false;
   8185  }
   8186 
   8187  const uint32_t rangeCount = aSelection.RangeCount();
   8188  for (const uint32_t i : IntegerRange(rangeCount)) {
   8189    MOZ_ASSERT(aSelection.RangeCount() == rangeCount);
   8190    RefPtr<const nsRange> range = aSelection.GetRangeAt(i);
   8191    if (NS_WARN_IF(!range)) {
   8192      // Don't bail yet, iterate through them all
   8193      continue;
   8194    }
   8195 
   8196    // Done when we find a range that we are in
   8197    if (range->IsPointInRange(aNode, aOffset, IgnoreErrors(),
   8198                              aAllowCrossShadowBoundary)) {
   8199      return true;
   8200    }
   8201  }
   8202 
   8203  return false;
   8204 }
   8205 
   8206 // static
   8207 void nsContentUtils::GetSelectionInTextControl(Selection* aSelection,
   8208                                               Element* aRoot,
   8209                                               uint32_t& aOutStartOffset,
   8210                                               uint32_t& aOutEndOffset) {
   8211  MOZ_ASSERT(aSelection && aRoot);
   8212 
   8213  // We don't care which end of this selection is anchor and which is focus.  In
   8214  // fact, we explicitly want to know which is the _start_ and which is the
   8215  // _end_, not anchor vs focus.
   8216  const nsRange* range = aSelection->GetAnchorFocusRange();
   8217  if (!range) {
   8218    // Nothing selected
   8219    aOutStartOffset = aOutEndOffset = 0;
   8220    return;
   8221  }
   8222 
   8223  // All the node pointers here are raw pointers for performance.  We shouldn't
   8224  // be doing anything in this function that invalidates the node tree.
   8225  nsINode* startContainer = range->GetStartContainer();
   8226  uint32_t startOffset = range->StartOffset();
   8227  nsINode* endContainer = range->GetEndContainer();
   8228  uint32_t endOffset = range->EndOffset();
   8229 
   8230  // We have at most two children, consisting of an optional text node followed
   8231  // by an optional <br>.
   8232  NS_ASSERTION(aRoot->GetChildCount() <= 2, "Unexpected children");
   8233  nsIContent* firstChild = aRoot->GetFirstChild();
   8234 #ifdef DEBUG
   8235  nsCOMPtr<nsIContent> lastChild = aRoot->GetLastChild();
   8236  NS_ASSERTION(startContainer == aRoot || startContainer == firstChild ||
   8237                   startContainer == lastChild,
   8238               "Unexpected startContainer");
   8239  NS_ASSERTION(endContainer == aRoot || endContainer == firstChild ||
   8240                   endContainer == lastChild,
   8241               "Unexpected endContainer");
   8242  // firstChild is either text or a <br> (hence an element).
   8243  MOZ_ASSERT_IF(firstChild, firstChild->IsText() || firstChild->IsElement());
   8244 #endif
   8245  if (!firstChild || firstChild->IsElement()) {
   8246    // No text node, so everything is 0
   8247    startOffset = endOffset = 0;
   8248  } else {
   8249    // First child is text.  If the start/end is already in the text node,
   8250    // or the start of the root node, no change needed.  If it's in the root
   8251    // node but not the start, or in the trailing <br>, we need to set the
   8252    // offset to the end.
   8253    if ((startContainer == aRoot && startOffset != 0) ||
   8254        (startContainer != aRoot && startContainer != firstChild)) {
   8255      startOffset = firstChild->Length();
   8256    }
   8257    if ((endContainer == aRoot && endOffset != 0) ||
   8258        (endContainer != aRoot && endContainer != firstChild)) {
   8259      endOffset = firstChild->Length();
   8260    }
   8261  }
   8262 
   8263  MOZ_ASSERT(startOffset <= endOffset);
   8264  aOutStartOffset = startOffset;
   8265  aOutEndOffset = endOffset;
   8266 }
   8267 
   8268 // static
   8269 HTMLEditor* nsContentUtils::GetHTMLEditor(nsPresContext* aPresContext) {
   8270  if (!aPresContext) {
   8271    return nullptr;
   8272  }
   8273  return GetHTMLEditor(aPresContext->GetDocShell());
   8274 }
   8275 
   8276 // static
   8277 HTMLEditor* nsContentUtils::GetHTMLEditor(nsDocShell* aDocShell) {
   8278  bool isEditable;
   8279  if (!aDocShell || NS_FAILED(aDocShell->GetEditable(&isEditable)) ||
   8280      !isEditable) {
   8281    return nullptr;
   8282  }
   8283  return aDocShell->GetHTMLEditor();
   8284 }
   8285 
   8286 // static
   8287 EditorBase* nsContentUtils::GetActiveEditor(nsPresContext* aPresContext) {
   8288  if (!aPresContext) {
   8289    return nullptr;
   8290  }
   8291 
   8292  return GetActiveEditor(aPresContext->Document()->GetWindow());
   8293 }
   8294 
   8295 // static
   8296 EditorBase* nsContentUtils::GetActiveEditor(nsPIDOMWindowOuter* aWindow) {
   8297  if (!aWindow || !aWindow->GetExtantDoc()) {
   8298    return nullptr;
   8299  }
   8300 
   8301  // If it's in designMode, nobody can have focus.  Therefore, the HTMLEditor
   8302  // handles all events.  I.e., it's focused editor in this case.
   8303  if (aWindow->GetExtantDoc()->IsInDesignMode()) {
   8304    return GetHTMLEditor(nsDocShell::Cast(aWindow->GetDocShell()));
   8305  }
   8306 
   8307  // If focused element is associated with TextEditor, it must be <input>
   8308  // element or <textarea> element.  Let's return it even if it's in a
   8309  // contenteditable element.
   8310  nsCOMPtr<nsPIDOMWindowOuter> focusedWindow;
   8311  if (Element* focusedElement = nsFocusManager::GetFocusedDescendant(
   8312          aWindow, nsFocusManager::SearchRange::eOnlyCurrentWindow,
   8313          getter_AddRefs(focusedWindow))) {
   8314    if (TextEditor* textEditor = focusedElement->GetTextEditorInternal()) {
   8315      return textEditor;
   8316    }
   8317  }
   8318 
   8319  // Otherwise, HTMLEditor may handle inputs even non-editable element has
   8320  // focus or nobody has focus.
   8321  return GetHTMLEditor(nsDocShell::Cast(aWindow->GetDocShell()));
   8322 }
   8323 
   8324 // static
   8325 TextEditor* nsContentUtils::GetExtantTextEditorFromAnonymousNode(
   8326    const nsIContent* aAnonymousContent) {
   8327  if (!aAnonymousContent) {
   8328    return nullptr;
   8329  }
   8330  nsIContent* parent = aAnonymousContent->FindFirstNonChromeOnlyAccessContent();
   8331  if (!parent || parent == aAnonymousContent) {
   8332    return nullptr;
   8333  }
   8334  if (const HTMLInputElement* const inputElement =
   8335          HTMLInputElement::FromNodeOrNull(parent)) {
   8336    return inputElement->GetExtantTextEditor();
   8337  }
   8338  if (const HTMLTextAreaElement* const textareaElement =
   8339          HTMLTextAreaElement::FromNodeOrNull(parent)) {
   8340    return textareaElement->GetExtantTextEditor();
   8341  }
   8342  return nullptr;
   8343 }
   8344 
   8345 // static
   8346 bool nsContentUtils::IsNodeInEditableRegion(nsINode* aNode) {
   8347  while (aNode) {
   8348    if (aNode->IsEditable()) {
   8349      return true;
   8350    }
   8351    aNode = aNode->GetParent();
   8352  }
   8353  return false;
   8354 }
   8355 
   8356 // static
   8357 bool nsContentUtils::IsForbiddenRequestHeader(const nsACString& aHeader,
   8358                                              const nsACString& aValue) {
   8359  if (IsForbiddenSystemRequestHeader(aHeader)) {
   8360    return true;
   8361  }
   8362 
   8363  if ((nsContentUtils::IsOverrideMethodHeader(aHeader) &&
   8364       nsContentUtils::ContainsForbiddenMethod(aValue))) {
   8365    return true;
   8366  }
   8367 
   8368  if (StringBeginsWith(aHeader, "proxy-"_ns,
   8369                       nsCaseInsensitiveCStringComparator) ||
   8370      StringBeginsWith(aHeader, "sec-"_ns,
   8371                       nsCaseInsensitiveCStringComparator)) {
   8372    return true;
   8373  }
   8374 
   8375  return false;
   8376 }
   8377 
   8378 // static
   8379 bool nsContentUtils::IsForbiddenSystemRequestHeader(const nsACString& aHeader) {
   8380  static const char* kInvalidHeaders[] = {"accept-charset",
   8381                                          "accept-encoding",
   8382                                          "access-control-request-headers",
   8383                                          "access-control-request-method",
   8384                                          "connection",
   8385                                          "content-length",
   8386                                          "cookie",
   8387                                          "cookie2",
   8388                                          "date",
   8389                                          "dnt",
   8390                                          "expect",
   8391                                          "host",
   8392                                          "keep-alive",
   8393                                          "origin",
   8394                                          "referer",
   8395                                          "set-cookie",
   8396                                          "te",
   8397                                          "trailer",
   8398                                          "transfer-encoding",
   8399                                          "upgrade",
   8400                                          "via"};
   8401  for (auto& kInvalidHeader : kInvalidHeaders) {
   8402    if (aHeader.LowerCaseEqualsASCII(kInvalidHeader)) {
   8403      return true;
   8404    }
   8405  }
   8406  return false;
   8407 }
   8408 
   8409 // static
   8410 bool nsContentUtils::IsForbiddenResponseHeader(const nsACString& aHeader) {
   8411  return (aHeader.LowerCaseEqualsASCII("set-cookie") ||
   8412          aHeader.LowerCaseEqualsASCII("set-cookie2"));
   8413 }
   8414 
   8415 // static
   8416 bool nsContentUtils::IsOverrideMethodHeader(const nsACString& headerName) {
   8417  return headerName.EqualsIgnoreCase("x-http-method-override") ||
   8418         headerName.EqualsIgnoreCase("x-http-method") ||
   8419         headerName.EqualsIgnoreCase("x-method-override");
   8420 }
   8421 
   8422 // static
   8423 bool nsContentUtils::ContainsForbiddenMethod(const nsACString& headerValue) {
   8424  bool hasInsecureMethod = false;
   8425  nsCCharSeparatedTokenizer tokenizer(headerValue, ',');
   8426 
   8427  while (tokenizer.hasMoreTokens()) {
   8428    const nsDependentCSubstring& value = tokenizer.nextToken();
   8429 
   8430    if (value.EqualsIgnoreCase("connect") || value.EqualsIgnoreCase("trace") ||
   8431        value.EqualsIgnoreCase("track")) {
   8432      hasInsecureMethod = true;
   8433      break;
   8434    }
   8435  }
   8436 
   8437  return hasInsecureMethod;
   8438 }
   8439 
   8440 Maybe<nsContentUtils::ParsedRange> nsContentUtils::ParseSingleRangeRequest(
   8441    const nsACString& aHeaderValue, bool aAllowWhitespace) {
   8442  // See https://fetch.spec.whatwg.org/#simple-range-header-value
   8443  mozilla::Tokenizer p(aHeaderValue);
   8444  Maybe<uint64_t> rangeStart;
   8445  Maybe<uint64_t> rangeEnd;
   8446 
   8447  // Step 2 and 3
   8448  if (!p.CheckWord("bytes")) {
   8449    return Nothing();
   8450  }
   8451 
   8452  // Step 4
   8453  if (aAllowWhitespace) {
   8454    p.SkipWhites();
   8455  }
   8456 
   8457  // Step 5 and 6
   8458  if (!p.CheckChar('=')) {
   8459    return Nothing();
   8460  }
   8461 
   8462  // Step 7
   8463  if (aAllowWhitespace) {
   8464    p.SkipWhites();
   8465  }
   8466 
   8467  // Step 8 and 9
   8468  uint64_t res;
   8469  if (p.ReadInteger(&res)) {
   8470    rangeStart = Some(res);
   8471  }
   8472 
   8473  // Step 10
   8474  if (aAllowWhitespace) {
   8475    p.SkipWhites();
   8476  }
   8477 
   8478  // Step 11
   8479  if (!p.CheckChar('-')) {
   8480    return Nothing();
   8481  }
   8482 
   8483  // Step 13
   8484  if (aAllowWhitespace) {
   8485    p.SkipWhites();
   8486  }
   8487 
   8488  // Step 14 and 15
   8489  if (p.ReadInteger(&res)) {
   8490    rangeEnd = Some(res);
   8491  }
   8492 
   8493  // Step 16
   8494  if (!p.CheckEOF()) {
   8495    return Nothing();
   8496  }
   8497 
   8498  // Step 17
   8499  if (!rangeStart && !rangeEnd) {
   8500    return Nothing();
   8501  }
   8502 
   8503  // Step 18
   8504  if (rangeStart && rangeEnd && *rangeStart > *rangeEnd) {
   8505    return Nothing();
   8506  }
   8507 
   8508  return Some(ParsedRange(rangeStart, rangeEnd));
   8509 }
   8510 
   8511 // static
   8512 bool nsContentUtils::IsCorsUnsafeRequestHeaderValue(
   8513    const nsACString& aHeaderValue) {
   8514  const char* cur = aHeaderValue.BeginReading();
   8515  const char* end = aHeaderValue.EndReading();
   8516 
   8517  while (cur != end) {
   8518    // Implementation of
   8519    // https://fetch.spec.whatwg.org/#cors-unsafe-request-header-byte Is less
   8520    // than a space but not a horizontal tab
   8521    if ((*cur < ' ' && *cur != '\t') || *cur == '"' || *cur == '(' ||
   8522        *cur == ')' || *cur == ':' || *cur == '<' || *cur == '>' ||
   8523        *cur == '?' || *cur == '@' || *cur == '[' || *cur == '\\' ||
   8524        *cur == ']' || *cur == '{' || *cur == '}' ||
   8525        *cur == 0x7F) {  // 0x75 is DEL
   8526      return true;
   8527    }
   8528    cur++;
   8529  }
   8530  return false;
   8531 }
   8532 
   8533 // static
   8534 bool nsContentUtils::IsAllowedNonCorsAccept(const nsACString& aHeaderValue) {
   8535  if (IsCorsUnsafeRequestHeaderValue(aHeaderValue)) {
   8536    return false;
   8537  }
   8538  return true;
   8539 }
   8540 
   8541 // static
   8542 bool nsContentUtils::IsAllowedNonCorsContentType(
   8543    const nsACString& aHeaderValue) {
   8544  nsAutoCString contentType;
   8545  nsAutoCString unused;
   8546 
   8547  if (IsCorsUnsafeRequestHeaderValue(aHeaderValue)) {
   8548    return false;
   8549  }
   8550 
   8551  nsresult rv = NS_ParseRequestContentType(aHeaderValue, contentType, unused);
   8552  if (NS_FAILED(rv)) {
   8553    return false;
   8554  }
   8555 
   8556  return contentType.LowerCaseEqualsLiteral("text/plain") ||
   8557         contentType.LowerCaseEqualsLiteral(
   8558             "application/x-www-form-urlencoded") ||
   8559         contentType.LowerCaseEqualsLiteral("multipart/form-data");
   8560 }
   8561 
   8562 // static
   8563 bool nsContentUtils::IsAllowedNonCorsLanguage(const nsACString& aHeaderValue) {
   8564  const char* cur = aHeaderValue.BeginReading();
   8565  const char* end = aHeaderValue.EndReading();
   8566 
   8567  while (cur != end) {
   8568    if ((*cur >= '0' && *cur <= '9') || (*cur >= 'A' && *cur <= 'Z') ||
   8569        (*cur >= 'a' && *cur <= 'z') || *cur == ' ' || *cur == '*' ||
   8570        *cur == ',' || *cur == '-' || *cur == '.' || *cur == ';' ||
   8571        *cur == '=') {
   8572      cur++;
   8573      continue;
   8574    }
   8575    return false;
   8576  }
   8577  return true;
   8578 }
   8579 
   8580 bool nsContentUtils::IsAllowedNonCorsRange(const nsACString& aHeaderValue) {
   8581  Maybe<ParsedRange> parsedRange = ParseSingleRangeRequest(aHeaderValue, false);
   8582  if (!parsedRange) {
   8583    return false;
   8584  }
   8585 
   8586  if (!parsedRange->Start()) {
   8587    return false;
   8588  }
   8589 
   8590  return true;
   8591 }
   8592 
   8593 // static
   8594 bool nsContentUtils::IsCORSSafelistedRequestHeader(const nsACString& aName,
   8595                                                   const nsACString& aValue) {
   8596  // see https://fetch.spec.whatwg.org/#cors-safelisted-request-header
   8597  if (aValue.Length() > 128) {
   8598    return false;
   8599  }
   8600  return (aName.LowerCaseEqualsLiteral("accept") &&
   8601          nsContentUtils::IsAllowedNonCorsAccept(aValue)) ||
   8602         (aName.LowerCaseEqualsLiteral("accept-language") &&
   8603          nsContentUtils::IsAllowedNonCorsLanguage(aValue)) ||
   8604         (aName.LowerCaseEqualsLiteral("content-language") &&
   8605          nsContentUtils::IsAllowedNonCorsLanguage(aValue)) ||
   8606         (aName.LowerCaseEqualsLiteral("content-type") &&
   8607          nsContentUtils::IsAllowedNonCorsContentType(aValue)) ||
   8608         (aName.LowerCaseEqualsLiteral("range") &&
   8609          nsContentUtils::IsAllowedNonCorsRange(aValue)) ||
   8610         (StaticPrefs::network_http_idempotencyKey_enabled() &&
   8611          aName.LowerCaseEqualsLiteral("idempotency-key"));
   8612 }
   8613 
   8614 mozilla::LogModule* nsContentUtils::ResistFingerprintingLog() {
   8615  return gResistFingerprintingLog;
   8616 }
   8617 mozilla::LogModule* nsContentUtils::DOMDumpLog() { return sDOMDumpLog; }
   8618 
   8619 bool nsContentUtils::GetNodeTextContent(const nsINode* aNode, bool aDeep,
   8620                                        nsAString& aResult,
   8621                                        const fallible_t& aFallible) {
   8622  aResult.Truncate();
   8623  return AppendNodeTextContent(aNode, aDeep, aResult, aFallible);
   8624 }
   8625 
   8626 void nsContentUtils::GetNodeTextContent(const nsINode* aNode, bool aDeep,
   8627                                        nsAString& aResult) {
   8628  if (!GetNodeTextContent(aNode, aDeep, aResult, fallible)) {
   8629    NS_ABORT_OOM(0);  // Unfortunately we don't know the allocation size
   8630  }
   8631 }
   8632 
   8633 void nsContentUtils::DestroyMatchString(void* aData) {
   8634  if (aData) {
   8635    nsString* matchString = static_cast<nsString*>(aData);
   8636    delete matchString;
   8637  }
   8638 }
   8639 
   8640 // Table ordered from most to least likely JS MIME types.
   8641 static constexpr std::string_view kJavascriptMIMETypes[] = {
   8642    "text/javascript",
   8643    "text/ecmascript",
   8644    "application/javascript",
   8645    "application/ecmascript",
   8646    "application/x-javascript",
   8647    "application/x-ecmascript",
   8648    "text/javascript1.0",
   8649    "text/javascript1.1",
   8650    "text/javascript1.2",
   8651    "text/javascript1.3",
   8652    "text/javascript1.4",
   8653    "text/javascript1.5",
   8654    "text/jscript",
   8655    "text/livescript",
   8656    "text/x-ecmascript",
   8657    "text/x-javascript"};
   8658 
   8659 bool nsContentUtils::IsJavascriptMIMEType(const nsAString& aMIMEType) {
   8660  for (std::string_view type : kJavascriptMIMETypes) {
   8661    if (aMIMEType.LowerCaseEqualsASCII(type.data(), type.length())) {
   8662      return true;
   8663    }
   8664  }
   8665  return false;
   8666 }
   8667 
   8668 bool nsContentUtils::IsJavascriptMIMEType(const nsACString& aMIMEType) {
   8669  for (std::string_view type : kJavascriptMIMETypes) {
   8670    if (aMIMEType.LowerCaseEqualsASCII(type.data(), type.length())) {
   8671      return true;
   8672    }
   8673  }
   8674  return false;
   8675 }
   8676 
   8677 // https://mimesniff.spec.whatwg.org/#json-mime-type
   8678 bool nsContentUtils::IsJsonMimeType(const nsAString& aMimeType) {
   8679  // Table ordered from most to least likely JSON MIME types.
   8680  static constexpr std::string_view jsonTypes[] = {"application/json",
   8681                                                   "text/json"};
   8682 
   8683  for (std::string_view type : jsonTypes) {
   8684    if (aMimeType.LowerCaseEqualsASCII(type.data(), type.length())) {
   8685      return true;
   8686    }
   8687  }
   8688 
   8689  // Below checks if the 'subtype' ends in "+json".
   8690  RefPtr<MimeType> parsed = MimeType::Parse(aMimeType);
   8691  if (!parsed) {
   8692    return false;
   8693  }
   8694 
   8695  nsAutoString subtype;
   8696  parsed->GetSubtype(subtype);
   8697  return StringEndsWith(subtype, u"+json"_ns);
   8698 }
   8699 
   8700 // https://html.spec.whatwg.org/#fetch-a-single-module-script, 13.7.3
   8701 bool nsContentUtils::HasCssMimeTypeEssence(const nsAString& aMimeType) {
   8702  nsString contentType, contentCharset;
   8703  if (MimeType::Parse(aMimeType, contentType, contentCharset)) {
   8704    return contentType.LowerCaseEqualsLiteral("text/css");
   8705  }
   8706  return false;
   8707 }
   8708 
   8709 // https://html.spec.whatwg.org/#fetch-a-single-module-script, 13.6
   8710 bool nsContentUtils::HasWasmMimeTypeEssence(const nsAString& aMimeType) {
   8711  nsString contentType, contentCharset;
   8712  if (MimeType::Parse(aMimeType, contentType, contentCharset)) {
   8713    return contentType.LowerCaseEqualsLiteral("application/wasm");
   8714  }
   8715  return false;
   8716 }
   8717 
   8718 bool nsContentUtils::PrefetchPreloadEnabled(nsIDocShell* aDocShell) {
   8719  //
   8720  // SECURITY CHECK: disable prefetching and preloading from mailnews!
   8721  //
   8722  // walk up the docshell tree to see if any containing
   8723  // docshell are of type MAIL.
   8724  //
   8725 
   8726  if (!aDocShell) {
   8727    return false;
   8728  }
   8729 
   8730  nsCOMPtr<nsIDocShell> docshell = aDocShell;
   8731  nsCOMPtr<nsIDocShellTreeItem> parentItem;
   8732 
   8733  do {
   8734    auto appType = docshell->GetAppType();
   8735    if (appType == nsIDocShell::APP_TYPE_MAIL) {
   8736      return false;  // do not prefetch, preload, preconnect from mailnews
   8737    }
   8738 
   8739    docshell->GetInProcessParent(getter_AddRefs(parentItem));
   8740    if (parentItem) {
   8741      docshell = do_QueryInterface(parentItem);
   8742      if (!docshell) {
   8743        NS_ERROR("cannot get a docshell from a treeItem!");
   8744        return false;
   8745      }
   8746    }
   8747  } while (parentItem);
   8748 
   8749  return true;
   8750 }
   8751 
   8752 uint64_t nsContentUtils::GetInnerWindowID(nsIRequest* aRequest) {
   8753  // can't do anything if there's no nsIRequest!
   8754  if (!aRequest) {
   8755    return 0;
   8756  }
   8757 
   8758  if (nsCOMPtr<nsIChannel> channel = do_QueryInterface(aRequest)) {
   8759    nsCOMPtr loadInfo = channel->LoadInfo();
   8760    if (auto id = loadInfo->GetInnerWindowID()) {
   8761      return id;
   8762    }
   8763    if (auto id = loadInfo->GetTriggeringWindowId()) {
   8764      return id;
   8765    }
   8766  }
   8767 
   8768  nsCOMPtr<nsILoadGroup> loadGroup;
   8769  nsresult rv = aRequest->GetLoadGroup(getter_AddRefs(loadGroup));
   8770 
   8771  if (NS_FAILED(rv) || !loadGroup) {
   8772    return 0;
   8773  }
   8774 
   8775  return GetInnerWindowID(loadGroup);
   8776 }
   8777 
   8778 uint64_t nsContentUtils::GetInnerWindowID(nsILoadGroup* aLoadGroup) {
   8779  if (!aLoadGroup) {
   8780    return 0;
   8781  }
   8782 
   8783  nsCOMPtr<nsIInterfaceRequestor> callbacks;
   8784  nsresult rv = aLoadGroup->GetNotificationCallbacks(getter_AddRefs(callbacks));
   8785  if (NS_FAILED(rv) || !callbacks) {
   8786    return 0;
   8787  }
   8788 
   8789  nsCOMPtr<nsILoadContext> loadContext = do_GetInterface(callbacks);
   8790  if (!loadContext) {
   8791    return 0;
   8792  }
   8793 
   8794  nsCOMPtr<mozIDOMWindowProxy> window;
   8795  rv = loadContext->GetAssociatedWindow(getter_AddRefs(window));
   8796  if (NS_FAILED(rv) || !window) {
   8797    return 0;
   8798  }
   8799 
   8800  auto* pwindow = nsPIDOMWindowOuter::From(window);
   8801  if (!pwindow) {
   8802    return 0;
   8803  }
   8804 
   8805  nsPIDOMWindowInner* inner = pwindow->GetCurrentInnerWindow();
   8806  return inner ? inner->WindowID() : 0;
   8807 }
   8808 
   8809 // static
   8810 void nsContentUtils::MaybeFixIPv6Host(nsACString& aHost) {
   8811  if (aHost.FindChar(':') != -1) {  // Escape IPv6 address
   8812    MOZ_ASSERT(!aHost.IsEmpty());
   8813    if (aHost.Length() >= 2 && aHost[0] != '[' &&
   8814        aHost[aHost.Length() - 1] != ']') {
   8815      aHost.Insert('[', 0);
   8816      aHost.Append(']');
   8817    }
   8818  }
   8819 }
   8820 
   8821 nsresult nsContentUtils::GetHostOrIPv6WithBrackets(nsIURI* aURI,
   8822                                                   nsACString& aHost) {
   8823  aHost.Truncate();
   8824  nsresult rv = aURI->GetHost(aHost);
   8825  if (NS_FAILED(rv)) {  // Some URIs do not have a host
   8826    return rv;
   8827  }
   8828 
   8829  MaybeFixIPv6Host(aHost);
   8830 
   8831  return NS_OK;
   8832 }
   8833 
   8834 nsresult nsContentUtils::GetHostOrIPv6WithBrackets(nsIURI* aURI,
   8835                                                   nsAString& aHost) {
   8836  nsAutoCString hostname;
   8837  nsresult rv = GetHostOrIPv6WithBrackets(aURI, hostname);
   8838  if (NS_FAILED(rv)) {
   8839    return rv;
   8840  }
   8841  CopyUTF8toUTF16(hostname, aHost);
   8842  return NS_OK;
   8843 }
   8844 
   8845 nsresult nsContentUtils::GetHostOrIPv6WithBrackets(nsIPrincipal* aPrincipal,
   8846                                                   nsACString& aHost) {
   8847  nsresult rv = aPrincipal->GetAsciiHost(aHost);
   8848  if (NS_FAILED(rv)) {  // Some URIs do not have a host
   8849    return rv;
   8850  }
   8851 
   8852  MaybeFixIPv6Host(aHost);
   8853  return NS_OK;
   8854 }
   8855 
   8856 CallState nsContentUtils::CallOnAllRemoteChildren(
   8857    MessageBroadcaster* aManager,
   8858    const std::function<CallState(BrowserParent*)>& aCallback) {
   8859  uint32_t browserChildCount = aManager->ChildCount();
   8860  for (uint32_t j = 0; j < browserChildCount; ++j) {
   8861    RefPtr<MessageListenerManager> childMM = aManager->GetChildAt(j);
   8862    if (!childMM) {
   8863      continue;
   8864    }
   8865 
   8866    RefPtr<MessageBroadcaster> nonLeafMM = MessageBroadcaster::From(childMM);
   8867    if (nonLeafMM) {
   8868      if (CallOnAllRemoteChildren(nonLeafMM, aCallback) == CallState::Stop) {
   8869        return CallState::Stop;
   8870      }
   8871      continue;
   8872    }
   8873 
   8874    mozilla::dom::ipc::MessageManagerCallback* cb = childMM->GetCallback();
   8875    if (cb) {
   8876      nsFrameLoader* fl = static_cast<nsFrameLoader*>(cb);
   8877      BrowserParent* remote = BrowserParent::GetFrom(fl);
   8878      if (remote && aCallback) {
   8879        if (aCallback(remote) == CallState::Stop) {
   8880          return CallState::Stop;
   8881        }
   8882      }
   8883    }
   8884  }
   8885 
   8886  return CallState::Continue;
   8887 }
   8888 
   8889 void nsContentUtils::CallOnAllRemoteChildren(
   8890    nsPIDOMWindowOuter* aWindow,
   8891    const std::function<CallState(BrowserParent*)>& aCallback) {
   8892  nsGlobalWindowOuter* window = nsGlobalWindowOuter::Cast(aWindow);
   8893  if (window->IsChromeWindow()) {
   8894    RefPtr<MessageBroadcaster> windowMM = window->GetMessageManager();
   8895    if (windowMM) {
   8896      CallOnAllRemoteChildren(windowMM, aCallback);
   8897    }
   8898  }
   8899 }
   8900 
   8901 bool nsContentUtils::IPCTransferableDataItemHasKnownFlavor(
   8902    const IPCTransferableDataItem& aItem) {
   8903  // Unknown types are converted to kCustomTypesMime.
   8904  if (aItem.flavor().EqualsASCII(kCustomTypesMime)) {
   8905    return true;
   8906  }
   8907 
   8908  for (const char* format : DataTransfer::kKnownFormats) {
   8909    if (aItem.flavor().EqualsASCII(format)) {
   8910      return true;
   8911    }
   8912  }
   8913 
   8914  return false;
   8915 }
   8916 
   8917 nsresult nsContentUtils::IPCTransferableDataToTransferable(
   8918    const IPCTransferableData& aTransferableData, bool aAddDataFlavor,
   8919    nsITransferable* aTransferable, const bool aFilterUnknownFlavors) {
   8920  nsresult rv;
   8921  const nsTArray<IPCTransferableDataItem>& items = aTransferableData.items();
   8922  for (const auto& item : items) {
   8923    if (aFilterUnknownFlavors && !IPCTransferableDataItemHasKnownFlavor(item)) {
   8924      NS_WARNING(
   8925          "Ignoring unknown flavor in "
   8926          "nsContentUtils::IPCTransferableDataToTransferable");
   8927      continue;
   8928    }
   8929 
   8930    if (aAddDataFlavor) {
   8931      aTransferable->AddDataFlavor(item.flavor().get());
   8932    }
   8933 
   8934    nsCOMPtr<nsISupports> transferData;
   8935    switch (item.data().type()) {
   8936      case IPCTransferableDataType::TIPCTransferableDataString: {
   8937        const auto& data = item.data().get_IPCTransferableDataString();
   8938        nsCOMPtr<nsISupportsString> dataWrapper =
   8939            do_CreateInstance(NS_SUPPORTS_STRING_CONTRACTID, &rv);
   8940        NS_ENSURE_SUCCESS(rv, rv);
   8941        rv = dataWrapper->SetData(nsDependentSubstring(
   8942            reinterpret_cast<const char16_t*>(data.data().Data()),
   8943            data.data().Size() / sizeof(char16_t)));
   8944        NS_ENSURE_SUCCESS(rv, rv);
   8945        transferData = dataWrapper;
   8946        break;
   8947      }
   8948      case IPCTransferableDataType::TIPCTransferableDataCString: {
   8949        const auto& data = item.data().get_IPCTransferableDataCString();
   8950        nsCOMPtr<nsISupportsCString> dataWrapper =
   8951            do_CreateInstance(NS_SUPPORTS_CSTRING_CONTRACTID, &rv);
   8952        NS_ENSURE_SUCCESS(rv, rv);
   8953        rv = dataWrapper->SetData(nsDependentCSubstring(
   8954            reinterpret_cast<const char*>(data.data().Data()),
   8955            data.data().Size()));
   8956        NS_ENSURE_SUCCESS(rv, rv);
   8957        transferData = dataWrapper;
   8958        break;
   8959      }
   8960      case IPCTransferableDataType::TIPCTransferableDataInputStream: {
   8961        const auto& data = item.data().get_IPCTransferableDataInputStream();
   8962        nsCOMPtr<nsIInputStream> stream;
   8963        rv = NS_NewByteInputStream(getter_AddRefs(stream),
   8964                                   AsChars(data.data().AsSpan()),
   8965                                   NS_ASSIGNMENT_COPY);
   8966        NS_ENSURE_SUCCESS(rv, rv);
   8967        transferData = stream.forget();
   8968        break;
   8969      }
   8970      case IPCTransferableDataType::TIPCTransferableDataImageContainer: {
   8971        const auto& data = item.data().get_IPCTransferableDataImageContainer();
   8972        nsCOMPtr<imgIContainer> container = IPCImageToImage(data.image());
   8973        if (!container) {
   8974          return NS_ERROR_FAILURE;
   8975        }
   8976        transferData = container;
   8977        break;
   8978      }
   8979      case IPCTransferableDataType::TIPCTransferableDataBlob: {
   8980        const auto& data = item.data().get_IPCTransferableDataBlob();
   8981        transferData = IPCBlobUtils::Deserialize(data.blob());
   8982        break;
   8983      }
   8984      case IPCTransferableDataType::T__None:
   8985        MOZ_ASSERT_UNREACHABLE();
   8986        return NS_ERROR_FAILURE;
   8987    }
   8988 
   8989    rv = aTransferable->SetTransferData(item.flavor().get(), transferData);
   8990    NS_ENSURE_SUCCESS(rv, rv);
   8991  }
   8992  return NS_OK;
   8993 }
   8994 
   8995 nsresult nsContentUtils::IPCTransferableToTransferable(
   8996    const IPCTransferable& aIPCTransferable, bool aAddDataFlavor,
   8997    nsITransferable* aTransferable, const bool aFilterUnknownFlavors) {
   8998  // Note that we need to set privacy status of transferable before adding any
   8999  // data into it.
   9000  aTransferable->SetIsPrivateData(aIPCTransferable.isPrivateData());
   9001 
   9002  nsresult rv =
   9003      IPCTransferableDataToTransferable(aIPCTransferable.data(), aAddDataFlavor,
   9004                                        aTransferable, aFilterUnknownFlavors);
   9005  NS_ENSURE_SUCCESS(rv, rv);
   9006 
   9007  if (aIPCTransferable.cookieJarSettings().isSome()) {
   9008    nsCOMPtr<nsICookieJarSettings> cookieJarSettings;
   9009    net::CookieJarSettings::Deserialize(
   9010        aIPCTransferable.cookieJarSettings().ref(),
   9011        getter_AddRefs(cookieJarSettings));
   9012    aTransferable->SetCookieJarSettings(cookieJarSettings);
   9013  }
   9014  aTransferable->SetReferrerInfo(aIPCTransferable.referrerInfo());
   9015  aTransferable->SetDataPrincipal(aIPCTransferable.dataPrincipal());
   9016  aTransferable->SetContentPolicyType(aIPCTransferable.contentPolicyType());
   9017 
   9018  return NS_OK;
   9019 }
   9020 
   9021 nsresult nsContentUtils::IPCTransferableDataItemToVariant(
   9022    const IPCTransferableDataItem& aItem, nsIWritableVariant* aVariant) {
   9023  MOZ_ASSERT(aVariant);
   9024 
   9025  switch (aItem.data().type()) {
   9026    case IPCTransferableDataType::TIPCTransferableDataString: {
   9027      const auto& data = aItem.data().get_IPCTransferableDataString();
   9028      return aVariant->SetAsAString(nsDependentSubstring(
   9029          reinterpret_cast<const char16_t*>(data.data().Data()),
   9030          data.data().Size() / sizeof(char16_t)));
   9031    }
   9032    case IPCTransferableDataType::TIPCTransferableDataCString: {
   9033      const auto& data = aItem.data().get_IPCTransferableDataCString();
   9034      return aVariant->SetAsACString(nsDependentCSubstring(
   9035          reinterpret_cast<const char*>(data.data().Data()),
   9036          data.data().Size()));
   9037    }
   9038    case IPCTransferableDataType::TIPCTransferableDataInputStream: {
   9039      const auto& data = aItem.data().get_IPCTransferableDataInputStream();
   9040      nsCOMPtr<nsIInputStream> stream;
   9041      nsresult rv = NS_NewByteInputStream(getter_AddRefs(stream),
   9042                                          AsChars(data.data().AsSpan()),
   9043                                          NS_ASSIGNMENT_COPY);
   9044      NS_ENSURE_SUCCESS(rv, rv);
   9045      return aVariant->SetAsISupports(stream);
   9046    }
   9047    case IPCTransferableDataType::TIPCTransferableDataImageContainer: {
   9048      const auto& data = aItem.data().get_IPCTransferableDataImageContainer();
   9049      nsCOMPtr<imgIContainer> container = IPCImageToImage(data.image());
   9050      if (!container) {
   9051        return NS_ERROR_FAILURE;
   9052      }
   9053      return aVariant->SetAsISupports(container);
   9054    }
   9055    case IPCTransferableDataType::TIPCTransferableDataBlob: {
   9056      const auto& data = aItem.data().get_IPCTransferableDataBlob();
   9057      RefPtr<BlobImpl> blobImpl = IPCBlobUtils::Deserialize(data.blob());
   9058      return aVariant->SetAsISupports(blobImpl);
   9059    }
   9060    case IPCTransferableDataType::T__None:
   9061      break;
   9062  }
   9063 
   9064  MOZ_ASSERT_UNREACHABLE();
   9065  return NS_ERROR_UNEXPECTED;
   9066 }
   9067 
   9068 void nsContentUtils::TransferablesToIPCTransferableDatas(
   9069    nsIArray* aTransferables, nsTArray<IPCTransferableData>& aIPC,
   9070    bool aInSyncMessage, mozilla::dom::ContentParent* aParent) {
   9071  aIPC.Clear();
   9072  if (aTransferables) {
   9073    uint32_t transferableCount = 0;
   9074    aTransferables->GetLength(&transferableCount);
   9075    for (uint32_t i = 0; i < transferableCount; ++i) {
   9076      IPCTransferableData* dt = aIPC.AppendElement();
   9077      nsCOMPtr<nsITransferable> transferable =
   9078          do_QueryElementAt(aTransferables, i);
   9079      TransferableToIPCTransferableData(transferable, dt, aInSyncMessage,
   9080                                        aParent);
   9081    }
   9082  }
   9083 }
   9084 
   9085 nsresult nsContentUtils::CalculateBufferSizeForImage(
   9086    const uint32_t& aStride, const IntSize& aImageSize,
   9087    const SurfaceFormat& aFormat, size_t* aMaxBufferSize,
   9088    size_t* aUsedBufferSize) {
   9089  CheckedInt32 requiredBytes =
   9090      CheckedInt32(aStride) * CheckedInt32(aImageSize.height);
   9091 
   9092  CheckedInt32 usedBytes =
   9093      requiredBytes - aStride +
   9094      (CheckedInt32(aImageSize.width) * BytesPerPixel(aFormat));
   9095  if (!usedBytes.isValid()) {
   9096    return NS_ERROR_FAILURE;
   9097  }
   9098 
   9099  MOZ_ASSERT(requiredBytes.isValid(), "usedBytes valid but not required?");
   9100  *aMaxBufferSize = requiredBytes.value();
   9101  *aUsedBufferSize = usedBytes.value();
   9102  return NS_OK;
   9103 }
   9104 
   9105 static already_AddRefed<DataSourceSurface> BigBufferToDataSurface(
   9106    const BigBuffer& aData, uint32_t aStride, const IntSize& aImageSize,
   9107    SurfaceFormat aFormat) {
   9108  if (!aData.Size() || !aImageSize.width || !aImageSize.height) {
   9109    return nullptr;
   9110  }
   9111 
   9112  // Validate shared memory buffer size
   9113  size_t imageBufLen = 0;
   9114  size_t maxBufLen = 0;
   9115  if (NS_FAILED(nsContentUtils::CalculateBufferSizeForImage(
   9116          aStride, aImageSize, aFormat, &maxBufLen, &imageBufLen))) {
   9117    return nullptr;
   9118  }
   9119  if (imageBufLen > aData.Size()) {
   9120    return nullptr;
   9121  }
   9122  return CreateDataSourceSurfaceFromData(aImageSize, aFormat, aData.Data(),
   9123                                         aStride);
   9124 }
   9125 
   9126 bool nsContentUtils::IsFlavorImage(const nsACString& aFlavor) {
   9127  return aFlavor.EqualsLiteral(kNativeImageMime) ||
   9128         aFlavor.EqualsLiteral(kJPEGImageMime) ||
   9129         aFlavor.EqualsLiteral(kJPGImageMime) ||
   9130         aFlavor.EqualsLiteral(kPNGImageMime) ||
   9131         aFlavor.EqualsLiteral(kGIFImageMime);
   9132 }
   9133 
   9134 // FIXME: This can probably be removed once bug 1783240 lands, as `nsString`
   9135 // will be implicitly serialized in shmem when sent over IPDL directly.
   9136 static IPCTransferableDataString AsIPCTransferableDataString(
   9137    Span<const char16_t> aInput) {
   9138  return IPCTransferableDataString{BigBuffer(AsBytes(aInput))};
   9139 }
   9140 
   9141 // FIXME: This can probably be removed once bug 1783240 lands, as `nsCString`
   9142 // will be implicitly serialized in shmem when sent over IPDL directly.
   9143 static IPCTransferableDataCString AsIPCTransferableDataCString(
   9144    Span<const char> aInput) {
   9145  return IPCTransferableDataCString{BigBuffer(AsBytes(aInput))};
   9146 }
   9147 
   9148 void nsContentUtils::TransferableToIPCTransferableData(
   9149    nsITransferable* aTransferable, IPCTransferableData* aTransferableData,
   9150    bool aInSyncMessage, mozilla::dom::ContentParent* aParent) {
   9151  MOZ_ASSERT_IF(XRE_IsParentProcess(), aParent);
   9152 
   9153  if (aTransferable) {
   9154    nsTArray<nsCString> flavorList;
   9155    aTransferable->FlavorsTransferableCanExport(flavorList);
   9156 
   9157    for (uint32_t j = 0; j < flavorList.Length(); ++j) {
   9158      nsCString& flavorStr = flavorList[j];
   9159      if (!flavorStr.Length()) {
   9160        continue;
   9161      }
   9162 
   9163      nsCOMPtr<nsISupports> data;
   9164      nsresult rv =
   9165          aTransferable->GetTransferData(flavorStr.get(), getter_AddRefs(data));
   9166 
   9167      if (NS_FAILED(rv) || !data) {
   9168        if (aInSyncMessage) {
   9169          // Can't do anything.
   9170          // FIXME: This shouldn't be the case anymore!
   9171          continue;
   9172        }
   9173 
   9174        // This is a hack to support kFilePromiseMime.
   9175        // On Windows there just needs to be an entry for it,
   9176        // and for OSX we need to create
   9177        // nsContentAreaDragDropDataProvider as nsIFlavorDataProvider.
   9178        if (flavorStr.EqualsLiteral(kFilePromiseMime)) {
   9179          IPCTransferableDataItem* item =
   9180              aTransferableData->items().AppendElement();
   9181          item->flavor() = flavorStr;
   9182          item->data() =
   9183              AsIPCTransferableDataString(NS_ConvertUTF8toUTF16(flavorStr));
   9184          continue;
   9185        }
   9186 
   9187        // Empty element, transfer only the flavor
   9188        IPCTransferableDataItem* item =
   9189            aTransferableData->items().AppendElement();
   9190        item->flavor() = flavorStr;
   9191        item->data() = AsIPCTransferableDataString(EmptyString());
   9192        continue;
   9193      }
   9194 
   9195      // We need to handle nsIInputStream before nsISupportsCString, otherwise
   9196      // nsStringInputStream would be converted into a wrong type.
   9197      if (nsCOMPtr<nsIInputStream> stream = do_QueryInterface(data)) {
   9198        IPCTransferableDataItem* item =
   9199            aTransferableData->items().AppendElement();
   9200        item->flavor() = flavorStr;
   9201        nsCString imageData;
   9202        DebugOnly<nsresult> rv =
   9203            NS_ConsumeStream(stream, UINT32_MAX, imageData);
   9204        MOZ_ASSERT(
   9205            rv != NS_BASE_STREAM_WOULD_BLOCK,
   9206            "cannot use async input streams in nsITransferable right now");
   9207        // FIXME: This can probably be simplified once bug 1783240 lands, as
   9208        // `nsCString` will be implicitly serialized in shmem when sent over
   9209        // IPDL directly.
   9210        item->data() =
   9211            IPCTransferableDataInputStream(BigBuffer(AsBytes(Span(imageData))));
   9212        continue;
   9213      }
   9214 
   9215      if (nsCOMPtr<nsISupportsString> text = do_QueryInterface(data)) {
   9216        nsAutoString dataAsString;
   9217        MOZ_ALWAYS_SUCCEEDS(text->GetData(dataAsString));
   9218 
   9219        IPCTransferableDataItem* item =
   9220            aTransferableData->items().AppendElement();
   9221        item->flavor() = flavorStr;
   9222        item->data() = AsIPCTransferableDataString(dataAsString);
   9223        continue;
   9224      }
   9225 
   9226      if (nsCOMPtr<nsISupportsCString> ctext = do_QueryInterface(data)) {
   9227        nsAutoCString dataAsString;
   9228        MOZ_ALWAYS_SUCCEEDS(ctext->GetData(dataAsString));
   9229 
   9230        IPCTransferableDataItem* item =
   9231            aTransferableData->items().AppendElement();
   9232        item->flavor() = flavorStr;
   9233        item->data() = AsIPCTransferableDataCString(dataAsString);
   9234        continue;
   9235      }
   9236 
   9237      if (nsCOMPtr<imgIContainer> image = do_QueryInterface(data)) {
   9238        // Images to be placed on the clipboard are imgIContainers.
   9239        RefPtr<mozilla::gfx::SourceSurface> surface = image->GetFrame(
   9240            imgIContainer::FRAME_CURRENT,
   9241            imgIContainer::FLAG_SYNC_DECODE | imgIContainer::FLAG_ASYNC_NOTIFY);
   9242        if (!surface) {
   9243          continue;
   9244        }
   9245        RefPtr<mozilla::gfx::DataSourceSurface> dataSurface =
   9246            surface->GetDataSurface();
   9247        if (!dataSurface) {
   9248          continue;
   9249        }
   9250 
   9251        auto imageData = nsContentUtils::SurfaceToIPCImage(*dataSurface);
   9252        if (!imageData) {
   9253          continue;
   9254        }
   9255 
   9256        IPCTransferableDataItem* item =
   9257            aTransferableData->items().AppendElement();
   9258        item->flavor() = flavorStr;
   9259        item->data() = IPCTransferableDataImageContainer(std::move(*imageData));
   9260        continue;
   9261      }
   9262 
   9263      // Otherwise, handle this as a file.
   9264      nsCOMPtr<BlobImpl> blobImpl;
   9265      if (nsCOMPtr<nsIFile> file = do_QueryInterface(data)) {
   9266        if (aParent) {
   9267          bool isDir = false;
   9268          if (NS_SUCCEEDED(file->IsDirectory(&isDir)) && isDir) {
   9269            nsAutoString path;
   9270            if (NS_WARN_IF(NS_FAILED(file->GetPath(path)))) {
   9271              continue;
   9272            }
   9273 
   9274            RefPtr<FileSystemSecurity> fss = FileSystemSecurity::GetOrCreate();
   9275            fss->GrantAccessToContentProcess(aParent->ChildID(), path);
   9276          }
   9277        }
   9278 
   9279        blobImpl = new FileBlobImpl(file);
   9280 
   9281        IgnoredErrorResult rv;
   9282 
   9283        // Ensure that file data is cached no that the content process
   9284        // has this data available to it when passed over:
   9285        blobImpl->GetSize(rv);
   9286        if (NS_WARN_IF(rv.Failed())) {
   9287          continue;
   9288        }
   9289 
   9290        blobImpl->GetLastModified(rv);
   9291        if (NS_WARN_IF(rv.Failed())) {
   9292          continue;
   9293        }
   9294      } else {
   9295        if (aInSyncMessage) {
   9296          // Can't do anything.
   9297          // FIXME: This shouldn't be the case anymore!
   9298          continue;
   9299        }
   9300 
   9301        blobImpl = do_QueryInterface(data);
   9302      }
   9303 
   9304      if (blobImpl) {
   9305        // If we failed to create the blob actor, then this blob probably
   9306        // can't get the file size for the underlying file, ignore it for
   9307        // now. TODO pass this through anyway.
   9308        IPCBlob ipcBlob;
   9309        nsresult rv = IPCBlobUtils::Serialize(blobImpl, ipcBlob);
   9310        if (NS_WARN_IF(NS_FAILED(rv))) {
   9311          continue;
   9312        }
   9313 
   9314        IPCTransferableDataItem* item =
   9315            aTransferableData->items().AppendElement();
   9316        item->flavor() = flavorStr;
   9317        item->data() = IPCTransferableDataBlob(ipcBlob);
   9318      }
   9319    }
   9320  }
   9321 }
   9322 
   9323 void nsContentUtils::TransferableToIPCTransferable(
   9324    nsITransferable* aTransferable, IPCTransferable* aIPCTransferable,
   9325    bool aInSyncMessage, mozilla::dom::ContentParent* aParent) {
   9326  IPCTransferableData ipcTransferableData;
   9327  TransferableToIPCTransferableData(aTransferable, &ipcTransferableData,
   9328                                    aInSyncMessage, aParent);
   9329 
   9330  Maybe<net::CookieJarSettingsArgs> cookieJarSettingsArgs;
   9331  if (nsCOMPtr<nsICookieJarSettings> cookieJarSettings =
   9332          aTransferable->GetCookieJarSettings()) {
   9333    net::CookieJarSettingsArgs args;
   9334    net::CookieJarSettings::Cast(cookieJarSettings)->Serialize(args);
   9335    cookieJarSettingsArgs = Some(std::move(args));
   9336  }
   9337 
   9338  aIPCTransferable->data() = std::move(ipcTransferableData);
   9339  aIPCTransferable->isPrivateData() = aTransferable->GetIsPrivateData();
   9340  aIPCTransferable->dataPrincipal() = aTransferable->GetDataPrincipal();
   9341  aIPCTransferable->cookieJarSettings() = std::move(cookieJarSettingsArgs);
   9342  aIPCTransferable->contentPolicyType() = aTransferable->GetContentPolicyType();
   9343  aIPCTransferable->referrerInfo() = aTransferable->GetReferrerInfo();
   9344 }
   9345 
   9346 Maybe<BigBuffer> nsContentUtils::GetSurfaceData(DataSourceSurface& aSurface,
   9347                                                size_t* aLength,
   9348                                                int32_t* aStride) {
   9349  mozilla::gfx::DataSourceSurface::MappedSurface map;
   9350  if (!aSurface.Map(mozilla::gfx::DataSourceSurface::MapType::READ, &map)) {
   9351    return Nothing();
   9352  }
   9353 
   9354  size_t bufLen = 0;
   9355  size_t maxBufLen = 0;
   9356  nsresult rv = nsContentUtils::CalculateBufferSizeForImage(
   9357      map.mStride, aSurface.GetSize(), aSurface.GetFormat(), &maxBufLen,
   9358      &bufLen);
   9359  if (NS_FAILED(rv)) {
   9360    aSurface.Unmap();
   9361    return Nothing();
   9362  }
   9363 
   9364  BigBuffer surfaceData(maxBufLen);
   9365  memcpy(surfaceData.Data(), map.mData, bufLen);
   9366  memset(surfaceData.Data() + bufLen, 0, maxBufLen - bufLen);
   9367 
   9368  *aLength = maxBufLen;
   9369  *aStride = map.mStride;
   9370 
   9371  aSurface.Unmap();
   9372  return Some(std::move(surfaceData));
   9373 }
   9374 
   9375 Maybe<IPCImage> nsContentUtils::SurfaceToIPCImage(DataSourceSurface& aSurface) {
   9376  size_t len = 0;
   9377  int32_t stride = 0;
   9378  auto mem = GetSurfaceData(aSurface, &len, &stride);
   9379  if (!mem) {
   9380    return Nothing();
   9381  }
   9382  return Some(IPCImage{std::move(*mem), uint32_t(stride), aSurface.GetFormat(),
   9383                       ImageIntSize::FromUnknownSize(aSurface.GetSize())});
   9384 }
   9385 
   9386 already_AddRefed<DataSourceSurface> nsContentUtils::IPCImageToSurface(
   9387    const IPCImage& aImage) {
   9388  return BigBufferToDataSurface(aImage.data(), aImage.stride(),
   9389                                aImage.size().ToUnknownSize(), aImage.format());
   9390 }
   9391 
   9392 already_AddRefed<imgIContainer> nsContentUtils::IPCImageToImage(
   9393    const IPCImage& aImage) {
   9394  RefPtr<DataSourceSurface> surface = IPCImageToSurface(aImage);
   9395  if (!surface) {
   9396    return nullptr;
   9397  }
   9398 
   9399  RefPtr<gfxDrawable> drawable =
   9400      new gfxSurfaceDrawable(surface, surface->GetSize());
   9401  nsCOMPtr<imgIContainer> imageContainer =
   9402      image::ImageOps::CreateFromDrawable(drawable);
   9403  return imageContainer.forget();
   9404 }
   9405 
   9406 Modifiers nsContentUtils::GetWidgetModifiers(int32_t aModifiers) {
   9407  Modifiers result = 0;
   9408  if (aModifiers & nsIDOMWindowUtils::MODIFIER_SHIFT) {
   9409    result |= mozilla::MODIFIER_SHIFT;
   9410  }
   9411  if (aModifiers & nsIDOMWindowUtils::MODIFIER_CONTROL) {
   9412    result |= mozilla::MODIFIER_CONTROL;
   9413  }
   9414  if (aModifiers & nsIDOMWindowUtils::MODIFIER_ALT) {
   9415    result |= mozilla::MODIFIER_ALT;
   9416  }
   9417  if (aModifiers & nsIDOMWindowUtils::MODIFIER_META) {
   9418    result |= mozilla::MODIFIER_META;
   9419  }
   9420  if (aModifiers & nsIDOMWindowUtils::MODIFIER_ALTGRAPH) {
   9421    result |= mozilla::MODIFIER_ALTGRAPH;
   9422  }
   9423  if (aModifiers & nsIDOMWindowUtils::MODIFIER_CAPSLOCK) {
   9424    result |= mozilla::MODIFIER_CAPSLOCK;
   9425  }
   9426  if (aModifiers & nsIDOMWindowUtils::MODIFIER_FN) {
   9427    result |= mozilla::MODIFIER_FN;
   9428  }
   9429  if (aModifiers & nsIDOMWindowUtils::MODIFIER_FNLOCK) {
   9430    result |= mozilla::MODIFIER_FNLOCK;
   9431  }
   9432  if (aModifiers & nsIDOMWindowUtils::MODIFIER_NUMLOCK) {
   9433    result |= mozilla::MODIFIER_NUMLOCK;
   9434  }
   9435  if (aModifiers & nsIDOMWindowUtils::MODIFIER_SCROLLLOCK) {
   9436    result |= mozilla::MODIFIER_SCROLLLOCK;
   9437  }
   9438  if (aModifiers & nsIDOMWindowUtils::MODIFIER_SYMBOL) {
   9439    result |= mozilla::MODIFIER_SYMBOL;
   9440  }
   9441  if (aModifiers & nsIDOMWindowUtils::MODIFIER_SYMBOLLOCK) {
   9442    result |= mozilla::MODIFIER_SYMBOLLOCK;
   9443  }
   9444  return result;
   9445 }
   9446 
   9447 nsIWidget* nsContentUtils::GetWidget(PresShell* aPresShell, nsPoint* aOffset) {
   9448  if (!aPresShell) {
   9449    return nullptr;
   9450  }
   9451  nsIFrame* frame = aPresShell->GetRootFrame();
   9452  if (!frame) {
   9453    return nullptr;
   9454  }
   9455  return aOffset ? frame->GetNearestWidget(*aOffset)
   9456                 : frame->GetNearestWidget();
   9457 }
   9458 
   9459 int16_t nsContentUtils::GetButtonsFlagForButton(int32_t aButton) {
   9460  switch (aButton) {
   9461    case -1:
   9462      return MouseButtonsFlag::eNoButtons;
   9463    case MouseButton::ePrimary:
   9464      return MouseButtonsFlag::ePrimaryFlag;
   9465    case MouseButton::eMiddle:
   9466      return MouseButtonsFlag::eMiddleFlag;
   9467    case MouseButton::eSecondary:
   9468      return MouseButtonsFlag::eSecondaryFlag;
   9469    case 3:
   9470      return MouseButtonsFlag::e4thFlag;
   9471    case 4:
   9472      return MouseButtonsFlag::e5thFlag;
   9473    case MouseButton::eEraser:
   9474      return MouseButtonsFlag::eEraserFlag;
   9475    default:
   9476      NS_ERROR("Button not known.");
   9477      return 0;
   9478  }
   9479 }
   9480 
   9481 LayoutDeviceIntPoint nsContentUtils::ToWidgetPoint(
   9482    const CSSPoint& aPoint, const nsPoint& aOffset,
   9483    nsPresContext* aPresContext) {
   9484  nsPoint layoutRelative = CSSPoint::ToAppUnits(aPoint) + aOffset;
   9485  nsPoint visualRelative =
   9486      ViewportUtils::LayoutToVisual(layoutRelative, aPresContext->PresShell());
   9487  return LayoutDeviceIntPoint::FromAppUnitsRounded(
   9488      visualRelative, aPresContext->AppUnitsPerDevPixel());
   9489 }
   9490 
   9491 namespace {
   9492 
   9493 class SynthesizedMouseEventCallback final : public nsISynthesizedEventCallback {
   9494  NS_DECL_ISUPPORTS
   9495 
   9496 public:
   9497  explicit SynthesizedMouseEventCallback(VoidFunction& aCallback)
   9498      : mCallback(&aCallback) {}
   9499 
   9500  MOZ_CAN_RUN_SCRIPT_BOUNDARY NS_IMETHOD OnCompleteDispatch() override {
   9501    MOZ_ASSERT(mCallback, "How can we have a null mCallback here?");
   9502 
   9503    ErrorResult rv;
   9504    MOZ_KnownLive(mCallback)->Call(rv);
   9505    if (MOZ_UNLIKELY(rv.Failed())) {
   9506      return rv.StealNSResult();
   9507    }
   9508 
   9509    return NS_OK;
   9510  }
   9511 
   9512 private:
   9513  virtual ~SynthesizedMouseEventCallback() = default;
   9514 
   9515  const RefPtr<VoidFunction> mCallback;
   9516 };
   9517 
   9518 NS_IMPL_ISUPPORTS(SynthesizedMouseEventCallback, nsISynthesizedEventCallback)
   9519 
   9520 }  // namespace
   9521 
   9522 Result<bool, nsresult> nsContentUtils::SynthesizeMouseEvent(
   9523    mozilla::PresShell* aPresShell, nsIWidget* aWidget, const nsAString& aType,
   9524    LayoutDeviceIntPoint& aRefPoint,
   9525    const SynthesizeMouseEventData& aMouseEventData,
   9526    const SynthesizeMouseEventOptions& aOptions,
   9527    const Optional<OwningNonNull<VoidFunction>>& aCallback) {
   9528  MOZ_ASSERT(aPresShell);
   9529  MOZ_ASSERT(aWidget);
   9530  AUTO_PROFILER_LABEL("nsContentUtils::SynthesizeMouseEvent", OTHER);
   9531 
   9532  if (aCallback.WasPassed()) {
   9533    if (!XRE_IsParentProcess()) {
   9534      // TODO(edgar): There is currently no real use case for synthesizing a
   9535      // mouse event with a callback from the content process. For now, throw an
   9536      // error in this case. We can add support later if a valid use case
   9537      // arises.
   9538      NS_WARNING(
   9539          "nsContentUtils::SynthesizeMouseEvent() does not support being "
   9540          "called in the content process with a callback");
   9541      return Err(NS_ERROR_FAILURE);
   9542    }
   9543 
   9544    if (!aOptions.mIsDOMEventSynthesized) {
   9545      // TODO(edgar): There is currently no real use case for synthesizing a
   9546      // mouse event with a callback that could be coalesced. For now, throw an
   9547      // error. We can add support later if a need arises.
   9548      NS_WARNING(
   9549          "nsContentUtils::SynthesizeMouseEvent() does not support being "
   9550          "called in the parent process with isDOMEventSynthesized=false, due "
   9551          "to the callback doesn't not support on coalesced events");
   9552      return Err(NS_ERROR_FAILURE);
   9553    }
   9554  }
   9555 
   9556  EventMessage msg;
   9557  Maybe<WidgetMouseEvent::ExitFrom> exitFrom;
   9558  bool contextMenuKey = false;
   9559  if (aType.EqualsLiteral("mousedown")) {
   9560    msg = eMouseDown;
   9561  } else if (aType.EqualsLiteral("mouseup")) {
   9562    msg = eMouseUp;
   9563  } else if (aType.EqualsLiteral("mousemove")) {
   9564    msg = eMouseMove;
   9565  } else if (aType.EqualsLiteral("mouseover")) {
   9566    msg = eMouseEnterIntoWidget;
   9567  } else if (aType.EqualsLiteral("mouseout")) {
   9568    msg = eMouseExitFromWidget;
   9569    exitFrom = Some(WidgetMouseEvent::ePlatformChild);
   9570  } else if (aType.EqualsLiteral("mousecancel")) {
   9571    msg = eMouseExitFromWidget;
   9572    exitFrom = Some(XRE_IsParentProcess() ? WidgetMouseEvent::ePlatformTopLevel
   9573                                          : WidgetMouseEvent::ePuppet);
   9574  } else if (aType.EqualsLiteral("mouselongtap")) {
   9575    msg = eMouseLongTap;
   9576  } else if (aType.EqualsLiteral("contextmenu")) {
   9577    msg = eContextMenu;
   9578    contextMenuKey =
   9579        !aMouseEventData.mButton &&
   9580        aMouseEventData.mInputSource != MouseEvent_Binding::MOZ_SOURCE_TOUCH;
   9581  } else if (aType.EqualsLiteral("MozMouseHittest")) {
   9582    msg = eMouseHitTest;
   9583  } else if (aType.EqualsLiteral("MozMouseExploreByTouch")) {
   9584    msg = eMouseExploreByTouch;
   9585  } else {
   9586    return Err(NS_ERROR_FAILURE);
   9587  }
   9588 
   9589  Maybe<WidgetPointerEvent> pointerEvent;
   9590  Maybe<WidgetMouseEvent> mouseEvent;
   9591  if (IsPointerEventMessage(msg)) {
   9592    if (MOZ_UNLIKELY(aOptions.mIsWidgetEventSynthesized)) {
   9593      MOZ_ASSERT_UNREACHABLE(
   9594          "The event shouldn't be dispatched as a synthesized event");
   9595      // `click`, `auxclick` nor `contextmenu` should not be dispatched as a
   9596      // synthesized event.
   9597      return Err(NS_ERROR_INVALID_ARG);
   9598    }
   9599    pointerEvent.emplace(true, msg, aWidget,
   9600                         contextMenuKey ? WidgetMouseEvent::eContextMenuKey
   9601                                        : WidgetMouseEvent::eNormal);
   9602  } else {
   9603    mouseEvent.emplace(true, msg, aWidget,
   9604                       aOptions.mIsWidgetEventSynthesized
   9605                           ? WidgetMouseEvent::eSynthesized
   9606                           : WidgetMouseEvent::eReal,
   9607                       contextMenuKey ? WidgetMouseEvent::eContextMenuKey
   9608                                      : WidgetMouseEvent::eNormal);
   9609  }
   9610 
   9611  nsCOMPtr<nsISynthesizedEventCallback> callback;
   9612  if (aCallback.WasPassed()) {
   9613    callback = MakeAndAddRef<SynthesizedMouseEventCallback>(aCallback.Value());
   9614  }
   9615 
   9616  mozilla::widget::AutoSynthesizedEventCallbackNotifier notifier(callback);
   9617 
   9618  WidgetMouseEvent& mouseOrPointerEvent =
   9619      pointerEvent.isSome() ? pointerEvent.ref() : mouseEvent.ref();
   9620  mouseOrPointerEvent.pointerId = aMouseEventData.mIdentifier;
   9621  mouseOrPointerEvent.mModifiers =
   9622      GetWidgetModifiers(aMouseEventData.mModifiers);
   9623  mouseOrPointerEvent.mButton = aMouseEventData.mButton;
   9624  mouseOrPointerEvent.mButtons =
   9625      aMouseEventData.mButtons.WasPassed()
   9626          ? aMouseEventData.mButtons.Value()
   9627          : (msg != eMouseDown
   9628                 ? 0
   9629                 : GetButtonsFlagForButton(aMouseEventData.mButton));
   9630  mouseOrPointerEvent.mPressure = aMouseEventData.mPressure;
   9631  mouseOrPointerEvent.mInputSource = aMouseEventData.mInputSource;
   9632  mouseOrPointerEvent.mClickCount =
   9633      aMouseEventData.mClickCount.WasPassed()
   9634          ? aMouseEventData.mClickCount.Value()
   9635          : ((msg == eMouseDown || msg == eMouseUp) ? 1 : 0);
   9636  mouseOrPointerEvent.mFlags.mIsSynthesizedForTests =
   9637      aOptions.mIsDOMEventSynthesized;
   9638  mouseOrPointerEvent.mExitFrom = exitFrom;
   9639  mouseOrPointerEvent.mCallbackId = notifier.SaveCallback();
   9640 
   9641  nsPresContext* presContext = aPresShell->GetPresContext();
   9642  if (!presContext) {
   9643    return Err(NS_ERROR_FAILURE);
   9644  }
   9645 
   9646  mouseOrPointerEvent.mRefPoint = aRefPoint;
   9647  mouseOrPointerEvent.mIgnoreRootScrollFrame = aOptions.mIgnoreRootScrollFrame;
   9648 
   9649  nsEventStatus status = nsEventStatus_eIgnore;
   9650  if (aOptions.mToWindow) {
   9651    nsresult rv = aPresShell->HandleEvent(aPresShell->GetRootFrame(),
   9652                                          &mouseOrPointerEvent, false, &status);
   9653    if (NS_FAILED(rv)) {
   9654      return Err(rv);
   9655    }
   9656  } else if (aOptions.mIsAsyncEnabled ||
   9657             StaticPrefs::test_events_async_enabled()) {
   9658    status = aWidget->DispatchInputEvent(&mouseOrPointerEvent).mContentStatus;
   9659  } else {
   9660    status = aWidget->DispatchEvent(&mouseOrPointerEvent);
   9661  }
   9662 
   9663  // The callback ID may be cleared when the event also needs to be dispatched
   9664  // to a content process. In such cases, the callback will be notified after
   9665  // the event has been dispatched in the target content process.
   9666  if (mouseOrPointerEvent.mCallbackId.isSome()) {
   9667    mozilla::widget::AutoSynthesizedEventCallbackNotifier::NotifySavedCallback(
   9668        mouseOrPointerEvent.mCallbackId.ref());
   9669  }
   9670 
   9671  return status == nsEventStatus_eConsumeNoDefault;
   9672 }
   9673 
   9674 /* static */
   9675 void nsContentUtils::FirePageHideEventForFrameLoaderSwap(
   9676    nsIDocShellTreeItem* aItem, EventTarget* aChromeEventHandler,
   9677    bool aOnlySystemGroup) {
   9678  MOZ_DIAGNOSTIC_ASSERT(aItem);
   9679  MOZ_DIAGNOSTIC_ASSERT(aChromeEventHandler);
   9680 
   9681  if (RefPtr<Document> doc = aItem->GetDocument()) {
   9682    doc->OnPageHide(true, aChromeEventHandler, aOnlySystemGroup);
   9683  }
   9684 
   9685  int32_t childCount = 0;
   9686  aItem->GetInProcessChildCount(&childCount);
   9687  AutoTArray<nsCOMPtr<nsIDocShellTreeItem>, 8> kids;
   9688  kids.AppendElements(childCount);
   9689  for (int32_t i = 0; i < childCount; ++i) {
   9690    aItem->GetInProcessChildAt(i, getter_AddRefs(kids[i]));
   9691  }
   9692 
   9693  for (uint32_t i = 0; i < kids.Length(); ++i) {
   9694    if (kids[i]) {
   9695      FirePageHideEventForFrameLoaderSwap(kids[i], aChromeEventHandler,
   9696                                          aOnlySystemGroup);
   9697    }
   9698  }
   9699 }
   9700 
   9701 // The pageshow event is fired for a given document only if IsShowing()
   9702 // returns the same thing as aFireIfShowing.  This gives us a way to fire
   9703 // pageshow only on documents that are still loading or only on documents that
   9704 // are already loaded.
   9705 /* static */
   9706 void nsContentUtils::FirePageShowEventForFrameLoaderSwap(
   9707    nsIDocShellTreeItem* aItem, EventTarget* aChromeEventHandler,
   9708    bool aFireIfShowing, bool aOnlySystemGroup) {
   9709  int32_t childCount = 0;
   9710  aItem->GetInProcessChildCount(&childCount);
   9711  AutoTArray<nsCOMPtr<nsIDocShellTreeItem>, 8> kids;
   9712  kids.AppendElements(childCount);
   9713  for (int32_t i = 0; i < childCount; ++i) {
   9714    aItem->GetInProcessChildAt(i, getter_AddRefs(kids[i]));
   9715  }
   9716 
   9717  for (uint32_t i = 0; i < kids.Length(); ++i) {
   9718    if (kids[i]) {
   9719      FirePageShowEventForFrameLoaderSwap(kids[i], aChromeEventHandler,
   9720                                          aFireIfShowing, aOnlySystemGroup);
   9721    }
   9722  }
   9723 
   9724  RefPtr<Document> doc = aItem->GetDocument();
   9725  if (doc && doc->IsShowing() == aFireIfShowing) {
   9726    doc->OnPageShow(true, aChromeEventHandler, aOnlySystemGroup);
   9727  }
   9728 }
   9729 
   9730 /* static */
   9731 already_AddRefed<nsPIWindowRoot> nsContentUtils::GetWindowRoot(Document* aDoc) {
   9732  if (aDoc) {
   9733    if (nsPIDOMWindowOuter* win = aDoc->GetWindow()) {
   9734      return win->GetTopWindowRoot();
   9735    }
   9736  }
   9737  return nullptr;
   9738 }
   9739 
   9740 /* static */
   9741 bool nsContentUtils::LinkContextIsURI(const nsAString& aAnchor,
   9742                                      nsIURI* aDocURI) {
   9743  if (aAnchor.IsEmpty()) {
   9744    // anchor parameter not present or empty -> same document reference
   9745    return true;
   9746  }
   9747 
   9748  // the document URI might contain a fragment identifier ("#...')
   9749  // we want to ignore that because it's invisible to the server
   9750  // and just affects the local interpretation in the recipient
   9751  nsCOMPtr<nsIURI> contextUri;
   9752  nsresult rv = NS_GetURIWithoutRef(aDocURI, getter_AddRefs(contextUri));
   9753 
   9754  if (NS_FAILED(rv)) {
   9755    // copying failed
   9756    return false;
   9757  }
   9758 
   9759  // resolve anchor against context
   9760  nsCOMPtr<nsIURI> resolvedUri;
   9761  rv = NS_NewURI(getter_AddRefs(resolvedUri), aAnchor, nullptr, contextUri);
   9762 
   9763  if (NS_FAILED(rv)) {
   9764    // resolving failed
   9765    return false;
   9766  }
   9767 
   9768  bool same;
   9769  rv = contextUri->Equals(resolvedUri, &same);
   9770  if (NS_FAILED(rv)) {
   9771    // comparison failed
   9772    return false;
   9773  }
   9774 
   9775  return same;
   9776 }
   9777 
   9778 /* static */
   9779 bool nsContentUtils::IsPreloadType(nsContentPolicyType aType) {
   9780  return (aType == nsIContentPolicy::TYPE_INTERNAL_SCRIPT_PRELOAD ||
   9781          aType == nsIContentPolicy::TYPE_INTERNAL_MODULE_PRELOAD ||
   9782          aType == nsIContentPolicy::TYPE_INTERNAL_IMAGE_PRELOAD ||
   9783          aType == nsIContentPolicy::TYPE_INTERNAL_STYLESHEET_PRELOAD ||
   9784          aType == nsIContentPolicy::TYPE_INTERNAL_FONT_PRELOAD ||
   9785          aType == nsIContentPolicy::TYPE_INTERNAL_JSON_PRELOAD ||
   9786          aType == nsIContentPolicy::TYPE_INTERNAL_FETCH_PRELOAD);
   9787 }
   9788 
   9789 // static
   9790 ReferrerPolicy nsContentUtils::GetReferrerPolicyFromChannel(
   9791    nsIChannel* aChannel) {
   9792  nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(aChannel);
   9793  if (!httpChannel) {
   9794    return ReferrerPolicy::_empty;
   9795  }
   9796 
   9797  nsresult rv;
   9798  nsAutoCString headerValue;
   9799  rv = httpChannel->GetResponseHeader("referrer-policy"_ns, headerValue);
   9800  if (NS_FAILED(rv) || headerValue.IsEmpty()) {
   9801    return ReferrerPolicy::_empty;
   9802  }
   9803 
   9804  return ReferrerInfo::ReferrerPolicyFromHeaderString(
   9805      NS_ConvertUTF8toUTF16(headerValue));
   9806 }
   9807 
   9808 // static
   9809 bool nsContentUtils::IsNonSubresourceRequest(nsIChannel* aChannel) {
   9810  nsLoadFlags loadFlags = 0;
   9811  aChannel->GetLoadFlags(&loadFlags);
   9812  if (loadFlags & nsIChannel::LOAD_DOCUMENT_URI) {
   9813    return true;
   9814  }
   9815 
   9816  nsCOMPtr<nsILoadInfo> loadInfo = aChannel->LoadInfo();
   9817  nsContentPolicyType type = loadInfo->InternalContentPolicyType();
   9818  return IsNonSubresourceInternalPolicyType(type);
   9819 }
   9820 
   9821 // static
   9822 bool nsContentUtils::IsNonSubresourceInternalPolicyType(
   9823    nsContentPolicyType aType) {
   9824  return aType == nsIContentPolicy::TYPE_DOCUMENT ||
   9825         aType == nsIContentPolicy::TYPE_INTERNAL_IFRAME ||
   9826         aType == nsIContentPolicy::TYPE_INTERNAL_FRAME ||
   9827         aType == nsIContentPolicy::TYPE_INTERNAL_WORKER ||
   9828         aType == nsIContentPolicy::TYPE_INTERNAL_SHARED_WORKER;
   9829 }
   9830 
   9831 // static public
   9832 bool nsContentUtils::IsThirdPartyTrackingResourceWindow(
   9833    nsPIDOMWindowInner* aWindow) {
   9834  MOZ_ASSERT(aWindow);
   9835 
   9836  Document* document = aWindow->GetExtantDoc();
   9837  if (!document) {
   9838    return false;
   9839  }
   9840 
   9841  nsCOMPtr<nsIClassifiedChannel> classifiedChannel =
   9842      do_QueryInterface(document->GetChannel());
   9843  if (!classifiedChannel) {
   9844    return false;
   9845  }
   9846 
   9847  return classifiedChannel->IsThirdPartyTrackingResource();
   9848 }
   9849 
   9850 // static public
   9851 bool nsContentUtils::IsFirstPartyTrackingResourceWindow(
   9852    nsPIDOMWindowInner* aWindow) {
   9853  MOZ_ASSERT(aWindow);
   9854 
   9855  Document* document = aWindow->GetExtantDoc();
   9856  if (!document) {
   9857    return false;
   9858  }
   9859 
   9860  nsCOMPtr<nsIClassifiedChannel> classifiedChannel =
   9861      do_QueryInterface(document->GetChannel());
   9862  if (!classifiedChannel) {
   9863    return false;
   9864  }
   9865 
   9866  uint32_t classificationFlags =
   9867      classifiedChannel->GetFirstPartyClassificationFlags();
   9868 
   9869  return mozilla::net::UrlClassifierCommon::IsTrackingClassificationFlag(
   9870      classificationFlags, NS_UsePrivateBrowsing(document->GetChannel()));
   9871 }
   9872 
   9873 namespace {
   9874 
   9875 // We put StringBuilder in the anonymous namespace to prevent anything outside
   9876 // this file from accidentally being linked against it.
   9877 class BulkAppender {
   9878  using size_type = typename nsAString::size_type;
   9879 
   9880 public:
   9881  explicit BulkAppender(BulkWriteHandle<char16_t>&& aHandle)
   9882      : mHandle(std::move(aHandle)), mPosition(0) {}
   9883  ~BulkAppender() = default;
   9884 
   9885  template <int N>
   9886  void AppendLiteral(const char16_t (&aStr)[N]) {
   9887    size_t len = N - 1;
   9888    MOZ_ASSERT(mPosition + len <= mHandle.Length());
   9889    memcpy(mHandle.Elements() + mPosition, aStr, len * sizeof(char16_t));
   9890    mPosition += len;
   9891  }
   9892 
   9893  void Append(Span<const char16_t> aStr) {
   9894    size_t len = aStr.Length();
   9895    MOZ_ASSERT(mPosition + len <= mHandle.Length());
   9896    // Both mHandle.Elements() and aStr.Elements() are guaranteed
   9897    // to be non-null (by the string implementation and by Span,
   9898    // respectively), so not checking the pointers for null before
   9899    // memcpy does not lead to UB even if len was zero.
   9900    memcpy(mHandle.Elements() + mPosition, aStr.Elements(),
   9901           len * sizeof(char16_t));
   9902    mPosition += len;
   9903  }
   9904 
   9905  void Append(Span<const char> aStr) {
   9906    size_t len = aStr.Length();
   9907    MOZ_ASSERT(mPosition + len <= mHandle.Length());
   9908    ConvertLatin1toUtf16(aStr, mHandle.AsSpan().From(mPosition));
   9909    mPosition += len;
   9910  }
   9911 
   9912  void Finish() { mHandle.Finish(mPosition, false); }
   9913 
   9914 private:
   9915  BulkWriteHandle<char16_t> mHandle;
   9916  size_type mPosition;
   9917 };
   9918 
   9919 class StringBuilderSIMD {
   9920 public:
   9921  static const bool SIMD = true;
   9922 };
   9923 
   9924 class StringBuilderALU {
   9925 public:
   9926  static const bool SIMD = false;
   9927 };
   9928 
   9929 class StringBuilder {
   9930 private:
   9931  class Unit {
   9932   public:
   9933    Unit() : mAtom(nullptr) { MOZ_COUNT_CTOR(StringBuilder::Unit); }
   9934    ~Unit() {
   9935      if (mType == Type::String || mType == Type::StringWithEncode) {
   9936        mString.~nsString();
   9937      }
   9938      MOZ_COUNT_DTOR(StringBuilder::Unit);
   9939    }
   9940 
   9941    enum class Type : uint8_t {
   9942      Unknown,
   9943      Atom,
   9944      String,
   9945      StringWithEncode,
   9946      Literal,
   9947      TextFragment,
   9948      TextFragmentWithEncode,
   9949    };
   9950 
   9951    struct LiteralSpan {
   9952      const char16_t* mData;
   9953      uint32_t mLength;
   9954 
   9955      Span<const char16_t> AsSpan() { return Span(mData, mLength); }
   9956    };
   9957 
   9958    union {
   9959      nsAtom* mAtom;
   9960      LiteralSpan mLiteral;
   9961      nsString mString;
   9962      const CharacterDataBuffer* mCharacterDataBuffer;
   9963    };
   9964    Type mType = Type::Unknown;
   9965  };
   9966 
   9967  static_assert(sizeof(void*) != 8 || sizeof(Unit) <= 3 * sizeof(void*),
   9968                "Unit should remain small");
   9969 
   9970 public:
   9971  // Try to keep the size of StringBuilder close to a jemalloc bucket size
   9972  // (the 16kb one in this case).
   9973  static constexpr uint32_t TARGET_SIZE = 16 * 1024;
   9974 
   9975  // The number of units we need to remove from the inline buffer so that the
   9976  // rest of the builder members fit. A more precise approach would be to
   9977  // calculate that extra size and use (TARGET_SIZE - OTHER_SIZE) /
   9978  // sizeof(Unit) or so, but this is simpler.
   9979  static constexpr uint32_t PADDING_UNITS = sizeof(void*) == 8 ? 1 : 2;
   9980 
   9981  static constexpr uint32_t STRING_BUFFER_UNITS =
   9982      TARGET_SIZE / sizeof(Unit) - PADDING_UNITS;
   9983 
   9984  StringBuilder() : mLast(this), mLength(0) { MOZ_COUNT_CTOR(StringBuilder); }
   9985 
   9986  MOZ_COUNTED_DTOR(StringBuilder)
   9987 
   9988  void Append(nsAtom* aAtom) {
   9989    Unit* u = AddUnit();
   9990    u->mAtom = aAtom;
   9991    u->mType = Unit::Type::Atom;
   9992    uint32_t len = aAtom->GetLength();
   9993    mLength += len;
   9994  }
   9995 
   9996  template <int N>
   9997  void Append(const char16_t (&aLiteral)[N]) {
   9998    constexpr uint32_t len = N - 1;
   9999    Unit* u = AddUnit();
  10000    u->mLiteral = {aLiteral, len};
  10001    u->mType = Unit::Type::Literal;
  10002    mLength += len;
  10003  }
  10004 
  10005  void Append(nsString&& aString) {
  10006    Unit* u = AddUnit();
  10007    uint32_t len = aString.Length();
  10008    new (&u->mString) nsString(std::move(aString));
  10009    u->mType = Unit::Type::String;
  10010    mLength += len;
  10011  }
  10012 
  10013  // aLen can be !isValid(), which will get propagated into mLength.
  10014  void AppendWithAttrEncode(nsString&& aString, CheckedInt<uint32_t> aLen) {
  10015    Unit* u = AddUnit();
  10016    new (&u->mString) nsString(std::move(aString));
  10017    u->mType = Unit::Type::StringWithEncode;
  10018    mLength += aLen;
  10019  }
  10020 
  10021  void Append(const CharacterDataBuffer* aCharacterDataBuffer) {
  10022    Unit* u = AddUnit();
  10023    u->mCharacterDataBuffer = aCharacterDataBuffer;
  10024    u->mType = Unit::Type::TextFragment;
  10025    uint32_t len = aCharacterDataBuffer->GetLength();
  10026    mLength += len;
  10027  }
  10028 
  10029  // aLen can be !isValid(), which will get propagated into mLength.
  10030  void AppendWithEncode(const CharacterDataBuffer* aCharacterDataBuffer,
  10031                        CheckedInt<uint32_t> aLen) {
  10032    Unit* u = AddUnit();
  10033    u->mCharacterDataBuffer = aCharacterDataBuffer;
  10034    u->mType = Unit::Type::TextFragmentWithEncode;
  10035    mLength += aLen;
  10036  }
  10037 
  10038  bool ToString(nsAString& aOut) {
  10039    if (!mLength.isValid()) {
  10040      return false;
  10041    }
  10042    auto appenderOrErr = aOut.BulkWrite(mLength.value(), 0, true);
  10043    if (appenderOrErr.isErr()) {
  10044      return false;
  10045    }
  10046 
  10047    BulkAppender appender{appenderOrErr.unwrap()};
  10048 
  10049    // Experiment with moving this higher up the call stack
  10050    // after we have experience from
  10051    // https://bugzilla.mozilla.org/show_bug.cgi?id=1997255
  10052    // on the parser side.
  10053    bool simd = mozilla::htmlaccel::htmlaccelEnabled();
  10054 
  10055    for (StringBuilder* current = this; current;
  10056         current = current->mNext.get()) {
  10057      uint32_t len = current->mUnits.Length();
  10058      for (uint32_t i = 0; i < len; ++i) {
  10059        Unit& u = current->mUnits[i];
  10060        switch (u.mType) {
  10061          case Unit::Type::Atom:
  10062            appender.Append(*(u.mAtom));
  10063            break;
  10064          case Unit::Type::String:
  10065            appender.Append(u.mString);
  10066            break;
  10067          case Unit::Type::StringWithEncode:
  10068            if (simd) {
  10069              EncodeAttrString<StringBuilderSIMD>(u.mString, appender);
  10070            } else {
  10071              EncodeAttrString<StringBuilderALU>(u.mString, appender);
  10072            }
  10073            break;
  10074          case Unit::Type::Literal:
  10075            appender.Append(u.mLiteral.AsSpan());
  10076            break;
  10077          case Unit::Type::TextFragment:
  10078            if (u.mCharacterDataBuffer->Is2b()) {
  10079              appender.Append(Span(u.mCharacterDataBuffer->Get2b(),
  10080                                   u.mCharacterDataBuffer->GetLength()));
  10081            } else {
  10082              appender.Append(Span(u.mCharacterDataBuffer->Get1b(),
  10083                                   u.mCharacterDataBuffer->GetLength()));
  10084            }
  10085            break;
  10086          case Unit::Type::TextFragmentWithEncode:
  10087            if (u.mCharacterDataBuffer->Is2b()) {
  10088              if (simd) {
  10089                EncodeTextFragment<StringBuilderSIMD>(
  10090                    Span(u.mCharacterDataBuffer->Get2b(),
  10091                         u.mCharacterDataBuffer->GetLength()),
  10092                    appender);
  10093 
  10094              } else {
  10095                EncodeTextFragment<StringBuilderALU>(
  10096                    Span(u.mCharacterDataBuffer->Get2b(),
  10097                         u.mCharacterDataBuffer->GetLength()),
  10098                    appender);
  10099              }
  10100            } else {
  10101              if (simd) {
  10102                EncodeTextFragment<StringBuilderSIMD>(
  10103                    Span(u.mCharacterDataBuffer->Get1b(),
  10104                         u.mCharacterDataBuffer->GetLength()),
  10105                    appender);
  10106 
  10107              } else {
  10108                EncodeTextFragment<StringBuilderALU>(
  10109                    Span(u.mCharacterDataBuffer->Get1b(),
  10110                         u.mCharacterDataBuffer->GetLength()),
  10111                    appender);
  10112              }
  10113            }
  10114            break;
  10115          default:
  10116            MOZ_CRASH("Unknown unit type?");
  10117        }
  10118      }
  10119    }
  10120    appender.Finish();
  10121    return true;
  10122  }
  10123 
  10124 private:
  10125  Unit* AddUnit() {
  10126    if (mLast->mUnits.Length() == STRING_BUFFER_UNITS) {
  10127      new StringBuilder(this);
  10128    }
  10129    return mLast->mUnits.AppendElement();
  10130  }
  10131 
  10132  explicit StringBuilder(StringBuilder* aFirst) : mLast(nullptr), mLength(0) {
  10133    MOZ_COUNT_CTOR(StringBuilder);
  10134    aFirst->mLast->mNext = WrapUnique(this);
  10135    aFirst->mLast = this;
  10136  }
  10137 
  10138  template <class S>
  10139  void EncodeAttrString(Span<const char16_t> aStr, BulkAppender& aAppender) {
  10140    size_t flushedUntil = 0;
  10141    size_t currentPosition = 0;
  10142    const char16_t* ptr = aStr.Elements();
  10143    const char16_t* end = ptr + aStr.Length();
  10144 
  10145  // continue by label (committee proposal P3568) isn't in C++, yet, so
  10146  // goto is used for what's logically continuing an outer loop.
  10147  //
  10148  // The purpose of two loop levels is to efficiently stay in the inner
  10149  // loop when there isn't a full SIMD stride left.
  10150  //
  10151  // Strange indent thanks to clang-format.
  10152  outer:
  10153    // Check for having at least a SIMD stride of data to avoid useless
  10154    // non-inline function calls when there isn't a full stride left.
  10155    // This should become unnecessary if it turns out to be feasible
  10156    // to inline the call without triggering
  10157    // https://github.com/llvm/llvm-project/issues/160886 .
  10158    if (S::SIMD && (end - ptr >= 16)) {
  10159      // Need to check ifdef inside here in order to make even non-opt
  10160      // build consider `S::SIMD` used.
  10161 #ifdef MOZ_MAY_HAVE_HTMLACCEL
  10162      size_t skipped =
  10163          mozilla::htmlaccel::SkipNonEscapedInAttributeValue(ptr, end);
  10164      ptr += skipped;
  10165      currentPosition += skipped;
  10166 #endif
  10167    }
  10168    while (ptr != end) {
  10169      char16_t c = *ptr;
  10170      ptr++;
  10171      switch (c) {
  10172        case '"':
  10173          aAppender.Append(aStr.FromTo(flushedUntil, currentPosition));
  10174          aAppender.AppendLiteral(u"&quot;");
  10175          currentPosition++;
  10176          flushedUntil = currentPosition;
  10177          goto outer;
  10178        case '&':
  10179          aAppender.Append(aStr.FromTo(flushedUntil, currentPosition));
  10180          aAppender.AppendLiteral(u"&amp;");
  10181          currentPosition++;
  10182          flushedUntil = currentPosition;
  10183          goto outer;
  10184        case 0x00A0:
  10185          aAppender.Append(aStr.FromTo(flushedUntil, currentPosition));
  10186          aAppender.AppendLiteral(u"&nbsp;");
  10187          currentPosition++;
  10188          flushedUntil = currentPosition;
  10189          goto outer;
  10190        case '<':
  10191          if (StaticPrefs::dom_security_html_serialization_escape_lt_gt()) {
  10192            aAppender.Append(aStr.FromTo(flushedUntil, currentPosition));
  10193            aAppender.AppendLiteral(u"&lt;");
  10194            currentPosition++;
  10195            flushedUntil = currentPosition;
  10196          } else {
  10197            currentPosition++;
  10198          }
  10199          goto outer;
  10200        case '>':
  10201          if (StaticPrefs::dom_security_html_serialization_escape_lt_gt()) {
  10202            aAppender.Append(aStr.FromTo(flushedUntil, currentPosition));
  10203            aAppender.AppendLiteral(u"&gt;");
  10204            currentPosition++;
  10205            flushedUntil = currentPosition;
  10206          } else {
  10207            currentPosition++;
  10208          }
  10209          goto outer;
  10210        default:
  10211          currentPosition++;
  10212          continue;
  10213      }
  10214    }
  10215    // Logically the outer loop ends here.
  10216    if (currentPosition > flushedUntil) {
  10217      aAppender.Append(aStr.FromTo(flushedUntil, currentPosition));
  10218    }
  10219  }
  10220 
  10221  template <class S, class T>
  10222  void EncodeTextFragment(Span<const T> aStr, BulkAppender& aAppender) {
  10223    size_t flushedUntil = 0;
  10224    size_t currentPosition = 0;
  10225    const T* ptr = aStr.Elements();
  10226    const T* end = ptr + aStr.Length();
  10227 
  10228  // continue by label (committee proposal P3568) isn't in C++, yet, so
  10229  // goto is used for what's logically continuing an outer loop.
  10230  //
  10231  // The purpose of two loop levels is to efficiently stay in the inner
  10232  // loop when there isn't a full SIMD stride left.
  10233  //
  10234  // Strange indent thanks to clang-format.
  10235  outer:
  10236    // Check for having at least a SIMD stride of data to avoid useless
  10237    // non-inline function calls when there isn't a full stride left.
  10238    // This should become unnecessary if it turns out to be feasible
  10239    // to inline the call without triggering
  10240    // https://github.com/llvm/llvm-project/issues/160886 .
  10241    if (S::SIMD && (end - ptr >= 16)) {
  10242      // Need to check ifdef inside here in order to make even non-opt
  10243      // build consider `S::SIMD` used.
  10244 #ifdef MOZ_MAY_HAVE_HTMLACCEL
  10245      size_t skipped = mozilla::htmlaccel::SkipNonEscapedInTextNode(ptr, end);
  10246      ptr += skipped;
  10247      currentPosition += skipped;
  10248 #endif
  10249    }
  10250    while (ptr != end) {
  10251      T c = *ptr;
  10252      ++ptr;
  10253      switch (c) {
  10254        case '<':
  10255          aAppender.Append(aStr.FromTo(flushedUntil, currentPosition));
  10256          aAppender.AppendLiteral(u"&lt;");
  10257          currentPosition++;
  10258          flushedUntil = currentPosition;
  10259          goto outer;
  10260        case '>':
  10261          aAppender.Append(aStr.FromTo(flushedUntil, currentPosition));
  10262          aAppender.AppendLiteral(u"&gt;");
  10263          currentPosition++;
  10264          flushedUntil = currentPosition;
  10265          goto outer;
  10266        case '&':
  10267          aAppender.Append(aStr.FromTo(flushedUntil, currentPosition));
  10268          aAppender.AppendLiteral(u"&amp;");
  10269          currentPosition++;
  10270          flushedUntil = currentPosition;
  10271          goto outer;
  10272        case T(0xA0):
  10273          aAppender.Append(aStr.FromTo(flushedUntil, currentPosition));
  10274          aAppender.AppendLiteral(u"&nbsp;");
  10275          currentPosition++;
  10276          flushedUntil = currentPosition;
  10277          goto outer;
  10278        default:
  10279          currentPosition++;
  10280          continue;
  10281      }
  10282    }
  10283    // Logically the outer loop ends here.
  10284    if (currentPosition > flushedUntil) {
  10285      aAppender.Append(aStr.FromTo(flushedUntil, currentPosition));
  10286    }
  10287  }
  10288 
  10289  AutoTArray<Unit, STRING_BUFFER_UNITS> mUnits;
  10290  UniquePtr<StringBuilder> mNext;
  10291  StringBuilder* mLast;
  10292  // mLength is used only in the first StringBuilder object in the linked
  10293  // list.
  10294  CheckedInt<uint32_t> mLength;
  10295 };
  10296 
  10297 static_assert(sizeof(StringBuilder) <= StringBuilder::TARGET_SIZE,
  10298              "StringBuilder should fit in the target bucket");
  10299 
  10300 }  // namespace
  10301 
  10302 static void AppendEncodedCharacters(const CharacterDataBuffer* aText,
  10303                                    StringBuilder& aBuilder) {
  10304  uint32_t numEncodedChars = 0;
  10305  uint32_t len = aText->GetLength();
  10306  if (aText->Is2b()) {
  10307    const char16_t* data = aText->Get2b();
  10308 #ifdef MOZ_MAY_HAVE_HTMLACCEL
  10309    if (mozilla::htmlaccel::htmlaccelEnabled()) {
  10310      numEncodedChars =
  10311          mozilla::htmlaccel::CountEscapedInTextNode(data, data + len);
  10312    } else
  10313 #endif
  10314    {
  10315      for (uint32_t i = 0; i < len; ++i) {
  10316        const char16_t c = data[i];
  10317        switch (c) {
  10318          case '<':
  10319          case '>':
  10320          case '&':
  10321          case 0x00A0:
  10322            ++numEncodedChars;
  10323            break;
  10324          default:
  10325            break;
  10326        }
  10327      }
  10328    }
  10329  } else {
  10330    const char* data = aText->Get1b();
  10331 #ifdef MOZ_MAY_HAVE_HTMLACCEL
  10332    if (mozilla::htmlaccel::htmlaccelEnabled()) {
  10333      numEncodedChars =
  10334          mozilla::htmlaccel::CountEscapedInTextNode(data, data + len);
  10335    } else
  10336 #endif
  10337    {
  10338      for (uint32_t i = 0; i < len; ++i) {
  10339        const unsigned char c = data[i];
  10340        switch (c) {
  10341          case '<':
  10342          case '>':
  10343          case '&':
  10344          case 0x00A0:
  10345            ++numEncodedChars;
  10346            break;
  10347          default:
  10348            break;
  10349        }
  10350      }
  10351    }
  10352  }
  10353 
  10354  if (numEncodedChars) {
  10355    // For simplicity, conservatively estimate the size of the string after
  10356    // encoding. This will result in reserving more memory than we actually
  10357    // need, but that should be fine unless the string has an enormous number
  10358    // of eg < in it. We subtract 1 for the null terminator, then 1 more for
  10359    // the existing character that will be replaced.
  10360    constexpr uint32_t maxCharExtraSpace =
  10361        std::max({std::size("&lt;"), std::size("&gt;"), std::size("&amp;"),
  10362                  std::size("&nbsp;")}) -
  10363        2;
  10364    static_assert(maxCharExtraSpace < 100, "Possible underflow");
  10365    CheckedInt<uint32_t> maxExtraSpace =
  10366        CheckedInt<uint32_t>(numEncodedChars) * maxCharExtraSpace;
  10367    aBuilder.AppendWithEncode(aText, maxExtraSpace + len);
  10368  } else {
  10369    aBuilder.Append(aText);
  10370  }
  10371 }
  10372 
  10373 static CheckedInt<uint32_t> ExtraSpaceNeededForAttrEncoding(
  10374    const nsAString& aValue) {
  10375  const char16_t* c = aValue.BeginReading();
  10376  const char16_t* end = aValue.EndReading();
  10377 
  10378  uint32_t numEncodedChars = 0;
  10379 #ifdef MOZ_MAY_HAVE_HTMLACCEL
  10380  if (mozilla::htmlaccel::htmlaccelEnabled()) {
  10381    numEncodedChars = mozilla::htmlaccel::CountEscapedInAttributeValue(c, end);
  10382  } else
  10383 #endif
  10384  {
  10385    while (c < end) {
  10386      switch (*c) {
  10387        case '"':
  10388        case '&':
  10389        case 0x00A0:  // NO-BREAK SPACE
  10390        case '<':
  10391        case '>':
  10392          ++numEncodedChars;
  10393          break;
  10394        default:
  10395          break;
  10396      }
  10397      ++c;
  10398    }
  10399  }
  10400 
  10401  if (!numEncodedChars) {
  10402    return 0;
  10403  }
  10404 
  10405  // For simplicity, conservatively estimate the size of the string after
  10406  // encoding. This will result in reserving more memory than we actually
  10407  // need, but that should be fine unless the string has an enormous number of
  10408  // & in it. We subtract 1 for the null terminator, then 1 more for the
  10409  // existing character that will be replaced.
  10410  constexpr uint32_t maxCharExtraSpace =
  10411      std::max({std::size("&quot;"), std::size("&amp;"), std::size("&nbsp;"),
  10412                std::size("&lt;"), std::size("&gt;")}) -
  10413      2;
  10414  static_assert(maxCharExtraSpace < 100, "Possible underflow");
  10415  return CheckedInt<uint32_t>(numEncodedChars) * maxCharExtraSpace;
  10416 }
  10417 
  10418 static void AppendEncodedAttributeValue(const nsAttrValue& aValue,
  10419                                        StringBuilder& aBuilder) {
  10420  if (nsAtom* atom = aValue.GetStoredAtom()) {
  10421    nsDependentAtomString atomStr(atom);
  10422    auto space = ExtraSpaceNeededForAttrEncoding(atomStr);
  10423    if (space.isValid() && !space.value()) {
  10424      aBuilder.Append(atom);
  10425    } else {
  10426      aBuilder.AppendWithAttrEncode(nsString(atomStr),
  10427                                    space + atomStr.Length());
  10428    }
  10429    return;
  10430  }
  10431  // NOTE(emilio): In most cases this will just be a reference to the stored
  10432  // nsStringBuffer.
  10433  nsString str;
  10434  aValue.ToString(str);
  10435  auto space = ExtraSpaceNeededForAttrEncoding(str);
  10436  if (!space.isValid() || space.value()) {
  10437    aBuilder.AppendWithAttrEncode(std::move(str), space + str.Length());
  10438  } else {
  10439    aBuilder.Append(std::move(str));
  10440  }
  10441 }
  10442 
  10443 static void StartElement(Element* aElement, StringBuilder& aBuilder) {
  10444  nsAtom* localName = aElement->NodeInfo()->NameAtom();
  10445  const int32_t tagNS = aElement->GetNameSpaceID();
  10446 
  10447  aBuilder.Append(u"<");
  10448  if (tagNS == kNameSpaceID_XHTML || tagNS == kNameSpaceID_SVG ||
  10449      tagNS == kNameSpaceID_MathML) {
  10450    aBuilder.Append(localName);
  10451  } else {
  10452    aBuilder.Append(nsString(aElement->NodeName()));
  10453  }
  10454 
  10455  if (CustomElementData* ceData = aElement->GetCustomElementData()) {
  10456    nsAtom* isAttr = ceData->GetIs(aElement);
  10457    if (isAttr && !aElement->HasAttr(nsGkAtoms::is)) {
  10458      aBuilder.Append(uR"( is=")");
  10459      aBuilder.Append(isAttr);
  10460      aBuilder.Append(uR"(")");
  10461    }
  10462  }
  10463 
  10464  uint32_t i = 0;
  10465  while (BorrowedAttrInfo info = aElement->GetAttrInfoAt(i++)) {
  10466    const nsAttrName* name = info.mName;
  10467 
  10468    int32_t attNs = name->NamespaceID();
  10469    nsAtom* attName = name->LocalName();
  10470 
  10471    // Filter out any attribute starting with [-|_]moz
  10472    // FIXME(emilio): Do we still need this?
  10473    nsDependentAtomString attrNameStr(attName);
  10474    if (StringBeginsWith(attrNameStr, u"_moz"_ns) ||
  10475        StringBeginsWith(attrNameStr, u"-moz"_ns)) {
  10476      continue;
  10477    }
  10478 
  10479    aBuilder.Append(u" ");
  10480 
  10481    if (MOZ_LIKELY(attNs == kNameSpaceID_None) ||
  10482        (attNs == kNameSpaceID_XMLNS && attName == nsGkAtoms::xmlns)) {
  10483      // Nothing else required
  10484    } else if (attNs == kNameSpaceID_XML) {
  10485      aBuilder.Append(u"xml:");
  10486    } else if (attNs == kNameSpaceID_XMLNS) {
  10487      aBuilder.Append(u"xmlns:");
  10488    } else if (attNs == kNameSpaceID_XLink) {
  10489      aBuilder.Append(u"xlink:");
  10490    } else if (nsAtom* prefix = name->GetPrefix()) {
  10491      aBuilder.Append(prefix);
  10492      aBuilder.Append(u":");
  10493    }
  10494 
  10495    aBuilder.Append(attName);
  10496    aBuilder.Append(uR"(=")");
  10497    AppendEncodedAttributeValue(*info.mValue, aBuilder);
  10498    aBuilder.Append(uR"(")");
  10499  }
  10500 
  10501  aBuilder.Append(u">");
  10502 
  10503  /*
  10504  // Per HTML spec we should append one \n if the first child of
  10505  // pre/textarea/listing is a textnode and starts with a \n.
  10506  // But because browsers haven't traditionally had that behavior,
  10507  // we're not changing our behavior either - yet.
  10508  if (aContent->IsHTMLElement()) {
  10509    if (localName == nsGkAtoms::pre || localName == nsGkAtoms::textarea ||
  10510        localName == nsGkAtoms::listing) {
  10511      nsIContent* fc = aContent->GetFirstChild();
  10512      if (fc &&
  10513          (fc->NodeType() == nsINode::TEXT_NODE ||
  10514           fc->NodeType() == nsINode::CDATA_SECTION_NODE)) {
  10515        const CharacterDataBuffer* text = fc->GetText();
  10516        if (text && text->GetLength() && text->CharAt(0) == char16_t('\n')) {
  10517          aBuilder.Append("\n");
  10518        }
  10519      }
  10520    }
  10521  }*/
  10522 }
  10523 
  10524 static inline bool ShouldEscape(nsIContent* aParent) {
  10525  if (!aParent || !aParent->IsHTMLElement()) {
  10526    return true;
  10527  }
  10528 
  10529  static const nsAtom* nonEscapingElements[] = {
  10530      nsGkAtoms::style,     nsGkAtoms::script,  nsGkAtoms::xmp,
  10531      nsGkAtoms::iframe,    nsGkAtoms::noembed, nsGkAtoms::noframes,
  10532      nsGkAtoms::plaintext, nsGkAtoms::noscript};
  10533  static mozilla::BitBloomFilter<12, nsAtom> sFilter;
  10534  static bool sInitialized = false;
  10535  if (!sInitialized) {
  10536    sInitialized = true;
  10537    for (auto& nonEscapingElement : nonEscapingElements) {
  10538      sFilter.add(nonEscapingElement);
  10539    }
  10540  }
  10541 
  10542  nsAtom* tag = aParent->NodeInfo()->NameAtom();
  10543  if (sFilter.mightContain(tag)) {
  10544    for (auto& nonEscapingElement : nonEscapingElements) {
  10545      if (tag == nonEscapingElement) {
  10546        if (MOZ_UNLIKELY(tag == nsGkAtoms::noscript) &&
  10547            MOZ_UNLIKELY(!aParent->OwnerDoc()->IsScriptEnabled())) {
  10548          return true;
  10549        }
  10550        return false;
  10551      }
  10552    }
  10553  }
  10554  return true;
  10555 }
  10556 
  10557 static inline bool IsVoidTag(Element* aElement) {
  10558  if (!aElement->IsHTMLElement()) {
  10559    return false;
  10560  }
  10561  return FragmentOrElement::IsHTMLVoid(aElement->NodeInfo()->NameAtom());
  10562 }
  10563 
  10564 static bool StartSerializingShadowDOM(
  10565    nsINode* aNode, StringBuilder& aBuilder, bool aSerializableShadowRoots,
  10566    const Sequence<OwningNonNull<ShadowRoot>>& aShadowRoots) {
  10567  ShadowRoot* shadow = aNode->GetShadowRoot();
  10568  if (!shadow || ((!aSerializableShadowRoots || !shadow->Serializable()) &&
  10569                  !aShadowRoots.Contains(shadow))) {
  10570    return false;
  10571  }
  10572 
  10573  aBuilder.Append(u"<template shadowrootmode=\"");
  10574  if (shadow->IsClosed()) {
  10575    aBuilder.Append(u"closed\"");
  10576  } else {
  10577    aBuilder.Append(u"open\"");
  10578  }
  10579 
  10580  if (shadow->DelegatesFocus()) {
  10581    aBuilder.Append(u" shadowrootdelegatesfocus=\"\"");
  10582  }
  10583  if (shadow->Serializable()) {
  10584    aBuilder.Append(u" shadowrootserializable=\"\"");
  10585  }
  10586  if (shadow->Clonable()) {
  10587    aBuilder.Append(u" shadowrootclonable=\"\"");
  10588  }
  10589 
  10590  aBuilder.Append(u">");
  10591 
  10592  if (!shadow->HasChildren()) {
  10593    aBuilder.Append(u"</template>");
  10594    return false;
  10595  }
  10596  return true;
  10597 }
  10598 
  10599 template <SerializeShadowRoots ShouldSerializeShadowRoots>
  10600 static void SerializeNodeToMarkupInternal(
  10601    nsINode* aRoot, bool aDescendantsOnly, StringBuilder& aBuilder,
  10602    bool aSerializableShadowRoots,
  10603    const Sequence<OwningNonNull<ShadowRoot>>& aShadowRoots) {
  10604  nsINode* current =
  10605      aDescendantsOnly ? aRoot->GetFirstChildOfTemplateOrNode() : aRoot;
  10606  if (!current) {
  10607    return;
  10608  }
  10609 
  10610  nsIContent* next;
  10611  while (true) {
  10612    bool isVoid = false;
  10613    switch (current->NodeType()) {
  10614      case nsINode::ELEMENT_NODE: {
  10615        Element* elem = current->AsElement();
  10616        StartElement(elem, aBuilder);
  10617 
  10618        if constexpr (ShouldSerializeShadowRoots == SerializeShadowRoots::Yes) {
  10619          if (StartSerializingShadowDOM(
  10620                  current, aBuilder, aSerializableShadowRoots, aShadowRoots)) {
  10621            current = current->GetShadowRoot()->GetFirstChild();
  10622            continue;
  10623          }
  10624        }
  10625 
  10626        isVoid = IsVoidTag(elem);
  10627        if (!isVoid && (next = current->GetFirstChildOfTemplateOrNode())) {
  10628          current = next;
  10629          continue;
  10630        }
  10631        break;
  10632      }
  10633 
  10634      case nsINode::TEXT_NODE:
  10635      case nsINode::CDATA_SECTION_NODE: {
  10636        const CharacterDataBuffer* characterDataBuffer =
  10637            &current->AsText()->DataBuffer();
  10638        nsIContent* parent = current->GetParent();
  10639        if (ShouldEscape(parent)) {
  10640          AppendEncodedCharacters(characterDataBuffer, aBuilder);
  10641        } else {
  10642          aBuilder.Append(characterDataBuffer);
  10643        }
  10644        break;
  10645      }
  10646 
  10647      case nsINode::COMMENT_NODE: {
  10648        aBuilder.Append(u"<!--");
  10649        aBuilder.Append(
  10650            static_cast<nsIContent*>(current)->GetCharacterDataBuffer());
  10651        aBuilder.Append(u"-->");
  10652        break;
  10653      }
  10654 
  10655      case nsINode::DOCUMENT_TYPE_NODE: {
  10656        aBuilder.Append(u"<!DOCTYPE ");
  10657        aBuilder.Append(nsString(current->NodeName()));
  10658        aBuilder.Append(u">");
  10659        break;
  10660      }
  10661 
  10662      case nsINode::PROCESSING_INSTRUCTION_NODE: {
  10663        aBuilder.Append(u"<?");
  10664        aBuilder.Append(nsString(current->NodeName()));
  10665        aBuilder.Append(u" ");
  10666        aBuilder.Append(
  10667            static_cast<nsIContent*>(current)->GetCharacterDataBuffer());
  10668        aBuilder.Append(u">");
  10669        break;
  10670      }
  10671    }
  10672 
  10673    while (true) {
  10674      if (!isVoid && current->NodeType() == nsINode::ELEMENT_NODE) {
  10675        aBuilder.Append(u"</");
  10676        nsIContent* elem = static_cast<nsIContent*>(current);
  10677        if (elem->IsHTMLElement() || elem->IsSVGElement() ||
  10678            elem->IsMathMLElement()) {
  10679          aBuilder.Append(elem->NodeInfo()->NameAtom());
  10680        } else {
  10681          aBuilder.Append(nsString(current->NodeName()));
  10682        }
  10683        aBuilder.Append(u">");
  10684      }
  10685      isVoid = false;
  10686 
  10687      if (current == aRoot) {
  10688        return;
  10689      }
  10690 
  10691      if ((next = current->GetNextSibling())) {
  10692        current = next;
  10693        break;
  10694      }
  10695 
  10696      if constexpr (ShouldSerializeShadowRoots == SerializeShadowRoots::Yes) {
  10697        // If the current node is a shadow root, then we must go to its host.
  10698        // Since shadow DOMs are serialized declaratively as template
  10699        // elements, we serialize the end tag of the template before going
  10700        // back to serializing the shadow host.
  10701        if (current->IsShadowRoot()) {
  10702          current = current->GetContainingShadowHost();
  10703          aBuilder.Append(u"</template>");
  10704 
  10705          if (current->HasChildren()) {
  10706            current = current->GetFirstChildOfTemplateOrNode();
  10707            break;
  10708          }
  10709          continue;
  10710        }
  10711      }
  10712 
  10713      current = current->GetParentNode();
  10714 
  10715      // Handle template element. If the parent is a template's content,
  10716      // then adjust the parent to be the template element.
  10717      if (current != aRoot &&
  10718          current->NodeType() == nsINode::DOCUMENT_FRAGMENT_NODE) {
  10719        DocumentFragment* frag = static_cast<DocumentFragment*>(current);
  10720        nsIContent* fragHost = frag->GetHost();
  10721        if (fragHost && fragHost->IsTemplateElement()) {
  10722          current = fragHost;
  10723        }
  10724      }
  10725 
  10726      if (aDescendantsOnly && current == aRoot) {
  10727        return;
  10728      }
  10729    }
  10730  }
  10731 }
  10732 
  10733 template <SerializeShadowRoots ShouldSerializeShadowRoots>
  10734 bool nsContentUtils::SerializeNodeToMarkup(
  10735    nsINode* aRoot, bool aDescendantsOnly, nsAString& aOut,
  10736    bool aSerializableShadowRoots,
  10737    const Sequence<OwningNonNull<ShadowRoot>>& aShadowRoots) {
  10738  // If you pass in a DOCUMENT_NODE, you must pass aDescendentsOnly as true
  10739  MOZ_ASSERT(aDescendantsOnly || aRoot->NodeType() != nsINode::DOCUMENT_NODE);
  10740 
  10741  StringBuilder builder;
  10742  if constexpr (ShouldSerializeShadowRoots == SerializeShadowRoots::Yes) {
  10743    if (aDescendantsOnly &&
  10744        StartSerializingShadowDOM(aRoot, builder, aSerializableShadowRoots,
  10745                                  aShadowRoots)) {
  10746      SerializeNodeToMarkupInternal<SerializeShadowRoots::Yes>(
  10747          aRoot->GetShadowRoot()->GetFirstChild(), false, builder,
  10748          aSerializableShadowRoots, aShadowRoots);
  10749      // The template tag is opened in StartSerializingShadowDOM, so we need
  10750      // to close it here before serializing any children of aRoot.
  10751      builder.Append(u"</template>");
  10752    }
  10753  }
  10754 
  10755  SerializeNodeToMarkupInternal<ShouldSerializeShadowRoots>(
  10756      aRoot, aDescendantsOnly, builder, aSerializableShadowRoots, aShadowRoots);
  10757  return builder.ToString(aOut);
  10758 }
  10759 
  10760 template bool nsContentUtils::SerializeNodeToMarkup<SerializeShadowRoots::No>(
  10761    nsINode* aRoot, bool aDescendantsOnly, nsAString& aOut,
  10762    bool aSerializableShadowRoots,
  10763    const Sequence<OwningNonNull<ShadowRoot>>& aShadowRoots);
  10764 template bool nsContentUtils::SerializeNodeToMarkup<SerializeShadowRoots::Yes>(
  10765    nsINode* aRoot, bool aDescendantsOnly, nsAString& aOut,
  10766    bool aSerializableShadowRoots,
  10767    const Sequence<OwningNonNull<ShadowRoot>>& aShadowRoots);
  10768 
  10769 bool nsContentUtils::IsSpecificAboutPage(JSObject* aGlobal, const char* aUri) {
  10770  // aUri must start with about: or this isn't the right function to be using.
  10771  MOZ_ASSERT(strncmp(aUri, "about:", 6) == 0);
  10772 
  10773  // Make sure the global is a window
  10774  MOZ_DIAGNOSTIC_ASSERT(JS_IsGlobalObject(aGlobal));
  10775  nsGlobalWindowInner* win = xpc::WindowOrNull(aGlobal);
  10776  if (!win) {
  10777    return false;
  10778  }
  10779 
  10780  nsCOMPtr<nsIPrincipal> principal = win->GetPrincipal();
  10781  NS_ENSURE_TRUE(principal, false);
  10782 
  10783  // First check the scheme to avoid getting long specs in the common case.
  10784  if (!principal->SchemeIs("about")) {
  10785    return false;
  10786  }
  10787 
  10788  nsAutoCString spec;
  10789  principal->GetAsciiSpec(spec);
  10790 
  10791  return spec.EqualsASCII(aUri);
  10792 }
  10793 
  10794 /* static */
  10795 void nsContentUtils::SetScrollbarsVisibility(nsIDocShell* aDocShell,
  10796                                             bool aVisible) {
  10797  if (!aDocShell) {
  10798    return;
  10799  }
  10800  auto pref = aVisible ? ScrollbarPreference::Auto : ScrollbarPreference::Never;
  10801  nsDocShell::Cast(aDocShell)->SetScrollbarPreference(pref);
  10802 }
  10803 
  10804 /* static */
  10805 nsIDocShell* nsContentUtils::GetDocShellForEventTarget(EventTarget* aTarget) {
  10806  if (!aTarget) {
  10807    return nullptr;
  10808  }
  10809 
  10810  nsCOMPtr<nsPIDOMWindowInner> innerWindow;
  10811  if (nsCOMPtr<nsINode> node = nsINode::FromEventTarget(aTarget)) {
  10812    bool ignore;
  10813    innerWindow =
  10814        do_QueryInterface(node->OwnerDoc()->GetScriptHandlingObject(ignore));
  10815  } else if ((innerWindow = nsPIDOMWindowInner::FromEventTarget(aTarget))) {
  10816    // Nothing else to do
  10817  } else if (nsCOMPtr<DOMEventTargetHelper> helper =
  10818                 do_QueryInterface(aTarget)) {
  10819    innerWindow = helper->GetOwnerWindow();
  10820  }
  10821 
  10822  if (innerWindow) {
  10823    return innerWindow->GetDocShell();
  10824  }
  10825 
  10826  return nullptr;
  10827 }
  10828 
  10829 /*
  10830 * Note: this function only relates to figuring out HTTPS state, which is an
  10831 * input to the Secure Context algorithm.  We are not actually implementing
  10832 * any part of the Secure Context algorithm itself here.
  10833 *
  10834 * This is a bit of a hack.  Ideally we'd propagate HTTPS state through
  10835 * nsIChannel as described in the Fetch and HTML specs, but making channels
  10836 * know about whether they should inherit HTTPS state, propagating information
  10837 * about who the channel's "client" is, exposing GetHttpsState API on channels
  10838 * and modifying the various cache implementations to store and retrieve HTTPS
  10839 * state involves a huge amount of code (see bug 1220687).  We avoid that for
  10840 * now using this function.
  10841 *
  10842 * This function takes advantage of the observation that we can return true if
  10843 * nsIContentSecurityManager::IsOriginPotentiallyTrustworthy returns true for
  10844 * the document's origin (e.g. the origin has a scheme of 'https' or host
  10845 * 'localhost' etc.).  Since we generally propagate a creator document's
  10846 * origin onto data:, blob:, etc. documents, this works for them too.
  10847 *
  10848 * The scenario where this observation breaks down is sandboxing without the
  10849 * 'allow-same-origin' flag, since in this case a document is given a unique
  10850 * origin (IsOriginPotentiallyTrustworthy would return false).  We handle that
  10851 * by using the origin that the document would have had had it not been
  10852 * sandboxed.
  10853 *
  10854 * DEFICIENCIES: Note that this function uses nsIScriptSecurityManager's
  10855 * getChannelResultPrincipalIfNotSandboxed, and that method's ignoring of
  10856 * sandboxing is limited to the immediate sandbox.  In the case that aDocument
  10857 * should inherit its origin (e.g. data: URI) but its parent has ended up
  10858 * with a unique origin due to sandboxing further up the parent chain we may
  10859 * end up returning false when we would ideally return true (since we will
  10860 * examine the parent's origin for 'https' and not finding it.)  This means
  10861 * that we may restrict the privileges of some pages unnecessarily in this
  10862 * edge case.
  10863 */
  10864 /* static */
  10865 bool nsContentUtils::HttpsStateIsModern(Document* aDocument) {
  10866  if (!aDocument) {
  10867    return false;
  10868  }
  10869 
  10870  nsCOMPtr<nsIPrincipal> principal = aDocument->NodePrincipal();
  10871 
  10872  if (principal->IsSystemPrincipal()) {
  10873    return true;
  10874  }
  10875 
  10876  // If aDocument is sandboxed, try and get the principal that it would have
  10877  // been given had it not been sandboxed:
  10878  if (principal->GetIsNullPrincipal() &&
  10879      (aDocument->GetSandboxFlags() & SANDBOXED_ORIGIN)) {
  10880    nsIChannel* channel = aDocument->GetChannel();
  10881    if (channel) {
  10882      nsCOMPtr<nsIScriptSecurityManager> ssm =
  10883          nsContentUtils::GetSecurityManager();
  10884      nsresult rv = ssm->GetChannelResultPrincipalIfNotSandboxed(
  10885          channel, getter_AddRefs(principal));
  10886      if (NS_FAILED(rv)) {
  10887        return false;
  10888      }
  10889      if (principal->IsSystemPrincipal()) {
  10890        // If a document with the system principal is sandboxing a subdocument
  10891        // that would normally inherit the embedding element's principal (e.g.
  10892        // a srcdoc document) then the embedding document does not trust the
  10893        // content that is written to the embedded document.  Unlike when the
  10894        // embedding document is https, in this case we have no indication as
  10895        // to whether the embedded document's contents are delivered securely
  10896        // or not, and the sandboxing would possibly indicate that they were
  10897        // not.  To play it safe we return false here.  (See bug 1162772
  10898        // comment 73-80.)
  10899        return false;
  10900      }
  10901    }
  10902  }
  10903 
  10904  if (principal->GetIsNullPrincipal()) {
  10905    return false;
  10906  }
  10907 
  10908  MOZ_ASSERT(principal->GetIsContentPrincipal());
  10909 
  10910  return principal->GetIsOriginPotentiallyTrustworthy();
  10911 }
  10912 
  10913 /* static */
  10914 bool nsContentUtils::ComputeIsSecureContext(nsIChannel* aChannel) {
  10915  MOZ_ASSERT(aChannel);
  10916 
  10917  nsCOMPtr<nsIScriptSecurityManager> ssm = nsContentUtils::GetSecurityManager();
  10918  nsCOMPtr<nsIPrincipal> principal;
  10919  nsresult rv = ssm->GetChannelResultPrincipalIfNotSandboxed(
  10920      aChannel, getter_AddRefs(principal));
  10921  if (NS_FAILED(rv)) {
  10922    return false;
  10923  }
  10924 
  10925  const RefPtr<nsILoadInfo> loadInfo = aChannel->LoadInfo();
  10926 
  10927  if (principal->IsSystemPrincipal()) {
  10928    // If the load would've been sandboxed, treat this load as an untrusted
  10929    // load, as system code considers sandboxed resources insecure.
  10930    return !loadInfo->GetLoadingSandboxed();
  10931  }
  10932 
  10933  if (principal->GetIsNullPrincipal()) {
  10934    return false;
  10935  }
  10936 
  10937  if (const RefPtr<WindowContext> windowContext =
  10938          WindowContext::GetById(loadInfo->GetInnerWindowID())) {
  10939    if (!windowContext->GetIsSecureContext()) {
  10940      return false;
  10941    }
  10942  }
  10943 
  10944  return principal->GetIsOriginPotentiallyTrustworthy();
  10945 }
  10946 
  10947 /* static */ bool nsContentUtils::DocumentHasOnionURI(Document* aDocument) {
  10948  if (!aDocument) {
  10949    return false;
  10950  }
  10951 
  10952  nsIURI* uri = aDocument->GetDocumentURI();
  10953  if (!uri) {
  10954    return false;
  10955  }
  10956 
  10957  nsAutoCString host;
  10958  if (NS_SUCCEEDED(uri->GetHost(host))) {
  10959    bool hasOnionURI = StringEndsWith(host, ".onion"_ns);
  10960    return hasOnionURI;
  10961  }
  10962 
  10963  return false;
  10964 }
  10965 
  10966 /* static */
  10967 void nsContentUtils::TryToUpgradeElement(Element* aElement) {
  10968  NodeInfo* nodeInfo = aElement->NodeInfo();
  10969  RefPtr<nsAtom> typeAtom =
  10970      aElement->GetCustomElementData()->GetCustomElementType();
  10971 
  10972  MOZ_ASSERT(nodeInfo->NameAtom()->Equals(nodeInfo->LocalName()));
  10973  CustomElementDefinition* definition =
  10974      nsContentUtils::LookupCustomElementDefinition(
  10975          nodeInfo->GetDocument(), nodeInfo->NameAtom(),
  10976          nodeInfo->NamespaceID(), typeAtom);
  10977  if (definition) {
  10978    nsContentUtils::EnqueueUpgradeReaction(aElement, definition);
  10979  } else {
  10980    // Add an unresolved custom element that is a candidate for upgrade when a
  10981    // custom element is connected to the document.
  10982    nsContentUtils::RegisterUnresolvedElement(aElement, typeAtom);
  10983  }
  10984 }
  10985 
  10986 MOZ_CAN_RUN_SCRIPT
  10987 static void DoCustomElementCreate(Element** aElement, JSContext* aCx,
  10988                                  Document* aDoc, NodeInfo* aNodeInfo,
  10989                                  CustomElementConstructor* aConstructor,
  10990                                  ErrorResult& aRv, FromParser aFromParser) {
  10991  JS::Rooted<JS::Value> constructResult(aCx);
  10992  aConstructor->Construct(&constructResult, aRv, "Custom Element Create",
  10993                          CallbackFunction::eRethrowExceptions);
  10994  if (aRv.Failed()) {
  10995    return;
  10996  }
  10997 
  10998  RefPtr<Element> element;
  10999  // constructResult is an ObjectValue because construction with a callback
  11000  // always forms the return value from a JSObject.
  11001  UNWRAP_OBJECT(Element, &constructResult, element);
  11002  if (aNodeInfo->NamespaceEquals(kNameSpaceID_XHTML)) {
  11003    if (!element || !element->IsHTMLElement()) {
  11004      aRv.ThrowTypeError<MSG_DOES_NOT_IMPLEMENT_INTERFACE>("\"this\"",
  11005                                                           "HTMLElement");
  11006      return;
  11007    }
  11008  } else {
  11009    if (!element || !element->IsXULElement()) {
  11010      aRv.ThrowTypeError<MSG_DOES_NOT_IMPLEMENT_INTERFACE>("\"this\"",
  11011                                                           "XULElement");
  11012      return;
  11013    }
  11014  }
  11015 
  11016  nsAtom* localName = aNodeInfo->NameAtom();
  11017 
  11018  if (aDoc != element->OwnerDoc() || element->GetParentNode() ||
  11019      element->HasChildren() || element->GetAttrCount() ||
  11020      element->NodeInfo()->NameAtom() != localName) {
  11021    aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
  11022    return;
  11023  }
  11024 
  11025  if (element->IsHTMLElement()) {
  11026    static_cast<HTMLElement*>(&*element)->InhibitRestoration(
  11027        !(aFromParser & FROM_PARSER_NETWORK));
  11028  }
  11029 
  11030  element.forget(aElement);
  11031 }
  11032 
  11033 /* static */
  11034 nsresult nsContentUtils::NewXULOrHTMLElement(
  11035    Element** aResult, mozilla::dom::NodeInfo* aNodeInfo,
  11036    FromParser aFromParser, nsAtom* aIsAtom,
  11037    mozilla::dom::CustomElementDefinition* aDefinition) {
  11038  RefPtr<mozilla::dom::NodeInfo> nodeInfo = aNodeInfo;
  11039  MOZ_ASSERT(nodeInfo->NamespaceEquals(kNameSpaceID_XHTML) ||
  11040                 nodeInfo->NamespaceEquals(kNameSpaceID_XUL),
  11041             "Can only create XUL or XHTML elements.");
  11042 
  11043  nsAtom* name = nodeInfo->NameAtom();
  11044  int32_t tag = eHTMLTag_unknown;
  11045  bool isCustomElementName = false;
  11046  if (nodeInfo->NamespaceEquals(kNameSpaceID_XHTML)) {
  11047    tag = nsHTMLTags::CaseSensitiveAtomTagToId(name);
  11048    isCustomElementName =
  11049        (tag == eHTMLTag_userdefined &&
  11050         nsContentUtils::IsCustomElementName(name, kNameSpaceID_XHTML));
  11051  } else {  // kNameSpaceID_XUL
  11052    if (aIsAtom) {
  11053      // Make sure the customized built-in element to be constructed confirms
  11054      // to our naming requirement, i.e. [is] must be a dashed name and
  11055      // the tag name must not.
  11056      // if so, set isCustomElementName to false to kick off all the logics
  11057      // that pick up aIsAtom.
  11058      if (nsContentUtils::IsNameWithDash(aIsAtom) &&
  11059          !nsContentUtils::IsNameWithDash(name)) {
  11060        isCustomElementName = false;
  11061      } else {
  11062        isCustomElementName =
  11063            nsContentUtils::IsCustomElementName(name, kNameSpaceID_XUL);
  11064      }
  11065    } else {
  11066      isCustomElementName =
  11067          nsContentUtils::IsCustomElementName(name, kNameSpaceID_XUL);
  11068    }
  11069  }
  11070 
  11071  nsAtom* tagAtom = nodeInfo->NameAtom();
  11072  nsAtom* typeAtom = nullptr;
  11073  bool isCustomElement = isCustomElementName || aIsAtom;
  11074  if (isCustomElement) {
  11075    typeAtom = isCustomElementName ? tagAtom : aIsAtom;
  11076  }
  11077 
  11078  MOZ_ASSERT_IF(aDefinition, isCustomElement);
  11079 
  11080  // https://dom.spec.whatwg.org/#concept-create-element
  11081  // We only handle the "synchronous custom elements flag is set" now.
  11082  // For the unset case (e.g. cloning a node), see bug 1319342 for that.
  11083  // Step 4.
  11084  RefPtr<CustomElementDefinition> definition = aDefinition;
  11085  if (isCustomElement && !definition) {
  11086    MOZ_ASSERT(nodeInfo->NameAtom()->Equals(nodeInfo->LocalName()));
  11087    definition = nsContentUtils::LookupCustomElementDefinition(
  11088        nodeInfo->GetDocument(), nodeInfo->NameAtom(), nodeInfo->NamespaceID(),
  11089        typeAtom);
  11090  }
  11091 
  11092  // It might be a problem that parser synchronously calls constructor, so
  11093  // filed bug 1378079 to figure out what we should do for parser case.
  11094  if (definition) {
  11095    /*
  11096     * Synchronous custom elements flag is determined by 3 places in spec,
  11097     * 1) create an element for a token, the flag is determined by
  11098     *    "will execute script" which is not originally created
  11099     *    for the HTML fragment parsing algorithm.
  11100     * 2) createElement and createElementNS, the flag is the same as
  11101     *    NOT_FROM_PARSER.
  11102     * 3) clone a node, our implementation will not go into this function.
  11103     * For the unset case which is non-synchronous only applied for
  11104     * inner/outerHTML.
  11105     */
  11106    bool synchronousCustomElements = aFromParser != dom::FROM_PARSER_FRAGMENT;
  11107    // Per discussion in https://github.com/w3c/webcomponents/issues/635,
  11108    // use entry global in those places that are called from JS APIs and use
  11109    // the node document's global object if it is called from parser.
  11110    nsIGlobalObject* global;
  11111    if (aFromParser == dom::NOT_FROM_PARSER) {
  11112      global = GetEntryGlobal();
  11113 
  11114      // Documents created from the PrototypeDocumentSink always use
  11115      // NOT_FROM_PARSER for non-XUL elements. We can get the global from the
  11116      // document in that case.
  11117      if (!global) {
  11118        Document* doc = nodeInfo->GetDocument();
  11119        if (doc && doc->LoadedFromPrototype()) {
  11120          global = doc->GetScopeObject();
  11121        }
  11122      }
  11123    } else {
  11124      global = nodeInfo->GetDocument()->GetScopeObject();
  11125    }
  11126    if (!global) {
  11127      // In browser chrome code, one may have access to a document which
  11128      // doesn't have scope object anymore.
  11129      return NS_ERROR_FAILURE;
  11130    }
  11131 
  11132    AutoAllowLegacyScriptExecution exemption;
  11133    AutoEntryScript aes(global, "create custom elements");
  11134    JSContext* cx = aes.cx();
  11135    ErrorResult rv;
  11136 
  11137    // Step 5.
  11138    if (definition->IsCustomBuiltIn()) {
  11139      // SetupCustomElement() should be called with an element that don't have
  11140      // CustomElementData setup, if not we will hit the assertion in
  11141      // SetCustomElementData().
  11142      // Built-in element
  11143      if (nodeInfo->NamespaceEquals(kNameSpaceID_XHTML)) {
  11144        *aResult =
  11145            CreateHTMLElement(tag, nodeInfo.forget(), aFromParser).take();
  11146      } else {
  11147        NS_IF_ADDREF(*aResult = nsXULElement::Construct(nodeInfo.forget()));
  11148      }
  11149      (*aResult)->SetCustomElementData(MakeUnique<CustomElementData>(typeAtom));
  11150      if (synchronousCustomElements) {
  11151        CustomElementRegistry::Upgrade(*aResult, definition, rv);
  11152        if (rv.MaybeSetPendingException(cx)) {
  11153          aes.ReportException();
  11154        }
  11155      } else {
  11156        nsContentUtils::EnqueueUpgradeReaction(*aResult, definition);
  11157      }
  11158 
  11159      return NS_OK;
  11160    }
  11161 
  11162    // Step 6.1.
  11163    if (synchronousCustomElements) {
  11164      definition->mPrefixStack.AppendElement(nodeInfo->GetPrefixAtom());
  11165      RefPtr<Document> doc = nodeInfo->GetDocument();
  11166      DoCustomElementCreate(aResult, cx, doc, nodeInfo,
  11167                            MOZ_KnownLive(definition->mConstructor), rv,
  11168                            aFromParser);
  11169      if (rv.MaybeSetPendingException(cx)) {
  11170        if (nodeInfo->NamespaceEquals(kNameSpaceID_XHTML)) {
  11171          NS_IF_ADDREF(*aResult = NS_NewHTMLUnknownElement(nodeInfo.forget(),
  11172                                                           aFromParser));
  11173        } else {
  11174          NS_IF_ADDREF(*aResult = nsXULElement::Construct(nodeInfo.forget()));
  11175        }
  11176        (*aResult)->SetDefined(false);
  11177      }
  11178      definition->mPrefixStack.RemoveLastElement();
  11179      return NS_OK;
  11180    }
  11181 
  11182    // Step 6.2.
  11183    if (nodeInfo->NamespaceEquals(kNameSpaceID_XHTML)) {
  11184      NS_IF_ADDREF(*aResult =
  11185                       NS_NewHTMLElement(nodeInfo.forget(), aFromParser));
  11186    } else {
  11187      NS_IF_ADDREF(*aResult = nsXULElement::Construct(nodeInfo.forget()));
  11188    }
  11189    (*aResult)->SetCustomElementData(
  11190        MakeUnique<CustomElementData>(definition->mType));
  11191    nsContentUtils::EnqueueUpgradeReaction(*aResult, definition);
  11192    return NS_OK;
  11193  }
  11194 
  11195  if (nodeInfo->NamespaceEquals(kNameSpaceID_XHTML)) {
  11196    // Per the Custom Element specification, unknown tags that are valid
  11197    // custom element names should be HTMLElement instead of
  11198    // HTMLUnknownElement.
  11199    if (isCustomElementName) {
  11200      NS_IF_ADDREF(*aResult =
  11201                       NS_NewHTMLElement(nodeInfo.forget(), aFromParser));
  11202    } else {
  11203      *aResult = CreateHTMLElement(tag, nodeInfo.forget(), aFromParser).take();
  11204    }
  11205  } else {
  11206    NS_IF_ADDREF(*aResult = nsXULElement::Construct(nodeInfo.forget()));
  11207  }
  11208 
  11209  if (!*aResult) {
  11210    return NS_ERROR_OUT_OF_MEMORY;
  11211  }
  11212 
  11213  if (isCustomElement) {
  11214    (*aResult)->SetCustomElementData(MakeUnique<CustomElementData>(typeAtom));
  11215    nsContentUtils::RegisterCallbackUpgradeElement(*aResult, typeAtom);
  11216  }
  11217 
  11218  return NS_OK;
  11219 }
  11220 
  11221 CustomElementRegistry* nsContentUtils::GetCustomElementRegistry(
  11222    Document* aDoc) {
  11223  MOZ_ASSERT(aDoc);
  11224 
  11225  if (!aDoc->GetDocShell()) {
  11226    return nullptr;
  11227  }
  11228 
  11229  nsPIDOMWindowInner* window = aDoc->GetInnerWindow();
  11230  if (!window) {
  11231    return nullptr;
  11232  }
  11233 
  11234  return window->CustomElements();
  11235 }
  11236 
  11237 /* static */
  11238 CustomElementDefinition* nsContentUtils::LookupCustomElementDefinition(
  11239    Document* aDoc, nsAtom* aNameAtom, uint32_t aNameSpaceID,
  11240    nsAtom* aTypeAtom) {
  11241  if (aNameSpaceID != kNameSpaceID_XUL && aNameSpaceID != kNameSpaceID_XHTML) {
  11242    return nullptr;
  11243  }
  11244 
  11245  RefPtr<CustomElementRegistry> registry = GetCustomElementRegistry(aDoc);
  11246  if (!registry) {
  11247    return nullptr;
  11248  }
  11249 
  11250  return registry->LookupCustomElementDefinition(aNameAtom, aNameSpaceID,
  11251                                                 aTypeAtom);
  11252 }
  11253 
  11254 /* static */
  11255 void nsContentUtils::RegisterCallbackUpgradeElement(Element* aElement,
  11256                                                    nsAtom* aTypeName) {
  11257  MOZ_ASSERT(aElement);
  11258 
  11259  Document* doc = aElement->OwnerDoc();
  11260  CustomElementRegistry* registry = GetCustomElementRegistry(doc);
  11261  if (registry) {
  11262    registry->RegisterCallbackUpgradeElement(aElement, aTypeName);
  11263  }
  11264 }
  11265 
  11266 /* static */
  11267 void nsContentUtils::RegisterUnresolvedElement(Element* aElement,
  11268                                               nsAtom* aTypeName) {
  11269  MOZ_ASSERT(aElement);
  11270 
  11271  Document* doc = aElement->OwnerDoc();
  11272  CustomElementRegistry* registry = GetCustomElementRegistry(doc);
  11273  if (registry) {
  11274    registry->RegisterUnresolvedElement(aElement, aTypeName);
  11275  }
  11276 }
  11277 
  11278 /* static */
  11279 void nsContentUtils::UnregisterUnresolvedElement(Element* aElement) {
  11280  MOZ_ASSERT(aElement);
  11281 
  11282  nsAtom* typeAtom = aElement->GetCustomElementData()->GetCustomElementType();
  11283  Document* doc = aElement->OwnerDoc();
  11284  CustomElementRegistry* registry = GetCustomElementRegistry(doc);
  11285  if (registry) {
  11286    registry->UnregisterUnresolvedElement(aElement, typeAtom);
  11287  }
  11288 }
  11289 
  11290 /* static */
  11291 void nsContentUtils::EnqueueUpgradeReaction(
  11292    Element* aElement, CustomElementDefinition* aDefinition) {
  11293  MOZ_ASSERT(aElement);
  11294 
  11295  Document* doc = aElement->OwnerDoc();
  11296 
  11297  // No DocGroup means no custom element reactions stack.
  11298  if (!doc->GetDocGroup()) {
  11299    return;
  11300  }
  11301 
  11302  CustomElementReactionsStack* stack =
  11303      doc->GetDocGroup()->CustomElementReactionsStack();
  11304  stack->EnqueueUpgradeReaction(aElement, aDefinition);
  11305 }
  11306 
  11307 /* static */
  11308 void nsContentUtils::EnqueueLifecycleCallback(
  11309    ElementCallbackType aType, Element* aCustomElement,
  11310    const LifecycleCallbackArgs& aArgs, CustomElementDefinition* aDefinition) {
  11311  // No DocGroup means no custom element reactions stack.
  11312  if (!aCustomElement->OwnerDoc()->GetDocGroup()) {
  11313    return;
  11314  }
  11315 
  11316  CustomElementRegistry::EnqueueLifecycleCallback(aType, aCustomElement, aArgs,
  11317                                                  aDefinition);
  11318 }
  11319 
  11320 /* static */
  11321 CustomElementFormValue nsContentUtils::ConvertToCustomElementFormValue(
  11322    const Nullable<OwningFileOrUSVStringOrFormData>& aState) {
  11323  if (aState.IsNull()) {
  11324    return void_t{};
  11325  }
  11326  const auto& state = aState.Value();
  11327  if (state.IsFile()) {
  11328    RefPtr<BlobImpl> impl = state.GetAsFile()->Impl();
  11329    return {std::move(impl)};
  11330  }
  11331  if (state.IsUSVString()) {
  11332    return state.GetAsUSVString();
  11333  }
  11334  return state.GetAsFormData()->ConvertToCustomElementFormValue();
  11335 }
  11336 
  11337 /* static */
  11338 Nullable<OwningFileOrUSVStringOrFormData>
  11339 nsContentUtils::ExtractFormAssociatedCustomElementValue(
  11340    nsIGlobalObject* aGlobal,
  11341    const mozilla::dom::CustomElementFormValue& aCEValue) {
  11342  MOZ_ASSERT(aGlobal);
  11343 
  11344  OwningFileOrUSVStringOrFormData value;
  11345  switch (aCEValue.type()) {
  11346    case CustomElementFormValue::TBlobImpl: {
  11347      RefPtr<File> file = File::Create(aGlobal, aCEValue.get_BlobImpl());
  11348      if (NS_WARN_IF(!file)) {
  11349        return {};
  11350      }
  11351      value.SetAsFile() = file;
  11352    } break;
  11353 
  11354    case CustomElementFormValue::TnsString:
  11355      value.SetAsUSVString() = aCEValue.get_nsString();
  11356      break;
  11357 
  11358    case CustomElementFormValue::TArrayOfFormDataTuple: {
  11359      const auto& array = aCEValue.get_ArrayOfFormDataTuple();
  11360      auto formData = MakeRefPtr<FormData>();
  11361 
  11362      for (auto i = 0ul; i < array.Length(); ++i) {
  11363        const auto& item = array.ElementAt(i);
  11364        switch (item.value().type()) {
  11365          case IPCFormDataValue::TnsString:
  11366            formData->AddNameValuePair(item.name(),
  11367                                       item.value().get_nsString());
  11368            break;
  11369 
  11370          case IPCFormDataValue::TBlobImpl: {
  11371            auto blobImpl = item.value().get_BlobImpl();
  11372            auto* blob = Blob::Create(aGlobal, blobImpl);
  11373            formData->AddNameBlobPair(item.name(), blob);
  11374          } break;
  11375 
  11376          default:
  11377            continue;
  11378        }
  11379      }
  11380 
  11381      value.SetAsFormData() = formData;
  11382    } break;
  11383    case CustomElementFormValue::Tvoid_t:
  11384      return {};
  11385    default:
  11386      NS_WARNING("Invalid CustomElementContentData type!");
  11387      return {};
  11388  }
  11389  return value;
  11390 }
  11391 
  11392 /* static */
  11393 void nsContentUtils::AppendDocumentLevelNativeAnonymousContentTo(
  11394    Document* aDocument, nsTArray<nsIContent*>& aElements) {
  11395  MOZ_ASSERT(aDocument);
  11396 #ifdef DEBUG
  11397  size_t oldLength = aElements.Length();
  11398 #endif
  11399 
  11400  if (PresShell* presShell = aDocument->GetPresShell()) {
  11401    if (ScrollContainerFrame* rootScrollContainerFrame =
  11402            presShell->GetRootScrollContainerFrame()) {
  11403      rootScrollContainerFrame->AppendAnonymousContentTo(aElements, 0);
  11404    }
  11405    if (nsCanvasFrame* canvasFrame = presShell->GetCanvasFrame()) {
  11406      canvasFrame->AppendAnonymousContentTo(aElements, 0);
  11407    }
  11408  }
  11409 
  11410  if (auto* el = aDocument->GetCustomContentContainer()) {
  11411    aElements.AppendElement(el);
  11412  }
  11413 
  11414 #ifdef DEBUG
  11415  for (size_t i = oldLength; i < aElements.Length(); i++) {
  11416    MOZ_ASSERT(
  11417        aElements[i]->GetProperty(nsGkAtoms::docLevelNativeAnonymousContent),
  11418        "Someone here has lied, or missed to flag the node");
  11419  }
  11420 #endif
  11421 }
  11422 
  11423 static void AppendNativeAnonymousChildrenFromFrame(nsIFrame* aFrame,
  11424                                                   nsTArray<nsIContent*>& aKids,
  11425                                                   uint32_t aFlags) {
  11426  if (nsIAnonymousContentCreator* ac = do_QueryFrame(aFrame)) {
  11427    ac->AppendAnonymousContentTo(aKids, aFlags);
  11428  }
  11429 }
  11430 
  11431 /* static */
  11432 void nsContentUtils::AppendNativeAnonymousChildren(const nsIContent* aContent,
  11433                                                   nsTArray<nsIContent*>& aKids,
  11434                                                   uint32_t aFlags) {
  11435  if (aContent->MayHaveAnonymousChildren()) {
  11436    if (nsIFrame* primaryFrame = aContent->GetPrimaryFrame()) {
  11437      // NAC created by the element's primary frame.
  11438      AppendNativeAnonymousChildrenFromFrame(primaryFrame, aKids, aFlags);
  11439 
  11440      // NAC created by any other non-primary frames for the element.
  11441      AutoTArray<nsIFrame::OwnedAnonBox, 8> ownedAnonBoxes;
  11442      primaryFrame->AppendOwnedAnonBoxes(ownedAnonBoxes);
  11443      for (nsIFrame::OwnedAnonBox& box : ownedAnonBoxes) {
  11444        MOZ_ASSERT(box.mAnonBoxFrame->GetContent() == aContent);
  11445        AppendNativeAnonymousChildrenFromFrame(box.mAnonBoxFrame, aKids,
  11446                                               aFlags);
  11447      }
  11448    }
  11449 
  11450    // View transition pseudos.
  11451    if (aContent->IsRootElement()) {
  11452      if (auto* vt = aContent->OwnerDoc()->GetActiveViewTransition()) {
  11453        if (auto* root = vt->GetSnapshotContainingBlock()) {
  11454          aKids.AppendElement(root);
  11455        }
  11456      }
  11457    }
  11458 
  11459    // Get manually created NAC (editor resize handles, etc.).
  11460    if (auto nac = static_cast<ManualNACArray*>(
  11461            aContent->GetProperty(nsGkAtoms::manualNACProperty))) {
  11462      aKids.AppendElements(*nac);
  11463    }
  11464  }
  11465 
  11466  // The root scroll frame is not the primary frame of the root element.
  11467  // Detect and handle this case.
  11468  if (!(aFlags & nsIContent::eSkipDocumentLevelNativeAnonymousContent) &&
  11469      aContent->IsRootElement()) {
  11470    AppendDocumentLevelNativeAnonymousContentTo(aContent->OwnerDoc(), aKids);
  11471  }
  11472 }
  11473 
  11474 bool nsContentUtils::IsImageAvailable(nsIContent* aLoadingNode, nsIURI* aURI,
  11475                                      nsIPrincipal* aDefaultTriggeringPrincipal,
  11476                                      CORSMode aCORSMode) {
  11477  nsCOMPtr<nsIPrincipal> triggeringPrincipal;
  11478  QueryTriggeringPrincipal(aLoadingNode, aDefaultTriggeringPrincipal,
  11479                           getter_AddRefs(triggeringPrincipal));
  11480  MOZ_ASSERT(triggeringPrincipal);
  11481 
  11482  Document* doc = aLoadingNode->OwnerDoc();
  11483  return IsImageAvailable(aURI, triggeringPrincipal, aCORSMode, doc);
  11484 }
  11485 
  11486 bool nsContentUtils::IsImageAvailable(nsIURI* aURI,
  11487                                      nsIPrincipal* aTriggeringPrincipal,
  11488                                      CORSMode aCORSMode, Document* aDoc) {
  11489  imgLoader* imgLoader = GetImgLoaderForDocument(aDoc);
  11490  return imgLoader->IsImageAvailable(aURI, aTriggeringPrincipal, aCORSMode,
  11491                                     aDoc);
  11492 }
  11493 
  11494 /* static */
  11495 bool nsContentUtils::QueryTriggeringPrincipal(
  11496    nsIContent* aLoadingNode, nsIPrincipal* aDefaultPrincipal,
  11497    nsIPrincipal** aTriggeringPrincipal) {
  11498  MOZ_ASSERT(aLoadingNode);
  11499  MOZ_ASSERT(aTriggeringPrincipal);
  11500 
  11501  bool result = false;
  11502  nsCOMPtr<nsIPrincipal> loadingPrincipal = aDefaultPrincipal;
  11503  if (!loadingPrincipal) {
  11504    loadingPrincipal = aLoadingNode->NodePrincipal();
  11505  }
  11506 
  11507  // If aLoadingNode is content, bail out early.
  11508  if (!aLoadingNode->NodePrincipal()->IsSystemPrincipal()) {
  11509    loadingPrincipal.forget(aTriggeringPrincipal);
  11510    return result;
  11511  }
  11512 
  11513  nsAutoString loadingStr;
  11514  if (aLoadingNode->IsElement()) {
  11515    aLoadingNode->AsElement()->GetAttr(
  11516        kNameSpaceID_None, nsGkAtoms::triggeringprincipal, loadingStr);
  11517  }
  11518 
  11519  // Fall back if 'triggeringprincipal' isn't specified,
  11520  if (loadingStr.IsEmpty()) {
  11521    loadingPrincipal.forget(aTriggeringPrincipal);
  11522    return result;
  11523  }
  11524 
  11525  nsCString binary;
  11526  nsCOMPtr<nsIPrincipal> serializedPrin =
  11527      BasePrincipal::FromJSON(NS_ConvertUTF16toUTF8(loadingStr));
  11528  if (serializedPrin) {
  11529    result = true;
  11530    serializedPrin.forget(aTriggeringPrincipal);
  11531  }
  11532 
  11533  if (!result) {
  11534    // Fallback if the deserialization is failed.
  11535    loadingPrincipal.forget(aTriggeringPrincipal);
  11536  }
  11537 
  11538  return result;
  11539 }
  11540 
  11541 /* static */
  11542 void nsContentUtils::GetContentPolicyTypeForUIImageLoading(
  11543    nsIContent* aLoadingNode, nsIPrincipal** aTriggeringPrincipal,
  11544    nsContentPolicyType& aContentPolicyType, uint64_t* aRequestContextID) {
  11545  MOZ_ASSERT(aRequestContextID);
  11546 
  11547  bool result = QueryTriggeringPrincipal(aLoadingNode, aTriggeringPrincipal);
  11548  if (result) {
  11549    // Set the content policy type to TYPE_INTERNAL_IMAGE_FAVICON for
  11550    // indicating it's a favicon loading.
  11551    aContentPolicyType = nsIContentPolicy::TYPE_INTERNAL_IMAGE_FAVICON;
  11552 
  11553    nsAutoString requestContextID;
  11554    if (aLoadingNode->IsElement()) {
  11555      aLoadingNode->AsElement()->GetAttr(
  11556          kNameSpaceID_None, nsGkAtoms::requestcontextid, requestContextID);
  11557    }
  11558    nsresult rv;
  11559    int64_t val = requestContextID.ToInteger64(&rv);
  11560    *aRequestContextID = NS_SUCCEEDED(rv) ? val : 0;
  11561  } else {
  11562    aContentPolicyType = nsIContentPolicy::TYPE_INTERNAL_IMAGE;
  11563  }
  11564 }
  11565 
  11566 /* static */
  11567 nsresult nsContentUtils::CreateJSValueFromSequenceOfObject(
  11568    JSContext* aCx, const Sequence<JSObject*>& aTransfer,
  11569    JS::MutableHandle<JS::Value> aValue) {
  11570  if (aTransfer.IsEmpty()) {
  11571    return NS_OK;
  11572  }
  11573 
  11574  JS::Rooted<JSObject*> array(aCx, JS::NewArrayObject(aCx, aTransfer.Length()));
  11575  if (!array) {
  11576    return NS_ERROR_OUT_OF_MEMORY;
  11577  }
  11578 
  11579  for (uint32_t i = 0; i < aTransfer.Length(); ++i) {
  11580    JS::Rooted<JSObject*> object(aCx, aTransfer[i]);
  11581    if (!object) {
  11582      continue;
  11583    }
  11584 
  11585    if (NS_WARN_IF(
  11586            !JS_DefineElement(aCx, array, i, object, JSPROP_ENUMERATE))) {
  11587      return NS_ERROR_OUT_OF_MEMORY;
  11588    }
  11589  }
  11590 
  11591  aValue.setObject(*array);
  11592  return NS_OK;
  11593 }
  11594 
  11595 /* static */
  11596 void nsContentUtils::StructuredClone(JSContext* aCx, nsIGlobalObject* aGlobal,
  11597                                     JS::Handle<JS::Value> aValue,
  11598                                     const StructuredSerializeOptions& aOptions,
  11599                                     JS::MutableHandle<JS::Value> aRetval,
  11600                                     ErrorResult& aError) {
  11601  JS::Rooted<JS::Value> transferArray(aCx, JS::UndefinedValue());
  11602  aError = nsContentUtils::CreateJSValueFromSequenceOfObject(
  11603      aCx, aOptions.mTransfer, &transferArray);
  11604  if (NS_WARN_IF(aError.Failed())) {
  11605    return;
  11606  }
  11607 
  11608  JS::CloneDataPolicy clonePolicy;
  11609  // We are definitely staying in the same agent cluster.
  11610  clonePolicy.allowIntraClusterClonableSharedObjects();
  11611  if (aGlobal->IsSharedMemoryAllowed()) {
  11612    clonePolicy.allowSharedMemoryObjects();
  11613  }
  11614 
  11615  StructuredCloneHolder holder(StructuredCloneHolder::CloningSupported,
  11616                               StructuredCloneHolder::TransferringSupported,
  11617                               JS::StructuredCloneScope::SameProcess);
  11618  holder.Write(aCx, aValue, transferArray, clonePolicy, aError);
  11619  if (NS_WARN_IF(aError.Failed())) {
  11620    return;
  11621  }
  11622 
  11623  holder.Read(aGlobal, aCx, aRetval, clonePolicy, aError);
  11624  if (NS_WARN_IF(aError.Failed())) {
  11625    return;
  11626  }
  11627 
  11628  nsTArray<RefPtr<MessagePort>> ports = holder.TakeTransferredPorts();
  11629  (void)ports;
  11630 }
  11631 
  11632 /* static */
  11633 bool nsContentUtils::ShouldBlockReservedKeys(WidgetKeyboardEvent* aKeyEvent) {
  11634  nsCOMPtr<nsIPrincipal> principal;
  11635  RefPtr<Element> targetElement =
  11636      Element::FromEventTargetOrNull(aKeyEvent->mOriginalTarget);
  11637  nsCOMPtr<nsIBrowser> targetBrowser;
  11638  if (targetElement) {
  11639    targetBrowser = targetElement->AsBrowser();
  11640  }
  11641  bool isRemoteBrowser = false;
  11642  if (targetBrowser) {
  11643    targetBrowser->GetIsRemoteBrowser(&isRemoteBrowser);
  11644  }
  11645 
  11646  if (isRemoteBrowser) {
  11647    targetBrowser->GetContentPrincipal(getter_AddRefs(principal));
  11648    return principal ? nsContentUtils::IsSitePermDeny(principal, "shortcuts"_ns)
  11649                     : false;
  11650  }
  11651 
  11652  if (targetElement) {
  11653    Document* doc = targetElement->GetUncomposedDoc();
  11654    if (doc) {
  11655      RefPtr<WindowContext> wc = doc->GetWindowContext();
  11656      if (wc) {
  11657        return wc->TopWindowContext()->GetShortcutsPermission() ==
  11658               nsIPermissionManager::DENY_ACTION;
  11659      }
  11660    }
  11661  }
  11662 
  11663  return false;
  11664 }
  11665 
  11666 /**
  11667 * Checks whether the given type is a supported document type for
  11668 * loading within the nsObjectLoadingContent specified by aContent.
  11669 *
  11670 * NOTE Helper method for nsContentUtils::HtmlObjectContentTypeForMIMEType.
  11671 * NOTE Does not take content policy or capabilities into account
  11672 */
  11673 static bool HtmlObjectContentSupportsDocument(const nsCString& aMimeType) {
  11674  nsCOMPtr<nsIWebNavigationInfo> info(
  11675      do_GetService(NS_WEBNAVIGATION_INFO_CONTRACTID));
  11676  if (!info) {
  11677    return false;
  11678  }
  11679 
  11680  uint32_t supported;
  11681  nsresult rv = info->IsTypeSupported(aMimeType, &supported);
  11682 
  11683  if (NS_FAILED(rv)) {
  11684    return false;
  11685  }
  11686 
  11687  if (supported != nsIWebNavigationInfo::UNSUPPORTED) {
  11688    // Don't want to support plugins as documents
  11689    return supported != nsIWebNavigationInfo::FALLBACK;
  11690  }
  11691 
  11692  // Try a stream converter
  11693  // NOTE: We treat any type we can convert from as a supported type. If a
  11694  // type is not actually supported, the URI loader will detect that and
  11695  // return an error, and we'll fallback.
  11696  nsCOMPtr<nsIStreamConverterService> convServ =
  11697      do_GetService("@mozilla.org/streamConverters;1");
  11698  bool canConvert = false;
  11699  if (convServ) {
  11700    rv = convServ->CanConvert(aMimeType.get(), "*/*", &canConvert);
  11701  }
  11702  return NS_SUCCEEDED(rv) && canConvert;
  11703 }
  11704 
  11705 /* static */
  11706 uint32_t nsContentUtils::HtmlObjectContentTypeForMIMEType(
  11707    const nsCString& aMIMEType, bool aIsSandboxed) {
  11708  if (aMIMEType.IsEmpty()) {
  11709    return nsIObjectLoadingContent::TYPE_FALLBACK;
  11710  }
  11711 
  11712  if (imgLoader::SupportImageWithMimeType(aMIMEType)) {
  11713    return nsIObjectLoadingContent::TYPE_DOCUMENT;
  11714  }
  11715 
  11716  // Faking support of the PDF content as a document for EMBED tags
  11717  // when internal PDF viewer is enabled.
  11718  if (aMIMEType.LowerCaseEqualsLiteral(APPLICATION_PDF) && IsPDFJSEnabled()) {
  11719    // Sandboxed iframes are just never allowed to display plugins. In the
  11720    // modern world, this just means "application/pdf".
  11721    return aIsSandboxed ? nsIObjectLoadingContent::TYPE_FALLBACK
  11722                        : nsIObjectLoadingContent::TYPE_DOCUMENT;
  11723  }
  11724 
  11725  if (HtmlObjectContentSupportsDocument(aMIMEType)) {
  11726    return nsIObjectLoadingContent::TYPE_DOCUMENT;
  11727  }
  11728 
  11729  return nsIObjectLoadingContent::TYPE_FALLBACK;
  11730 }
  11731 
  11732 /* static */
  11733 bool nsContentUtils::IsLocalRefURL(const nsAString& aString) {
  11734  return !aString.IsEmpty() && aString[0] == '#';
  11735 }
  11736 
  11737 // We use only 53 bits for the ID so that it can be converted to and from a JS
  11738 // value without loss of precision. The upper bits of the ID hold the process
  11739 // ID. The lower bits identify the object itself.
  11740 static constexpr uint64_t kIdTotalBits = 53;
  11741 static constexpr uint64_t kIdProcessBits = 22;
  11742 static constexpr uint64_t kIdBits = kIdTotalBits - kIdProcessBits;
  11743 
  11744 /* static */
  11745 uint64_t nsContentUtils::GenerateProcessSpecificId(uint64_t aId) {
  11746  uint64_t processId = 0;
  11747  if (XRE_IsContentProcess()) {
  11748    ContentChild* cc = ContentChild::GetSingleton();
  11749    processId = cc->GetID();
  11750  }
  11751 
  11752  MOZ_RELEASE_ASSERT(processId < (uint64_t(1) << kIdProcessBits));
  11753  uint64_t processBits = processId & ((uint64_t(1) << kIdProcessBits) - 1);
  11754 
  11755  uint64_t id = aId;
  11756  MOZ_RELEASE_ASSERT(id < (uint64_t(1) << kIdBits));
  11757  uint64_t bits = id & ((uint64_t(1) << kIdBits) - 1);
  11758 
  11759  return (processBits << kIdBits) | bits;
  11760 }
  11761 
  11762 /* static */
  11763 std::tuple<uint64_t, uint64_t> nsContentUtils::SplitProcessSpecificId(
  11764    uint64_t aId) {
  11765  return {aId >> kIdBits, aId & ((uint64_t(1) << kIdBits) - 1)};
  11766 }
  11767 
  11768 // Next process-local Tab ID.
  11769 static uint64_t gNextTabId = 0;
  11770 
  11771 /* static */
  11772 uint64_t nsContentUtils::GenerateTabId() {
  11773  return GenerateProcessSpecificId(++gNextTabId);
  11774 }
  11775 
  11776 // Next process-local Browser ID.
  11777 static uint64_t gNextBrowserId = 0;
  11778 
  11779 /* static */
  11780 uint64_t nsContentUtils::GenerateBrowserId() {
  11781  return GenerateProcessSpecificId(++gNextBrowserId);
  11782 }
  11783 
  11784 // Next process-local Browsing Context ID.
  11785 static uint64_t gNextBrowsingContextId = 0;
  11786 
  11787 /* static */
  11788 uint64_t nsContentUtils::GenerateBrowsingContextId() {
  11789  return GenerateProcessSpecificId(++gNextBrowsingContextId);
  11790 }
  11791 
  11792 // Next process-local Window ID.
  11793 static uint64_t gNextWindowId = 0;
  11794 
  11795 /* static */
  11796 uint64_t nsContentUtils::GenerateWindowId() {
  11797  return GenerateProcessSpecificId(++gNextWindowId);
  11798 }
  11799 
  11800 // Next process-local load.
  11801 static Atomic<uint64_t> gNextLoadIdentifier(0);
  11802 
  11803 /* static */
  11804 uint64_t nsContentUtils::GenerateLoadIdentifier() {
  11805  return GenerateProcessSpecificId(++gNextLoadIdentifier);
  11806 }
  11807 
  11808 /* static */
  11809 bool nsContentUtils::GetUserIsInteracting() {
  11810  return UserInteractionObserver::sUserActive;
  11811 }
  11812 
  11813 /* static */
  11814 bool nsContentUtils::GetSourceMapURL(nsIHttpChannel* aChannel,
  11815                                     nsACString& aResult) {
  11816  nsresult rv = aChannel->GetResponseHeader("SourceMap"_ns, aResult);
  11817  if (NS_FAILED(rv)) {
  11818    rv = aChannel->GetResponseHeader("X-SourceMap"_ns, aResult);
  11819  }
  11820  return NS_SUCCEEDED(rv);
  11821 }
  11822 
  11823 /* static */
  11824 bool nsContentUtils::IsMessageInputEvent(const IPC::Message& aMsg) {
  11825  if ((aMsg.type() & mozilla::dom::PBrowser::PBrowserStart) ==
  11826      mozilla::dom::PBrowser::PBrowserStart) {
  11827    switch (aMsg.type()) {
  11828      case mozilla::dom::PBrowser::Msg_RealMouseMoveEvent__ID:
  11829      case mozilla::dom::PBrowser::Msg_RealMouseButtonEvent__ID:
  11830      case mozilla::dom::PBrowser::Msg_RealMouseEnterExitWidgetEvent__ID:
  11831      case mozilla::dom::PBrowser::Msg_RealKeyEvent__ID:
  11832      case mozilla::dom::PBrowser::Msg_MouseWheelEvent__ID:
  11833      case mozilla::dom::PBrowser::Msg_RealTouchEvent__ID:
  11834      case mozilla::dom::PBrowser::Msg_RealTouchMoveEvent__ID:
  11835      case mozilla::dom::PBrowser::Msg_RealDragEvent__ID:
  11836      case mozilla::dom::PBrowser::Msg_UpdateDimensions__ID:
  11837        return true;
  11838    }
  11839  }
  11840  return false;
  11841 }
  11842 
  11843 /* static */
  11844 bool nsContentUtils::IsMessageCriticalInputEvent(const IPC::Message& aMsg) {
  11845  if ((aMsg.type() & mozilla::dom::PBrowser::PBrowserStart) ==
  11846      mozilla::dom::PBrowser::PBrowserStart) {
  11847    switch (aMsg.type()) {
  11848      case mozilla::dom::PBrowser::Msg_RealMouseButtonEvent__ID:
  11849      case mozilla::dom::PBrowser::Msg_RealKeyEvent__ID:
  11850      case mozilla::dom::PBrowser::Msg_MouseWheelEvent__ID:
  11851      case mozilla::dom::PBrowser::Msg_RealTouchEvent__ID:
  11852      case mozilla::dom::PBrowser::Msg_RealDragEvent__ID:
  11853        return true;
  11854    }
  11855  }
  11856  return false;
  11857 }
  11858 
  11859 static const char* kUserInteractionInactive = "user-interaction-inactive";
  11860 static const char* kUserInteractionActive = "user-interaction-active";
  11861 
  11862 void nsContentUtils::UserInteractionObserver::Init() {
  11863  // Listen for the observer messages from EventStateManager which are telling
  11864  // us whether or not the user is interacting.
  11865  nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
  11866  obs->AddObserver(this, kUserInteractionInactive, false);
  11867  obs->AddObserver(this, kUserInteractionActive, false);
  11868 
  11869  // We can't register ourselves as an annotator yet, as the
  11870  // BackgroundHangMonitor hasn't started yet. It will have started by the
  11871  // time we have the chance to spin the event loop.
  11872  RefPtr<UserInteractionObserver> self = this;
  11873  NS_DispatchToMainThread(NS_NewRunnableFunction(
  11874      "nsContentUtils::UserInteractionObserver::Init",
  11875      [=]() { BackgroundHangMonitor::RegisterAnnotator(*self); }));
  11876 }
  11877 
  11878 void nsContentUtils::UserInteractionObserver::Shutdown() {
  11879  nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
  11880  if (obs) {
  11881    obs->RemoveObserver(this, kUserInteractionInactive);
  11882    obs->RemoveObserver(this, kUserInteractionActive);
  11883  }
  11884 
  11885  BackgroundHangMonitor::UnregisterAnnotator(*this);
  11886 }
  11887 
  11888 /**
  11889 * NB: This function is always called by the BackgroundHangMonitor thread.
  11890 *     Plan accordingly
  11891 */
  11892 void nsContentUtils::UserInteractionObserver::AnnotateHang(
  11893    BackgroundHangAnnotations& aAnnotations) {
  11894  // NOTE: Only annotate the hang report if the user is known to be interacting.
  11895  if (sUserActive) {
  11896    aAnnotations.AddAnnotation(u"UserInteracting"_ns, true);
  11897  }
  11898 }
  11899 
  11900 NS_IMETHODIMP
  11901 nsContentUtils::UserInteractionObserver::Observe(nsISupports* aSubject,
  11902                                                 const char* aTopic,
  11903                                                 const char16_t* aData) {
  11904  if (!strcmp(aTopic, kUserInteractionInactive)) {
  11905    if (sUserActive && XRE_IsParentProcess()) {
  11906      glean::RecordPowerMetrics();
  11907    }
  11908    sUserActive = false;
  11909  } else if (!strcmp(aTopic, kUserInteractionActive)) {
  11910    if (!sUserActive && XRE_IsParentProcess()) {
  11911      glean::RecordPowerMetrics();
  11912 
  11913      nsCOMPtr<nsIUserIdleServiceInternal> idleService =
  11914          do_GetService("@mozilla.org/widget/useridleservice;1");
  11915      if (idleService) {
  11916        idleService->ResetIdleTimeOut(0);
  11917      }
  11918    }
  11919 
  11920    sUserActive = true;
  11921  } else {
  11922    NS_WARNING("Unexpected observer notification");
  11923  }
  11924  return NS_OK;
  11925 }
  11926 
  11927 Atomic<bool> nsContentUtils::UserInteractionObserver::sUserActive(false);
  11928 NS_IMPL_ISUPPORTS(nsContentUtils::UserInteractionObserver, nsIObserver)
  11929 
  11930 /* static */
  11931 bool nsContentUtils::IsSpecialName(const nsAString& aName) {
  11932  return aName.LowerCaseEqualsLiteral("_blank") ||
  11933         aName.LowerCaseEqualsLiteral("_top") ||
  11934         aName.LowerCaseEqualsLiteral("_parent") ||
  11935         aName.LowerCaseEqualsLiteral("_self");
  11936 }
  11937 
  11938 /* static */
  11939 bool nsContentUtils::IsOverridingWindowName(const nsAString& aName) {
  11940  return !aName.IsEmpty() && !IsSpecialName(aName);
  11941 }
  11942 
  11943 // Unfortunately, we can't unwrap an IDL object using only a concrete type.
  11944 // We need to calculate type data based on the IDL typename. Which means
  11945 // wrapping our templated function in a macro.
  11946 #define EXTRACT_EXN_VALUES(T, ...)                                    \
  11947  ExtractExceptionValuesImpl<mozilla::dom::prototypes::id::T,         \
  11948                             T##_Binding::NativeType, T>(__VA_ARGS__) \
  11949      .isOk()
  11950 
  11951 template <prototypes::ID PrototypeID, class NativeType, typename T>
  11952 static Result<Ok, nsresult> ExtractExceptionValuesImpl(
  11953    JSContext* aCx, JS::Handle<JSObject*> aObj, nsACString& aFilename,
  11954    uint32_t* aLineOut, uint32_t* aColumnOut, nsString& aMessageOut) {
  11955  AssertStaticUnwrapOK<PrototypeID>();
  11956  RefPtr<T> exn;
  11957  MOZ_TRY((UnwrapObject<PrototypeID, NativeType>(aObj, exn, nullptr)));
  11958 
  11959  if (nsCOMPtr<nsIStackFrame> location = exn->GetLocation()) {
  11960    location->GetFilename(aCx, aFilename);
  11961    *aLineOut = location->GetLineNumber(aCx);
  11962    *aColumnOut = location->GetColumnNumber(aCx);
  11963  } else {
  11964    aFilename.Truncate();
  11965    *aLineOut = 0;
  11966    *aColumnOut = 0;
  11967  }
  11968 
  11969  exn->GetName(aMessageOut);
  11970  aMessageOut.AppendLiteral(": ");
  11971 
  11972  nsAutoString message;
  11973  exn->GetMessageMoz(message);
  11974  aMessageOut.Append(message);
  11975  return Ok();
  11976 }
  11977 
  11978 /* static */
  11979 bool nsContentUtils::ExtractExceptionValues(
  11980    JSContext* aCx, JS::Handle<JSObject*> aObj, nsACString& aFilename,
  11981    uint32_t* aLineOut, uint32_t* aColumnOut, nsString& aMessageOut) {
  11982  return EXTRACT_EXN_VALUES(DOMException, aCx, aObj, aFilename, aLineOut,
  11983                            aColumnOut, aMessageOut) ||
  11984         EXTRACT_EXN_VALUES(Exception, aCx, aObj, aFilename, aLineOut,
  11985                            aColumnOut, aMessageOut);
  11986 }
  11987 
  11988 /* static */
  11989 void nsContentUtils::ExtractErrorValues(
  11990    JSContext* aCx, JS::Handle<JS::Value> aValue, nsACString& aSourceSpecOut,
  11991    uint32_t* aLineOut, uint32_t* aColumnOut, nsString& aMessageOut) {
  11992  MOZ_ASSERT(aLineOut);
  11993  MOZ_ASSERT(aColumnOut);
  11994 
  11995  if (aValue.isObject()) {
  11996    JS::Rooted<JSObject*> obj(aCx, &aValue.toObject());
  11997 
  11998    // Try to process as an Error object.  Use the file/line/column values
  11999    // from the Error as they will be more specific to the root cause of
  12000    // the problem.
  12001    if (JSErrorReport* err = JS_ErrorFromException(aCx, obj)) {
  12002      // Use xpc to extract the error message only.  We don't actually send
  12003      // this report anywhere.
  12004      RefPtr<xpc::ErrorReport> report = new xpc::ErrorReport();
  12005      report->Init(err,
  12006                   nullptr,  // toString result
  12007                   false,    // chrome
  12008                   0);       // window ID
  12009 
  12010      if (!report->mFileName.IsEmpty()) {
  12011        aSourceSpecOut = report->mFileName;
  12012        *aLineOut = report->mLineNumber;
  12013        *aColumnOut = report->mColumn;
  12014      }
  12015      aMessageOut.Assign(report->mErrorMsg);
  12016    }
  12017 
  12018    // Next, try to unwrap the rejection value as a (DOM)Exception.
  12019    else if (ExtractExceptionValues(aCx, obj, aSourceSpecOut, aLineOut,
  12020                                    aColumnOut, aMessageOut)) {
  12021      return;
  12022    }
  12023  }
  12024 
  12025  // If we could not unwrap a specific error type, then perform default safe
  12026  // string conversions on primitives.  Objects will result in "[Object]"
  12027  // unfortunately.
  12028  if (aMessageOut.IsEmpty()) {
  12029    nsAutoJSString jsString;
  12030    if (jsString.init(aCx, aValue)) {
  12031      aMessageOut = jsString;
  12032    } else {
  12033      JS_ClearPendingException(aCx);
  12034    }
  12035  }
  12036 }
  12037 
  12038 #undef EXTRACT_EXN_VALUES
  12039 
  12040 /* static */
  12041 bool nsContentUtils::ContentIsLink(nsIContent* aContent) {
  12042  if (!aContent || !aContent->IsElement()) {
  12043    return false;
  12044  }
  12045 
  12046  if (aContent->IsHTMLElement(nsGkAtoms::a)) {
  12047    return true;
  12048  }
  12049 
  12050  return aContent->AsElement()->AttrValueIs(kNameSpaceID_XLink, nsGkAtoms::type,
  12051                                            nsGkAtoms::simple, eCaseMatters);
  12052 }
  12053 
  12054 /* static */
  12055 already_AddRefed<ContentFrameMessageManager>
  12056 nsContentUtils::TryGetBrowserChildGlobal(nsISupports* aFrom) {
  12057  RefPtr<nsFrameLoaderOwner> frameLoaderOwner = do_QueryObject(aFrom);
  12058  if (!frameLoaderOwner) {
  12059    return nullptr;
  12060  }
  12061 
  12062  RefPtr<nsFrameLoader> frameLoader = frameLoaderOwner->GetFrameLoader();
  12063  if (!frameLoader) {
  12064    return nullptr;
  12065  }
  12066 
  12067  RefPtr<ContentFrameMessageManager> manager =
  12068      frameLoader->GetBrowserChildMessageManager();
  12069  return manager.forget();
  12070 }
  12071 
  12072 /* static */
  12073 uint32_t nsContentUtils::InnerOrOuterWindowCreated() {
  12074  MOZ_ASSERT(NS_IsMainThread());
  12075  ++sInnerOrOuterWindowCount;
  12076  return ++sInnerOrOuterWindowSerialCounter;
  12077 }
  12078 
  12079 /* static */
  12080 void nsContentUtils::InnerOrOuterWindowDestroyed() {
  12081  MOZ_ASSERT(NS_IsMainThread());
  12082  MOZ_ASSERT(sInnerOrOuterWindowCount > 0);
  12083  --sInnerOrOuterWindowCount;
  12084 }
  12085 
  12086 /* static */
  12087 nsresult nsContentUtils::AnonymizeURI(nsIURI* aURI, nsCString& aAnonymizedURI) {
  12088  MOZ_ASSERT(aURI);
  12089 
  12090  if (aURI->SchemeIs("data")) {
  12091    aAnonymizedURI.Assign("data:..."_ns);
  12092    return NS_OK;
  12093  }
  12094  // Anonymize the URL.
  12095  // Strip the URL of any possible username/password and make it ready to be
  12096  // presented in the UI.
  12097  nsCOMPtr<nsIURI> exposableURI = net::nsIOService::CreateExposableURI(aURI);
  12098  return exposableURI->GetSpec(aAnonymizedURI);
  12099 }
  12100 
  12101 static bool JSONCreator(const char16_t* aBuf, uint32_t aLen, void* aData) {
  12102  nsAString* result = static_cast<nsAString*>(aData);
  12103  return result->Append(aBuf, aLen, fallible);
  12104 }
  12105 
  12106 /* static */
  12107 bool nsContentUtils::StringifyJSON(JSContext* aCx, JS::Handle<JS::Value> aValue,
  12108                                   nsAString& aOutStr, JSONBehavior aBehavior) {
  12109  MOZ_ASSERT(aCx);
  12110  switch (aBehavior) {
  12111    case UndefinedIsNullStringLiteral: {
  12112      aOutStr.Truncate();
  12113      JS::Rooted<JS::Value> value(aCx, aValue);
  12114      return JS_Stringify(aCx, &value, nullptr, JS::NullHandleValue,
  12115                          JSONCreator, &aOutStr);
  12116    }
  12117    case UndefinedIsVoidString: {
  12118      aOutStr.SetIsVoid(true);
  12119      return JS::ToJSON(aCx, aValue, nullptr, JS::NullHandleValue, JSONCreator,
  12120                        &aOutStr);
  12121    }
  12122    default:
  12123      MOZ_ASSERT_UNREACHABLE("Invalid value for aBehavior");
  12124      return false;
  12125  }
  12126 }
  12127 
  12128 /* static */
  12129 bool nsContentUtils::
  12130    HighPriorityEventPendingForTopLevelDocumentBeforeContentfulPaint(
  12131        Document* aDocument) {
  12132  MOZ_ASSERT(XRE_IsContentProcess(),
  12133             "This function only makes sense in content processes");
  12134 
  12135  if (aDocument && !aDocument->IsLoadedAsData()) {
  12136    if (nsPresContext* presContext = FindPresContextForDocument(aDocument)) {
  12137      MOZ_ASSERT(!presContext->IsChrome(),
  12138                 "Should never have a chrome PresContext in a content process");
  12139 
  12140      return !presContext->GetInProcessRootContentDocumentPresContext()
  12141                  ->HadFirstContentfulPaint() &&
  12142             nsThreadManager::MainThreadHasPendingHighPriorityEvents();
  12143    }
  12144  }
  12145  return false;
  12146 }
  12147 
  12148 static nsGlobalWindowInner* GetInnerWindowForGlobal(nsIGlobalObject* aGlobal) {
  12149  NS_ENSURE_TRUE(aGlobal, nullptr);
  12150 
  12151  if (auto* window = aGlobal->GetAsInnerWindow()) {
  12152    return nsGlobalWindowInner::Cast(window);
  12153  }
  12154 
  12155  // When Extensions run content scripts inside a sandbox, it uses
  12156  // sandboxPrototype to make them appear as though they're running in the
  12157  // scope of the page. So when a content script invokes postMessage, it expects
  12158  // the |source| of the received message to be the window set as the
  12159  // sandboxPrototype. This used to work incidentally for unrelated reasons, but
  12160  // now we need to do some special handling to support it.
  12161  JS::Rooted<JSObject*> scope(RootingCx(), aGlobal->GetGlobalJSObject());
  12162  NS_ENSURE_TRUE(scope, nullptr);
  12163 
  12164  if (xpc::IsSandbox(scope)) {
  12165    AutoJSAPI jsapi;
  12166    MOZ_ALWAYS_TRUE(jsapi.Init(scope));
  12167    JSContext* cx = jsapi.cx();
  12168    // Our current Realm on aCx is the sandbox.  Using that for unwrapping
  12169    // makes sense: if the sandbox can unwrap the window, we can use it.
  12170    return xpc::SandboxWindowOrNull(scope, cx);
  12171  }
  12172 
  12173  // The calling window must be holding a reference, so we can return a weak
  12174  // pointer.
  12175  return nsGlobalWindowInner::Cast(aGlobal->GetAsInnerWindow());
  12176 }
  12177 
  12178 /* static */
  12179 nsGlobalWindowInner* nsContentUtils::IncumbentInnerWindow() {
  12180  return GetInnerWindowForGlobal(GetIncumbentGlobal());
  12181 }
  12182 
  12183 /* static */
  12184 nsGlobalWindowInner* nsContentUtils::EntryInnerWindow() {
  12185  return GetInnerWindowForGlobal(GetEntryGlobal());
  12186 }
  12187 
  12188 /* static */
  12189 bool nsContentUtils::IsURIInPrefList(nsIURI* aURI, const char* aPrefName) {
  12190  MOZ_ASSERT(aPrefName);
  12191 
  12192  nsAutoCString list;
  12193  Preferences::GetCString(aPrefName, list);
  12194  ToLowerCase(list);
  12195  return IsURIInList(aURI, list);
  12196 }
  12197 
  12198 /* static */
  12199 bool nsContentUtils::IsURIInList(nsIURI* aURI, const nsCString& aList) {
  12200 #ifdef DEBUG
  12201  nsAutoCString listLowerCase(aList);
  12202  ToLowerCase(listLowerCase);
  12203  MOZ_ASSERT(listLowerCase.Equals(aList),
  12204             "The aList argument should be lower-case");
  12205 #endif
  12206 
  12207  if (!aURI) {
  12208    return false;
  12209  }
  12210 
  12211  if (aList.IsEmpty()) {
  12212    return false;
  12213  }
  12214 
  12215  nsAutoCString scheme;
  12216  aURI->GetScheme(scheme);
  12217  if (!scheme.EqualsLiteral("http") && !scheme.EqualsLiteral("https")) {
  12218    return false;
  12219  }
  12220 
  12221  // The list is comma separated domain list.  Each item may start with "*.".
  12222  // If starts with "*.", it matches any sub-domains.
  12223 
  12224  nsCCharSeparatedTokenizer tokenizer(aList, ',');
  12225  while (tokenizer.hasMoreTokens()) {
  12226    const nsCString token(tokenizer.nextToken());
  12227 
  12228    nsAutoCString host;
  12229    aURI->GetHost(host);
  12230    if (host.IsEmpty()) {
  12231      return false;
  12232    }
  12233    ToLowerCase(host);
  12234 
  12235    for (;;) {
  12236      int32_t index = token.Find(host);
  12237      if (index >= 0 &&
  12238          static_cast<uint32_t>(index) + host.Length() <= token.Length()) {
  12239        // If we found a full match, return true.
  12240        size_t indexAfterHost = index + host.Length();
  12241        if (index == 0 && indexAfterHost == token.Length()) {
  12242          return true;
  12243        }
  12244        // If next character is '/', we need to check the path too.
  12245        // We assume the path in the list means "/foo" + "*".
  12246        if (token[indexAfterHost] == '/') {
  12247          nsDependentCSubstring pathInList(
  12248              token, indexAfterHost,
  12249              static_cast<nsDependentCSubstring::size_type>(-1));
  12250          nsAutoCString filePath;
  12251          aURI->GetFilePath(filePath);
  12252          ToLowerCase(filePath);
  12253          if (StringBeginsWith(filePath, pathInList) &&
  12254              (filePath.Length() == pathInList.Length() ||
  12255               pathInList.EqualsLiteral("/") ||
  12256               filePath[pathInList.Length() - 1] == '/' ||
  12257               filePath[pathInList.Length() - 1] == '?' ||
  12258               filePath[pathInList.Length() - 1] == '#')) {
  12259            return true;
  12260          }
  12261        }
  12262      }
  12263      int32_t startIndexOfCurrentLevel = host[0] == '*' ? 1 : 0;
  12264      int32_t startIndexOfNextLevel =
  12265          host.Find(".", startIndexOfCurrentLevel + 1);
  12266      if (startIndexOfNextLevel <= 0) {
  12267        break;
  12268      }
  12269      host.ReplaceLiteral(0, startIndexOfNextLevel, "*");
  12270    }
  12271  }
  12272 
  12273  return false;
  12274 }
  12275 
  12276 /* static */
  12277 LayoutDeviceIntMargin nsContentUtils::GetWindowSafeAreaInsets(
  12278    nsIScreen* aScreen, const LayoutDeviceIntMargin& aSafeAreaInsets,
  12279    const LayoutDeviceIntRect& aWindowRect) {
  12280  // This calculates safe area insets of window from screen rectangle, window
  12281  // rectangle and safe area insets of screen.
  12282  //
  12283  // +----------------------------------------+ <-- screen
  12284  // |  +-------------------------------+  <------- window
  12285  // |  | window's safe area inset top) |     |
  12286  // +--+-------------------------------+--+  |
  12287  // |  |                               |  |<------ safe area rectangle of
  12288  // |  |                               |  |  |     screen
  12289  // +--+-------------------------------+--+  |
  12290  // |  |window's safe area inset bottom|     |
  12291  // |  +-------------------------------+     |
  12292  // +----------------------------------------+
  12293  //
  12294  LayoutDeviceIntMargin windowSafeAreaInsets;
  12295  if (windowSafeAreaInsets == aSafeAreaInsets) {
  12296    // no safe area insets.
  12297    return windowSafeAreaInsets;
  12298  }
  12299 
  12300  const LayoutDeviceIntRect screenRect = aScreen->GetRect();
  12301  LayoutDeviceIntRect safeAreaRect = screenRect;
  12302  safeAreaRect.Deflate(aSafeAreaInsets);
  12303 
  12304  // FIXME(bug 1754323): This can trigger because the screen rect is not
  12305  // orientation-aware.
  12306  // MOZ_ASSERT(screenRect.Contains(windowRect),
  12307  //            "Screen doesn't contain window rect? Something seems off");
  12308 
  12309  // window's rect of safe area
  12310  safeAreaRect = safeAreaRect.Intersect(aWindowRect);
  12311 
  12312  windowSafeAreaInsets.top = safeAreaRect.y - aWindowRect.y;
  12313  windowSafeAreaInsets.left = safeAreaRect.x - aWindowRect.x;
  12314  windowSafeAreaInsets.right =
  12315      aWindowRect.x + aWindowRect.width - (safeAreaRect.x + safeAreaRect.width);
  12316  windowSafeAreaInsets.bottom = aWindowRect.y + aWindowRect.height -
  12317                                (safeAreaRect.y + safeAreaRect.height);
  12318 
  12319  windowSafeAreaInsets.EnsureAtLeast(LayoutDeviceIntMargin());
  12320  // This shouldn't be needed, but it wallpapers orientation issues, see bug
  12321  // 1754323.
  12322  windowSafeAreaInsets.EnsureAtMost(aSafeAreaInsets);
  12323 
  12324  return windowSafeAreaInsets;
  12325 }
  12326 
  12327 /* static */
  12328 nsContentUtils::SubresourceCacheValidationInfo
  12329 nsContentUtils::GetSubresourceCacheValidationInfo(nsIRequest* aRequest,
  12330                                                  nsIURI* aURI) {
  12331  SubresourceCacheValidationInfo info;
  12332  if (nsCOMPtr<nsICacheInfoChannel> cache = do_QueryInterface(aRequest)) {
  12333    uint32_t value = 0;
  12334    if (NS_SUCCEEDED(cache->GetCacheTokenExpirationTime(&value))) {
  12335      // NOTE: If the cache doesn't expire, the value should be
  12336      // nsICacheEntry::NO_EXPIRATION_TIME.
  12337      info.mExpirationTime.emplace(CacheExpirationTime::ExpireAt(value));
  12338    }
  12339  }
  12340 
  12341  // Determine whether the cache entry must be revalidated when we try to use
  12342  // it. Currently, only HTTP specifies this information...
  12343  if (nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(aRequest)) {
  12344    (void)httpChannel->IsNoStoreResponse(&info.mMustRevalidate);
  12345 
  12346    if (!info.mMustRevalidate) {
  12347      (void)httpChannel->IsNoCacheResponse(&info.mMustRevalidate);
  12348    }
  12349  }
  12350 
  12351  // data: URIs are safe to cache across documents under any circumstance, so we
  12352  // special-case them here even though the channel itself doesn't have any
  12353  // caching policy. Same for chrome:// uris.
  12354  //
  12355  // TODO(emilio): Figure out which other schemes that don't have caching
  12356  // policies are safe to cache. Blobs should be...
  12357  const bool knownCacheable = [&] {
  12358    if (!aURI) {
  12359      return false;
  12360    }
  12361    if (aURI->SchemeIs("data") || aURI->SchemeIs("moz-page-thumb") ||
  12362        aURI->SchemeIs("moz-extension")) {
  12363      return true;
  12364    }
  12365    if (aURI->SchemeIs("chrome") || aURI->SchemeIs("resource")) {
  12366      return !StaticPrefs::nglayout_debug_disable_xul_cache();
  12367    }
  12368    return false;
  12369  }();
  12370 
  12371  if (knownCacheable) {
  12372    MOZ_ASSERT(!info.mExpirationTime);
  12373    MOZ_ASSERT(!info.mMustRevalidate);
  12374    info.mExpirationTime = Some(CacheExpirationTime::Never());
  12375  }
  12376 
  12377  return info;
  12378 }
  12379 
  12380 CacheExpirationTime nsContentUtils::GetSubresourceCacheExpirationTime(
  12381    nsIRequest* aRequest, nsIURI* aURI) {
  12382  auto info = GetSubresourceCacheValidationInfo(aRequest, aURI);
  12383 
  12384  // For now, we never cache entries that we have to revalidate, or whose
  12385  // channel don't support caching.
  12386  if (info.mMustRevalidate || !info.mExpirationTime) {
  12387    return CacheExpirationTime::AlreadyExpired();
  12388  }
  12389  return *info.mExpirationTime;
  12390 }
  12391 
  12392 /* static */
  12393 bool nsContentUtils::ShouldBypassSubResourceCache(Document* aDoc) {
  12394  RefPtr<nsILoadGroup> lg = aDoc->GetDocumentLoadGroup();
  12395  if (!lg) {
  12396    return false;
  12397  }
  12398  nsLoadFlags flags;
  12399  if (NS_FAILED(lg->GetLoadFlags(&flags))) {
  12400    return false;
  12401  }
  12402  return flags & (nsIRequest::LOAD_BYPASS_CACHE |
  12403                  nsICachingChannel::LOAD_BYPASS_LOCAL_CACHE);
  12404 }
  12405 
  12406 nsCString nsContentUtils::TruncatedURLForDisplay(nsIURI* aURL, size_t aMaxLen) {
  12407  nsCString spec;
  12408  if (aURL) {
  12409    aURL->GetSpec(spec);
  12410    spec.Truncate(std::min(aMaxLen, spec.Length()));
  12411  }
  12412  return spec;
  12413 }
  12414 
  12415 /* static */
  12416 nsresult nsContentUtils::AnonymizeId(nsAString& aId,
  12417                                     const nsACString& aOriginKey,
  12418                                     OriginFormat aFormat) {
  12419  MOZ_ASSERT(NS_IsMainThread());
  12420 
  12421  nsresult rv;
  12422  nsCString rawKey;
  12423  if (aFormat == OriginFormat::Base64) {
  12424    rv = Base64Decode(aOriginKey, rawKey);
  12425    NS_ENSURE_SUCCESS(rv, rv);
  12426  } else {
  12427    rawKey = aOriginKey;
  12428  }
  12429 
  12430  HMAC hmac;
  12431  rv = hmac.Begin(
  12432      SEC_OID_SHA256,
  12433      Span(reinterpret_cast<const uint8_t*>(rawKey.get()), rawKey.Length()));
  12434  NS_ENSURE_SUCCESS(rv, rv);
  12435 
  12436  NS_ConvertUTF16toUTF8 id(aId);
  12437  rv = hmac.Update(reinterpret_cast<const uint8_t*>(id.get()), id.Length());
  12438  NS_ENSURE_SUCCESS(rv, rv);
  12439 
  12440  nsTArray<uint8_t> macBytes;
  12441  rv = hmac.End(macBytes);
  12442  NS_ENSURE_SUCCESS(rv, rv);
  12443 
  12444  nsCString macBase64;
  12445  rv = Base64Encode(
  12446      nsDependentCSubstring(reinterpret_cast<const char*>(macBytes.Elements()),
  12447                            macBytes.Length()),
  12448      macBase64);
  12449  NS_ENSURE_SUCCESS(rv, rv);
  12450 
  12451  CopyUTF8toUTF16(macBase64, aId);
  12452  return NS_OK;
  12453 }
  12454 
  12455 void nsContentUtils::RequestGeckoTaskBurst() {
  12456  nsCOMPtr<nsIAppShell> appShell = do_GetService(NS_APPSHELL_CID);
  12457  if (appShell) {
  12458    appShell->GeckoTaskBurst();
  12459  }
  12460 }
  12461 
  12462 nsIContent* nsContentUtils::GetClosestLinkInFlatTree(nsIContent* aContent) {
  12463  for (nsIContent* content = aContent; content;
  12464       content = content->GetFlattenedTreeParent()) {
  12465    if (nsContentUtils::IsDraggableLink(content)) {
  12466      return content;
  12467    }
  12468  }
  12469  return nullptr;
  12470 }
  12471 
  12472 template <TreeKind aKind>
  12473 MOZ_ALWAYS_INLINE const nsINode* GetParent(const nsINode* aNode) {
  12474  if constexpr (aKind == TreeKind::DOM) {
  12475    return aNode->GetParentNode();
  12476  }
  12477  if constexpr (aKind == TreeKind::ShadowIncludingDOM) {
  12478    return aNode->GetParentOrShadowHostNode();
  12479  }
  12480  return aNode->GetFlattenedTreeParentNode();
  12481 }
  12482 
  12483 template <TreeKind aKind>
  12484 Maybe<int32_t> nsContentUtils::GetIndexInParent(const nsINode* aParent,
  12485                                                const nsINode* aNode) {
  12486  Maybe<uint32_t> idx;
  12487  if constexpr (aKind == TreeKind::DOM ||
  12488                aKind == TreeKind::ShadowIncludingDOM) {
  12489    idx = aParent->ComputeIndexOf(aNode);
  12490  } else {
  12491    idx = aParent->ComputeFlatTreeIndexOf(aNode);
  12492  }
  12493 
  12494  if (idx) {
  12495    return idx.map([](auto i) { return AssertedCast<int32_t>(i); });
  12496  }
  12497 
  12498  if constexpr (aKind == TreeKind::ShadowIncludingDOM) {
  12499    if (const auto* sr = ShadowRoot::FromNode(aNode)) {
  12500      return sr->GetHost() == aParent ? Some(-1) : Nothing();
  12501    }
  12502  }
  12503 
  12504  // Handle pseudo-element and anonymous node ordering:
  12505  //   ::marker -> ::before -> regular siblings -> all other NAC -> ::after
  12506  // This matches the order of AllChildrenIterator.
  12507  if (NS_WARN_IF(!aNode->IsRootOfNativeAnonymousSubtree())) {
  12508    // If aNode is mid unbind, we can reach this.
  12509    return Nothing();
  12510  }
  12511 
  12512  if (NS_WARN_IF(aNode->GetParentNode() != aParent)) {
  12513    // We can't be an anon child if not correctly parented.
  12514    return Nothing();
  12515  }
  12516 
  12517  if (aNode->IsGeneratedContentContainerForBackdrop()) {
  12518    return Some(-4);
  12519  }
  12520 
  12521  if (aNode->IsGeneratedContentContainerForMarker()) {
  12522    return Some(-3);
  12523  }
  12524 
  12525  if (aNode->IsGeneratedContentContainerForBefore()) {
  12526    return Some(-2);
  12527  }
  12528 
  12529  AutoTArray<nsIContent*, 8> anonKids;
  12530 
  12531  int32_t siblingCount = aKind == TreeKind::DOM
  12532                             ? aParent->GetChildCount()
  12533                             : FlattenedChildIterator::GetLength(aParent);
  12534 
  12535  MOZ_ASSERT(aParent->MayHaveAnonymousChildren());
  12536  MOZ_ASSERT(aParent->IsContent());
  12537  nsContentUtils::AppendNativeAnonymousChildren(aParent->AsContent(), anonKids,
  12538                                                nsIContent::eAllChildren);
  12539 
  12540  if (aNode->IsGeneratedContentContainerForAfter()) {
  12541    return Some(int32_t(siblingCount + anonKids.Length()));
  12542  }
  12543  auto index = anonKids.IndexOf(aNode);
  12544  if (index == anonKids.NoIndex) {
  12545    MOZ_ASSERT_UNREACHABLE(
  12546        "Missing parent -> child link somehow?"
  12547        "Potentially unstable ordering");
  12548    return Nothing();
  12549  }
  12550  return Some(siblingCount + int32_t(index));
  12551 }
  12552 
  12553 template <TreeKind aTreeKind>
  12554 int32_t nsContentUtils::CompareTreePosition(const nsINode* aNode1,
  12555                                            const nsINode* aNode2,
  12556                                            const nsINode* aCommonAncestor,
  12557                                            NodeIndexCache* aCache) {
  12558  MOZ_ASSERT(aNode1, "aNode1 must not be null");
  12559  MOZ_ASSERT(aNode2, "aNode2 must not be null");
  12560 
  12561  if (NS_WARN_IF(aNode1 == aNode2)) {
  12562    return 0;
  12563  }
  12564 
  12565  // TODO: Maybe handle flat tree too or other common cases?
  12566  if constexpr (aTreeKind == TreeKind::DOM ||
  12567                aTreeKind == TreeKind::ShadowIncludingDOM) {
  12568    if (aNode1->GetNextSibling() == aNode2) {
  12569      return -1;
  12570    }
  12571    if (aNode1->GetPreviousSibling() == aNode2) {
  12572      return 1;
  12573    }
  12574  }
  12575 
  12576  AutoTArray<const nsINode*, 32> node1Ancestors;
  12577  const nsINode* c1;
  12578  for (c1 = aNode1; c1 && c1 != aCommonAncestor;
  12579       c1 = GetParent<aTreeKind>(c1)) {
  12580    node1Ancestors.AppendElement(c1);
  12581  }
  12582  if (!c1 && aCommonAncestor) {
  12583    // So, it turns out aCommonAncestor was not an ancestor of c1. Oops.
  12584    // Never mind. We can continue as if aCommonAncestor was null.
  12585    aCommonAncestor = nullptr;
  12586  }
  12587 
  12588  AutoTArray<const nsINode*, 32> node2Ancestors;
  12589  const nsINode* c2;
  12590  for (c2 = aNode2; c2 && c2 != aCommonAncestor;
  12591       c2 = GetParent<aTreeKind>(c2)) {
  12592    node2Ancestors.AppendElement(c2);
  12593  }
  12594  if (!c2 && aCommonAncestor) {
  12595    // So, it turns out aCommonAncestor was not an ancestor of c2.
  12596    // We need to retry with no common ancestor hint.
  12597    return CompareTreePosition<aTreeKind>(aNode1, aNode2, nullptr, aCache);
  12598  }
  12599 
  12600  int last1 = node1Ancestors.Length() - 1;
  12601  int last2 = node2Ancestors.Length() - 1;
  12602  const nsINode* node1Ancestor = nullptr;
  12603  const nsINode* node2Ancestor = nullptr;
  12604  while (last1 >= 0 && last2 >= 0 &&
  12605         ((node1Ancestor = node1Ancestors.ElementAt(last1)) ==
  12606          (node2Ancestor = node2Ancestors.ElementAt(last2)))) {
  12607    last1--;
  12608    last2--;
  12609  }
  12610 
  12611  if (last1 < 0) {
  12612    if (last2 < 0) {
  12613      NS_ASSERTION(aNode1 == aNode2, "internal error?");
  12614      return 0;
  12615    }
  12616    // aContent1 is an ancestor of aContent2
  12617    return -1;
  12618  }
  12619 
  12620  if (last2 < 0) {
  12621    // aContent2 is an ancestor of aContent1
  12622    return 1;
  12623  }
  12624  // node1Ancestor != node2Ancestor, so they must be siblings with the
  12625  // same parent
  12626  const nsINode* parent = GetParent<aTreeKind>(node1Ancestor);
  12627  if (NS_WARN_IF(!parent)) {  // different documents??
  12628    return 0;
  12629  }
  12630  Maybe<int32_t> index1;
  12631  Maybe<int32_t> index2;
  12632  if (aCache) {
  12633    aCache->ComputeIndicesOf<aTreeKind>(parent, node1Ancestor, node2Ancestor,
  12634                                        index1, index2);
  12635  } else {
  12636    index1 = GetIndexInParent<aTreeKind>(parent, node1Ancestor);
  12637    index2 = GetIndexInParent<aTreeKind>(parent, node2Ancestor);
  12638  }
  12639  if (NS_WARN_IF(index1.isNothing()) || NS_WARN_IF(index2.isNothing())) {
  12640    // This should generally never happen, but can happen mid-unbind or in other
  12641    // edge cases, deal with it somewhat reasonably.
  12642    return 0;
  12643  }
  12644  return static_cast<int32_t>(static_cast<int64_t>(*index1) - *index2);
  12645 }
  12646 
  12647 nsIContent* nsContentUtils::AttachDeclarativeShadowRoot(
  12648    nsIContent* aHost, ShadowRootMode aMode, bool aIsClonable,
  12649    bool aIsSerializable, bool aDelegatesFocus,
  12650    const nsAString& aReferenceTarget) {
  12651  RefPtr<Element> host = mozilla::dom::Element::FromNodeOrNull(aHost);
  12652  if (!host || host->GetShadowRoot()) {
  12653    // https://html.spec.whatwg.org/#parsing-main-inhead:shadow-host
  12654    return nullptr;
  12655  }
  12656 
  12657  ShadowRootInit init;
  12658  init.mMode = aMode;
  12659  init.mDelegatesFocus = aDelegatesFocus;
  12660  init.mSlotAssignment = SlotAssignmentMode::Named;
  12661  init.mClonable = aIsClonable;
  12662  init.mSerializable = aIsSerializable;
  12663 
  12664  RefPtr shadowRoot = host->AttachShadow(init, IgnoreErrors());
  12665  if (shadowRoot) {
  12666    shadowRoot->SetIsDeclarative(
  12667        nsGenericHTMLFormControlElement::ShadowRootDeclarative::Yes);
  12668    // https://html.spec.whatwg.org/#parsing-main-inhead:available-to-element-internals
  12669    shadowRoot->SetAvailableToElementInternals();
  12670    shadowRoot->SetReferenceTarget(aReferenceTarget);
  12671  }
  12672  return shadowRoot;
  12673 }
  12674 
  12675 // https://html.spec.whatwg.org/#the-navigation-must-be-a-replace
  12676 /* static */ bool nsContentUtils::NavigationMustBeAReplace(
  12677    nsIURI& aURI, const Document& aDocument) {
  12678  // The navigation must be a replace, given a URL url and a Document document,
  12679  // if any of the following are true:
  12680  // - url's scheme is "javascript"; or
  12681  // - document's is initial about:blank is true.
  12682  return aURI.SchemeIs("javascript") ||
  12683         (NS_IsAboutBlank(aDocument.GetDocumentURI()) &&
  12684          aDocument.IsInitialDocument());
  12685 }
  12686 
  12687 template int32_t nsContentUtils::CompareTreePosition<TreeKind::DOM>(
  12688    const nsINode*, const nsINode*, const nsINode*, NodeIndexCache*);
  12689 template int32_t nsContentUtils::CompareTreePosition<TreeKind::Flat>(
  12690    const nsINode*, const nsINode*, const nsINode*, NodeIndexCache*);
  12691 template int32_t
  12692 nsContentUtils::CompareTreePosition<TreeKind::ShadowIncludingDOM>(
  12693    const nsINode*, const nsINode*, const nsINode*, NodeIndexCache*);