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"""); 10175 currentPosition++; 10176 flushedUntil = currentPosition; 10177 goto outer; 10178 case '&': 10179 aAppender.Append(aStr.FromTo(flushedUntil, currentPosition)); 10180 aAppender.AppendLiteral(u"&"); 10181 currentPosition++; 10182 flushedUntil = currentPosition; 10183 goto outer; 10184 case 0x00A0: 10185 aAppender.Append(aStr.FromTo(flushedUntil, currentPosition)); 10186 aAppender.AppendLiteral(u" "); 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"<"); 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">"); 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"<"); 10257 currentPosition++; 10258 flushedUntil = currentPosition; 10259 goto outer; 10260 case '>': 10261 aAppender.Append(aStr.FromTo(flushedUntil, currentPosition)); 10262 aAppender.AppendLiteral(u">"); 10263 currentPosition++; 10264 flushedUntil = currentPosition; 10265 goto outer; 10266 case '&': 10267 aAppender.Append(aStr.FromTo(flushedUntil, currentPosition)); 10268 aAppender.AppendLiteral(u"&"); 10269 currentPosition++; 10270 flushedUntil = currentPosition; 10271 goto outer; 10272 case T(0xA0): 10273 aAppender.Append(aStr.FromTo(flushedUntil, currentPosition)); 10274 aAppender.AppendLiteral(u" "); 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("<"), std::size(">"), std::size("&"), 10362 std::size(" ")}) - 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("""), std::size("&"), std::size(" "), 10412 std::size("<"), std::size(">")}) - 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 ¤t->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*);