From da4c7e7ed675c3bf405668739c3012d140856109 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Wed, 15 May 2024 05:34:42 +0200 Subject: Adding upstream version 126.0. Signed-off-by: Daniel Baumann --- dom/animation/Animation.cpp | 30 +- dom/animation/Animation.h | 4 +- dom/animation/AnimationEventDispatcher.cpp | 4 - dom/animation/AnimationEventDispatcher.h | 34 +- dom/animation/test/chrome.toml | 4 - dom/audiochannel/nsIAudioChannelAgent.idl | 4 +- dom/base/AbstractRange.cpp | 204 +++- dom/base/AbstractRange.h | 35 +- dom/base/BodyUtil.cpp | 7 +- dom/base/ChromeUtils.cpp | 18 +- dom/base/ContentIterator.cpp | 283 ++++- dom/base/ContentIterator.h | 50 +- dom/base/ContentProcessMessageManager.cpp | 13 +- dom/base/ContentProcessMessageManager.h | 4 +- dom/base/DirectionalityUtils.cpp | 6 +- dom/base/Document.cpp | 156 ++- dom/base/Document.h | 32 +- dom/base/Element.cpp | 87 +- dom/base/Element.h | 14 +- dom/base/EventSource.cpp | 10 +- dom/base/FragmentDirective.cpp | 879 +++++++++++++++ dom/base/FragmentDirective.h | 111 ++ dom/base/Link.cpp | 160 ++- dom/base/Link.h | 38 +- dom/base/Location.cpp | 24 +- dom/base/PlacesEventCounts.cpp | 58 + dom/base/PlacesEventCounts.h | 39 + dom/base/PlacesObservers.cpp | 22 +- dom/base/PlacesObservers.h | 4 + dom/base/RangeBoundary.h | 29 + dom/base/RangeUtils.cpp | 7 +- dom/base/ScriptableContentIterator.cpp | 16 + dom/base/Selection.cpp | 273 +++-- dom/base/Selection.h | 51 +- dom/base/StaticRange.cpp | 10 + dom/base/StaticRange.h | 16 + dom/base/ThirdPartyUtil.cpp | 14 +- dom/base/UseCounters.conf | 5 - dom/base/crashtests/1697256.html | 3 +- dom/base/crashtests/1887930.html | 7 + dom/base/crashtests/1887963_1.html | 15 + dom/base/crashtests/1887963_2.html | 15 + dom/base/crashtests/1887974.html | 18 + dom/base/crashtests/1890888.html | 13 + dom/base/crashtests/crashtests.list | 5 + dom/base/fragmentdirectives/Cargo.toml | 13 + dom/base/fragmentdirectives/cbindgen.toml | 15 + .../fragmentdirectives/fragment_directive_impl.rs | 342 ++++++ dom/base/fragmentdirectives/lib.rs | 158 +++ dom/base/fragmentdirectives/test.rs | 599 +++++++++++ dom/base/moz.build | 10 + dom/base/nsContentUtils.cpp | 30 +- dom/base/nsContentUtils.h | 12 +- dom/base/nsFocusManager.cpp | 67 +- dom/base/nsFocusManager.h | 16 +- dom/base/nsFrameLoader.cpp | 3 +- dom/base/nsFrameLoaderOwner.cpp | 8 +- dom/base/nsFrameMessageManager.cpp | 5 +- dom/base/nsGlobalWindowInner.cpp | 4 +- dom/base/nsGlobalWindowInner.h | 4 +- dom/base/nsGlobalWindowOuter.cpp | 83 +- dom/base/nsGlobalWindowOuter.h | 4 +- dom/base/nsIContentInlines.h | 32 +- dom/base/nsIEventSourceEventService.idl | 2 +- dom/base/nsINode.cpp | 66 +- dom/base/nsINode.h | 21 +- dom/base/nsIScriptableContentIterator.idl | 7 +- dom/base/nsISelectionController.idl | 7 +- dom/base/nsJSEnvironment.cpp | 67 +- dom/base/nsMimeTypeArray.cpp | 4 + dom/base/nsMimeTypeArray.h | 4 +- dom/base/nsRange.cpp | 406 +++++-- dom/base/nsRange.h | 149 ++- dom/base/nsWrapperCache.h | 6 + dom/base/rust/lib.rs | 2 - dom/base/test/browser.toml | 2 +- .../test/browser_aboutnewtab_process_selection.js | 4 +- dom/base/test/file_window_close.html | 2 +- .../fullscreen/browser_fullscreen-bug-1798219.js | 4 +- .../browser_fullscreen-navigation-history-race.js | 4 +- .../browser_fullscreen-navigation-history.js | 4 +- .../browser_fullscreen-window-open-race.js | 2 +- .../test/fullscreen/file_MozDomFullscreen.html | 2 +- .../fullscreen/file_fullscreen-iframe-middle.html | 2 +- .../fullscreen/file_fullscreen-iframe-top.html | 2 +- dom/base/test/fullscreen/fullscreen_helpers.js | 7 +- .../fullscreen/test_MozDomFullscreen_event.xhtml | 2 +- dom/base/test/jsmodules/chrome.toml | 12 + dom/base/test/jsmodules/export_star_ambiguous.mjs | 1 + .../test/jsmodules/import_ambiguous_export.mjs | 1 + .../jsmodules/import_ambiguous_export_star.mjs | 1 + dom/base/test/jsmodules/import_circular.mjs | 1 + dom/base/test/jsmodules/import_circular_1.mjs | 1 + dom/base/test/jsmodules/import_no_export.mjs | 2 +- .../test/jsmodules/import_no_indirect_export.mjs | 2 +- .../test/jsmodules/importmaps/classic_script.js | 1 + dom/base/test/jsmodules/importmaps/mochitest.toml | 7 + .../test/jsmodules/importmaps/module_chain_1.mjs | 2 + .../test/jsmodules/importmaps/module_chain_2.mjs | 1 + .../module_importMap_with_nonexisting_module.mjs | 4 + .../test_dynamic_importMap_load_completes.html | 33 + ...est_dynamic_importMap_with_external_script.html | 81 ++ .../test_importMap_with_nonexisting_module.html | 25 + dom/base/test/jsmodules/module_a.mjs | 2 + dom/base/test/jsmodules/module_b.mjs | 2 + dom/base/test/jsmodules/module_c.mjs | 3 + dom/base/test/jsmodules/module_d.mjs | 1 + dom/base/test/jsmodules/module_e.mjs | 1 + .../test/jsmodules/test_import_errorMessage.html | 40 +- .../test/jsmodules/test_import_errorMessage2.html | 35 + dom/base/test/mochitest.toml | 2 + dom/base/test/test_bug564863-2.xhtml | 6 +- .../test_content_iterator_subtree_shadow_tree.html | 290 +++++ dom/base/test/test_embed_xorigin_document.html | 5 - dom/base/test/test_range_bounds.html | 48 +- dom/base/test/unit/test_xhr_standalone.js | 5 + dom/base/use_counter_metrics.yaml | 146 +-- dom/bindings/BindingUtils.cpp | 377 +++---- dom/bindings/BindingUtils.h | 204 +++- dom/bindings/Bindings.conf | 14 +- dom/bindings/CallbackObject.cpp | 2 +- dom/bindings/Codegen.py | 345 +++--- dom/bindings/Configuration.py | 91 +- dom/bindings/DOMJSClass.h | 21 +- dom/bindings/JSSlots.h | 17 +- dom/bindings/WebIDLGlobalNameHash.cpp | 25 +- dom/bindings/mozwebidlcodegen/__init__.py | 79 +- .../mozwebidlcodegen/test/test_mozwebidlcodegen.py | 9 +- dom/bindings/nsIScriptError.idl | 12 +- dom/bindings/parser/WebIDL.py | 234 +++- dom/bindings/test/TestBindingHeader.h | 65 +- dom/bindings/test/TestCodeGen.webidl | 18 +- dom/bindings/test/TestExampleGen.webidl | 5 +- dom/bindings/test/test_dom_xrays.html | 23 +- dom/cache/Cache.cpp | 43 +- dom/cache/Cache.h | 18 +- dom/cache/CacheStorage.cpp | 2 +- dom/cache/CacheStorage.h | 2 +- dom/cache/CacheTypes.ipdlh | 2 +- dom/cache/DBSchema.cpp | 9 +- dom/cache/FileUtils.cpp | 2 +- dom/cache/StreamList.cpp | 11 + dom/cache/TypeUtils.cpp | 16 +- dom/cache/TypeUtils.h | 10 +- dom/canvas/CanvasRenderingContext2D.cpp | 95 +- dom/canvas/ClientWebGLContext.cpp | 197 +++- dom/canvas/ClientWebGLContext.h | 34 +- dom/canvas/DrawTargetWebgl.cpp | 18 +- dom/canvas/DrawTargetWebgl.h | 2 + dom/canvas/HostWebGLContext.cpp | 11 + dom/canvas/HostWebGLContext.h | 2 - dom/canvas/PWebGL.ipdl | 4 +- dom/canvas/QueueParamTraits.h | 9 - dom/canvas/TexUnpackBlob.cpp | 2 + dom/canvas/TiedFields.h | 52 +- dom/canvas/WebGL2Context.h | 6 +- dom/canvas/WebGL2ContextState.cpp | 4 - dom/canvas/WebGL2ContextSync.cpp | 57 +- dom/canvas/WebGLChild.cpp | 7 + dom/canvas/WebGLChild.h | 1 + dom/canvas/WebGLContext.cpp | 2 + dom/canvas/WebGLContext.h | 14 +- dom/canvas/WebGLContextState.cpp | 77 +- dom/canvas/WebGLFramebuffer.cpp | 29 +- dom/canvas/WebGLIpdl.h | 72 +- dom/canvas/WebGLParent.cpp | 9 - dom/canvas/WebGLParent.h | 1 - dom/canvas/WebGLSync.cpp | 35 +- dom/canvas/WebGLSync.h | 37 +- dom/canvas/WebGLTypes.h | 53 + dom/canvas/test/crossorigin/mochitest.toml | 4 + dom/canvas/test/mochitest.toml | 6 +- dom/canvas/test/test_imagebitmap.html | 4 +- dom/canvas/test/test_imagebitmap_cropping.html | 4 +- .../checkout/conformance/canvas/00_test_list.txt | 2 + .../canvas/drawingbuffer-storage-test.html | 27 + .../conformance/extensions/00_test_list.txt | 6 + .../conformance/extensions/ext-clip-control.html | 185 ++++ .../conformance/extensions/ext-depth-clamp.html | 168 +++ .../extensions/ext-disjoint-timer-query.html | 59 + .../extensions/ext-polygon-offset-clamp.html | 172 +++ .../checkout/conformance/extensions/ext-sRGB.html | 34 + .../extensions/ext-texture-compression-rgtc.html | 8 +- .../ext-texture-mirror-clamp-to-edge.html | 217 ++++ .../extensions/khr-parallel-shader-compile.html | 26 + .../extensions/oes-texture-float-with-video.html | 1 - .../conformance/extensions/oes-texture-float.html | 65 ++ .../oes-texture-half-float-with-video.html | 1 - .../extensions/webgl-blend-func-extended.html | 26 + .../conformance/extensions/webgl-polygon-mode.html | 185 ++++ .../conformance/reading/read-pixels-test.html | 37 + .../textures/misc/exif-orientation.html | 44 +- .../misc/tex-video-using-tex-unit-non-zero.html | 2 - .../textures/misc/texture-npot-video.html | 1 - .../textures/misc/texture-srgb-upload.html | 16 +- .../textures/misc/texture-upload-size.html | 1 - .../conformance/uniforms/uniform-location.html | 4 + .../checkout/conformance2/00_test_list.txt | 1 + .../checkout/conformance2/canvas/00_test_list.txt | 1 + .../canvas/drawingbuffer-storage-test.html | 27 + .../conformance2/extensions/00_test_list.txt | 11 + .../extensions/ext-conservative-depth.html | 145 +++ .../ext-disjoint-timer-query-webgl2.html | 58 + .../conformance2/extensions/ext-render-snorm.html | 201 ++++ .../nv-shader-noperspective-interpolation.html | 251 +++++ .../extensions/oes-sample-variables.html | 474 ++++++++ .../oes-shader-multisample-interpolation.html | 313 ++++++ .../extensions/webgl-blend-func-extended.html | 26 + .../extensions/webgl-clip-cull-distance.html | 475 +++++++++ .../extensions/webgl-provoking-vertex.html | 165 +++ .../extensions/webgl-render-shared-exponent.html | 251 +++++ .../webgl-shader-pixel-local-storage.html | 445 ++++++++ .../extensions/webgl-stencil-texturing.html | 279 +++++ .../checkout/conformance2/glsl3/00_test_list.txt | 1 + .../checkout/conformance2/glsl3/texture-bias.html | 146 +++ .../checkout/conformance2/query/00_test_list.txt | 1 + .../query/occlusion-query-scissor.html | 117 ++ .../framebuffer-object-attachment.html | 112 +- .../renderbuffers/framebuffer-texture-layer.html | 31 + .../conformance2/rendering/00_test_list.txt | 1 - .../rendering/builtin-vert-attribs.html | 408 ------- .../conformance2/textures/misc/00_test_list.txt | 1 + .../textures/misc/tex-image-10bpc.html | 66 ++ .../tex-image-with-bad-args-from-dom-elements.html | 1 - .../textures/misc/tex-new-formats.html | 30 + .../transform_feedback/simultaneous_binding.html | 11 + .../transform_feedback/transform_feedback.html | 66 ++ .../checkout/conformance2/wasm/00_test_list.txt | 12 + .../wasm/bufferdata-16gb-wasm-memory.html | 51 + .../wasm/bufferdata-4gb-wasm-memory.html | 51 + .../wasm/buffersubdata-16gb-wasm-memory.html | 52 + .../wasm/buffersubdata-4gb-wasm-memory.html | 52 + .../wasm/getbuffersubdata-16gb-wasm-memory.html | 48 + .../wasm/getbuffersubdata-4gb-wasm-memory.html | 48 + .../wasm/readpixels-16gb-wasm-memory.html | 51 + .../wasm/readpixels-4gb-wasm-memory.html | 50 + .../wasm/teximage2d-16gb-wasm-memory.html | 85 ++ .../wasm/teximage2d-4gb-wasm-memory.html | 85 ++ .../wasm/texsubimage2d-16gb-wasm-memory.html | 86 ++ .../wasm/texsubimage2d-4gb-wasm-memory.html | 86 ++ .../checkout/deqp/framework/common/tcuTestCase.js | 29 +- .../checkout/extra/tex-image-with-video-test.js | 1 - .../test/webgl-conf/checkout/js/js-test-pre.js | 21 +- .../checkout/js/tests/compressed-texture-utils.js | 6 +- .../js/tests/drawingbuffer-storage-test.js | 275 +++++ .../js/tests/ext-color-buffer-half-float.js | 68 ++ ...nd-sub-image-2d-with-image-bitmap-from-video.js | 1 - .../tests/tex-image-and-sub-image-2d-with-video.js | 1 - ...nd-sub-image-3d-with-image-bitmap-from-video.js | 1 - .../tests/tex-image-and-sub-image-3d-with-video.js | 1 - .../checkout/js/tests/webgl-blend-func-extended.js | 548 ++++++++++ .../webgl-conf/checkout/js/webgl-test-harness.js | 45 +- .../webgl-conf/checkout/py/lint/lint.allowlist | 3 +- .../exif-orientation-test-6-90-ccw.jpg | Bin 4841 -> 0 bytes .../exif-orientation-test-6-90-cw.jpg | Bin 0 -> 4840 bytes .../exif-orientation-test-8-90-ccw.jpg | Bin 0 -> 4841 bytes .../exif-orientation-test-8-90-cw.jpg | Bin 4840 -> 0 bytes .../resources/exif-orientation-test-6-90-ccw.jpg | Bin 4901 -> 0 bytes .../resources/exif-orientation-test-6-90-cw.jpg | Bin 0 -> 4901 bytes .../resources/exif-orientation-test-8-90-ccw.jpg | Bin 0 -> 4900 bytes .../resources/exif-orientation-test-8-90-cw.jpg | Bin 4900 -> 0 bytes .../checkout/resources/npot-video.theora.ogv | Bin 24630 -> 0 bytes .../resources/red-gradient-8x1-10bit-untagged.png | Bin 0 -> 90 bytes .../checkout/resources/red-green.theora.ogv | Bin 10292 -> 0 bytes .../test/webgl-conf/checkout/test-guidelines.md | 6 +- dom/canvas/test/webgl-conf/cherry_picks.txt | 59 +- .../test/webgl-conf/generated-mochitest.toml | 182 +++- ...mance2__canvas__drawingbuffer-storage-test.html | 17 + ...mance2__extensions__ext-conservative-depth.html | 17 + ...conformance2__extensions__ext-render-snorm.html | 17 + ...ons__nv-shader-noperspective-interpolation.html | 17 + ...ormance2__extensions__oes-sample-variables.html | 17 + ...ions__oes-shader-multisample-interpolation.html | 17 + ...ce2__extensions__webgl-blend-func-extended.html | 17 + ...nce2__extensions__webgl-clip-cull-distance.html | 17 + ...mance2__extensions__webgl-provoking-vertex.html | 17 + ...__extensions__webgl-render-shared-exponent.html | 17 + ...tensions__webgl-shader-pixel-local-storage.html | 17 + ...ance2__extensions__webgl-stencil-texturing.html | 17 + .../test_2_conformance2__glsl3__texture-bias.html | 17 + ...nformance2__query__occlusion-query-scissor.html | 17 + ...formance2__rendering__builtin-vert-attribs.html | 17 - ...formance2__textures__misc__tex-image-10bpc.html | 17 + ...rmance2__wasm__bufferdata-16gb-wasm-memory.html | 17 + ...ormance2__wasm__bufferdata-4gb-wasm-memory.html | 17 + ...nce2__wasm__buffersubdata-16gb-wasm-memory.html | 17 + ...ance2__wasm__buffersubdata-4gb-wasm-memory.html | 17 + ...2__wasm__getbuffersubdata-16gb-wasm-memory.html | 17 + ...e2__wasm__getbuffersubdata-4gb-wasm-memory.html | 17 + ...rmance2__wasm__readpixels-16gb-wasm-memory.html | 17 + ...ormance2__wasm__readpixels-4gb-wasm-memory.html | 17 + ...rmance2__wasm__teximage2d-16gb-wasm-memory.html | 17 + ...ormance2__wasm__teximage2d-4gb-wasm-memory.html | 17 + ...nce2__wasm__texsubimage2d-16gb-wasm-memory.html | 17 + ...ance2__wasm__texsubimage2d-4gb-wasm-memory.html | 17 + ..._conformance__extensions__ext-clip-control.html | 17 + ...2_conformance__extensions__ext-depth-clamp.html | 17 + ...ance__extensions__ext-polygon-offset-clamp.html | 17 + ...tensions__ext-texture-mirror-clamp-to-edge.html | 17 + ...onformance__extensions__webgl-polygon-mode.html | 17 + ..._conformance__extensions__ext-clip-control.html | 17 + ...t_conformance__extensions__ext-depth-clamp.html | 17 + ...ance__extensions__ext-polygon-offset-clamp.html | 17 + ...tensions__ext-texture-mirror-clamp-to-edge.html | 17 + ...nce__extensions__webgl-blend-func-extended.html | 17 + ...onformance__extensions__webgl-polygon-mode.html | 17 + dom/canvas/test/webgl-conf/mochitest-errata.toml | 62 ++ dom/canvas/test/webgl-conf/moz.yaml | 4 +- dom/canvas/test/webgl-mochitest/mochitest.toml | 13 +- .../test/webgl-mochitest/red-green.theora.ogv | Bin 10292 -> 0 bytes .../test_video_fastpath_theora.html | 21 - dom/chrome-webidl/ChromeUtils.webidl | 1 + dom/chrome-webidl/InspectorUtils.webidl | 21 +- dom/chrome-webidl/PlacesObservers.webidl | 8 + dom/chrome-webidl/StripOnShareRule.webidl | 4 +- dom/chrome-webidl/UniFFI.webidl | 2 +- .../identity/IdentityCredential.cpp | 15 +- .../identity/IdentityNetworkHelpers.h | 2 +- dom/events/Clipboard.cpp | 5 + dom/events/EventDispatcher.cpp | 29 +- dom/events/EventListenerManager.cpp | 41 +- dom/events/EventNameList.h | 5 +- dom/events/EventStateManager.cpp | 243 ++++- dom/events/EventStateManager.h | 10 +- dom/events/InvokeEvent.cpp | 8 +- dom/events/PointerEventHandler.cpp | 50 +- dom/events/PointerEventHandler.h | 12 +- dom/events/TextEvent.cpp | 68 ++ dom/events/TextEvent.h | 46 + dom/events/moz.build | 2 + dom/events/test/clipboard/mochitest.toml | 7 + .../test/clipboard/test_async_clipboard.xhtml | 10 +- .../test_async_clipboard_datatransfer.html | 89 ++ dom/events/test/mochitest.toml | 2 - dom/events/test/pointerevents/mochitest.toml | 15 - ...ge-touch-action-onpointerdown_touch-manual.html | 39 - .../test_wpt_pointerevent_constructor.html | 26 - ...st_wpt_pointerevent_pointerId_scope-manual.html | 27 - ...ge-touch-action-onpointerdown_touch-manual.html | 135 --- .../wpt/pointerevent_constructor.html | 106 -- .../wpt/pointerevent_pointerId_scope-manual.html | 82 -- dom/events/test/test_all_synthetic_events.html | 10 +- dom/events/test/test_eventctors.html | 22 + dom/events/test/test_marquee_events.html | 31 - dom/fetch/Fetch.cpp | 2 +- dom/fetch/Fetch.h | 4 +- dom/fetch/FetchDriver.cpp | 2 +- dom/fetch/FetchTypes.ipdlh | 2 +- dom/fetch/FetchUtil.cpp | 9 +- dom/fetch/InternalRequest.cpp | 4 +- dom/fetch/InternalRequest.h | 20 +- dom/fetch/Request.cpp | 130 +-- dom/fetch/Request.h | 18 +- dom/fetch/Response.cpp | 32 +- dom/fetch/Response.h | 6 +- dom/fetch/tests/browser.toml | 5 + .../tests/browser_default_credentialless_fetch.js | 46 + dom/file/ipc/RemoteLazyInputStreamThread.cpp | 2 +- dom/file/uri/BlobURLProtocolHandler.cpp | 2 - dom/fs/api/FileSystemManager.cpp | 2 +- dom/fs/api/FileSystemWritableFileStream.cpp | 43 +- dom/fs/child/FileSystemShutdownBlocker.cpp | 14 + dom/fs/include/fs/FileSystemShutdownBlocker.h | 2 + .../FileSystemDatabaseManagerVersion001.cpp | 2 +- .../FileSystemDatabaseManagerVersion001.h | 2 +- .../FileSystemDatabaseManagerVersion002.cpp | 3 + dom/fs/test/common/test_writableFileStream.js | 106 ++ dom/fs/test/crashtests/1874334-2.html | 8 + dom/fs/test/crashtests/1874334.html | 16 + dom/fs/test/crashtests/crashtests.list | 3 + dom/fs/test/crashtests/sw1874334-2.js | 12 + dom/html/CustomStateSet.cpp | 55 +- dom/html/CustomStateSet.h | 2 - dom/html/HTMLAnchorElement.h | 8 +- dom/html/HTMLAreaElement.cpp | 2 - dom/html/HTMLAreaElement.h | 12 +- dom/html/HTMLInputElement.cpp | 2 +- dom/html/HTMLMarqueeElement.cpp | 18 - dom/html/HTMLMarqueeElement.h | 6 - dom/html/HTMLMediaElement.cpp | 35 +- dom/html/HTMLMediaElement.h | 4 +- dom/html/TextControlState.cpp | 3 +- dom/html/nsGenericHTMLElement.cpp | 39 +- dom/html/nsGenericHTMLElement.h | 8 +- dom/html/test/browser_ImageDocument_svg_zoom.js | 2 +- dom/html/test/browser_bug436200.js | 7 - ..._input_password_click_show_password_button.html | 12 +- dom/html/test/test_document-element-inserted.html | 2 +- dom/indexedDB/ActorsChild.cpp | 12 +- dom/indexedDB/ActorsChild.h | 14 +- dom/indexedDB/ActorsParent.cpp | 1128 +++++++++++++++----- dom/indexedDB/ActorsParentCommon.cpp | 2 +- dom/indexedDB/DatabaseFileManager.h | 14 +- dom/indexedDB/IDBCursor.cpp | 8 +- dom/indexedDB/IDBDatabase.cpp | 26 +- dom/indexedDB/IDBDatabase.h | 3 +- dom/indexedDB/IDBFactory.cpp | 236 ++-- dom/indexedDB/IDBFactory.h | 9 + dom/indexedDB/IDBObjectStore.cpp | 29 +- dom/indexedDB/IDBObjectStore.h | 12 +- dom/indexedDB/IDBTransaction.cpp | 73 +- dom/indexedDB/IDBTransaction.h | 32 +- dom/indexedDB/IndexedDatabase.cpp | 3 +- dom/indexedDB/IndexedDatabaseManager.cpp | 84 +- dom/indexedDB/IndexedDatabaseManager.h | 8 + dom/indexedDB/Key.cpp | 52 +- dom/indexedDB/Key.h | 12 +- dom/indexedDB/KeyPath.cpp | 16 +- dom/indexedDB/KeyPath.h | 7 +- dom/indexedDB/PBackgroundIDBCursor.ipdl | 4 +- dom/indexedDB/PBackgroundIDBDatabase.ipdl | 5 +- dom/indexedDB/PBackgroundIDBFactory.ipdl | 10 + dom/indexedDB/PBackgroundIDBTransaction.ipdl | 4 +- .../PBackgroundIDBVersionChangeTransaction.ipdl | 4 +- dom/indexedDB/SchemaUpgrades.cpp | 2 +- dom/indexedDB/SerializationHelpers.h | 11 +- dom/indexedDB/test/mochitest-common.toml | 3 + dom/indexedDB/test/test_open_and_databases.html | 19 + dom/indexedDB/test/test_third_party.html | 8 +- .../test/unit/test_connection_idle_maintenance.js | 2 +- .../unit/test_connection_idle_maintenance_stop.js | 2 +- dom/indexedDB/test/unit/test_open_and_databases.js | 76 ++ .../test/unit/xpcshell-head-parent-process.js | 8 +- dom/indexedDB/test/unit/xpcshell-shared.toml | 2 + dom/interfaces/base/nsIBrowser.idl | 2 +- dom/interfaces/base/nsIDOMWindowUtils.idl | 26 +- dom/interfaces/base/nsIServiceWorkerManager.idl | 8 +- .../base/nsITextInputProcessorCallback.idl | 2 +- .../payments/nsIPaymentActionResponse.idl | 8 +- dom/interfaces/push/moz.build | 2 +- dom/interfaces/push/nsIPushService.idl | 8 +- .../security/nsIContentSecurityPolicy.idl | 14 +- dom/interfaces/security/nsIReferrerInfo.idl | 2 +- dom/interfaces/storage/nsIDOMStorageManager.idl | 8 +- dom/ipc/BrowserChild.cpp | 62 +- dom/ipc/BrowserChild.h | 15 +- dom/ipc/BrowserParent.cpp | 2 - dom/ipc/CoalescedTouchData.cpp | 6 +- dom/ipc/ContentChild.cpp | 16 +- dom/ipc/ContentChild.h | 3 +- dom/ipc/ContentParent.cpp | 21 +- dom/ipc/ContentParent.h | 9 +- dom/ipc/ContentParent_NotifyUpdatedDictionaries.h | 17 + dom/ipc/DOMTypes.ipdlh | 1 + dom/ipc/MMPrinter.cpp | 29 +- dom/ipc/MMPrinter.h | 33 +- dom/ipc/PBrowser.ipdl | 29 +- dom/ipc/PContent.ipdl | 14 +- dom/ipc/ProcessIsolation.cpp | 5 +- dom/ipc/StructuredCloneData.h | 22 +- dom/ipc/WindowGlobalChild.cpp | 35 +- dom/ipc/WindowGlobalParent.cpp | 37 +- dom/ipc/jsactor/JSActor.cpp | 10 +- dom/ipc/moz.build | 1 + dom/ipc/nsIHangReport.idl | 2 +- dom/ipc/nsILoginDetectionService.idl | 2 +- .../tests/JSWindowActor/browser_event_listener.js | 10 +- dom/ipc/tests/browser.toml | 2 + dom/ipc/tests/browser_isactiveintab.js | 138 +++ dom/locales/en-US/chrome/dom/dom.properties | 2 +- dom/localstorage/ActorsParent.cpp | 4 +- dom/localstorage/LocalStorageManager2.cpp | 2 +- dom/localstorage/PBackgroundLSDatabase.ipdl | 2 +- dom/localstorage/SnappyUtils.cpp | 2 +- dom/localstorage/nsILocalStorageManager.idl | 2 +- dom/manifest/Manifest.sys.mjs | 37 +- dom/manifest/test/browser_Manifest_install.js | 45 +- dom/media/AudioConverter.h | 1 - dom/media/AudioPacketizer.h | 9 +- dom/media/CubebUtils.cpp | 56 +- dom/media/DOMMediaStream.cpp | 9 + dom/media/DOMMediaStream.h | 10 +- dom/media/DecoderTraits.cpp | 5 +- dom/media/DeviceInputTrack.cpp | 24 +- dom/media/DeviceInputTrack.h | 19 +- dom/media/EncoderTraits.h | 5 +- dom/media/ExternalEngineStateMachine.cpp | 153 ++- dom/media/ExternalEngineStateMachine.h | 20 +- dom/media/GraphDriver.cpp | 12 +- dom/media/MediaData.cpp | 9 + dom/media/MediaData.h | 8 +- dom/media/MediaDecoder.cpp | 41 +- dom/media/MediaDecoder.h | 2 + dom/media/MediaDecoderStateMachine.cpp | 33 +- dom/media/MediaDecoderStateMachine.h | 6 + dom/media/MediaDecoderStateMachineBase.h | 3 +- dom/media/MediaFormatReader.cpp | 46 +- dom/media/MediaFormatReader.h | 19 +- dom/media/MediaInfo.h | 8 +- dom/media/MediaManager.cpp | 18 +- dom/media/MediaRecorder.cpp | 12 +- dom/media/MediaStreamWindowCapturer.cpp | 16 +- dom/media/MediaStreamWindowCapturer.h | 5 +- dom/media/MediaTrackGraph.cpp | 3 + dom/media/MediaTrackGraph.h | 4 + dom/media/MediaTrackGraphImpl.h | 9 +- dom/media/PeerConnection.sys.mjs | 59 + dom/media/TimedPacketizer.h | 73 ++ dom/media/VideoUtils.cpp | 7 +- dom/media/VideoUtils.h | 28 - dom/media/autoplay/test/mochitest/mochitest.toml | 2 +- ...autoplay_policy_play_before_loadedmetadata.html | 4 +- dom/media/eme/EMEUtils.cpp | 79 +- dom/media/eme/EMEUtils.h | 12 +- dom/media/eme/KeySystemConfig.cpp | 363 ++++--- dom/media/eme/KeySystemConfig.h | 36 +- dom/media/eme/MediaKeySession.cpp | 67 +- dom/media/eme/MediaKeySession.h | 6 + dom/media/eme/MediaKeySystemAccess.cpp | 196 ++-- dom/media/eme/MediaKeySystemAccess.h | 16 +- dom/media/eme/MediaKeySystemAccessManager.cpp | 67 +- dom/media/eme/MediaKeySystemAccessManager.h | 2 + dom/media/eme/mediafoundation/WMFCDMImpl.cpp | 155 +-- dom/media/eme/mediafoundation/WMFCDMImpl.h | 24 +- dom/media/eme/mediafoundation/WMFCDMProxy.cpp | 2 +- dom/media/eme/metrics.yaml | 42 + dom/media/gmp/ChromiumCDMChild.cpp | 2 +- dom/media/gmp/ChromiumCDMProxy.cpp | 9 +- dom/media/gmp/GMPChild.cpp | 11 +- dom/media/gmp/moz.build | 2 + .../gmp/mozIGeckoMediaPluginChromeService.idl | 4 +- .../widevine-adapter/content_decryption_module.h | 1 - dom/media/gtest/TestAudioInputProcessing.cpp | 127 ++- dom/media/gtest/TestAudioPacketizer.cpp | 51 +- dom/media/gtest/TestAudioTrackGraph.cpp | 311 ++++-- dom/media/gtest/TestDeviceInputTrack.cpp | 8 +- dom/media/gtest/TestMediaDataEncoder.cpp | 125 +-- dom/media/hls/HLSDecoder.cpp | 46 +- dom/media/hls/HLSDecoder.h | 10 +- dom/media/hls/metrics.yaml | 70 ++ dom/media/ipc/MFCDMChild.cpp | 34 +- dom/media/ipc/MFCDMChild.h | 3 +- dom/media/ipc/MFCDMParent.cpp | 229 ++-- dom/media/ipc/MFCDMParent.h | 12 +- dom/media/ipc/MFMediaEngineChild.cpp | 33 +- dom/media/ipc/MFMediaEngineChild.h | 9 +- dom/media/ipc/MFMediaEngineParent.cpp | 15 +- dom/media/ipc/MFMediaEngineParent.h | 2 +- dom/media/ipc/MediaIPCUtils.h | 3 +- dom/media/ipc/PMFCDM.ipdl | 11 +- dom/media/ipc/PMFMediaEngine.ipdl | 2 +- dom/media/ipc/RemoteMediaDataDecoder.cpp | 17 +- dom/media/ipc/RemoteMediaDataDecoder.h | 16 +- dom/media/mediacontrol/ContentMediaController.cpp | 6 +- .../mediacontrol/ContentPlaybackController.cpp | 4 +- dom/media/mediacontrol/MediaControlKeyManager.cpp | 2 +- dom/media/mediacontrol/MediaControlUtils.h | 56 +- dom/media/mediacontrol/MediaStatusManager.cpp | 10 +- dom/media/mediacontrol/tests/browser/browser.toml | 1 - .../tests/browser/file_error_media.html | 2 +- dom/media/metrics.yaml | 28 +- dom/media/moz.build | 1 + dom/media/ogg/OggDecoder.cpp | 10 +- dom/media/platforms/EncoderConfig.cpp | 27 + dom/media/platforms/EncoderConfig.h | 190 ++++ dom/media/platforms/PlatformEncoderModule.cpp | 33 +- dom/media/platforms/PlatformEncoderModule.h | 193 +--- .../platforms/agnostic/AgnosticDecoderModule.cpp | 12 +- dom/media/platforms/agnostic/bytestreams/H264.cpp | 3 +- dom/media/platforms/agnostic/bytestreams/H264.h | 36 +- dom/media/platforms/apple/AppleDecoderModule.cpp | 49 +- dom/media/platforms/apple/AppleDecoderModule.h | 1 + dom/media/platforms/apple/AppleVTDecoder.cpp | 32 +- dom/media/platforms/apple/AppleVTDecoder.h | 2 +- dom/media/platforms/apple/AppleVTEncoder.cpp | 56 +- dom/media/platforms/apple/AppleVTEncoder.h | 5 +- dom/media/platforms/ffmpeg/FFmpegAudioEncoder.cpp | 458 ++++++++ dom/media/platforms/ffmpeg/FFmpegAudioEncoder.h | 70 ++ dom/media/platforms/ffmpeg/FFmpegDataDecoder.cpp | 20 +- dom/media/platforms/ffmpeg/FFmpegDataEncoder.cpp | 495 +++++++++ dom/media/platforms/ffmpeg/FFmpegDataEncoder.h | 107 ++ dom/media/platforms/ffmpeg/FFmpegEncoderModule.cpp | 18 + dom/media/platforms/ffmpeg/FFmpegEncoderModule.h | 4 + dom/media/platforms/ffmpeg/FFmpegLibWrapper.cpp | 3 + dom/media/platforms/ffmpeg/FFmpegLibWrapper.h | 5 + dom/media/platforms/ffmpeg/FFmpegLog.h | 11 +- dom/media/platforms/ffmpeg/FFmpegUtils.cpp | 23 + dom/media/platforms/ffmpeg/FFmpegUtils.h | 56 + dom/media/platforms/ffmpeg/FFmpegVideoDecoder.cpp | 8 +- dom/media/platforms/ffmpeg/FFmpegVideoEncoder.cpp | 571 +--------- dom/media/platforms/ffmpeg/FFmpegVideoEncoder.h | 73 +- dom/media/platforms/ffmpeg/ffmpeg57/moz.build | 2 + dom/media/platforms/ffmpeg/ffmpeg58/moz.build | 2 + dom/media/platforms/ffmpeg/ffmpeg59/moz.build | 2 + dom/media/platforms/ffmpeg/ffmpeg60/moz.build | 2 + dom/media/platforms/ffmpeg/ffvpx/moz.build | 3 + dom/media/platforms/ffmpeg/libav53/moz.build | 2 + dom/media/platforms/ffmpeg/libav54/moz.build | 2 + dom/media/platforms/ffmpeg/libav55/moz.build | 2 + dom/media/platforms/ffmpeg/moz.build | 4 +- dom/media/platforms/moz.build | 2 + dom/media/platforms/wmf/DXVA2Manager.cpp | 73 +- dom/media/platforms/wmf/MFCDMSession.cpp | 3 +- dom/media/platforms/wmf/MFMediaEngineStream.cpp | 9 + dom/media/platforms/wmf/WMFDataEncoderUtils.h | 1 - dom/media/platforms/wmf/WMFEncoderModule.cpp | 7 + dom/media/platforms/wmf/WMFUtils.cpp | 4 +- .../platforms/wrappers/MediaChangeMonitor.cpp | 7 + dom/media/platforms/wrappers/MediaChangeMonitor.h | 30 +- dom/media/test/320x240.ogv | Bin 28942 -> 0 bytes dom/media/test/320x240.ogv^headers^ | 1 - dom/media/test/320x240.webm | Bin 0 -> 50163 bytes dom/media/test/320x240.webm^headers^ | 1 + dom/media/test/448636.ogv | Bin 7799 -> 0 bytes dom/media/test/bogus.ogv | 45 - .../browser_glean_first_frame_loaded_time.js | 12 +- dom/media/test/browser/head.js | 118 +- dom/media/test/bug482461-theora.ogv | Bin 280904 -> 0 bytes dom/media/test/bug482461.ogv | Bin 305785 -> 0 bytes dom/media/test/bug495129.ogv | Bin 122207 -> 0 bytes dom/media/test/bug498380.ogv | Bin 65535 -> 0 bytes dom/media/test/bug498855-1.ogv | Bin 20480 -> 0 bytes dom/media/test/bug498855-2.ogv | Bin 20480 -> 0 bytes dom/media/test/bug498855-3.ogv | Bin 20480 -> 0 bytes dom/media/test/bug499519.ogv | Bin 20480 -> 0 bytes dom/media/test/bug500311.ogv | Bin 55834 -> 0 bytes dom/media/test/bug504613.ogv | Bin 35000 -> 0 bytes dom/media/test/bug504644.ogv | Bin 131114 -> 0 bytes dom/media/test/bug504843.ogv | Bin 65536 -> 0 bytes dom/media/test/bug506094.ogv | Bin 8195 -> 0 bytes dom/media/test/bug516323.indexed.ogv | Bin 162193 -> 0 bytes dom/media/test/bug516323.ogv | Bin 161789 -> 0 bytes dom/media/test/bug523816.ogv | Bin 40585 -> 0 bytes dom/media/test/bug556821.ogv | Bin 196608 -> 0 bytes dom/media/test/bug557094.ogv | Bin 76966 -> 0 bytes dom/media/test/can_play_type_ogg.js | 21 +- dom/media/test/chained-video.ogv | Bin 57906 -> 0 bytes dom/media/test/crashtests/576612-1.html | 15 - dom/media/test/make-headers.sh | 4 +- dom/media/test/manifest.js | 283 +---- dom/media/test/mochitest.toml | 67 +- dom/media/test/mochitest_background_video.toml | 68 -- dom/media/test/mochitest_bugs.toml | 68 -- dom/media/test/mochitest_compat.toml | 72 +- dom/media/test/mochitest_eme.toml | 68 -- dom/media/test/mochitest_eme_compat.toml | 68 -- dom/media/test/mochitest_media_recorder.toml | 68 +- dom/media/test/mochitest_seek.toml | 68 -- dom/media/test/mochitest_stream.toml | 76 +- dom/media/test/multiple-bos.ogg | Bin 33045 -> 0 bytes dom/media/test/reftest/color_quads/reftest.list | 2 +- dom/media/test/reftest/reftest.list | 6 +- dom/media/test/sample-fisbone-skeleton4.ogv | Bin 8747 -> 0 bytes dom/media/test/sample-fisbone-wrong-header.ogv | Bin 8703 -> 0 bytes dom/media/test/seek-short.ogv | Bin 79921 -> 0 bytes dom/media/test/seek.ogv | Bin 285310 -> 0 bytes dom/media/test/seekLies.sjs | 4 +- dom/media/test/short-video.ogv | Bin 16049 -> 0 bytes dom/media/test/test_bug1248229.html | 2 +- dom/media/test/test_closing_connections.html | 2 +- dom/media/test/test_decoder_disable.html | 4 +- dom/media/test/test_error_in_video_document.html | 9 +- dom/media/test/test_load_same_resource.html | 2 +- dom/media/test/test_media_selection.html | 2 +- dom/media/test/test_preload_suspend.html | 2 +- dom/media/test/test_standalone.html | 63 +- dom/media/test/test_streams_element_capture.html | 5 + .../test/test_streams_element_capture_twice.html | 2 +- dom/media/test/test_videoDocumentTitle.html | 4 +- .../test_video_stats_resistfingerprinting.html | 1 - dom/media/utils/PerformanceRecorder.cpp | 2 + dom/media/utils/PerformanceRecorder.h | 1 + dom/media/utils/TelemetryProbesReporter.cpp | 71 +- dom/media/utils/TelemetryProbesReporter.h | 22 +- dom/media/webaudio/MediaStreamAudioSourceNode.cpp | 18 +- dom/media/webaudio/MediaStreamAudioSourceNode.h | 27 +- dom/media/webaudio/test/mochitest_audio.toml | 1 - dom/media/webaudio/test/test_mediaDecoding.html | 12 - dom/media/webcodecs/AudioData.cpp | 84 +- dom/media/webcodecs/AudioData.h | 7 +- dom/media/webcodecs/AudioDecoder.cpp | 69 +- dom/media/webcodecs/AudioEncoder.cpp | 488 +++++++++ dom/media/webcodecs/AudioEncoder.h | 76 ++ dom/media/webcodecs/DecoderTemplate.cpp | 4 +- dom/media/webcodecs/DecoderTypes.h | 44 +- dom/media/webcodecs/EncoderTemplate.cpp | 522 ++++----- dom/media/webcodecs/EncoderTemplate.h | 56 +- dom/media/webcodecs/EncoderTypes.h | 74 +- dom/media/webcodecs/VideoDecoder.cpp | 34 +- dom/media/webcodecs/VideoEncoder.cpp | 42 +- dom/media/webcodecs/VideoEncoder.h | 2 +- dom/media/webcodecs/WebCodecsUtils.cpp | 71 +- dom/media/webcodecs/WebCodecsUtils.h | 17 +- dom/media/webcodecs/crashtests/1881079.html | 35 + dom/media/webcodecs/crashtests/crashtests.list | 4 +- dom/media/webcodecs/moz.build | 2 + dom/media/webrtc/MediaEnginePrefs.h | 2 + dom/media/webrtc/MediaEngineWebRTCAudio.cpp | 279 ++--- dom/media/webrtc/MediaEngineWebRTCAudio.h | 57 +- dom/media/webrtc/jsapi/PeerConnectionCtx.cpp | 5 +- dom/media/webrtc/jsapi/RTCRtpScriptTransform.cpp | 16 + dom/media/webrtc/jsapi/RTCRtpScriptTransformer.cpp | 2 +- dom/media/webrtc/jsapi/RTCRtpScriptTransformer.h | 2 +- dom/media/webrtc/jsapi/WebrtcGlobalInformation.cpp | 2 +- dom/media/webrtc/libwebrtcglue/AudioConduit.cpp | 2 +- dom/media/webrtc/libwebrtcglue/VideoConduit.cpp | 2 +- .../webrtc/libwebrtcglue/WebrtcCallWrapper.cpp | 17 +- .../webrtc/libwebrtcglue/WebrtcGmpVideoCodec.h | 2 +- .../libwebrtcglue/WebrtcMediaDataEncoderCodec.cpp | 20 +- .../libwebrtcglue/WebrtcMediaDataEncoderCodec.h | 2 +- .../mochitests/test_peerConnection_toJSON.html | 9 +- .../webrtc/third_party_build/default_config_env | 20 +- dom/media/webrtc/third_party_build/elm_rebase.sh | 15 + .../webrtc/third_party_build/fetch_github_repo.py | 4 + .../webrtc/third_party_build/vendor-libwebrtc.py | 1 - dom/media/webrtc/transport/test/ice_unittest.cpp | 19 +- .../webspeech/recognition/SpeechRecognition.cpp | 25 +- .../webspeech/recognition/SpeechRecognition.h | 22 +- .../webspeech/synth/nsISynthVoiceRegistry.idl | 4 +- dom/media/webvtt/TextTrack.cpp | 36 +- dom/metrics.yaml | 17 - dom/midi/midir_impl/src/lib.rs | 1 - dom/network/TCPSocket.cpp | 3 - dom/notification/NotificationStorage.sys.mjs | 121 +-- dom/notification/old/NotificationDB.sys.mjs | 84 +- dom/payments/test/mochitest.toml | 2 +- dom/power/WakeLockJS.cpp | 64 +- dom/power/WakeLockJS.h | 11 +- dom/power/tests/file_empty.html | 1 + dom/power/tests/mochitest.toml | 5 + .../test_wakelock_on_initial_about_blank.html | 49 + dom/push/moz.build | 2 +- dom/push/test/test_utils.js | 6 +- dom/quota/ActorsParent.cpp | 48 +- dom/quota/QuotaManager.h | 6 + dom/quota/StorageOriginAttributes.cpp | 39 +- dom/quota/scripts/analyze_qm_failures.py | 137 --- dom/quota/scripts/fetch_fn_names.sh | 17 - dom/quota/scripts/fetch_qm_failures.py | 142 --- dom/quota/scripts/fn_anchors.py | 68 -- dom/quota/scripts/qm-try-analysis/.containerignore | 5 + dom/quota/scripts/qm-try-analysis/Containerfile | 24 + dom/quota/scripts/qm-try-analysis/README.md | 104 ++ dom/quota/scripts/qm-try-analysis/poetry.lock | 283 +++++ dom/quota/scripts/qm-try-analysis/pyproject.toml | 25 + .../qm-try-analysis/qm_try_analysis/__init__.py | 0 .../qm-try-analysis/qm_try_analysis/analyze.py | 150 +++ .../scripts/qm-try-analysis/qm_try_analysis/cli.py | 22 + .../qm-try-analysis/qm_try_analysis/fetch.py | 137 +++ .../qm_try_analysis/fetch_fn_names.sh | 17 + .../qm-try-analysis/qm_try_analysis/fn_anchors.py | 76 ++ .../qm-try-analysis/qm_try_analysis/logging.py | 21 + .../qm-try-analysis/qm_try_analysis/report.py | 266 +++++ .../qm_try_analysis/stackanalysis.py | 396 +++++++ .../qm-try-analysis/qm_try_analysis/telemetry.py | 54 + .../qm-try-analysis/qm_try_analysis/utils.py | 91 ++ .../qm-try-analysis/tests/test_reporting.py | 96 ++ dom/quota/scripts/stackanalysis.py | 396 ------- dom/quota/scripts/telemetry.py | 54 - dom/quota/scripts/utils.py | 89 -- dom/quota/test/marionette/dummy.py | 0 dom/quota/test/marionette/manifest.toml | 3 +- dom/quota/test/marionette/quota_test_case.py | 180 +++- .../marionette/test_private_repository_cleanup.py | 94 ++ dom/reporting/ReportDeliver.cpp | 2 +- dom/script/ScriptLoadContext.h | 2 +- dom/script/ScriptLoader.cpp | 57 +- dom/script/ScriptLoader.h | 3 +- dom/security/ReferrerInfo.cpp | 6 +- dom/security/ReferrerInfo.h | 2 +- dom/security/fuzztest/csp_fuzzer.cpp | 2 +- dom/security/nsCSPContext.cpp | 14 +- dom/security/nsCSPContext.h | 2 +- dom/security/nsCSPParser.cpp | 70 +- dom/security/nsCSPParser.h | 4 + dom/security/nsContentSecurityUtils.cpp | 3 +- dom/security/nsHTTPSOnlyUtils.cpp | 35 +- dom/security/nsHTTPSOnlyUtils.h | 12 + .../general/file_block_script_wrong_mime_sw.js | 51 + dom/security/test/general/mochitest.toml | 1 + .../test/general/test_block_script_wrong_mime.html | 105 +- dom/security/test/gtest/TestCSPParser.cpp | 3 +- dom/security/test/https-first/browser.toml | 8 +- .../browser_beforeunload_permit_http.js | 2 +- .../https-first/browser_subdocument_downgrade.js | 60 ++ dom/security/test/https-first/file_empty.html | 1 + .../file_mixed_content_auto_upgrade.html | 2 +- .../test/https-first/file_multiple_redirection.sjs | 18 +- .../https-first/file_subdocument_downgrade.sjs | 8 + dom/security/test/https-first/test.ogv | Bin 2344665 -> 0 bytes dom/security/test/https-first/test.webm | Bin 0 -> 97465 bytes .../https-first/test_multiple_redirection.html | 8 +- dom/security/test/mixedcontentblocker/browser.toml | 2 +- .../browser_mixed_content_auth_download.js | 6 +- ...dcontent_and_mixed_content_display_upgrade.html | 2 +- .../test/mixedcontentblocker/file_server.sjs | 4 +- .../test/mixedcontentblocker/mochitest.toml | 4 +- dom/security/test/mixedcontentblocker/test.ogv | Bin 2344665 -> 0 bytes dom/security/test/mixedcontentblocker/test.webm | Bin 0 -> 97465 bytes dom/security/test/referrer-policy/browser.toml | 7 +- ...rowser_referrer_disallow_cross_site_relaxing.js | 2 + dom/security/trusted-types/TrustedTypePolicy.cpp | 94 +- dom/security/trusted-types/TrustedTypePolicy.h | 49 +- .../trusted-types/TrustedTypePolicyFactory.cpp | 48 +- .../trusted-types/TrustedTypePolicyFactory.h | 21 +- dom/security/trusted-types/TrustedTypeUtils.h | 12 +- dom/serializers/nsDocumentEncoder.cpp | 8 +- dom/serviceworkers/ServiceWorkerPrivate.cpp | 8 +- dom/serviceworkers/ServiceWorkerScriptCache.cpp | 50 +- dom/serviceworkers/ServiceWorkerScriptCache.h | 2 +- dom/serviceworkers/ServiceWorkerUpdateJob.cpp | 3 +- dom/smil/SMILAnimationController.cpp | 35 +- dom/smil/SMILAnimationController.h | 6 +- dom/storage/LocalStorageCache.cpp | 9 +- dom/storage/Storage.cpp | 6 +- dom/storage/Storage.h | 9 +- dom/streams/TransformStream.cpp | 8 - dom/streams/UnderlyingSinkCallbackHelpers.cpp | 29 +- dom/streams/UnderlyingSinkCallbackHelpers.h | 10 +- dom/svg/SVGAElement.cpp | 10 + dom/svg/SVGAElement.h | 4 +- dom/svg/SVGContentUtils.cpp | 85 +- dom/svg/SVGElement.cpp | 2 +- dom/svg/SVGElement.h | 4 +- dom/svg/SVGFEImageElement.cpp | 17 + dom/svg/SVGFEImageElement.h | 1 + dom/svg/SVGGeometryElement.cpp | 2 +- dom/svg/SVGGraphicsElement.cpp | 6 +- dom/svg/SVGImageElement.cpp | 17 + dom/svg/SVGImageElement.h | 2 + dom/svg/SVGPathData.cpp | 337 +++--- dom/svg/SVGPathData.h | 14 +- dom/svg/SVGPathElement.cpp | 2 +- dom/svg/SVGPathSegUtils.cpp | 144 ++- dom/svg/SVGPathSegUtils.h | 12 +- dom/svg/SVGSVGElement.cpp | 39 +- dom/svg/SVGUseElement.cpp | 5 + dom/svg/SVGUseElement.h | 1 + dom/svg/crashtests/1858792.html | 15 + dom/svg/crashtests/crashtests.list | 1 + dom/svg/test/getCTM-helper.svg | 2 + dom/svg/test/test_getCTM.html | 38 +- .../browser/browser_windowProxy_transplant.js | 6 - dom/tests/mochitest/general/test_bug861217.html | 29 +- dom/tests/mochitest/general/test_interfaces.js | 16 +- .../localstorage/frameLocalStorageSessionOnly.html | 8 - dom/tests/mochitest/localstorage/mochitest.toml | 1 - .../webcomponents/test_xul_custom_element.xhtml | 12 +- dom/tests/reftest/prettyprint-space-ref.xhtml | 67 ++ dom/tests/reftest/prettyprint-space.xml | 1 + dom/tests/reftest/reftest.list | 1 + dom/tests/unit/test_PromiseDebugging.js | 1 - dom/tests/unit/test_xhr_init.js | 8 + dom/url/URL.cpp | 223 ++-- dom/url/URL.h | 85 +- dom/url/URLMainThread.cpp | 33 +- dom/url/URLMainThread.h | 11 +- dom/url/URLSearchParams.cpp | 60 +- dom/url/URLSearchParams.h | 26 +- dom/url/URLWorker.cpp | 40 +- dom/url/URLWorker.h | 16 +- dom/webauthn/AndroidWebAuthnService.cpp | 10 +- dom/webauthn/MacOSWebAuthnService.mm | 73 +- dom/webauthn/WebAuthnArgs.cpp | 13 +- dom/webauthn/WebAuthnResult.cpp | 22 +- dom/webauthn/WebAuthnResult.h | 10 +- dom/webauthn/WebAuthnService.cpp | 179 +++- dom/webauthn/WebAuthnService.h | 22 +- dom/webauthn/WinWebAuthnService.cpp | 13 +- .../authrs_bridge/src/about_webauthn_controller.rs | 4 +- dom/webauthn/authrs_bridge/src/lib.rs | 125 +-- dom/webauthn/nsIWebAuthnArgs.idl | 10 +- dom/webauthn/nsIWebAuthnAttObj.idl | 2 + dom/webauthn/nsIWebAuthnResult.idl | 9 +- dom/webauthn/nsIWebAuthnService.idl | 20 +- .../browser_webauthn_conditional_mediation.js | 6 +- .../tests/browser/browser_webauthn_prompts.js | 104 +- dom/webgpu/BindGroup.cpp | 20 +- dom/webgpu/BindGroupLayout.cpp | 18 +- dom/webgpu/Buffer.cpp | 18 +- dom/webgpu/Buffer.h | 2 +- dom/webgpu/CanvasContext.cpp | 18 +- dom/webgpu/CommandEncoder.cpp | 27 +- dom/webgpu/CompilationInfo.cpp | 15 +- dom/webgpu/CompilationMessage.cpp | 8 +- dom/webgpu/CompilationMessage.h | 8 +- dom/webgpu/ComputePipeline.cpp | 29 +- dom/webgpu/Instance.cpp | 2 +- dom/webgpu/ObjectModel.h | 40 +- dom/webgpu/PipelineLayout.cpp | 20 +- dom/webgpu/RenderBundle.cpp | 19 +- dom/webgpu/RenderPipeline.cpp | 33 +- dom/webgpu/Sampler.cpp | 20 +- dom/webgpu/ShaderModule.cpp | 20 +- dom/webgpu/Texture.cpp | 18 +- dom/webgpu/Texture.h | 2 +- dom/webgpu/TextureView.cpp | 20 +- dom/webgpu/ipc/WebGPUChild.cpp | 2 +- dom/webgpu/ipc/WebGPUChild.h | 2 - dom/webgpu/ipc/WebGPUParent.cpp | 1 - dom/webgpu/ipc/WebGPUSerialize.h | 2 +- dom/webgpu/mochitest/mochitest-no-pref.toml | 1 + dom/webgpu/mochitest/mochitest.toml | 63 +- .../mochitest/test_compilation_message_pos.html | 58 + dom/webidl/AudioEncoder.webidl | 80 ++ dom/webidl/CSSPageRule.webidl | 4 +- dom/webidl/CSSScopeRule.webidl | 14 + dom/webidl/CSSStartingStyleRule.webidl | 12 + dom/webidl/Document.webidl | 27 +- dom/webidl/DocumentFragment.webidl | 14 +- dom/webidl/Element.webidl | 23 +- dom/webidl/FragmentDirective.webidl | 11 + dom/webidl/GleanMetrics.webidl | 10 + dom/webidl/HTMLHyperlinkElementUtils.webidl | 22 +- dom/webidl/HTMLMarqueeElement.webidl | 4 - dom/webidl/HashChangeEvent.webidl | 6 - dom/webidl/IDBDatabase.webidl | 10 +- dom/webidl/IDBFactory.webidl | 7 + dom/webidl/IDBTransaction.webidl | 4 + dom/webidl/IdentityCredential.webidl | 6 +- dom/webidl/MediaDebugInfo.webidl | 3 + dom/webidl/MutationEvent.webidl | 2 - dom/webidl/NonElementParentNode.webidl | 12 + dom/webidl/ParentNode.webidl | 5 + dom/webidl/RTCIceCandidate.webidl | 49 +- dom/webidl/RTCStatsReport.webidl | 7 - dom/webidl/Range.webidl | 9 + dom/webidl/Request.webidl | 23 +- dom/webidl/Response.webidl | 4 +- dom/webidl/Selection.webidl | 5 + dom/webidl/Storage.webidl | 3 - dom/webidl/TextEvent.webidl | 21 + dom/webidl/TrustedTypes.webidl | 7 +- dom/webidl/URL.webidl | 37 +- dom/webidl/URLSearchParams.webidl | 18 +- dom/webidl/XMLHttpRequest.webidl | 7 +- dom/webidl/moz.build | 14 +- .../api/WebTransportDatagramDuplexStream.cpp | 8 +- .../api/WebTransportDatagramDuplexStream.h | 2 +- dom/workers/RuntimeService.cpp | 6 + dom/workers/ScriptLoader.cpp | 33 +- dom/workers/Worker.cpp | 6 + dom/workers/Worker.h | 27 + dom/workers/WorkerLoadInfo.cpp | 2 +- dom/workers/WorkerLoadInfo.h | 2 +- dom/workers/WorkerPrivate.cpp | 106 +- dom/workers/WorkerPrivate.h | 4 +- dom/workers/WorkerRunnable.cpp | 16 - dom/workers/WorkerScope.cpp | 2 +- dom/workers/WorkerScope.h | 4 +- dom/workers/WorkerThread.cpp | 18 +- dom/workers/loader/CacheLoadHandler.cpp | 11 +- dom/workers/loader/NetworkLoadHandler.cpp | 4 +- dom/workers/loader/WorkerLoadContext.h | 2 +- dom/workers/loader/WorkerModuleLoader.cpp | 9 + dom/workers/loader/WorkerModuleLoader.h | 2 + dom/workers/nsIWorkerDebugger.idl | 6 +- dom/workers/remoteworkers/RemoteWorkerChild.cpp | 2 +- dom/workers/remoteworkers/RemoteWorkerTypes.ipdlh | 2 +- dom/workers/test/call_throws.js | 4 + dom/workers/test/chrome.toml | 4 - dom/workers/test/crashtests/1858809.html | 14 + dom/workers/test/crashtests/crashtests.list | 1 + dom/workers/test/importScripts_3rdParty_worker.js | 199 ++-- dom/workers/test/mime_type_is_csv.js | 1 + dom/workers/test/mime_type_is_csv.js^headers^ | 1 + dom/workers/test/mochitest.toml | 5 + dom/workers/test/redirect_with_query_args.sjs | 22 + dom/workers/test/test_importScripts_3rdparty.html | 633 +++++++++-- dom/workers/test/test_worker_interfaces.js | 2 + dom/workers/test/toplevel_throws.js | 1 + dom/worklet/WorkletFetchHandler.cpp | 26 +- dom/xhr/XMLHttpRequest.cpp | 23 +- dom/xhr/XMLHttpRequestMainThread.cpp | 17 +- dom/xhr/XMLHttpRequestWorker.cpp | 8 +- dom/xml/resources/XMLPrettyPrint.css | 8 + dom/xml/resources/XMLPrettyPrint.xsl | 11 +- dom/xul/ChromeObserver.cpp | 7 - 968 files changed, 28444 insertions(+), 10491 deletions(-) create mode 100644 dom/base/FragmentDirective.cpp create mode 100644 dom/base/FragmentDirective.h create mode 100644 dom/base/PlacesEventCounts.cpp create mode 100644 dom/base/PlacesEventCounts.h create mode 100644 dom/base/crashtests/1887930.html create mode 100644 dom/base/crashtests/1887963_1.html create mode 100644 dom/base/crashtests/1887963_2.html create mode 100644 dom/base/crashtests/1887974.html create mode 100644 dom/base/crashtests/1890888.html create mode 100644 dom/base/fragmentdirectives/Cargo.toml create mode 100644 dom/base/fragmentdirectives/cbindgen.toml create mode 100644 dom/base/fragmentdirectives/fragment_directive_impl.rs create mode 100644 dom/base/fragmentdirectives/lib.rs create mode 100644 dom/base/fragmentdirectives/test.rs create mode 100644 dom/base/test/jsmodules/export_star_ambiguous.mjs create mode 100644 dom/base/test/jsmodules/import_ambiguous_export.mjs create mode 100644 dom/base/test/jsmodules/import_ambiguous_export_star.mjs create mode 100644 dom/base/test/jsmodules/import_circular.mjs create mode 100644 dom/base/test/jsmodules/import_circular_1.mjs create mode 100644 dom/base/test/jsmodules/importmaps/classic_script.js create mode 100644 dom/base/test/jsmodules/importmaps/module_chain_1.mjs create mode 100644 dom/base/test/jsmodules/importmaps/module_chain_2.mjs create mode 100644 dom/base/test/jsmodules/importmaps/module_importMap_with_nonexisting_module.mjs create mode 100644 dom/base/test/jsmodules/importmaps/test_dynamic_importMap_load_completes.html create mode 100644 dom/base/test/jsmodules/importmaps/test_dynamic_importMap_with_external_script.html create mode 100644 dom/base/test/jsmodules/importmaps/test_importMap_with_nonexisting_module.html create mode 100644 dom/base/test/jsmodules/module_a.mjs create mode 100644 dom/base/test/jsmodules/module_b.mjs create mode 100644 dom/base/test/jsmodules/module_c.mjs create mode 100644 dom/base/test/jsmodules/module_d.mjs create mode 100644 dom/base/test/jsmodules/module_e.mjs create mode 100644 dom/base/test/jsmodules/test_import_errorMessage2.html create mode 100644 dom/base/test/test_content_iterator_subtree_shadow_tree.html create mode 100644 dom/canvas/test/webgl-conf/checkout/conformance/canvas/drawingbuffer-storage-test.html create mode 100644 dom/canvas/test/webgl-conf/checkout/conformance/extensions/ext-clip-control.html create mode 100644 dom/canvas/test/webgl-conf/checkout/conformance/extensions/ext-depth-clamp.html create mode 100644 dom/canvas/test/webgl-conf/checkout/conformance/extensions/ext-polygon-offset-clamp.html create mode 100644 dom/canvas/test/webgl-conf/checkout/conformance/extensions/ext-texture-mirror-clamp-to-edge.html create mode 100644 dom/canvas/test/webgl-conf/checkout/conformance/extensions/webgl-blend-func-extended.html create mode 100644 dom/canvas/test/webgl-conf/checkout/conformance/extensions/webgl-polygon-mode.html create mode 100644 dom/canvas/test/webgl-conf/checkout/conformance2/canvas/drawingbuffer-storage-test.html create mode 100644 dom/canvas/test/webgl-conf/checkout/conformance2/extensions/ext-conservative-depth.html create mode 100644 dom/canvas/test/webgl-conf/checkout/conformance2/extensions/ext-render-snorm.html create mode 100644 dom/canvas/test/webgl-conf/checkout/conformance2/extensions/nv-shader-noperspective-interpolation.html create mode 100644 dom/canvas/test/webgl-conf/checkout/conformance2/extensions/oes-sample-variables.html create mode 100644 dom/canvas/test/webgl-conf/checkout/conformance2/extensions/oes-shader-multisample-interpolation.html create mode 100644 dom/canvas/test/webgl-conf/checkout/conformance2/extensions/webgl-blend-func-extended.html create mode 100644 dom/canvas/test/webgl-conf/checkout/conformance2/extensions/webgl-clip-cull-distance.html create mode 100644 dom/canvas/test/webgl-conf/checkout/conformance2/extensions/webgl-provoking-vertex.html create mode 100644 dom/canvas/test/webgl-conf/checkout/conformance2/extensions/webgl-render-shared-exponent.html create mode 100644 dom/canvas/test/webgl-conf/checkout/conformance2/extensions/webgl-shader-pixel-local-storage.html create mode 100644 dom/canvas/test/webgl-conf/checkout/conformance2/extensions/webgl-stencil-texturing.html create mode 100644 dom/canvas/test/webgl-conf/checkout/conformance2/glsl3/texture-bias.html create mode 100644 dom/canvas/test/webgl-conf/checkout/conformance2/query/occlusion-query-scissor.html delete mode 100644 dom/canvas/test/webgl-conf/checkout/conformance2/rendering/builtin-vert-attribs.html create mode 100644 dom/canvas/test/webgl-conf/checkout/conformance2/textures/misc/tex-image-10bpc.html create mode 100644 dom/canvas/test/webgl-conf/checkout/conformance2/wasm/00_test_list.txt create mode 100644 dom/canvas/test/webgl-conf/checkout/conformance2/wasm/bufferdata-16gb-wasm-memory.html create mode 100644 dom/canvas/test/webgl-conf/checkout/conformance2/wasm/bufferdata-4gb-wasm-memory.html create mode 100644 dom/canvas/test/webgl-conf/checkout/conformance2/wasm/buffersubdata-16gb-wasm-memory.html create mode 100644 dom/canvas/test/webgl-conf/checkout/conformance2/wasm/buffersubdata-4gb-wasm-memory.html create mode 100644 dom/canvas/test/webgl-conf/checkout/conformance2/wasm/getbuffersubdata-16gb-wasm-memory.html create mode 100644 dom/canvas/test/webgl-conf/checkout/conformance2/wasm/getbuffersubdata-4gb-wasm-memory.html create mode 100644 dom/canvas/test/webgl-conf/checkout/conformance2/wasm/readpixels-16gb-wasm-memory.html create mode 100644 dom/canvas/test/webgl-conf/checkout/conformance2/wasm/readpixels-4gb-wasm-memory.html create mode 100644 dom/canvas/test/webgl-conf/checkout/conformance2/wasm/teximage2d-16gb-wasm-memory.html create mode 100644 dom/canvas/test/webgl-conf/checkout/conformance2/wasm/teximage2d-4gb-wasm-memory.html create mode 100644 dom/canvas/test/webgl-conf/checkout/conformance2/wasm/texsubimage2d-16gb-wasm-memory.html create mode 100644 dom/canvas/test/webgl-conf/checkout/conformance2/wasm/texsubimage2d-4gb-wasm-memory.html create mode 100644 dom/canvas/test/webgl-conf/checkout/js/tests/drawingbuffer-storage-test.js create mode 100644 dom/canvas/test/webgl-conf/checkout/js/tests/webgl-blend-func-extended.js delete mode 100644 dom/canvas/test/webgl-conf/checkout/resources/exif-orientation-originals/exif-orientation-test-6-90-ccw.jpg create mode 100644 dom/canvas/test/webgl-conf/checkout/resources/exif-orientation-originals/exif-orientation-test-6-90-cw.jpg create mode 100644 dom/canvas/test/webgl-conf/checkout/resources/exif-orientation-originals/exif-orientation-test-8-90-ccw.jpg delete mode 100644 dom/canvas/test/webgl-conf/checkout/resources/exif-orientation-originals/exif-orientation-test-8-90-cw.jpg delete mode 100644 dom/canvas/test/webgl-conf/checkout/resources/exif-orientation-test-6-90-ccw.jpg create mode 100644 dom/canvas/test/webgl-conf/checkout/resources/exif-orientation-test-6-90-cw.jpg create mode 100644 dom/canvas/test/webgl-conf/checkout/resources/exif-orientation-test-8-90-ccw.jpg delete mode 100644 dom/canvas/test/webgl-conf/checkout/resources/exif-orientation-test-8-90-cw.jpg delete mode 100644 dom/canvas/test/webgl-conf/checkout/resources/npot-video.theora.ogv create mode 100644 dom/canvas/test/webgl-conf/checkout/resources/red-gradient-8x1-10bit-untagged.png delete mode 100644 dom/canvas/test/webgl-conf/checkout/resources/red-green.theora.ogv create mode 100644 dom/canvas/test/webgl-conf/generated/test_2_conformance2__canvas__drawingbuffer-storage-test.html create mode 100644 dom/canvas/test/webgl-conf/generated/test_2_conformance2__extensions__ext-conservative-depth.html create mode 100644 dom/canvas/test/webgl-conf/generated/test_2_conformance2__extensions__ext-render-snorm.html create mode 100644 dom/canvas/test/webgl-conf/generated/test_2_conformance2__extensions__nv-shader-noperspective-interpolation.html create mode 100644 dom/canvas/test/webgl-conf/generated/test_2_conformance2__extensions__oes-sample-variables.html create mode 100644 dom/canvas/test/webgl-conf/generated/test_2_conformance2__extensions__oes-shader-multisample-interpolation.html create mode 100644 dom/canvas/test/webgl-conf/generated/test_2_conformance2__extensions__webgl-blend-func-extended.html create mode 100644 dom/canvas/test/webgl-conf/generated/test_2_conformance2__extensions__webgl-clip-cull-distance.html create mode 100644 dom/canvas/test/webgl-conf/generated/test_2_conformance2__extensions__webgl-provoking-vertex.html create mode 100644 dom/canvas/test/webgl-conf/generated/test_2_conformance2__extensions__webgl-render-shared-exponent.html create mode 100644 dom/canvas/test/webgl-conf/generated/test_2_conformance2__extensions__webgl-shader-pixel-local-storage.html create mode 100644 dom/canvas/test/webgl-conf/generated/test_2_conformance2__extensions__webgl-stencil-texturing.html create mode 100644 dom/canvas/test/webgl-conf/generated/test_2_conformance2__glsl3__texture-bias.html create mode 100644 dom/canvas/test/webgl-conf/generated/test_2_conformance2__query__occlusion-query-scissor.html delete mode 100644 dom/canvas/test/webgl-conf/generated/test_2_conformance2__rendering__builtin-vert-attribs.html create mode 100644 dom/canvas/test/webgl-conf/generated/test_2_conformance2__textures__misc__tex-image-10bpc.html create mode 100644 dom/canvas/test/webgl-conf/generated/test_2_conformance2__wasm__bufferdata-16gb-wasm-memory.html create mode 100644 dom/canvas/test/webgl-conf/generated/test_2_conformance2__wasm__bufferdata-4gb-wasm-memory.html create mode 100644 dom/canvas/test/webgl-conf/generated/test_2_conformance2__wasm__buffersubdata-16gb-wasm-memory.html create mode 100644 dom/canvas/test/webgl-conf/generated/test_2_conformance2__wasm__buffersubdata-4gb-wasm-memory.html create mode 100644 dom/canvas/test/webgl-conf/generated/test_2_conformance2__wasm__getbuffersubdata-16gb-wasm-memory.html create mode 100644 dom/canvas/test/webgl-conf/generated/test_2_conformance2__wasm__getbuffersubdata-4gb-wasm-memory.html create mode 100644 dom/canvas/test/webgl-conf/generated/test_2_conformance2__wasm__readpixels-16gb-wasm-memory.html create mode 100644 dom/canvas/test/webgl-conf/generated/test_2_conformance2__wasm__readpixels-4gb-wasm-memory.html create mode 100644 dom/canvas/test/webgl-conf/generated/test_2_conformance2__wasm__teximage2d-16gb-wasm-memory.html create mode 100644 dom/canvas/test/webgl-conf/generated/test_2_conformance2__wasm__teximage2d-4gb-wasm-memory.html create mode 100644 dom/canvas/test/webgl-conf/generated/test_2_conformance2__wasm__texsubimage2d-16gb-wasm-memory.html create mode 100644 dom/canvas/test/webgl-conf/generated/test_2_conformance2__wasm__texsubimage2d-4gb-wasm-memory.html create mode 100644 dom/canvas/test/webgl-conf/generated/test_2_conformance__extensions__ext-clip-control.html create mode 100644 dom/canvas/test/webgl-conf/generated/test_2_conformance__extensions__ext-depth-clamp.html create mode 100644 dom/canvas/test/webgl-conf/generated/test_2_conformance__extensions__ext-polygon-offset-clamp.html create mode 100644 dom/canvas/test/webgl-conf/generated/test_2_conformance__extensions__ext-texture-mirror-clamp-to-edge.html create mode 100644 dom/canvas/test/webgl-conf/generated/test_2_conformance__extensions__webgl-polygon-mode.html create mode 100644 dom/canvas/test/webgl-conf/generated/test_conformance__extensions__ext-clip-control.html create mode 100644 dom/canvas/test/webgl-conf/generated/test_conformance__extensions__ext-depth-clamp.html create mode 100644 dom/canvas/test/webgl-conf/generated/test_conformance__extensions__ext-polygon-offset-clamp.html create mode 100644 dom/canvas/test/webgl-conf/generated/test_conformance__extensions__ext-texture-mirror-clamp-to-edge.html create mode 100644 dom/canvas/test/webgl-conf/generated/test_conformance__extensions__webgl-blend-func-extended.html create mode 100644 dom/canvas/test/webgl-conf/generated/test_conformance__extensions__webgl-polygon-mode.html delete mode 100644 dom/canvas/test/webgl-mochitest/red-green.theora.ogv delete mode 100644 dom/canvas/test/webgl-mochitest/test_video_fastpath_theora.html create mode 100644 dom/events/TextEvent.cpp create mode 100644 dom/events/TextEvent.h create mode 100644 dom/events/test/clipboard/test_async_clipboard_datatransfer.html delete mode 100644 dom/events/test/pointerevents/test_wpt_pointerevent_change-touch-action-onpointerdown_touch-manual.html delete mode 100644 dom/events/test/pointerevents/test_wpt_pointerevent_constructor.html delete mode 100644 dom/events/test/pointerevents/test_wpt_pointerevent_pointerId_scope-manual.html delete mode 100644 dom/events/test/pointerevents/wpt/pointerevent_change-touch-action-onpointerdown_touch-manual.html delete mode 100644 dom/events/test/pointerevents/wpt/pointerevent_constructor.html delete mode 100644 dom/events/test/pointerevents/wpt/pointerevent_pointerId_scope-manual.html delete mode 100644 dom/events/test/test_marquee_events.html create mode 100644 dom/fetch/tests/browser_default_credentialless_fetch.js create mode 100644 dom/fs/test/crashtests/1874334-2.html create mode 100644 dom/fs/test/crashtests/1874334.html create mode 100644 dom/fs/test/crashtests/sw1874334-2.js create mode 100644 dom/indexedDB/test/test_open_and_databases.html create mode 100644 dom/indexedDB/test/unit/test_open_and_databases.js create mode 100644 dom/ipc/ContentParent_NotifyUpdatedDictionaries.h create mode 100644 dom/ipc/tests/browser_isactiveintab.js create mode 100644 dom/media/TimedPacketizer.h create mode 100644 dom/media/eme/metrics.yaml create mode 100644 dom/media/hls/metrics.yaml create mode 100644 dom/media/platforms/EncoderConfig.cpp create mode 100644 dom/media/platforms/EncoderConfig.h create mode 100644 dom/media/platforms/ffmpeg/FFmpegAudioEncoder.cpp create mode 100644 dom/media/platforms/ffmpeg/FFmpegAudioEncoder.h create mode 100644 dom/media/platforms/ffmpeg/FFmpegDataEncoder.cpp create mode 100644 dom/media/platforms/ffmpeg/FFmpegDataEncoder.h create mode 100644 dom/media/platforms/ffmpeg/FFmpegUtils.cpp create mode 100644 dom/media/platforms/ffmpeg/FFmpegUtils.h delete mode 100644 dom/media/test/320x240.ogv delete mode 100644 dom/media/test/320x240.ogv^headers^ create mode 100644 dom/media/test/320x240.webm create mode 100644 dom/media/test/320x240.webm^headers^ delete mode 100644 dom/media/test/448636.ogv delete mode 100644 dom/media/test/bogus.ogv delete mode 100644 dom/media/test/bug482461-theora.ogv delete mode 100644 dom/media/test/bug482461.ogv delete mode 100644 dom/media/test/bug495129.ogv delete mode 100644 dom/media/test/bug498380.ogv delete mode 100644 dom/media/test/bug498855-1.ogv delete mode 100644 dom/media/test/bug498855-2.ogv delete mode 100644 dom/media/test/bug498855-3.ogv delete mode 100644 dom/media/test/bug499519.ogv delete mode 100644 dom/media/test/bug500311.ogv delete mode 100644 dom/media/test/bug504613.ogv delete mode 100644 dom/media/test/bug504644.ogv delete mode 100644 dom/media/test/bug504843.ogv delete mode 100644 dom/media/test/bug506094.ogv delete mode 100644 dom/media/test/bug516323.indexed.ogv delete mode 100644 dom/media/test/bug516323.ogv delete mode 100644 dom/media/test/bug523816.ogv delete mode 100644 dom/media/test/bug556821.ogv delete mode 100644 dom/media/test/bug557094.ogv delete mode 100644 dom/media/test/chained-video.ogv delete mode 100644 dom/media/test/crashtests/576612-1.html delete mode 100644 dom/media/test/multiple-bos.ogg delete mode 100644 dom/media/test/sample-fisbone-skeleton4.ogv delete mode 100644 dom/media/test/sample-fisbone-wrong-header.ogv delete mode 100644 dom/media/test/seek-short.ogv delete mode 100644 dom/media/test/seek.ogv delete mode 100644 dom/media/test/short-video.ogv create mode 100644 dom/media/webcodecs/AudioEncoder.cpp create mode 100644 dom/media/webcodecs/AudioEncoder.h create mode 100644 dom/media/webcodecs/crashtests/1881079.html create mode 100644 dom/power/tests/file_empty.html create mode 100644 dom/power/tests/test_wakelock_on_initial_about_blank.html delete mode 100755 dom/quota/scripts/analyze_qm_failures.py delete mode 100755 dom/quota/scripts/fetch_fn_names.sh delete mode 100755 dom/quota/scripts/fetch_qm_failures.py delete mode 100644 dom/quota/scripts/fn_anchors.py create mode 100644 dom/quota/scripts/qm-try-analysis/.containerignore create mode 100644 dom/quota/scripts/qm-try-analysis/Containerfile create mode 100644 dom/quota/scripts/qm-try-analysis/README.md create mode 100644 dom/quota/scripts/qm-try-analysis/poetry.lock create mode 100644 dom/quota/scripts/qm-try-analysis/pyproject.toml create mode 100644 dom/quota/scripts/qm-try-analysis/qm_try_analysis/__init__.py create mode 100755 dom/quota/scripts/qm-try-analysis/qm_try_analysis/analyze.py create mode 100644 dom/quota/scripts/qm-try-analysis/qm_try_analysis/cli.py create mode 100644 dom/quota/scripts/qm-try-analysis/qm_try_analysis/fetch.py create mode 100755 dom/quota/scripts/qm-try-analysis/qm_try_analysis/fetch_fn_names.sh create mode 100644 dom/quota/scripts/qm-try-analysis/qm_try_analysis/fn_anchors.py create mode 100644 dom/quota/scripts/qm-try-analysis/qm_try_analysis/logging.py create mode 100644 dom/quota/scripts/qm-try-analysis/qm_try_analysis/report.py create mode 100644 dom/quota/scripts/qm-try-analysis/qm_try_analysis/stackanalysis.py create mode 100644 dom/quota/scripts/qm-try-analysis/qm_try_analysis/telemetry.py create mode 100644 dom/quota/scripts/qm-try-analysis/qm_try_analysis/utils.py create mode 100644 dom/quota/scripts/qm-try-analysis/tests/test_reporting.py delete mode 100644 dom/quota/scripts/stackanalysis.py delete mode 100644 dom/quota/scripts/telemetry.py delete mode 100644 dom/quota/scripts/utils.py delete mode 100644 dom/quota/test/marionette/dummy.py create mode 100644 dom/quota/test/marionette/test_private_repository_cleanup.py create mode 100644 dom/security/test/general/file_block_script_wrong_mime_sw.js create mode 100644 dom/security/test/https-first/browser_subdocument_downgrade.js create mode 100644 dom/security/test/https-first/file_empty.html create mode 100644 dom/security/test/https-first/file_subdocument_downgrade.sjs delete mode 100644 dom/security/test/https-first/test.ogv create mode 100644 dom/security/test/https-first/test.webm delete mode 100644 dom/security/test/mixedcontentblocker/test.ogv create mode 100644 dom/security/test/mixedcontentblocker/test.webm create mode 100644 dom/svg/crashtests/1858792.html delete mode 100644 dom/tests/mochitest/localstorage/frameLocalStorageSessionOnly.html create mode 100644 dom/tests/reftest/prettyprint-space-ref.xhtml create mode 100644 dom/tests/reftest/prettyprint-space.xml create mode 100644 dom/webgpu/mochitest/test_compilation_message_pos.html create mode 100644 dom/webidl/AudioEncoder.webidl create mode 100644 dom/webidl/CSSScopeRule.webidl create mode 100644 dom/webidl/CSSStartingStyleRule.webidl create mode 100644 dom/webidl/FragmentDirective.webidl create mode 100644 dom/webidl/NonElementParentNode.webidl create mode 100644 dom/webidl/TextEvent.webidl create mode 100644 dom/workers/test/call_throws.js create mode 100644 dom/workers/test/crashtests/1858809.html create mode 100644 dom/workers/test/mime_type_is_csv.js create mode 100644 dom/workers/test/mime_type_is_csv.js^headers^ create mode 100644 dom/workers/test/redirect_with_query_args.sjs create mode 100644 dom/workers/test/toplevel_throws.js (limited to 'dom') diff --git a/dom/animation/Animation.cpp b/dom/animation/Animation.cpp index ad52495e67..fbfd689c9a 100644 --- a/dom/animation/Animation.cpp +++ b/dom/animation/Animation.cpp @@ -11,7 +11,6 @@ #include "AnimationUtils.h" #include "mozAutoDocUpdate.h" #include "mozilla/dom/AnimationBinding.h" -#include "mozilla/dom/AnimationPlaybackEvent.h" #include "mozilla/dom/Document.h" #include "mozilla/dom/DocumentInlines.h" #include "mozilla/dom/DocumentTimeline.h" @@ -645,7 +644,8 @@ void Animation::Cancel(PostRestyleMode aPostRestyle) { } ResetFinishedPromise(); - QueuePlaybackEvent(u"cancel"_ns, GetTimelineCurrentTimeAsTimeStamp()); + QueuePlaybackEvent(nsGkAtoms::oncancel, + GetTimelineCurrentTimeAsTimeStamp()); } StickyTimeDuration activeTime = @@ -1189,7 +1189,7 @@ void Animation::Remove() { UpdateEffect(PostRestyleMode::IfNeeded); PostUpdate(); - QueuePlaybackEvent(u"remove"_ns, GetTimelineCurrentTimeAsTimeStamp()); + QueuePlaybackEvent(nsGkAtoms::onremove, GetTimelineCurrentTimeAsTimeStamp()); } bool Animation::HasLowerCompositeOrderThan(const Animation& aOther) const { @@ -1869,10 +1869,11 @@ void Animation::DoFinishNotificationImmediately(MicroTaskRunnable* aAsync) { MaybeResolveFinishedPromise(); - QueuePlaybackEvent(u"finish"_ns, AnimationTimeToTimeStamp(EffectEnd())); + QueuePlaybackEvent(nsGkAtoms::onfinish, + AnimationTimeToTimeStamp(EffectEnd())); } -void Animation::QueuePlaybackEvent(const nsAString& aName, +void Animation::QueuePlaybackEvent(nsAtom* aOnEvent, TimeStamp&& aScheduledEventTime) { // Use document for timing. // https://drafts.csswg.org/web-animations-1/#document-for-timing @@ -1886,20 +1887,19 @@ void Animation::QueuePlaybackEvent(const nsAString& aName, return; } - AnimationPlaybackEventInit init; - if (aName.EqualsLiteral("finish") || aName.EqualsLiteral("remove")) { - init.mCurrentTime = GetCurrentTimeAsDouble(); + Nullable currentTime; + if (aOnEvent == nsGkAtoms::onfinish || aOnEvent == nsGkAtoms::onremove) { + currentTime = GetCurrentTimeAsDouble(); } + + Nullable timelineTime; if (mTimeline) { - init.mTimelineTime = mTimeline->GetCurrentTimeAsDouble(); + timelineTime = mTimeline->GetCurrentTimeAsDouble(); } - RefPtr event = - AnimationPlaybackEvent::Constructor(this, aName, init); - event->SetTrusted(true); - - presContext->AnimationEventDispatcher()->QueueEvent(AnimationEventInfo( - std::move(event), std::move(aScheduledEventTime), this)); + presContext->AnimationEventDispatcher()->QueueEvent( + AnimationEventInfo(aOnEvent, currentTime, timelineTime, + std::move(aScheduledEventTime), this)); } bool Animation::IsRunningOnCompositor() const { diff --git a/dom/animation/Animation.h b/dom/animation/Animation.h index d7edfedfc2..e80370370e 100644 --- a/dom/animation/Animation.h +++ b/dom/animation/Animation.h @@ -26,6 +26,7 @@ struct JSContext; class nsCSSPropertyIDSet; class nsIFrame; class nsIGlobalObject; +class nsAtom; namespace mozilla { @@ -450,8 +451,7 @@ class Animation : public DOMEventTargetHelper, void DoFinishNotification(SyncNotifyFlag aSyncNotifyFlag); friend class AsyncFinishNotification; void DoFinishNotificationImmediately(MicroTaskRunnable* aAsync = nullptr); - void QueuePlaybackEvent(const nsAString& aName, - TimeStamp&& aScheduledEventTime); + void QueuePlaybackEvent(nsAtom* aOnEvent, TimeStamp&& aScheduledEventTime); /** * Remove this animation from the pending animation tracker and reset diff --git a/dom/animation/AnimationEventDispatcher.cpp b/dom/animation/AnimationEventDispatcher.cpp index 86884abbe5..0ec5686d8f 100644 --- a/dom/animation/AnimationEventDispatcher.cpp +++ b/dom/animation/AnimationEventDispatcher.cpp @@ -91,10 +91,6 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(AnimationEventDispatcher) ImplCycleCollectionTraverse( cb, target->mElement, "mozilla::AnimationEventDispatcher.mPendingEvents.mTarget"); - } else { - ImplCycleCollectionTraverse( - cb, info.mData.as().mEvent, - "mozilla::AnimationEventDispatcher.mPendingEvents.mEvent"); } ImplCycleCollectionTraverse( cb, info.mAnimation, diff --git a/dom/animation/AnimationEventDispatcher.h b/dom/animation/AnimationEventDispatcher.h index 98d1557901..dc11972f6d 100644 --- a/dom/animation/AnimationEventDispatcher.h +++ b/dom/animation/AnimationEventDispatcher.h @@ -12,6 +12,7 @@ #include "mozilla/Attributes.h" #include "mozilla/ContentEvents.h" #include "mozilla/EventDispatcher.h" +#include "mozilla/EventListenerManager.h" #include "mozilla/Variant.h" #include "mozilla/dom/AnimationPlaybackEvent.h" #include "mozilla/dom/KeyframeEffect.h" @@ -44,7 +45,10 @@ struct AnimationEventInfo { }; struct WebAnimationData { - RefPtr mEvent; + const RefPtr mOnEvent; + const dom::Nullable mCurrentTime; + const dom::Nullable mTimelineTime; + const TimeStamp mEventEnqueueTimeStamp{TimeStamp::Now()}; }; using Data = Variant; @@ -100,12 +104,15 @@ struct AnimationEventInfo { } // For web animation events - AnimationEventInfo(RefPtr&& aEvent, + AnimationEventInfo(nsAtom* aOnEvent, + const dom::Nullable& aCurrentTime, + const dom::Nullable& aTimelineTime, TimeStamp&& aScheduledEventTimeStamp, dom::Animation* aAnimation) : mAnimation(aAnimation), mScheduledEventTimeStamp(std::move(aScheduledEventTimeStamp)), - mData(WebAnimationData{std::move(aEvent)}) {} + mData(WebAnimationData{RefPtr{aOnEvent}, aCurrentTime, aTimelineTime}) { + } AnimationEventInfo(const AnimationEventInfo& aOther) = delete; AnimationEventInfo& operator=(const AnimationEventInfo& aOther) = delete; @@ -137,10 +144,27 @@ struct AnimationEventInfo { // TODO: Convert this to MOZ_CAN_RUN_SCRIPT (bug 1415230) MOZ_CAN_RUN_SCRIPT_BOUNDARY void Dispatch(nsPresContext* aPresContext) { if (mData.is()) { - RefPtr playbackEvent = mData.as().mEvent; + const auto& data = mData.as(); + EventListenerManager* elm = mAnimation->GetExistingListenerManager(); + if (!elm || !elm->HasListenersFor(data.mOnEvent)) { + return; + } + + dom::AnimationPlaybackEventInit init; + init.mCurrentTime = data.mCurrentTime; + init.mTimelineTime = data.mTimelineTime; + MOZ_ASSERT(nsDependentAtomString(data.mOnEvent).Find(u"on"_ns) == 0, + "mOnEvent atom should start with 'on'!"); + RefPtr event = + dom::AnimationPlaybackEvent::Constructor( + mAnimation, Substring(nsDependentAtomString(data.mOnEvent), 2), + init); + event->SetTrusted(true); + event->WidgetEventPtr()->AssignEventTime( + WidgetEventTime(data.mEventEnqueueTimeStamp)); RefPtr target = mAnimation; EventDispatcher::DispatchDOMEvent(target, nullptr /* WidgetEvent */, - playbackEvent, aPresContext, + event, aPresContext, nullptr /* nsEventStatus */); return; } diff --git a/dom/animation/test/chrome.toml b/dom/animation/test/chrome.toml index fa1d21457c..0783d9bb5a 100644 --- a/dom/animation/test/chrome.toml +++ b/dom/animation/test/chrome.toml @@ -5,10 +5,6 @@ prefs = [ "layout.css.basic-shape-rect.enabled=true", "layout.css.basic-shape-xywh.enabled=true", "layout.css.individual-transform.enabled=true", - "layout.css.motion-path-basic-shapes.enabled=true", - "layout.css.motion-path-coord-box.enabled=true", - "layout.css.motion-path-offset-position.enabled=true", - "layout.css.motion-path-ray.enabled=true", ] support-files = [ "testcommon.js", diff --git a/dom/audiochannel/nsIAudioChannelAgent.idl b/dom/audiochannel/nsIAudioChannelAgent.idl index 69a8344449..2a25d2cede 100644 --- a/dom/audiochannel/nsIAudioChannelAgent.idl +++ b/dom/audiochannel/nsIAudioChannelAgent.idl @@ -36,7 +36,7 @@ interface nsIAudioChannelAgentCallback : nsISupports /** * Notified when the window volume/mute is changed */ - void windowVolumeChanged(in float aVolume, in bool aMuted); + void windowVolumeChanged(in float aVolume, in boolean aMuted); /** * Notified when the window needs to be suspended or resumed. @@ -46,7 +46,7 @@ interface nsIAudioChannelAgentCallback : nsISupports /** * Notified when the capture state is changed. */ - void windowAudioCaptureChanged(in bool aCapture); + void windowAudioCaptureChanged(in boolean aCapture); }; /** diff --git a/dom/base/AbstractRange.cpp b/dom/base/AbstractRange.cpp index 91234bf0a7..c9138a19d2 100644 --- a/dom/base/AbstractRange.cpp +++ b/dom/base/AbstractRange.cpp @@ -10,6 +10,7 @@ #include "mozilla/Assertions.h" #include "mozilla/Attributes.h" #include "mozilla/RangeUtils.h" +#include "mozilla/dom/ChildIterator.h" #include "mozilla/dom/Document.h" #include "mozilla/dom/StaticRange.h" #include "mozilla/dom/Selection.h" @@ -87,6 +88,29 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(AbstractRange) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mRegisteredClosestCommonInclusiveAncestor) NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END +// When aMarkDesendants is true, Set +// DescendantOfClosestCommonInclusiveAncestorForRangeInSelection flag for the +// flattened children of aNode. When aMarkDesendants is false, unset that flag +// for the flattened children of aNode. +void UpdateDescendantsInFlattenedTree(const nsIContent& aNode, + bool aMarkDesendants) { + if (!aNode.IsElement() || aNode.IsHTMLElement(nsGkAtoms::slot)) { + return; + } + + FlattenedChildIterator iter(&aNode); + for (nsIContent* child = iter.GetNextChild(); child; + child = iter.GetNextChild()) { + if (aMarkDesendants) { + child->SetDescendantOfClosestCommonInclusiveAncestorForRangeInSelection(); + } else { + child + ->ClearDescendantOfClosestCommonInclusiveAncestorForRangeInSelection(); + } + UpdateDescendantsInFlattenedTree(*child, aMarkDesendants); + } +} + void AbstractRange::MarkDescendants(const nsINode& aNode) { // Set NodeIsDescendantOfClosestCommonInclusiveAncestorForRangeInSelection on // aNode's descendants unless aNode is already marked as a range common @@ -95,10 +119,22 @@ void AbstractRange::MarkDescendants(const nsINode& aNode) { if (!aNode.IsMaybeSelected()) { // don't set the Descendant bit on |aNode| itself nsINode* node = aNode.GetNextNode(&aNode); + if (!node) { + if (aNode.GetShadowRootForSelection()) { + UpdateDescendantsInFlattenedTree(*aNode.AsContent(), true); + } + return; + } while (node) { node->SetDescendantOfClosestCommonInclusiveAncestorForRangeInSelection(); if (!node->IsClosestCommonInclusiveAncestorForRangeInSelection()) { - node = node->GetNextNode(&aNode); + if (StaticPrefs::dom_shadowdom_selection_across_boundary_enabled()) { + UpdateDescendantsInFlattenedTree(*node->AsContent(), true); + // sub-tree of node has been marked already + node = node->GetNextNonChildNode(&aNode); + } else { + node = node->GetNextNode(&aNode); + } } else { // optimize: skip this sub-tree since it's marked already. node = node->GetNextNonChildNode(&aNode); @@ -116,10 +152,22 @@ void AbstractRange::UnmarkDescendants(const nsINode& aNode) { .IsDescendantOfClosestCommonInclusiveAncestorForRangeInSelection()) { // we know |aNode| doesn't have any bit set nsINode* node = aNode.GetNextNode(&aNode); + if (!node) { + if (aNode.GetShadowRootForSelection()) { + UpdateDescendantsInFlattenedTree(*aNode.AsContent(), false); + } + return; + } while (node) { node->ClearDescendantOfClosestCommonInclusiveAncestorForRangeInSelection(); if (!node->IsClosestCommonInclusiveAncestorForRangeInSelection()) { - node = node->GetNextNode(&aNode); + if (StaticPrefs::dom_shadowdom_selection_across_boundary_enabled()) { + UpdateDescendantsInFlattenedTree(*node->AsContent(), false); + // sub-tree has been marked already + node = node->GetNextNonChildNode(&aNode); + } else { + node = node->GetNextNode(&aNode); + } } else { // We found an ancestor of an overlapping range, skip its descendants. node = node->GetNextNonChildNode(&aNode); @@ -185,10 +233,62 @@ bool AbstractRange::MaybeCacheToReuse(RangeType& aInstance) { return true; } -nsINode* AbstractRange::GetClosestCommonInclusiveAncestor() const { - return mIsPositioned ? nsContentUtils::GetClosestCommonInclusiveAncestor( - mStart.Container(), mEnd.Container()) - : nullptr; +nsINode* AbstractRange::GetClosestCommonInclusiveAncestor( + AllowRangeCrossShadowBoundary aAllowCrossShadowBoundary) const { + if (!mIsPositioned) { + return nullptr; + } + nsINode* startContainer = + aAllowCrossShadowBoundary == AllowRangeCrossShadowBoundary::Yes + ? GetMayCrossShadowBoundaryStartContainer() + : GetStartContainer(); + nsINode* endContainer = + aAllowCrossShadowBoundary == AllowRangeCrossShadowBoundary::Yes + ? GetMayCrossShadowBoundaryEndContainer() + : GetEndContainer(); + + if (MayCrossShadowBoundary() && + aAllowCrossShadowBoundary == AllowRangeCrossShadowBoundary::Yes) { + // Since both the start container and the end container are + // guaranteed to be in the same composed document. + // If one of the boundary is a document, use that document + // as the common ancestor since both nodes. + const bool oneBoundaryIsDocument = + (startContainer && startContainer->IsDocument()) || + (endContainer && endContainer->IsDocument()); + if (oneBoundaryIsDocument) { + MOZ_ASSERT_IF( + startContainer && startContainer->IsDocument(), + !endContainer || endContainer->GetComposedDoc() == startContainer); + MOZ_ASSERT_IF( + endContainer && endContainer->IsDocument(), + !startContainer || startContainer->GetComposedDoc() == endContainer); + + return startContainer ? startContainer->GetComposedDoc() + : endContainer->GetComposedDoc(); + } + + const auto rescope = [](nsINode*& aContainer) { + if (!aContainer) { + return; + } + // RangeBoundary allows the container to be shadow roots; When + // this happens, we should use the shadow host here. + if (auto* shadowRoot = ShadowRoot::FromNode(aContainer)) { + aContainer = shadowRoot->GetHost(); + return; + } + }; + + rescope(startContainer); + rescope(endContainer); + + return nsContentUtils::GetCommonFlattenedTreeAncestorForSelection( + startContainer ? startContainer->AsContent() : nullptr, + endContainer ? endContainer->AsContent() : nullptr); + } + return nsContentUtils::GetClosestCommonInclusiveAncestor(startContainer, + endContainer); } // static @@ -237,9 +337,28 @@ nsresult AbstractRange::SetStartAndEndInternal( return NS_ERROR_DOM_INDEX_SIZE_ERR; } - // If they have different root, this should be collapsed at the end point. + // Different root if (newStartRoot != newEndRoot) { - aRange->DoSetRange(aEndBoundary, aEndBoundary, newEndRoot); + if (aRange->IsStaticRange()) { + // StaticRange allows nodes in different trees, so set start and end + // accordingly + aRange->DoSetRange(aStartBoundary, aEndBoundary, newEndRoot); + } else { + MOZ_ASSERT(aRange->IsDynamicRange()); + // In contrast, nsRange keeps both. It has a pair of start and end + // which they have been collapsed to one end, and it also may have a pair + // of start and end which are the original value. + aRange->DoSetRange(aEndBoundary, aEndBoundary, newEndRoot); + + // Don't create the cross shadow bounday range if the one of the roots is + // an UA widget regardless whether the boundaries are allowed to cross + // shadow boundary or not. + if (!IsRootUAWidget(newStartRoot) && !IsRootUAWidget(newEndRoot)) { + aRange->AsDynamicRange() + ->CreateOrUpdateCrossShadowBoundaryRangeIfNeeded(aStartBoundary, + aEndBoundary); + } + } return NS_OK; } @@ -274,7 +393,10 @@ void AbstractRange::RegisterSelection(Selection& aSelection) { bool isFirstSelection = mSelections.IsEmpty(); mSelections.AppendElement(&aSelection); if (isFirstSelection && !mRegisteredClosestCommonInclusiveAncestor) { - nsINode* commonAncestor = GetClosestCommonInclusiveAncestor(); + nsINode* commonAncestor = GetClosestCommonInclusiveAncestor( + StaticPrefs::dom_shadowdom_selection_across_boundary_enabled() + ? AllowRangeCrossShadowBoundary::Yes + : AllowRangeCrossShadowBoundary::No); MOZ_ASSERT(commonAncestor, "unexpected disconnected nodes"); RegisterClosestCommonInclusiveAncestor(commonAncestor); } @@ -358,7 +480,8 @@ void AbstractRange::UnregisterClosestCommonInclusiveAncestor( void AbstractRange::UpdateCommonAncestorIfNecessary() { nsINode* oldCommonAncestor = mRegisteredClosestCommonInclusiveAncestor; - nsINode* newCommonAncestor = GetClosestCommonInclusiveAncestor(); + nsINode* newCommonAncestor = + GetClosestCommonInclusiveAncestor(AllowRangeCrossShadowBoundary::Yes); if (newCommonAncestor != oldCommonAncestor) { if (oldCommonAncestor) { UnregisterClosestCommonInclusiveAncestor(oldCommonAncestor, false); @@ -379,6 +502,59 @@ void AbstractRange::UpdateCommonAncestorIfNecessary() { } } +const RangeBoundary& AbstractRange::MayCrossShadowBoundaryStartRef() const { + return IsDynamicRange() ? AsDynamicRange()->MayCrossShadowBoundaryStartRef() + : mStart; +} + +const RangeBoundary& AbstractRange::MayCrossShadowBoundaryEndRef() const { + return IsDynamicRange() ? AsDynamicRange()->MayCrossShadowBoundaryEndRef() + : mEnd; +} + +nsIContent* AbstractRange::GetMayCrossShadowBoundaryChildAtStartOffset() const { + return IsDynamicRange() + ? AsDynamicRange()->GetMayCrossShadowBoundaryChildAtStartOffset() + : mStart.GetChildAtOffset(); +} + +nsIContent* AbstractRange::GetMayCrossShadowBoundaryChildAtEndOffset() const { + return IsDynamicRange() + ? AsDynamicRange()->GetMayCrossShadowBoundaryChildAtEndOffset() + : mEnd.GetChildAtOffset(); +} + +nsINode* AbstractRange::GetMayCrossShadowBoundaryStartContainer() const { + return IsDynamicRange() + ? AsDynamicRange()->GetMayCrossShadowBoundaryStartContainer() + : mStart.Container(); +} + +nsINode* AbstractRange::GetMayCrossShadowBoundaryEndContainer() const { + return IsDynamicRange() + ? AsDynamicRange()->GetMayCrossShadowBoundaryEndContainer() + : mEnd.Container(); +} + +bool AbstractRange::MayCrossShadowBoundary() const { + return IsDynamicRange() ? !!AsDynamicRange()->GetCrossShadowBoundaryRange() + : false; +} + +uint32_t AbstractRange::MayCrossShadowBoundaryStartOffset() const { + return IsDynamicRange() + ? AsDynamicRange()->MayCrossShadowBoundaryStartOffset() + : static_cast(*mStart.Offset( + RangeBoundary::OffsetFilter::kValidOrInvalidOffsets)); +} + +uint32_t AbstractRange::MayCrossShadowBoundaryEndOffset() const { + return IsDynamicRange() + ? AsDynamicRange()->MayCrossShadowBoundaryEndOffset() + : static_cast(*mEnd.Offset( + RangeBoundary::OffsetFilter::kValidOrInvalidOffsets)); +} + nsINode* AbstractRange::GetParentObject() const { return mOwner; } JSObject* AbstractRange::WrapObject(JSContext* aCx, @@ -395,4 +571,12 @@ void AbstractRange::ClearForReuse() { mCalledByJS = false; } +/*static*/ +bool AbstractRange::IsRootUAWidget(const nsINode* aRoot) { + MOZ_ASSERT(aRoot); + if (const ShadowRoot* shadowRoot = ShadowRoot::FromNode(aRoot)) { + return shadowRoot->IsUAWidget(); + } + return false; +} } // namespace mozilla::dom diff --git a/dom/base/AbstractRange.h b/dom/base/AbstractRange.h index c70aaf19ec..2f9b59158a 100644 --- a/dom/base/AbstractRange.h +++ b/dom/base/AbstractRange.h @@ -31,10 +31,15 @@ class Document; class Selection; class StaticRange; +enum class AllowRangeCrossShadowBoundary : bool { No, Yes }; + class AbstractRange : public nsISupports, public nsWrapperCache, // For linking together selection-associated ranges. public mozilla::LinkedListElement { + using AllowRangeCrossShadowBoundary = + mozilla::dom::AllowRangeCrossShadowBoundary; + protected: explicit AbstractRange(nsINode* aNode, bool aIsDynamicRange); virtual ~AbstractRange(); @@ -51,18 +56,33 @@ class AbstractRange : public nsISupports, NS_DECL_CYCLE_COLLECTING_ISUPPORTS NS_DECL_CYCLE_COLLECTION_WRAPPERCACHE_CLASS(AbstractRange) + /** + * All of the MayCrossShadowBoundary* methods are used to get the boundary + * endpoints that cross shadow boundaries. They would return + * the same value as the non-MayCrossShadowBoundary* methods if the range + * boundaries don't cross shadow boundaries. + */ const RangeBoundary& StartRef() const { return mStart; } + const RangeBoundary& MayCrossShadowBoundaryStartRef() const; + const RangeBoundary& EndRef() const { return mEnd; } + const RangeBoundary& MayCrossShadowBoundaryEndRef() const; nsIContent* GetChildAtStartOffset() const { return mStart.GetChildAtOffset(); } + nsIContent* GetMayCrossShadowBoundaryChildAtStartOffset() const; + nsIContent* GetChildAtEndOffset() const { return mEnd.GetChildAtOffset(); } + nsIContent* GetMayCrossShadowBoundaryChildAtEndOffset() const; + bool IsPositioned() const { return mIsPositioned; } /** * https://dom.spec.whatwg.org/#concept-tree-inclusive-ancestor */ - nsINode* GetClosestCommonInclusiveAncestor() const; + nsINode* GetClosestCommonInclusiveAncestor( + AllowRangeCrossShadowBoundary aAllowCrossShadowBoundary = + AllowRangeCrossShadowBoundary::No) const; // WebIDL @@ -75,7 +95,12 @@ class AbstractRange : public nsISupports, // `IsPositioned()` directly. nsINode* GetStartContainer() const { return mStart.Container(); } + nsINode* GetMayCrossShadowBoundaryStartContainer() const; + nsINode* GetEndContainer() const { return mEnd.Container(); } + nsINode* GetMayCrossShadowBoundaryEndContainer() const; + + bool MayCrossShadowBoundary() const; Document* GetComposedDocOfContainers() const { return mStart.Container() ? mStart.Container()->GetComposedDoc() : nullptr; @@ -86,12 +111,15 @@ class AbstractRange : public nsISupports, return static_cast( *mStart.Offset(RangeBoundary::OffsetFilter::kValidOrInvalidOffsets)); } + uint32_t MayCrossShadowBoundaryStartOffset() const; // FYI: Returns 0 if it's not positioned. uint32_t EndOffset() const { return static_cast( *mEnd.Offset(RangeBoundary::OffsetFilter::kValidOrInvalidOffsets)); } + uint32_t MayCrossShadowBoundaryEndOffset() const; + bool Collapsed() const { return !mIsPositioned || (mStart.Container() == mEnd.Container() && StartOffset() == EndOffset()); @@ -132,6 +160,11 @@ class AbstractRange : public nsISupports, */ bool IsInSelection(const mozilla::dom::Selection& aSelection) const; + /** + * Return true if aRoot is a UA shadow root. + */ + static bool IsRootUAWidget(const nsINode* aRoot); + protected: template diff --git a/dom/base/BodyUtil.cpp b/dom/base/BodyUtil.cpp index e8de3d18ec..45ca58e2d9 100644 --- a/dom/base/BodyUtil.cpp +++ b/dom/base/BodyUtil.cpp @@ -422,9 +422,10 @@ already_AddRefed BodyUtil::ConsumeFormData( if (isValidUrlEncodedMimeType) { RefPtr fd = new FormData(aParent); DebugOnly status = URLParams::Parse( - aStr, true, [&fd](const nsAString& aName, const nsAString& aValue) { - ErrorResult rv; - fd->Append(aName, aValue, rv); + aStr, true, [&fd](const nsACString& aName, const nsACString& aValue) { + IgnoredErrorResult rv; + fd->Append(NS_ConvertUTF8toUTF16(aName), + NS_ConvertUTF8toUTF16(aValue), rv); MOZ_ASSERT(!rv.Failed()); return true; }); diff --git a/dom/base/ChromeUtils.cpp b/dom/base/ChromeUtils.cpp index 0df1cd3c9b..407f33e044 100644 --- a/dom/base/ChromeUtils.cpp +++ b/dom/base/ChromeUtils.cpp @@ -1289,9 +1289,10 @@ void ChromeUtils::GetBaseDomainFromPartitionKey(dom::GlobalObject& aGlobal, nsString scheme; nsString pkBaseDomain; int32_t port; + bool ancestor; - if (!mozilla::OriginAttributes::ParsePartitionKey(aPartitionKey, scheme, - pkBaseDomain, port)) { + if (!mozilla::OriginAttributes::ParsePartitionKey( + aPartitionKey, scheme, pkBaseDomain, port, ancestor)) { aRv.Throw(NS_ERROR_FAILURE); return; } @@ -1317,7 +1318,10 @@ void ChromeUtils::GetPartitionKeyFromURL(dom::GlobalObject& aGlobal, } mozilla::OriginAttributes attrs; - attrs.SetPartitionKey(uri); + // For now, uses assume the partition key is cross-site. + // We will need to not make this assumption to allow access + // to same-site partitioned cookies in the cookie extension API. + attrs.SetPartitionKey(uri, false); aPartitionKey = attrs.mPartitionKey; } @@ -1515,10 +1519,10 @@ already_AddRefed ChromeUtils::RequestProcInfo(GlobalObject& aGlobal, // DOM windows. /* aUtilityInfo = */ std::move(utilityActors), /* aChild = */ 0 // Without a ContentProcess, no ChildId. -#ifdef XP_MACOSX +#ifdef XP_DARWIN , /* aChildTask = */ aGeckoProcess->GetChildTask() -#endif // XP_MACOSX +#endif // XP_DARWIN ); }); @@ -1617,10 +1621,10 @@ already_AddRefed ChromeUtils::RequestProcInfo(GlobalObject& aGlobal, /* aWindowInfo = */ std::move(windows), /* aUtilityInfo = */ nsTArray(), /* aChild = */ contentParent->ChildID() -#ifdef XP_MACOSX +#ifdef XP_DARWIN , /* aChildTask = */ contentParent->Process()->GetChildTask() -#endif // XP_MACOSX +#endif // XP_DARWIN ); } diff --git a/dom/base/ContentIterator.cpp b/dom/base/ContentIterator.cpp index 6819353520..0b405f0348 100644 --- a/dom/base/ContentIterator.cpp +++ b/dom/base/ContentIterator.cpp @@ -7,6 +7,7 @@ #include "ContentIterator.h" #include "mozilla/Assertions.h" +#include "mozilla/dom/ShadowRoot.h" #include "mozilla/DebugOnly.h" #include "mozilla/RangeBoundary.h" #include "mozilla/RangeUtils.h" @@ -26,6 +27,69 @@ using namespace dom; __VA_ARGS__); \ template aResultType ContentIteratorBase::aMethodName(__VA_ARGS__) +/** + * IteratorHelpers contains the static methods to help extra values + * based on whether or not the iterator allows to iterate nodes cross the shadow + * boundary. + */ +struct IteratorHelpers { + IteratorHelpers() = delete; + + static nsINode* GetStartContainer(AbstractRange* aRange, + bool aAllowCrossShadowBoundary) { + MOZ_ASSERT(aRange); + return (StaticPrefs::dom_shadowdom_selection_across_boundary_enabled() && + aAllowCrossShadowBoundary) + ? aRange->GetMayCrossShadowBoundaryStartContainer() + : aRange->GetStartContainer(); + } + + static int32_t StartOffset(AbstractRange* aRange, + bool aAllowCrossShadowBoundary) { + MOZ_ASSERT(aRange); + return (StaticPrefs::dom_shadowdom_selection_across_boundary_enabled() && + aAllowCrossShadowBoundary) + ? aRange->MayCrossShadowBoundaryStartOffset() + : aRange->StartOffset(); + } + + static nsINode* GetEndContainer(AbstractRange* aRange, + bool aAllowCrossShadowBoundary) { + MOZ_ASSERT(aRange); + return (StaticPrefs::dom_shadowdom_selection_across_boundary_enabled() && + aAllowCrossShadowBoundary) + ? aRange->GetMayCrossShadowBoundaryEndContainer() + : aRange->GetEndContainer(); + } + + static int32_t EndOffset(AbstractRange* aRange, + bool aAllowCrossShadowBoundary) { + MOZ_ASSERT(aRange); + return (StaticPrefs::dom_shadowdom_selection_across_boundary_enabled() && + aAllowCrossShadowBoundary) + ? aRange->MayCrossShadowBoundaryEndOffset() + : aRange->EndOffset(); + } + + // FIXME(sefeng): This doesn't work with slots / flattened tree. + static nsINode* GetParentNode(nsINode& aNode, + bool aAllowCrossShadowBoundary) { + return (StaticPrefs::dom_shadowdom_selection_across_boundary_enabled() && + aAllowCrossShadowBoundary) + ? aNode.GetParentOrShadowHostNode() + : aNode.GetParentNode(); + } + + static ShadowRoot* GetShadowRoot(const nsINode* aNode, + bool aAllowCrossShadowBoundary) { + MOZ_ASSERT(aNode); + return (StaticPrefs::dom_shadowdom_selection_across_boundary_enabled() && + aAllowCrossShadowBoundary) + ? aNode->GetShadowRootForSelection() + : nullptr; + } +}; + static bool ComparePostMode(const RawRangeBoundary& aStart, const RawRangeBoundary& aEnd, nsINode& aNode) { nsINode* parent = aNode.GetParentNode(); @@ -541,17 +605,45 @@ nsINode* ContentIteratorBase::GetDeepFirstChild(nsINode* aRoot) { // static template nsIContent* ContentIteratorBase::GetDeepFirstChild( - nsIContent* aRoot) { + nsIContent* aRoot, bool aAllowCrossShadowBoundary) { if (NS_WARN_IF(!aRoot)) { return nullptr; } nsIContent* node = aRoot; - nsIContent* child = node->GetFirstChild(); + nsIContent* child = nullptr; + + if (ShadowRoot* shadowRoot = + IteratorHelpers::GetShadowRoot(node, aAllowCrossShadowBoundary)) { + // When finding the deepest child of node, if this node has a + // web exposed shadow root, we use this shadow root to find the deepest + // child. + // If the first candidate should be a slotted content, + // shadowRoot->GetFirstChild() should be able to return the element. + // It's probably correct I think. Then it's up to the caller of this + // iterator to decide whether to use the slot's assigned nodes or not. + MOZ_ASSERT(aAllowCrossShadowBoundary); + child = shadowRoot->GetFirstChild(); + } else { + child = node->GetFirstChild(); + } while (child) { node = child; - child = node->GetFirstChild(); + if (ShadowRoot* shadowRoot = + IteratorHelpers::GetShadowRoot(node, aAllowCrossShadowBoundary)) { + // When finding the deepest child of node, if this node has a + // web exposed shadow root, we use this shadow root to find the deepest + // child. + // If the first candidate should be a slotted content, + // shadowRoot->GetFirstChild() should be able to return the + // element. It's probably correct I think. Then it's up to the caller of + // this iterator to decide whether to use the slot's assigned nodes or + // not. + child = shadowRoot->GetFirstChild(); + } else { + child = node->GetFirstChild(); + } } return node; @@ -569,23 +661,41 @@ nsINode* ContentIteratorBase::GetDeepLastChild(nsINode* aRoot) { // static template -nsIContent* ContentIteratorBase::GetDeepLastChild(nsIContent* aRoot) { +nsIContent* ContentIteratorBase::GetDeepLastChild( + nsIContent* aRoot, bool aAllowCrossShadowBoundary) { if (NS_WARN_IF(!aRoot)) { return nullptr; } nsIContent* node = aRoot; - while (node->HasChildren()) { - nsIContent* child = node->GetLastChild(); - node = child; + + ShadowRoot* shadowRoot = + IteratorHelpers::GetShadowRoot(node, aAllowCrossShadowBoundary); + // FIXME(sefeng): This doesn't work with slots / flattened tree. + while (node->HasChildren() || (shadowRoot && shadowRoot->HasChildren())) { + if (node->HasChildren()) { + node = node->GetLastChild(); + } else { + MOZ_ASSERT(shadowRoot); + // If this node doesn't have a child, but it's also a shadow host + // that can be selected, we go into this shadow tree. + node = shadowRoot->GetLastChild(); + } + shadowRoot = + IteratorHelpers::GetShadowRoot(node, aAllowCrossShadowBoundary); } return node; } -// Get the next sibling, or parent's next sibling, or grandpa's next sibling... +// Get the next sibling, or parent's next sibling, or shadow host's next +// sibling (when aAllowCrossShadowBoundary is true), or grandpa's next +// sibling... +// // static +// template -nsIContent* ContentIteratorBase::GetNextSibling(nsINode* aNode) { +nsIContent* ContentIteratorBase::GetNextSibling( + nsINode* aNode, bool aAllowCrossShadowBoundary) { if (NS_WARN_IF(!aNode)) { return nullptr; } @@ -594,18 +704,32 @@ nsIContent* ContentIteratorBase::GetNextSibling(nsINode* aNode) { return next; } - nsINode* parent = aNode->GetParentNode(); + nsINode* parent = + IteratorHelpers::GetParentNode(*aNode, aAllowCrossShadowBoundary); if (NS_WARN_IF(!parent)) { return nullptr; } - return ContentIteratorBase::GetNextSibling(parent); + if (aAllowCrossShadowBoundary) { + // This is temporary solution. + // For shadow root, instead of getting to the sibling of the parent + // directly, we need to get into the light tree of the parent to handle + // slotted contents. + if (ShadowRoot* shadowRoot = ShadowRoot::FromNode(aNode)) { + if (nsIContent* child = parent->GetFirstChild()) { + return child; + } + } + } + + return ContentIteratorBase::GetNextSibling(parent, aAllowCrossShadowBoundary); } -// Get the prev sibling, or parent's prev sibling, or grandpa's prev sibling... -// static +// Get the prev sibling, or parent's prev sibling, or shadow host's prev sibling +// (when aAllowCrossShadowBoundary is true), or grandpa's prev sibling... static template -nsIContent* ContentIteratorBase::GetPrevSibling(nsINode* aNode) { +nsIContent* ContentIteratorBase::GetPrevSibling( + nsINode* aNode, bool aAllowCrossShadowBoundary) { if (NS_WARN_IF(!aNode)) { return nullptr; } @@ -614,12 +738,13 @@ nsIContent* ContentIteratorBase::GetPrevSibling(nsINode* aNode) { return prev; } - nsINode* parent = aNode->GetParentNode(); + nsINode* parent = + IteratorHelpers::GetParentNode(*aNode, aAllowCrossShadowBoundary); if (NS_WARN_IF(!parent)) { return nullptr; } - return ContentIteratorBase::GetPrevSibling(parent); + return ContentIteratorBase::GetPrevSibling(parent, aAllowCrossShadowBoundary); } template @@ -860,19 +985,41 @@ nsresult ContentSubtreeIterator::Init(const RawRangeBoundary& aStartBoundary, return InitWithRange(); } +nsresult ContentSubtreeIterator::InitWithAllowCrossShadowBoundary( + AbstractRange* aRange) { + MOZ_ASSERT(aRange); + + if (NS_WARN_IF(!aRange->IsPositioned())) { + return NS_ERROR_INVALID_ARG; + } + + mRange = aRange; + + mAllowCrossShadowBoundary = AllowRangeCrossShadowBoundary::Yes; + return InitWithRange(); +} + void ContentSubtreeIterator::CacheInclusiveAncestorsOfEndContainer() { mInclusiveAncestorsOfEndContainer.Clear(); - nsINode* const endContainer = mRange->GetEndContainer(); + nsINode* const endContainer = + IteratorHelpers::GetEndContainer(mRange, IterAllowCrossShadowBoundary()); nsIContent* endNode = endContainer->IsContent() ? endContainer->AsContent() : nullptr; while (endNode) { mInclusiveAncestorsOfEndContainer.AppendElement(endNode); - endNode = endNode->GetParent(); + // Cross the boundary for contents in shadow tree. + nsINode* parent = IteratorHelpers::GetParentNode( + *endNode, IterAllowCrossShadowBoundary()); + if (!parent || !parent->IsContent()) { + break; + } + endNode = parent->AsContent(); } } nsIContent* ContentSubtreeIterator::DetermineCandidateForFirstContent() const { - nsINode* startContainer = mRange->GetStartContainer(); + nsINode* startContainer = IteratorHelpers::GetStartContainer( + mRange, IterAllowCrossShadowBoundary()); nsIContent* firstCandidate = nullptr; // find first node in range nsINode* node = nullptr; @@ -880,9 +1027,14 @@ nsIContent* ContentSubtreeIterator::DetermineCandidateForFirstContent() const { // no children, start at the node itself node = startContainer; } else { - nsIContent* child = mRange->GetChildAtStartOffset(); - MOZ_ASSERT(child == - startContainer->GetChildAt_Deprecated(mRange->StartOffset())); + nsIContent* child = + IterAllowCrossShadowBoundary() + ? mRange->GetMayCrossShadowBoundaryChildAtStartOffset() + : mRange->GetChildAtStartOffset(); + + MOZ_ASSERT(child == startContainer->GetChildAt_Deprecated( + IteratorHelpers::StartOffset( + mRange, IterAllowCrossShadowBoundary()))); if (!child) { // offset after last child node = startContainer; @@ -893,11 +1045,13 @@ nsIContent* ContentSubtreeIterator::DetermineCandidateForFirstContent() const { if (!firstCandidate) { // then firstCandidate is next node after node - firstCandidate = ContentIteratorBase::GetNextSibling(node); + firstCandidate = ContentIteratorBase::GetNextSibling( + node, IterAllowCrossShadowBoundary()); } if (firstCandidate) { - firstCandidate = ContentIteratorBase::GetDeepFirstChild(firstCandidate); + firstCandidate = ContentIteratorBase::GetDeepFirstChild( + firstCandidate, IterAllowCrossShadowBoundary()); } return firstCandidate; @@ -926,9 +1080,12 @@ nsIContent* ContentSubtreeIterator::DetermineFirstContent() const { nsIContent* ContentSubtreeIterator::DetermineCandidateForLastContent() const { nsIContent* lastCandidate{nullptr}; - nsINode* endContainer = mRange->GetEndContainer(); + nsINode* endContainer = + IteratorHelpers::GetEndContainer(mRange, IterAllowCrossShadowBoundary()); // now to find the last node - int32_t offset = mRange->EndOffset(); + int32_t offset = + IteratorHelpers::EndOffset(mRange, IterAllowCrossShadowBoundary()); + int32_t numChildren = endContainer->GetChildCount(); nsINode* node = nullptr; @@ -939,7 +1096,9 @@ nsIContent* ContentSubtreeIterator::DetermineCandidateForLastContent() const { if (!offset || !numChildren) { node = endContainer; } else { - lastCandidate = mRange->EndRef().Ref(); + lastCandidate = IterAllowCrossShadowBoundary() + ? mRange->MayCrossShadowBoundaryEndRef().Ref() + : mRange->EndRef().Ref(); MOZ_ASSERT(lastCandidate == endContainer->GetChildAt_Deprecated(--offset)); NS_ASSERTION(lastCandidate, "tree traversal trouble in ContentSubtreeIterator::Init"); @@ -947,11 +1106,13 @@ nsIContent* ContentSubtreeIterator::DetermineCandidateForLastContent() const { if (!lastCandidate) { // then lastCandidate is prev node before node - lastCandidate = ContentIteratorBase::GetPrevSibling(node); + lastCandidate = ContentIteratorBase::GetPrevSibling( + node, IterAllowCrossShadowBoundary()); } if (lastCandidate) { - lastCandidate = ContentIteratorBase::GetDeepLastChild(lastCandidate); + lastCandidate = ContentIteratorBase::GetDeepLastChild( + lastCandidate, IterAllowCrossShadowBoundary()); } return lastCandidate; @@ -962,11 +1123,17 @@ nsresult ContentSubtreeIterator::InitWithRange() { MOZ_ASSERT(mRange->IsPositioned()); // get the start node and offset, convert to nsINode - mClosestCommonInclusiveAncestor = mRange->GetClosestCommonInclusiveAncestor(); - nsINode* startContainer = mRange->GetStartContainer(); - const int32_t startOffset = mRange->StartOffset(); - nsINode* endContainer = mRange->GetEndContainer(); - const int32_t endOffset = mRange->EndOffset(); + mClosestCommonInclusiveAncestor = + mRange->GetClosestCommonInclusiveAncestor(mAllowCrossShadowBoundary); + + nsINode* startContainer = IteratorHelpers::GetStartContainer( + mRange, IterAllowCrossShadowBoundary()); + const int32_t startOffset = + IteratorHelpers::StartOffset(mRange, IterAllowCrossShadowBoundary()); + nsINode* endContainer = + IteratorHelpers::GetEndContainer(mRange, IterAllowCrossShadowBoundary()); + const int32_t endOffset = + IteratorHelpers::EndOffset(mRange, IterAllowCrossShadowBoundary()); MOZ_ASSERT(mClosestCommonInclusiveAncestor && startContainer && endContainer); // Bug 767169 MOZ_ASSERT(uint32_t(startOffset) <= startContainer->Length() && @@ -1044,14 +1211,23 @@ void ContentSubtreeIterator::Next() { return; } - nsINode* nextNode = ContentIteratorBase::GetNextSibling(mCurNode); + nsINode* nextNode = ContentIteratorBase::GetNextSibling( + mCurNode, IterAllowCrossShadowBoundary()); + NS_ASSERTION(nextNode, "No next sibling!?! This could mean deadlock!"); int32_t i = mInclusiveAncestorsOfEndContainer.IndexOf(nextNode); while (i != -1) { // as long as we are finding ancestors of the endpoint of the range, // dive down into their children - nextNode = nextNode->GetFirstChild(); + ShadowRoot* root = IteratorHelpers::GetShadowRoot( + Element::FromNode(nextNode), IterAllowCrossShadowBoundary()); + if (!root) { + nextNode = nextNode->GetFirstChild(); + } else { + nextNode = mRange->MayCrossShadowBoundary() ? root->GetFirstChild() + : nextNode->GetFirstChild(); + } NS_ASSERTION(nextNode, "Iterator error, expected a child node!"); // should be impossible to get a null pointer. If we went all the way @@ -1098,7 +1274,8 @@ nsresult ContentSubtreeIterator::PositionAt(nsINode* aCurNode) { nsIContent* ContentSubtreeIterator::GetTopAncestorInRange( nsINode* aNode) const { - if (!aNode || !aNode->GetParentNode()) { + if (!aNode || + !IteratorHelpers::GetParentNode(*aNode, IterAllowCrossShadowBoundary())) { return nullptr; } @@ -1114,15 +1291,23 @@ nsIContent* ContentSubtreeIterator::GetTopAncestorInRange( return nullptr; } + nsIContent* lastContentInShadowTree = nullptr; while (content) { - nsIContent* parent = content->GetParent(); + nsINode* parent = IteratorHelpers::GetParentNode( + *content, IterAllowCrossShadowBoundary()); + // content always has a parent. If its parent is the root, however -- // i.e., either it's not content, or it is content but its own parent is // null -- then we're finished, since we don't go up to the root. // + // Caveat: If iteration crossing shadow boundary is allowed + // and the root is a shadow root, we keep going up to the + // shadow host and continue. + // // We have to special-case this because CompareNodeToRange treats the root // node differently -- see bug 765205. - if (!parent || !parent->GetParentNode()) { + if (!parent || !IteratorHelpers::GetParentNode( + *parent, IterAllowCrossShadowBoundary())) { return content; } @@ -1130,10 +1315,28 @@ nsIContent* ContentSubtreeIterator::GetTopAncestorInRange( RangeUtils::IsNodeContainedInRange(*parent, mRange); MOZ_ALWAYS_TRUE(isNodeContainedInRange); if (!isNodeContainedInRange.value()) { + if (IterAllowCrossShadowBoundary() && content->IsShadowRoot()) { + MOZ_ASSERT(parent->GetShadowRoot() == content); + // host element is not in range, the last content in tree + // should be the ancestor. + MOZ_ASSERT(lastContentInShadowTree); + return lastContentInShadowTree; + } return content; } - content = parent; + // When we cross the boundary, we keep a reference to the + // last content that is in tree, because if we later + // find the shadow host element is not in the range, that means + // the last content in the tree should be top ancestor in range. + // + // Using shadow root doesn't make sense here because it doesn't + // represent a actual content. + if (IterAllowCrossShadowBoundary() && parent->IsShadowRoot()) { + lastContentInShadowTree = content; + } + + content = parent->AsContent(); } MOZ_CRASH("This should only be possible if aNode was null"); diff --git a/dom/base/ContentIterator.h b/dom/base/ContentIterator.h index b645c4147e..67962d41d5 100644 --- a/dom/base/ContentIterator.h +++ b/dom/base/ContentIterator.h @@ -82,15 +82,26 @@ class ContentIteratorBase { // Recursively get the deepest first/last child of aRoot. This will return // aRoot itself if it has no children. static nsINode* GetDeepFirstChild(nsINode* aRoot); - static nsIContent* GetDeepFirstChild(nsIContent* aRoot); + // If aAllowCrossShadowBoundary is true, it'll continue with the shadow tree + // when it reaches to a shadow host. + static nsIContent* GetDeepFirstChild(nsIContent* aRoot, + bool aAllowCrossShadowBoundary); static nsINode* GetDeepLastChild(nsINode* aRoot); - static nsIContent* GetDeepLastChild(nsIContent* aRoot); + // If aAllowCrossShadowBoundary is true, it'll continue with the shadow tree + // when it reaches to a shadow host. + static nsIContent* GetDeepLastChild(nsIContent* aRoot, + bool aAllowCrossShadowBoundary); // Get the next/previous sibling of aNode, or its parent's, or grandparent's, // etc. Returns null if aNode and all its ancestors have no next/previous // sibling. - static nsIContent* GetNextSibling(nsINode* aNode); - static nsIContent* GetPrevSibling(nsINode* aNode); + // + // If aAllowCrossShadowBoundary is true, it'll continue with the shadow host + // when it reaches to a shadow root. + static nsIContent* GetNextSibling(nsINode* aNode, + bool aAllowCrossShadowBoundary = false); + static nsIContent* GetPrevSibling(nsINode* aNode, + bool aAllowCrossShadowBoundary = false); nsINode* NextNode(nsINode* aNode); nsINode* PrevNode(nsINode* aNode); @@ -219,6 +230,29 @@ class ContentSubtreeIterator final : public SafeContentIteratorBase { virtual nsresult Init(nsINode* aRoot) override; virtual nsresult Init(dom::AbstractRange* aRange) override; + + /** + * Initialize the iterator with aRange that does correct things + * when the aRange's start and/or the end containers are + * in shadow dom. + * + * If both start and end containers are in light dom, the iterator + * won't do anything special. + * + * When the start container is in shadow dom, the iterator can + * find the correct start node by crossing the shadow + * boundary when needed. + * + * When the end container is in shadow dom, the iterator can find + * the correct end node by crossing the shadow boundary when + * needed. Also when the next node is an ancestor of + * the end node, it can correctly iterate into the + * subtree of it by crossing the shadow boundary. + * + * Examples of what nodes will be returned can be found + * at test_content_iterator_subtree_shadow_tree.html. + */ + nsresult InitWithAllowCrossShadowBoundary(dom::AbstractRange* aRange); virtual nsresult Init(nsINode* aStartContainer, uint32_t aStartOffset, nsINode* aEndContainer, uint32_t aEndOffset) override; virtual nsresult Init(const RawRangeBoundary& aStartBoundary, @@ -276,10 +310,18 @@ class ContentSubtreeIterator final : public SafeContentIteratorBase { // the range's start and end nodes will never be considered "in" it. nsIContent* GetTopAncestorInRange(nsINode* aNode) const; + bool IterAllowCrossShadowBoundary() const { + return mAllowCrossShadowBoundary == dom::AllowRangeCrossShadowBoundary::Yes; + } + RefPtr mRange; // See . AutoTArray mInclusiveAncestorsOfEndContainer; + + // Whether this iterator allows to iterate nodes across shadow boundary. + dom::AllowRangeCrossShadowBoundary mAllowCrossShadowBoundary = + dom::AllowRangeCrossShadowBoundary::No; }; } // namespace mozilla diff --git a/dom/base/ContentProcessMessageManager.cpp b/dom/base/ContentProcessMessageManager.cpp index 7661d1036f..9723341782 100644 --- a/dom/base/ContentProcessMessageManager.cpp +++ b/dom/base/ContentProcessMessageManager.cpp @@ -104,6 +104,7 @@ JSObject* ContentProcessMessageManager::GetOrCreateWrapper() { jsapi.Init(); if (!GetOrCreateDOMReflectorNoWrap(jsapi.cx(), this, &val)) { + JS_ClearPendingException(jsapi.cx()); return nullptr; } } @@ -111,11 +112,15 @@ JSObject* ContentProcessMessageManager::GetOrCreateWrapper() { return &val.toObject(); } -void ContentProcessMessageManager::LoadScript(const nsAString& aURL) { +bool ContentProcessMessageManager::LoadScript(const nsAString& aURL) { Init(); - JS::Rooted messageManager(mozilla::dom::RootingCx(), - GetOrCreateWrapper()); - LoadScriptInternal(messageManager, aURL, true); + JSObject* wrapper = GetOrCreateWrapper(); + if (wrapper) { + JS::Rooted messageManager(mozilla::dom::RootingCx(), wrapper); + LoadScriptInternal(messageManager, aURL, true); + return true; + } + return false; } void ContentProcessMessageManager::SetInitialProcessData( diff --git a/dom/base/ContentProcessMessageManager.h b/dom/base/ContentProcessMessageManager.h index b7c54ba452..0d437b5e50 100644 --- a/dom/base/ContentProcessMessageManager.h +++ b/dom/base/ContentProcessMessageManager.h @@ -58,7 +58,7 @@ class ContentProcessMessageManager : public nsIMessageSender, virtual JSObject* WrapObject(JSContext* aCx, JS::Handle aGivenProto) override; - JSObject* GetOrCreateWrapper(); + [[nodiscard]] JSObject* GetOrCreateWrapper(); using MessageManagerGlobal::AddMessageListener; using MessageManagerGlobal::AddWeakMessageListener; @@ -84,7 +84,7 @@ class ContentProcessMessageManager : public nsIMessageSender, return xpc::NativeGlobal(xpc::PrivilegedJunkScope()); } - virtual void LoadScript(const nsAString& aURL); + [[nodiscard]] virtual bool LoadScript(const nsAString& aURL); bool IsProcessScoped() const override { return true; } diff --git a/dom/base/DirectionalityUtils.cpp b/dom/base/DirectionalityUtils.cpp index dd427c61b1..2e4ada4800 100644 --- a/dom/base/DirectionalityUtils.cpp +++ b/dom/base/DirectionalityUtils.cpp @@ -191,10 +191,8 @@ static bool ParticipatesInAutoDirection(const nsIContent* aContent) { if (aContent->IsShadowRoot()) { return true; } - dom::NodeInfo* ni = aContent->NodeInfo(); - return ni->NamespaceID() == kNameSpaceID_XHTML && - !ni->Equals(nsGkAtoms::script) && !ni->Equals(nsGkAtoms::style) && - !ni->Equals(nsGkAtoms::input) && !ni->Equals(nsGkAtoms::textarea); + return !aContent->IsAnyOfHTMLElements(nsGkAtoms::script, nsGkAtoms::style, + nsGkAtoms::input, nsGkAtoms::textarea); } /** diff --git a/dom/base/Document.cpp b/dom/base/Document.cpp index 4e9286a91e..8cbf8b8075 100644 --- a/dom/base/Document.cpp +++ b/dom/base/Document.cpp @@ -172,6 +172,8 @@ #include "mozilla/dom/FeaturePolicy.h" #include "mozilla/dom/FeaturePolicyUtils.h" #include "mozilla/dom/FontFaceSet.h" +#include "mozilla/dom/FragmentDirective.h" +#include "mozilla/dom/fragmentdirectives_ffi_generated.h" #include "mozilla/dom/FromParser.h" #include "mozilla/dom/HighlightRegistry.h" #include "mozilla/dom/HTMLAllCollection.h" @@ -198,6 +200,7 @@ #include "mozilla/dom/NetErrorInfoBinding.h" #include "mozilla/dom/NodeInfo.h" #include "mozilla/dom/NodeIterator.h" +#include "mozilla/dom/nsHTTPSOnlyUtils.h" #include "mozilla/dom/PContentChild.h" #include "mozilla/dom/PWindowGlobalChild.h" #include "mozilla/dom/PageTransitionEvent.h" @@ -1437,7 +1440,6 @@ Document::Document(const char* aContentType) mThrowOnDynamicMarkupInsertionCounter(0), mIgnoreOpensDuringUnloadCounter(0), mSavedResolution(1.0f), - mSavedResolutionBeforeMVM(1.0f), mGeneration(0), mCachedTabSizeGeneration(0), mNextFormNumber(0), @@ -2484,6 +2486,7 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INTERNAL(Document) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFontFaceSet) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mReadyForIdle) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocumentL10n) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFragmentDirective) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mHighlightRegistry) // Traverse all Document nsCOMPtrs. @@ -2631,6 +2634,7 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(Document) NS_IMPL_CYCLE_COLLECTION_UNLINK(mFontFaceSet) NS_IMPL_CYCLE_COLLECTION_UNLINK(mReadyForIdle) NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocumentL10n) + NS_IMPL_CYCLE_COLLECTION_UNLINK(mFragmentDirective) NS_IMPL_CYCLE_COLLECTION_UNLINK(mHighlightRegistry) NS_IMPL_CYCLE_COLLECTION_UNLINK(mParser) NS_IMPL_CYCLE_COLLECTION_UNLINK(mOnloadBlocker) @@ -4065,6 +4069,21 @@ void Document::StopDocumentLoad() { void Document::SetDocumentURI(nsIURI* aURI) { nsCOMPtr oldBase = GetDocBaseURI(); mDocumentURI = aURI; + // This loosely implements §3.4.1 of Text Fragments + // https://wicg.github.io/scroll-to-text-fragment/#invoking-text-directives + // Unlike specified in the spec, the fragment directive is not stripped from + // the URL in the session history entry. Instead it is removed when the URL is + // set in the `Document`. Also, instead of storing the `uninvokedDirective` in + // `Document` as mentioned in the spec, the extracted directives are moved to + // the `FragmentDirective` object which deals with finding the ranges to + // highlight in `ScrollToRef()`. + // XXX(:jjaschke): This is only a temporary solution. + // https://bugzil.la/1881429 is filed for revisiting this. + nsTArray textDirectives; + FragmentDirective::ParseAndRemoveFragmentDirectiveFromFragment( + mDocumentURI, &textDirectives); + FragmentDirective()->SetTextDirectives(std::move(textDirectives)); + nsIURI* newBase = GetDocBaseURI(); mChromeRulesEnabled = URLExtraData::ChromeRulesEnabled(aURI); @@ -4103,10 +4122,11 @@ void Document::SetDocumentURI(nsIURI* aURI) { } } -static void GetFormattedTimeString(PRTime aTime, +static void GetFormattedTimeString(PRTime aTime, bool aUniversal, nsAString& aFormattedTimeString) { PRExplodedTime prtime; - PR_ExplodeTime(aTime, PR_LocalTimeParameters, &prtime); + PR_ExplodeTime(aTime, aUniversal ? PR_GMTParameters : PR_LocalTimeParameters, + &prtime); // "MM/DD/YYYY hh:mm:ss" char formatedTime[24]; if (SprintfLiteral(formatedTime, "%02d/%02d/%04d %02d:%02d:%02d", @@ -4124,7 +4144,9 @@ void Document::GetLastModified(nsAString& aLastModified) const { if (!mLastModified.IsEmpty()) { aLastModified.Assign(mLastModified); } else { - GetFormattedTimeString(PR_Now(), aLastModified); + GetFormattedTimeString(PR_Now(), + ShouldResistFingerprinting(RFPTarget::JSDateTimeUTC), + aLastModified); } } @@ -6401,7 +6423,7 @@ void Document::SetLastFocusTime(const TimeStamp& aFocusTime) { mLastFocusTime = aFocusTime; } -void Document::GetReferrer(nsAString& aReferrer) const { +void Document::GetReferrer(nsACString& aReferrer) const { aReferrer.Truncate(); if (!mReferrerInfo) { return; @@ -6412,13 +6434,7 @@ void Document::GetReferrer(nsAString& aReferrer) const { return; } - nsAutoCString uri; - nsresult rv = URLDecorationStripper::StripTrackingIdentifiers(referrer, uri); - if (NS_WARN_IF(NS_FAILED(rv))) { - return; - } - - CopyUTF8toUTF16(uri, aReferrer); + URLDecorationStripper::StripTrackingIdentifiers(referrer, aReferrer); } void Document::GetCookie(nsAString& aCookie, ErrorResult& aRv) { @@ -7683,6 +7699,10 @@ static void NotifyActivityChangedCallback(nsISupports* aSupports) { void Document::NotifyActivityChanged() { EnumerateActivityObservers(NotifyActivityChangedCallback); + // https://w3c.github.io/screen-wake-lock/#handling-document-loss-of-full-activity + if (!IsActive()) { + UnlockAllWakeLocks(WakeLockType::Screen); + } } void Document::SetContainer(nsDocShell* aContainer) { @@ -11111,7 +11131,9 @@ void Document::RetrieveRelevantHeaders(nsIChannel* aChannel) { mLastModified.Truncate(); if (modDate != 0) { - GetFormattedTimeString(modDate, mLastModified); + GetFormattedTimeString(modDate, + ShouldResistFingerprinting(RFPTarget::JSDateTimeUTC), + mLastModified); } } @@ -12537,14 +12559,6 @@ void Document::UpdateDocumentStates(DocumentState aMaybeChangedStates, } } - if (aMaybeChangedStates.HasAtLeastOneOfStates(DocumentState::LWTHEME)) { - if (ComputeDocumentLWTheme()) { - mState |= DocumentState::LWTHEME; - } else { - mState &= ~DocumentState::LWTHEME; - } - } - if (aMaybeChangedStates.HasState(DocumentState::WINDOW_INACTIVE)) { BrowsingContext* bc = GetBrowsingContext(); if (!bc || !bc->GetIsActiveBrowserWindow()) { @@ -13089,25 +13103,29 @@ void Document::SetScrollToRef(nsIURI* aDocumentURI) { // https://html.spec.whatwg.org/#scrolling-to-a-fragment void Document::ScrollToRef() { - if (mScrolledToRefAlready) { - RefPtr presShell = GetPresShell(); - if (presShell) { - presShell->ScrollToAnchor(); - } + RefPtr presShell = GetPresShell(); + if (!presShell) { return; } - - // 2. If fragment is the empty string, then return the special value top of - // the document. - if (mScrollToRef.IsEmpty()) { + if (mScrolledToRefAlready) { + presShell->ScrollToAnchor(); return; } - RefPtr presShell = GetPresShell(); - if (!presShell) { + // If text directives is non-null, then highlight the text directives and + // scroll to the last one. + // XXX(:jjaschke): Document policy integration should happen here + // as soon as https://bugzil.la/1860915 lands. + // XXX(:jjaschke): Same goes for User Activation and security aspects, + // tracked in https://bugzil.la/1888756. + const bool didScrollToTextFragment = + presShell->HighlightAndGoToTextFragment(true); + + // 2. If fragment is the empty string and no text directives have been + // scrolled to, then return the special value top of the document. + if (didScrollToTextFragment || mScrollToRef.IsEmpty()) { return; } - // 3. Let potentialIndicatedElement be the result of finding a potential // indicated element given document and fragment. NS_ConvertUTF8toUTF16 ref(mScrollToRef); @@ -15089,11 +15107,6 @@ void Document::HideAllPopoversUntil(nsINode& aEndpoint, } while (repeatingHide); } -MOZ_CAN_RUN_SCRIPT_BOUNDARY void -Document::HideAllPopoversWithoutRunningScript() { - return HideAllPopoversUntil(*this, false, false); -} - void Document::HidePopover(Element& aPopover, bool aFocusPreviousElement, bool aFireEvents, ErrorResult& aRv) { RefPtr popoverHTMLEl = @@ -15548,13 +15561,21 @@ bool Document::HasPendingFullscreenRequests() { return !iter.AtEnd(); } +MOZ_CAN_RUN_SCRIPT_BOUNDARY bool Document::ApplyFullscreen(UniquePtr aRequest) { if (!FullscreenElementReadyCheck(*aRequest)) { return false; } + Element* elem = aRequest->Element(); + + RefPtr hideUntil = elem->GetTopmostPopoverAncestor(nullptr, false); + if (!hideUntil) { + hideUntil = OwnerDoc(); + } + RefPtr doc = aRequest->Document(); - doc->HideAllPopoversWithoutRunningScript(); + doc->HideAllPopoversUntil(*hideUntil, false, true); // Stash a reference to any existing fullscreen doc, we'll use this later // to detect if the origin which is fullscreen has changed. @@ -15580,7 +15601,6 @@ bool Document::ApplyFullscreen(UniquePtr aRequest) { // Set the fullscreen element. This sets the fullscreen style on the // element, and the fullscreen-ancestor styles on ancestors of the element // in this document. - Element* elem = aRequest->Element(); SetFullscreenElement(*elem); // Set the iframe fullscreen flag. if (auto* iframe = HTMLIFrameElement::FromNode(elem)) { @@ -15698,6 +15718,11 @@ void Document::UpdateVisibilityState(DispatchVisibilityChange aDispatchEvent) { for (auto* listener : mWorkerListeners) { listener->OnVisible(visible); } + + // https://w3c.github.io/screen-wake-lock/#handling-document-loss-of-visibility + if (!visible) { + UnlockAllWakeLocks(WakeLockType::Screen); + } } } @@ -16558,16 +16583,6 @@ void Document::SetStateObject(nsIStructuredCloneContainer* scContainer) { mCachedStateObjectValid = false; } -bool Document::ComputeDocumentLWTheme() const { - if (!NodePrincipal()->IsSystemPrincipal()) { - return false; - } - - Element* element = GetRootElement(); - return element && element->AttrValueIs(kNameSpaceID_None, nsGkAtoms::lwtheme, - nsGkAtoms::_true, eCaseMatters); -} - already_AddRefed Document::CreateHTMLElement(nsAtom* aTag) { RefPtr nodeInfo; nodeInfo = mNodeInfoManager->GetNodeInfo(aTag, nullptr, kNameSpaceID_XHTML, @@ -17525,6 +17540,18 @@ Document::CreatePermissionGrantPromise( p = new StorageAccessAPIHelper::StorageAccessPermissionGrantPromise:: Private(__func__); + // Before we prompt, see if we are same-site + if (aFrameOnly) { + nsIChannel* channel = self->GetChannel(); + if (channel) { + nsCOMPtr loadInfo = channel->LoadInfo(); + if (!loadInfo->GetIsThirdPartyContextToTopWindow()) { + p->Resolve(StorageAccessAPIHelper::eAllow, __func__); + return p; + } + } + } + RefPtr promise; // Test the permission MOZ_ASSERT(XRE_IsContentProcess()); @@ -18357,9 +18384,13 @@ class UnlockAllWakeLockRunnable final : public Runnable { void Document::UnlockAllWakeLocks(WakeLockType aType) { // Perform unlock in a runnable to prevent UnlockAll being MOZ_CAN_RUN_SCRIPT - RefPtr runnable = - MakeRefPtr(aType, this); - NS_DispatchToMainThread(runnable); + if (!ActiveWakeLocks(aType).IsEmpty()) { + RefPtr runnable = + MakeRefPtr(aType, this); + nsresult rv = NS_DispatchToMainThread(runnable); + MOZ_DIAGNOSTIC_ASSERT(NS_SUCCEEDED(rv)); + Unused << rv; + } } RefPtr @@ -18637,12 +18668,24 @@ nsICookieJarSettings* Document::CookieJarSettings() { net::CookieJarSettings::Cast(mCookieJarSettings) ->SetFingerprintingRandomizationKey(randomKey); } + + // Inerit the top level windowContext id from the parent. + net::CookieJarSettings::Cast(mCookieJarSettings) + ->SetTopLevelWindowContextId( + net::CookieJarSettings::Cast(inProcessParent->CookieJarSettings()) + ->GetTopLevelWindowContextId()); } else { mCookieJarSettings = net::CookieJarSettings::Create(NodePrincipal()); + + if (IsTopLevelContentDocument()) { + net::CookieJarSettings::Cast(mCookieJarSettings) + ->SetTopLevelWindowContextId(InnerWindowID()); + } } if (auto* wgc = GetWindowGlobalChild()) { net::CookieJarSettingsArgs csArgs; + net::CookieJarSettings::Cast(mCookieJarSettings)->Serialize(csArgs); // Update cookie settings in the parent process if (!wgc->SendUpdateCookieJarSettings(csArgs)) { @@ -19042,6 +19085,13 @@ HighlightRegistry& Document::HighlightRegistry() { return *mHighlightRegistry; } +FragmentDirective* Document::FragmentDirective() { + if (!mFragmentDirective) { + mFragmentDirective = MakeRefPtr(this); + } + return mFragmentDirective; +} + RadioGroupContainer& Document::OwnedRadioGroupContainer() { if (!mRadioGroupContainer) { mRadioGroupContainer = MakeUnique(); diff --git a/dom/base/Document.h b/dom/base/Document.h index a52c61addf..0b0d0ca3d0 100644 --- a/dom/base/Document.h +++ b/dom/base/Document.h @@ -244,6 +244,7 @@ class EventListener; struct FailedCertSecurityInfo; class FeaturePolicy; class FontFaceSet; +class FragmentDirective; class FrameRequestCallback; class ImageTracker; class HighlightRegistry; @@ -2992,15 +2993,6 @@ class Document : public nsINode, SetStateObject(aDocument->mStateObjectContainer); } - /** - * Returns true if there is a lightweight theme specified. This is used to - * determine the state of the :-moz-lwtheme pseudo-class. - */ - bool ComputeDocumentLWTheme() const; - void ResetDocumentLWTheme() { - UpdateDocumentStates(DocumentState::LWTHEME, true); - } - // Whether we're a media document or not. enum class MediaDocumentKind { NotMedia, @@ -3281,7 +3273,7 @@ class Document : public nsINode, void SetDomain(const nsAString& aDomain, mozilla::ErrorResult& rv); void GetCookie(nsAString& aCookie, mozilla::ErrorResult& rv); void SetCookie(const nsAString& aCookie, mozilla::ErrorResult& rv); - void GetReferrer(nsAString& aReferrer) const; + void GetReferrer(nsACString& aReferrer) const; void GetLastModified(nsAString& aLastModified) const; void GetReadyState(nsAString& aReadyState) const; @@ -3377,7 +3369,6 @@ class Document : public nsINode, bool aFocusPreviousElement, bool aFireEvents); - MOZ_CAN_RUN_SCRIPT_BOUNDARY void HideAllPopoversWithoutRunningScript(); // Hides the given popover element, see // https://html.spec.whatwg.org/multipage/popover.html#hide-popover-algorithm MOZ_CAN_RUN_SCRIPT void HidePopover(Element& popover, @@ -4100,6 +4091,13 @@ class Document : public nsINode, */ class HighlightRegistry& HighlightRegistry(); + /** + * @brief Returns the `FragmentDirective` object which contains information + * and functionality to extract or create text directives. + * Guaranteed to be non-null. + */ + class FragmentDirective* FragmentDirective(); + bool ShouldResistFingerprinting(RFPTarget aTarget) const; bool IsInPrivateBrowsing() const; @@ -4159,7 +4157,8 @@ class Document : public nsINode, // Apply the fullscreen state to the document, and trigger related // events. It returns false if the fullscreen element ready check // fails and nothing gets changed. - bool ApplyFullscreen(UniquePtr); + MOZ_CAN_RUN_SCRIPT_BOUNDARY bool ApplyFullscreen( + UniquePtr); void RemoveDocStyleSheetsFromStyleSets(); void ResetStylesheetsToURI(nsIURI* aURI); @@ -5314,9 +5313,6 @@ class Document : public nsINode, // Pres shell resolution saved before entering fullscreen mode. float mSavedResolution; - // Pres shell resolution saved before creating a MobileViewportManager. - float mSavedResolutionBeforeMVM; - nsCOMPtr mCookieJarSettings; bool mHasStoragePermission; @@ -5381,6 +5377,7 @@ class Document : public nsINode, nsTArray mCanvasUsage; uint64_t mLastCanvasUsage = 0; + RefPtr mFragmentDirective; UniquePtr mRadioGroupContainer; public: @@ -5392,11 +5389,6 @@ class Document : public nsINode, nsRefPtrHashtable, nsXULPrototypeElement> mL10nProtoElements; - float GetSavedResolutionBeforeMVM() { return mSavedResolutionBeforeMVM; } - void SetSavedResolutionBeforeMVM(float aResolution) { - mSavedResolutionBeforeMVM = aResolution; - } - void LoadEventFired(); RadioGroupContainer& OwnedRadioGroupContainer(); diff --git a/dom/base/Element.cpp b/dom/base/Element.cpp index be31000278..b6f5d5c3be 100644 --- a/dom/base/Element.cpp +++ b/dom/base/Element.cpp @@ -1055,6 +1055,14 @@ already_AddRefed Element::GetScreen() { return nullptr; } +double Element::CurrentCSSZoom() { + nsIFrame* f = GetPrimaryFrame(FlushType::Frames); + if (!f) { + return 1.0; + } + return f->Style()->EffectiveZoom().ToFloat(); +} + already_AddRefed Element::GetBoundingClientRect() { RefPtr rect = new DOMRect(ToSupports(OwnerDoc())); @@ -1233,15 +1241,14 @@ bool Element::CanAttachShadowDOM() const { return true; } -// https://dom.spec.whatwg.org/commit-snapshots/1eadf0a4a271acc92013d1c0de8c730ac96204f9/#dom-element-attachshadow -already_AddRefed Element::AttachShadow( - const ShadowRootInit& aInit, ErrorResult& aError, - ShadowRootDeclarative aNewShadowIsDeclarative) { +// https://dom.spec.whatwg.org/#dom-element-attachshadow +already_AddRefed Element::AttachShadow(const ShadowRootInit& aInit, + ErrorResult& aError) { /** * Step 1, 2, and 3. */ if (!CanAttachShadowDOM()) { - aError.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR); + aError.ThrowNotSupportedError("Unable to attach ShadowDOM"); return nullptr; } @@ -1249,21 +1256,27 @@ already_AddRefed Element::AttachShadow( * 4. If element is a shadow host, then: */ if (RefPtr root = GetShadowRoot()) { - /* - * 1. If element’s shadow root’s declarative is false, then throw an - * "NotSupportedError" DOMException. + /** + * 1. Let currentShadowRoot be element’s shadow root. + * + * 2. If any of the following are true: + * currentShadowRoot’s declarative is false; or + * currentShadowRoot’s mode is not mode, + * then throw a "NotSupportedError" DOMException. */ - if (!root->IsDeclarative()) { - aError.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR); + if (!root->IsDeclarative() || root->Mode() != aInit.mMode) { + aError.ThrowNotSupportedError( + "Unable to re-attach to existing ShadowDOM"); return nullptr; } - // https://github.com/whatwg/dom/issues/1235 - root->SetIsDeclarative(aNewShadowIsDeclarative); - /* - * 2. Otherwise, remove all of element’s shadow root’s children, in tree - * order, and return. + /** + * 3. Otherwise: + * 1. Remove all of currentShadowRoot’s children, in tree order. + * 2. Set currentShadowRoot’s declarative to false. + * 3. Return. */ root->ReplaceChildren(nullptr, aError); + root->SetIsDeclarative(ShadowRootDeclarative::No); return root.forget(); } @@ -1273,14 +1286,12 @@ already_AddRefed Element::AttachShadow( return AttachShadowWithoutNameChecks( aInit.mMode, DelegatesFocus(aInit.mDelegatesFocus), aInit.mSlotAssignment, - ShadowRootClonable(aInit.mClonable), - ShadowRootDeclarative(aNewShadowIsDeclarative)); + ShadowRootClonable(aInit.mClonable)); } already_AddRefed Element::AttachShadowWithoutNameChecks( ShadowRootMode aMode, DelegatesFocus aDelegatesFocus, - SlotAssignmentMode aSlotAssignment, ShadowRootClonable aClonable, - ShadowRootDeclarative aDeclarative) { + SlotAssignmentMode aSlotAssignment, ShadowRootClonable aClonable) { nsAutoScriptBlocker scriptBlocker; auto* nim = mNodeInfo->NodeInfoManager(); @@ -1304,9 +1315,9 @@ already_AddRefed Element::AttachShadowWithoutNameChecks( * context object's node document, host is context object, * and mode is init's mode. */ - RefPtr shadowRoot = - new (nim) ShadowRoot(this, aMode, aDelegatesFocus, aSlotAssignment, - aClonable, aDeclarative, nodeInfo.forget()); + RefPtr shadowRoot = new (nim) + ShadowRoot(this, aMode, aDelegatesFocus, aSlotAssignment, aClonable, + ShadowRootDeclarative::No, nodeInfo.forget()); if (NodeOrAncestorHasDirAuto()) { shadowRoot->SetAncestorHasDirAuto(); @@ -1336,6 +1347,22 @@ already_AddRefed Element::AttachShadowWithoutNameChecks( dispatcher->PostDOMEvent(); } + const LinkedList* ranges = + GetExistingClosestCommonInclusiveAncestorRanges(); + if (ranges) { + for (const AbstractRange* range : *ranges) { + if (range->MayCrossShadowBoundary()) { + MOZ_ASSERT(range->IsDynamicRange()); + StaticRange* crossBoundaryRange = + range->AsDynamicRange()->GetCrossShadowBoundaryRange(); + MOZ_ASSERT(crossBoundaryRange); + // We may have previously selected this node before it + // becomes a shadow host, so we need to reset the values + // in RangeBoundaries to accommodate the change. + crossBoundaryRange->NotifyNodeBecomesShadowHost(this); + } + } + } /** * 10. Return shadow. */ @@ -1460,13 +1487,7 @@ void Element::GetAttribute(const nsAString& aName, DOMString& aReturn) { if (val) { val->ToString(aReturn); } else { - if (IsXULElement()) { - // XXX should be SetDOMStringToNull(aReturn); - // See bug 232598 - // aReturn is already empty - } else { - aReturn.SetNull(); - } + aReturn.SetNull(); } } @@ -1930,9 +1951,7 @@ nsresult Element::BindToTree(BindContext& aContext, nsINode& aParent) { // This has to be here, rather than in nsGenericHTMLElement::BindToTree, // because it has to happen after updating the parent pointer, but before // recursively binding the kids. - if (IsHTMLElement()) { - SetDirOnBind(this, nsIContent::FromNode(aParent)); - } + SetDirOnBind(this, nsIContent::FromNode(aParent)); UpdateEditableState(false); @@ -2145,9 +2164,7 @@ void Element::UnbindFromTree(UnbindContext& aContext) { // This has to be here, rather than in nsGenericHTMLElement::UnbindFromTree, // because it has to happen after unsetting the parent pointer, but before // recursively unbinding the kids. - if (IsHTMLElement()) { - ResetDir(this); - } + ResetDir(this); for (nsIContent* child = GetFirstChild(); child; child = child->GetNextSibling()) { diff --git a/dom/base/Element.h b/dom/base/Element.h index 40a8052aef..fb88274a88 100644 --- a/dom/base/Element.h +++ b/dom/base/Element.h @@ -257,7 +257,8 @@ class Element : public FragmentOrElement { #ifdef MOZILLA_INTERNAL_API explicit Element(already_AddRefed&& aNodeInfo) : FragmentOrElement(std::move(aNodeInfo)), - mState(ElementState::READONLY | ElementState::DEFINED) { + mState(ElementState::READONLY | ElementState::DEFINED | + ElementState::LTR) { MOZ_ASSERT(mNodeInfo->NodeType() == ELEMENT_NODE, "Bad NodeType in aNodeInfo"); SetIsElement(); @@ -1352,10 +1353,8 @@ class Element : public FragmentOrElement { enum class ShadowRootDeclarative : bool { No, Yes }; MOZ_CAN_RUN_SCRIPT_BOUNDARY - already_AddRefed AttachShadow( - const ShadowRootInit& aInit, ErrorResult& aError, - ShadowRootDeclarative aNewShadowIsDeclarative = - ShadowRootDeclarative::No); + already_AddRefed AttachShadow(const ShadowRootInit& aInit, + ErrorResult& aError); bool CanAttachShadowDOM() const; enum class DelegatesFocus : bool { No, Yes }; @@ -1364,8 +1363,7 @@ class Element : public FragmentOrElement { already_AddRefed AttachShadowWithoutNameChecks( ShadowRootMode aMode, DelegatesFocus = DelegatesFocus::No, SlotAssignmentMode aSlotAssignmentMode = SlotAssignmentMode::Named, - ShadowRootClonable aClonable = ShadowRootClonable::No, - ShadowRootDeclarative aDeclarative = ShadowRootDeclarative::No); + ShadowRootClonable aClonable = ShadowRootClonable::No); // Attach UA Shadow Root if it is not attached. enum class NotifyUAWidgetSetup : bool { No, Yes }; @@ -1499,6 +1497,8 @@ class Element : public FragmentOrElement { return CSSPixel::FromAppUnits(GetClientAreaRect().Width()); } + MOZ_CAN_RUN_SCRIPT double CurrentCSSZoom(); + // This function will return the block size of first line box, no matter if // the box is 'block' or 'inline'. The return unit is pixel. If the element // can't get a primary frame, we will return be zero. diff --git a/dom/base/EventSource.cpp b/dom/base/EventSource.cpp index f70db487dd..def3c90ec0 100644 --- a/dom/base/EventSource.cpp +++ b/dom/base/EventSource.cpp @@ -568,7 +568,15 @@ nsresult EventSourceImpl::ParseURL(const nsAString& aURL) { NS_ENSURE_SUCCESS(rv, rv); nsCOMPtr srcURI; - rv = NS_NewURI(getter_AddRefs(srcURI), aURL, nullptr, baseURI); + nsCOMPtr doc = + mIsMainThread ? GetEventSource()->GetDocumentIfCurrent() : nullptr; + if (doc) { + rv = NS_NewURI(getter_AddRefs(srcURI), aURL, doc->GetDocumentCharacterSet(), + baseURI); + } else { + rv = NS_NewURI(getter_AddRefs(srcURI), aURL, nullptr, baseURI); + } + NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_SYNTAX_ERR); nsAutoString origin; diff --git a/dom/base/FragmentDirective.cpp b/dom/base/FragmentDirective.cpp new file mode 100644 index 0000000000..3300a85751 --- /dev/null +++ b/dom/base/FragmentDirective.cpp @@ -0,0 +1,879 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim:set ts=2 sw=2 sts=2 et cindent: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "FragmentDirective.h" +#include +#include "RangeBoundary.h" +#include "mozilla/Assertions.h" +#include "Document.h" +#include "mozilla/dom/FragmentDirectiveBinding.h" +#include "mozilla/dom/FragmentOrElement.h" +#include "mozilla/dom/NodeBinding.h" +#include "mozilla/dom/Text.h" +#include "mozilla/intl/WordBreaker.h" +#include "nsComputedDOMStyle.h" +#include "nsContentUtils.h" +#include "nsDOMAttributeMap.h" +#include "nsGkAtoms.h" +#include "nsICSSDeclaration.h" +#include "nsIFrame.h" +#include "nsINode.h" +#include "nsIURIMutator.h" +#include "nsRange.h" +#include "nsString.h" + +namespace mozilla::dom { +static LazyLogModule sFragmentDirectiveLog("FragmentDirective"); + +/** Converts a `TextDirective` into a percent-encoded string. */ +nsCString ToString(const TextDirective& aTextDirective) { + nsCString str; + create_text_directive(&aTextDirective, &str); + return str; +} + +NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(FragmentDirective, mDocument) +NS_IMPL_CYCLE_COLLECTING_ADDREF(FragmentDirective) +NS_IMPL_CYCLE_COLLECTING_RELEASE(FragmentDirective) +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(FragmentDirective) + NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY + NS_INTERFACE_MAP_ENTRY(nsISupports) +NS_INTERFACE_MAP_END + +FragmentDirective::FragmentDirective(Document* aDocument) + : mDocument(aDocument) {} + +JSObject* FragmentDirective::WrapObject(JSContext* aCx, + JS::Handle aGivenProto) { + return FragmentDirective_Binding::Wrap(aCx, this, aGivenProto); +} + +void FragmentDirective::ParseAndRemoveFragmentDirectiveFromFragment( + nsCOMPtr& aURI, nsTArray* aTextDirectives) { + if (!aURI || !StaticPrefs::dom_text_fragments_enabled()) { + return; + } + bool hasRef = false; + aURI->GetHasRef(&hasRef); + if (!hasRef) { + return; + } + + nsAutoCString hash; + aURI->GetRef(hash); + + ParsedFragmentDirectiveResult fragmentDirective; + const bool hasRemovedFragmentDirective = + parse_fragment_directive(&hash, &fragmentDirective); + if (!hasRemovedFragmentDirective) { + return; + } + Unused << NS_MutateURI(aURI) + .SetRef(fragmentDirective.url_without_fragment_directive) + .Finalize(aURI); + if (aTextDirectives) { + aTextDirectives->SwapElements(fragmentDirective.text_directives); + } +} + +nsTArray> FragmentDirective::FindTextFragmentsInDocument() { + MOZ_ASSERT(mDocument); + mDocument->FlushPendingNotifications(FlushType::Frames); + nsTArray> textDirectiveRanges; + for (const TextDirective& textDirective : mUninvokedTextDirectives) { + if (RefPtr range = FindRangeForTextDirective(textDirective)) { + textDirectiveRanges.AppendElement(range); + } + } + mUninvokedTextDirectives.Clear(); + return textDirectiveRanges; +} + +/** + * @brief Determine if `aNode` should be considered when traversing the DOM. + * + * A node is "search invisible" if it is an element in the HTML namespace and + * 1. The computed value of its `display` property is `none` + * 2. It serializes as void + * 3. It is one of the following types: + * - HTMLIFrameElement + * - HTMLImageElement + * - HTMLMeterElement + * - HTMLObjectElement + * - HTMLProgressElement + * - HTMLStyleElement + * - HTMLScriptElement + * - HTMLVideoElement + * - HTMLAudioElement + * 4. It is a `select` element whose `multiple` content attribute is absent + * + * see https://wicg.github.io/scroll-to-text-fragment/#search-invisible + */ +bool NodeIsSearchInvisible(nsINode& aNode) { + if (!aNode.IsElement()) { + return false; + } + // 2. If the node serializes as void. + nsAtom* nodeNameAtom = aNode.NodeInfo()->NameAtom(); + if (FragmentOrElement::IsHTMLVoid(nodeNameAtom)) { + return true; + } + // 3. Is any of the following types: HTMLIFrameElement, HTMLImageElement, + // HTMLMeterElement, HTMLObjectElement, HTMLProgressElement, HTMLStyleElement, + // HTMLScriptElement, HTMLVideoElement, HTMLAudioElement + if (aNode.IsAnyOfHTMLElements( + nsGkAtoms::iframe, nsGkAtoms::image, nsGkAtoms::meter, + nsGkAtoms::object, nsGkAtoms::progress, nsGkAtoms::style, + nsGkAtoms::script, nsGkAtoms::video, nsGkAtoms::audio)) { + return true; + } + // 4. Is a select element whose multiple content attribute is absent. + if (aNode.IsHTMLElement(nsGkAtoms::select)) { + return aNode.GetAttributes()->GetNamedItem(u"multiple"_ns) == nullptr; + } + // This is tested last because it's the most expensive check. + // 1. The computed value of its 'display' property is 'none'. + const Element* nodeAsElement = Element::FromNode(aNode); + const RefPtr computedStyle = + nsComputedDOMStyle::GetComputedStyleNoFlush(nodeAsElement); + return !computedStyle || + computedStyle->StyleDisplay()->mDisplay == StyleDisplay::None; +} + +/** + * @brief Returns true if `aNode` has block-level display. + * A node has block-level display if it is an element and the computed value + * of its display property is any of + * - block + * - table + * - flow-root + * - grid + * - flex + * - list-item + * + * See https://wicg.github.io/scroll-to-text-fragment/#has-block-level-display + */ +bool NodeHasBlockLevelDisplay(nsINode& aNode) { + if (!aNode.IsElement()) { + return false; + } + const Element* nodeAsElement = Element::FromNode(aNode); + const RefPtr computedStyle = + nsComputedDOMStyle::GetComputedStyleNoFlush(nodeAsElement); + if (!computedStyle) { + return false; + } + const StyleDisplay& styleDisplay = computedStyle->StyleDisplay()->mDisplay; + return styleDisplay == StyleDisplay::Block || + styleDisplay == StyleDisplay::Table || + styleDisplay == StyleDisplay::FlowRoot || + styleDisplay == StyleDisplay::Grid || + styleDisplay == StyleDisplay::Flex || styleDisplay.IsListItem(); +} + +/** + * @brief Get the Block Ancestor For `aNode`. + * + * see https://wicg.github.io/scroll-to-text-fragment/#nearest-block-ancestor + */ +nsINode* GetBlockAncestorForNode(nsINode* aNode) { + // 1. Let curNode be node. + RefPtr curNode = aNode; + // 2. While curNode is non-null + while (curNode) { + // 2.1. If curNode is not a Text node and it has block-level display then + // return curNode. + if (!curNode->IsText() && NodeHasBlockLevelDisplay(*curNode)) { + return curNode; + } + // 2.2. Otherwise, set curNode to curNode’s parent. + curNode = curNode->GetParentNode(); + } + // 3.Return node’s node document's document element. + return aNode->GetOwnerDocument(); +} + +/** + * @brief Returns true if `aNode` is part of a non-searchable subtree. + * + * A node is part of a non-searchable subtree if it is or has a shadow-including + * ancestor that is search invisible. + * + * see https://wicg.github.io/scroll-to-text-fragment/#non-searchable-subtree + */ +bool NodeIsPartOfNonSearchableSubTree(nsINode& aNode) { + nsINode* node = &aNode; + do { + if (NodeIsSearchInvisible(*node)) { + return true; + } + } while ((node = node->GetParentOrShadowHostNode())); + return false; +} + +/** + * @brief Return true if `aNode` is a visible Text node. + * + * A node is a visible text node if it is a Text node, the computed value of + * its parent element's visibility property is visible, and it is being + * rendered. + * + * see https://wicg.github.io/scroll-to-text-fragment/#visible-text-node + */ +bool NodeIsVisibleTextNode(const nsINode& aNode) { + const Text* text = Text::FromNode(aNode); + if (!text) { + return false; + } + const nsIFrame* frame = text->GetPrimaryFrame(); + return frame && frame->StyleVisibility()->IsVisible(); +} + +enum class TextScanDirection { Left = -1, Right = 1 }; + +/** + * @brief Tests if there is whitespace at the given position and direction. + * + * This algorithm tests for whitespaces and ` ` at `aPos`. + * It returns the size of the whitespace found at the position, i.e. 5/6 for + * ` /;` and 1 otherwise. + * + * This function follows a subsection of this section of the spec, but has been + * adapted to be able to scan in both directions: + * https://wicg.github.io/scroll-to-text-fragment/#next-non-whitespace-position + */ +uint32_t IsWhitespaceAtPosition(nsString& aText, uint32_t aPos, + TextScanDirection aDirection) { + if (aText.Length() == 0) { + return 0; + } + if (aDirection == TextScanDirection::Right) { + if (aText.Length() > (aPos + 5)) { + if (Substring(aText, aPos, 5).Equals(u" ")) { + return aText.Length() > (aPos + 6) && aText.CharAt(aPos + 6) == u';' + ? 6 + : 5; + } + } + } else { + if (aPos > 6 && Substring(aText, aPos - 6, 6).Equals(u" ")) { + return 6; + } + if (aPos > 5 && Substring(aText, aPos - 5, 5).Equals(u" ")) { + return 5; + } + } + return uint32_t(IsSpaceCharacter(aText.CharAt(aPos))); +} + +/** Advances the start of `aRange` to the next non-whitespace position. + * The function follows this section of the spec: + * https://wicg.github.io/scroll-to-text-fragment/#next-non-whitespace-position + */ +void AdvanceStartToNextNonWhitespacePosition(nsRange& aRange) { + // 1. While range is not collapsed: + while (!aRange.Collapsed()) { + // 1.1. Let node be range's start node. + RefPtr node = aRange.GetStartContainer(); + MOZ_ASSERT(node); + // 1.2. Let offset be range's start offset. + const uint32_t offset = aRange.StartOffset(); + // 1.3. If node is part of a non-searchable subtree or if node is not a + // visible text node or if offset is equal to node's length then: + if (NodeIsPartOfNonSearchableSubTree(*node) || + !NodeIsVisibleTextNode(*node) || offset == node->Length()) { + // 1.3.1. Set range's start node to the next node, in shadow-including + // tree order. + // 1.3.2. Set range's start offset to 0. + if (NS_FAILED(aRange.SetStart(node->GetNextNode(), 0))) { + return; + } + // 1.3.3. Continue. + continue; + } + const Text* text = Text::FromNode(node); + nsAutoString textData; + text->GetData(textData); + // These steps are moved to `IsWhitespaceAtPosition()`. + // 1.4. If the substring data of node at offset offset and count 6 is equal + // to the string " " then: + // 1.4.1. Add 6 to range’s start offset. + // 1.5. Otherwise, if the substring data of node at offset offset and count + // 5 is equal to the string " " then: + // 1.5.1. Add 5 to range’s start offset. + // 1.6. Otherwise: + // 1.6.1 Let cp be the code point at the offset index in node’s data. + // 1.6.2 If cp does not have the White_Space property set, return. + // 1.6.3 Add 1 to range’s start offset. + const uint32_t whitespace = + IsWhitespaceAtPosition(textData, offset, TextScanDirection::Right); + if (whitespace == 0) { + return; + } + + aRange.SetStart(node, offset + whitespace); + } +} + +/** + * @brief Moves `aRangeBoundary` one word in `aDirection`. + * + * Word boundaries are determined using `intl::WordBreaker::FindWord()`. + * + * + * @param aRangeBoundary[in] The range boundary that should be moved. + * Must be set and valid. + * @param aDirection[in] The direction into which to move. + * @return A new `RangeBoundary` which is moved to the next word. + */ +RangeBoundary MoveRangeBoundaryOneWord(const RangeBoundary& aRangeBoundary, + TextScanDirection aDirection) { + MOZ_ASSERT(aRangeBoundary.IsSetAndValid()); + RefPtr curNode = aRangeBoundary.Container(); + uint32_t offset = *aRangeBoundary.Offset( + RangeBoundary::OffsetFilter::kValidOrInvalidOffsets); + + const int offsetIncrement = int(aDirection); + // Get the text node of the start of the range and the offset. + // This is the current position of the start of the range. + nsAutoString text; + if (NodeIsVisibleTextNode(*curNode)) { + const Text* textNode = Text::FromNode(curNode); + textNode->GetData(text); + + // Assuming that the current position might not be at a word boundary, + // advance to the word boundary at word begin/end. + if (!IsWhitespaceAtPosition(text, offset, aDirection)) { + const intl::WordRange wordRange = + intl::WordBreaker::FindWord(text, offset); + if (aDirection == TextScanDirection::Right && + offset != wordRange.mBegin) { + offset = wordRange.mEnd; + } else if (aDirection == TextScanDirection::Left && + offset != wordRange.mEnd) { + // The additional -1 is necessary to move to offset to *before* the + // start of the word. + offset = wordRange.mBegin - 1; + } + } + } + // Now, skip any whitespace, so that `offset` points to the word boundary of + // the next word (which is the one this algorithm actually aims to move over). + while (curNode) { + if (!NodeIsVisibleTextNode(*curNode) || NodeIsSearchInvisible(*curNode) || + offset >= curNode->Length()) { + curNode = aDirection == TextScanDirection::Left ? curNode->GetPrevNode() + : curNode->GetNextNode(); + if (!curNode) { + break; + } + offset = + aDirection == TextScanDirection::Left ? curNode->Length() - 1 : 0; + if (const Text* textNode = Text::FromNode(curNode)) { + textNode->GetData(text); + } + continue; + } + if (const uint32_t whitespace = + IsWhitespaceAtPosition(text, offset, aDirection)) { + offset += offsetIncrement * whitespace; + continue; + } + + // At this point, the caret has been moved to the next non-whitespace + // position. + // find word boundaries at the current position + const intl::WordRange wordRange = intl::WordBreaker::FindWord(text, offset); + offset = aDirection == TextScanDirection::Left ? wordRange.mBegin + : wordRange.mEnd; + + return {curNode, offset}; + } + return {}; +} + +RefPtr FragmentDirective::FindRangeForTextDirective( + const TextDirective& aTextDirective) { + MOZ_LOG(sFragmentDirectiveLog, LogLevel::Info, + ("FragmentDirective::%s(): Find range for text directive '%s'.", + __FUNCTION__, ToString(aTextDirective).Data())); + // 1. Let searchRange be a range with start (document, 0) and end (document, + // document’s length) + ErrorResult rv; + RefPtr searchRange = + nsRange::Create(mDocument, 0, mDocument, mDocument->Length(), rv); + if (rv.Failed()) { + return nullptr; + } + // 2. While searchRange is not collapsed: + while (!searchRange->Collapsed()) { + // 2.1. Let potentialMatch be null. + RefPtr potentialMatch; + // 2.2. If parsedValues’s prefix is not null: + if (!aTextDirective.prefix.IsEmpty()) { + // 2.2.1. Let prefixMatch be the the result of running the find a string + // in range steps with query parsedValues’s prefix, searchRange + // searchRange, wordStartBounded true and wordEndBounded false. + RefPtr prefixMatch = + FindStringInRange(searchRange, aTextDirective.prefix, true, false); + // 2.2.2. If prefixMatch is null, return null. + if (!prefixMatch) { + return nullptr; + } + // 2.2.3. Set searchRange’s start to the first boundary point after + // prefixMatch’s start + const RangeBoundary boundaryPoint = MoveRangeBoundaryOneWord( + {prefixMatch->GetStartContainer(), prefixMatch->StartOffset()}, + TextScanDirection::Right); + if (!boundaryPoint.IsSetAndValid()) { + return nullptr; + } + searchRange->SetStart(boundaryPoint.AsRaw(), rv); + if (rv.Failed()) { + return nullptr; + } + + // 2.2.4. Let matchRange be a range whose start is prefixMatch’s end and + // end is searchRange’s end. + RefPtr matchRange = nsRange::Create( + prefixMatch->GetEndContainer(), prefixMatch->EndOffset(), + searchRange->GetEndContainer(), searchRange->EndOffset(), rv); + if (rv.Failed()) { + return nullptr; + } + // 2.2.5. Advance matchRange’s start to the next non-whitespace position. + AdvanceStartToNextNonWhitespacePosition(*matchRange); + // 2.2.6. If matchRange is collapsed return null. + // (This can happen if prefixMatch’s end or its subsequent non-whitespace + // position is at the end of the document.) + if (matchRange->Collapsed()) { + return nullptr; + } + // 2.2.7. Assert: matchRange’s start node is a Text node. + // (matchRange’s start now points to the next non-whitespace text data + // following a matched prefix.) + MOZ_ASSERT(matchRange->GetStartContainer()->IsText()); + + // 2.2.8. Let mustEndAtWordBoundary be true if parsedValues’s end is + // non-null or parsedValues’s suffix is null, false otherwise. + const bool mustEndAtWordBoundary = + !aTextDirective.end.IsEmpty() || aTextDirective.suffix.IsEmpty(); + // 2.2.9. Set potentialMatch to the result of running the find a string in + // range steps with query parsedValues’s start, searchRange matchRange, + // wordStartBounded false, and wordEndBounded mustEndAtWordBoundary. + potentialMatch = FindStringInRange(matchRange, aTextDirective.start, + false, mustEndAtWordBoundary); + // 2.2.10. If potentialMatch is null, return null. + if (!potentialMatch) { + return nullptr; + } + // 2.2.11. If potentialMatch’s start is not matchRange’s start, then + // continue. + // (In this case, we found a prefix but it was followed by something other + // than a matching text so we’ll continue searching for the next instance + // of prefix.) + if (potentialMatch->GetStartContainer() != + matchRange->GetStartContainer()) { + continue; + } + } + // 2.3. Otherwise: + else { + // 2.3.1. Let mustEndAtWordBoundary be true if parsedValues’s end is + // non-null or parsedValues’s suffix is null, false otherwise. + const bool mustEndAtWordBoundary = + !aTextDirective.end.IsEmpty() || aTextDirective.suffix.IsEmpty(); + // 2.3.2. Set potentialMatch to the result of running the find a string in + // range steps with query parsedValues’s start, searchRange searchRange, + // wordStartBounded true, and wordEndBounded mustEndAtWordBoundary. + potentialMatch = FindStringInRange(searchRange, aTextDirective.start, + true, mustEndAtWordBoundary); + // 2.3.3. If potentialMatch is null, return null. + if (!potentialMatch) { + return nullptr; + } + // 2.3.4. Set searchRange’s start to the first boundary point after + // potentialMatch’s start + RangeBoundary newRangeBoundary = MoveRangeBoundaryOneWord( + {potentialMatch->GetStartContainer(), potentialMatch->StartOffset()}, + TextScanDirection::Right); + if (!newRangeBoundary.IsSetAndValid()) { + return nullptr; + } + searchRange->SetStart(newRangeBoundary.AsRaw(), rv); + if (rv.Failed()) { + return nullptr; + } + } + // 2.4. Let rangeEndSearchRange be a range whose start is potentialMatch’s + // end and whose end is searchRange’s end. + RefPtr rangeEndSearchRange = nsRange::Create( + potentialMatch->GetEndContainer(), potentialMatch->EndOffset(), + searchRange->GetEndContainer(), searchRange->EndOffset(), rv); + if (rv.Failed()) { + return nullptr; + } + // 2.5. While rangeEndSearchRange is not collapsed: + while (!rangeEndSearchRange->Collapsed()) { + // 2.5.1. If parsedValues’s end item is non-null, then: + if (!aTextDirective.end.IsEmpty()) { + // 2.5.1.1. Let mustEndAtWordBoundary be true if parsedValues’s suffix + // is null, false otherwise. + const bool mustEndAtWordBoundary = aTextDirective.suffix.IsEmpty(); + // 2.5.1.2. Let endMatch be the result of running the find a string in + // range steps with query parsedValues’s end, searchRange + // rangeEndSearchRange, wordStartBounded true, and wordEndBounded + // mustEndAtWordBoundary. + RefPtr endMatch = + FindStringInRange(rangeEndSearchRange, aTextDirective.end, true, + mustEndAtWordBoundary); + // 2.5.1.3. If endMatch is null then return null. + if (!endMatch) { + return nullptr; + } + // 2.5.1.4. Set potentialMatch’s end to endMatch’s end. + potentialMatch->SetEnd(endMatch->GetEndContainer(), + endMatch->EndOffset()); + } + // 2.5.2. Assert: potentialMatch is non-null, not collapsed and represents + // a range exactly containing an instance of matching text. + MOZ_ASSERT(potentialMatch && !potentialMatch->Collapsed()); + + // 2.5.3. If parsedValues’s suffix is null, return potentialMatch. + if (aTextDirective.suffix.IsEmpty()) { + return potentialMatch; + } + // 2.5.4. Let suffixRange be a range with start equal to potentialMatch’s + // end and end equal to searchRange’s end. + RefPtr suffixRange = nsRange::Create( + potentialMatch->GetEndContainer(), potentialMatch->EndOffset(), + searchRange->GetEndContainer(), searchRange->EndOffset(), rv); + if (rv.Failed()) { + return nullptr; + } + // 2.5.5. Advance suffixRange's start to the next non-whitespace position. + AdvanceStartToNextNonWhitespacePosition(*suffixRange); + + // 2.5.6. Let suffixMatch be result of running the find a string in range + // steps with query parsedValue's suffix, searchRange suffixRange, + // wordStartBounded false, and wordEndBounded true. + RefPtr suffixMatch = + FindStringInRange(suffixRange, aTextDirective.suffix, false, true); + + // 2.5.7. If suffixMatch is null, return null. + // (If the suffix doesn't appear in the remaining text of the document, + // there's no possible way to make a match.) + if (!suffixMatch) { + return nullptr; + } + // 2.5.8. If suffixMatch's start is suffixRange's start, return + // potentialMatch. + if (suffixMatch->GetStartContainer() == + suffixRange->GetStartContainer() && + suffixMatch->StartOffset() == suffixRange->StartOffset()) { + return potentialMatch; + } + // 2.5.9. If parsedValue's end item is null then break; + // (If this is an exact match and the suffix doesn’t match, start + // searching for the next range start by breaking out of this loop without + // rangeEndSearchRange being collapsed. If we’re looking for a range + // match, we’ll continue iterating this inner loop since the range start + // will already be correct.) + if (aTextDirective.end.IsEmpty()) { + break; + } + // 2.5.10. Set rangeEndSearchRange's start to potentialMatch's end. + // (Otherwise, it is possible that we found the correct range start, but + // not the correct range end. Continue the inner loop to keep searching + // for another matching instance of rangeEnd.) + rangeEndSearchRange->SetStart(potentialMatch->GetEndContainer(), + potentialMatch->EndOffset()); + } + // 2.6. If rangeEndSearchRange is collapsed then: + if (rangeEndSearchRange->Collapsed()) { + // 2.6.1. Assert parsedValue's end item is non-null. + // (This can only happen for range matches due to the break for exact + // matches in step 9 of the above loop. If we couldn’t find a valid + // rangeEnd+suffix pair anywhere in the doc then there’s no possible way + // to make a match.) + // XXX(:jjaschke): should this really assert? + MOZ_ASSERT(!aTextDirective.end.IsEmpty()); + } + } + // 3. Return null. + return nullptr; +} + +/** + * @brief Convenience function that returns true if the given position in a + * string is a word boundary. + * + * This is a thin wrapper around the `WordBreaker::FindWord()` function. + * + * @param aText The text input. + * @param aPosition The position to check. + * @return true if there is a word boundary at `aPosition`. + * @return false otherwise. + */ +bool IsAtWordBoundary(const nsAString& aText, uint32_t aPosition) { + const intl::WordRange wordRange = + intl::WordBreaker::FindWord(aText, aPosition); + return wordRange.mBegin == aPosition || wordRange.mEnd == aPosition; +} + +enum class IsEndIndex : bool { No, Yes }; +RangeBoundary GetBoundaryPointAtIndex( + uint32_t aIndex, const nsTArray>& aTextNodeList, + IsEndIndex aIsEndIndex) { + // 1. Let counted be 0. + uint32_t counted = 0; + // 2. For each curNode of nodes: + for (Text* curNode : aTextNodeList) { + // 2.1. Let nodeEnd be counted + curNode’s length. + uint32_t nodeEnd = counted + curNode->Length(); + // 2.2. If isEnd is true, add 1 to nodeEnd. + if (aIsEndIndex == IsEndIndex::Yes) { + ++nodeEnd; + } + // 2.3. If nodeEnd is greater than index then: + if (nodeEnd > aIndex) { + // 2.3.1. Return the boundary point (curNode, index − counted). + return RangeBoundary(curNode->AsNode(), aIndex - counted); + } + // 2.4. Increment counted by curNode’s length. + counted += curNode->Length(); + } + return {}; +} + +RefPtr FindRangeFromNodeList( + nsRange* aSearchRange, const nsAString& aQuery, + const nsTArray>& aTextNodeList, bool aWordStartBounded, + bool aWordEndBounded) { + // 1. Let searchBuffer be the concatenation of the data of each item in nodes. + // XXX(:jjaschke): There's an open issue here that deals with what + // data is supposed to be (text data vs. rendered text) + // https://github.com/WICG/scroll-to-text-fragment/issues/98 + uint32_t bufferLength = 0; + for (const Text* text : aTextNodeList) { + bufferLength += text->Length(); + } + // bail out if the search query is longer than the text data. + if (bufferLength < aQuery.Length()) { + return nullptr; + } + nsAutoString searchBuffer; + searchBuffer.SetCapacity(bufferLength); + for (Text* text : aTextNodeList) { + text->AppendTextTo(searchBuffer); + } + // 2. Let searchStart be 0. + // 3. If the first item in nodes is searchRange’s start node then set + // searchStart to searchRange’s start offset. + uint32_t searchStart = + aTextNodeList.SafeElementAt(0) == aSearchRange->GetStartContainer() + ? aSearchRange->StartOffset() + : 0; + + // 4. Let start and end be boundary points, initially null. + RangeBoundary start, end; + // 5. Let matchIndex be null. + // "null" here doesn't mean 0, instead "not set". 0 would be a valid index. + // Therefore, "null" is represented by the value -1. + int32_t matchIndex = -1; + + // 6. While matchIndex is null + // As explained above, "null" == -1 in this algorithm. + while (matchIndex == -1) { + // 6.1. Set matchIndex to the index of the first instance of queryString in + // searchBuffer, starting at searchStart. The string search must be + // performed using a base character comparison, or the primary level, as + // defined in [UTS10]. + // [UTS10] + // Ken Whistler; Markus Scherer.Unicode Collation Algorithm.26 August 2022. + // Unicode Technical Standard #10. + // URL : https://www.unicode.org/reports/tr10/tr10-47.html + + // XXX(:jjaschke): For the initial implementation, a standard case-sensitive + // find-in-string is used. + // See: https://github.com/WICG/scroll-to-text-fragment/issues/233 + matchIndex = searchBuffer.Find(aQuery, searchStart); + // 6.2. If matchIndex is null, return null. + if (matchIndex == -1) { + return nullptr; + } + + // 6.3. Let endIx be matchIndex + queryString’s length. + // endIx is the index of the last character in the match + 1. + const uint32_t endIx = matchIndex + aQuery.Length(); + + // 6.4. Set start to the boundary point result of get boundary point at + // index matchIndex run over nodes with isEnd false. + start = GetBoundaryPointAtIndex(matchIndex, aTextNodeList, IsEndIndex::No); + // 6.5. Set end to the boundary point result of get boundary point at index + // endIx run over nodes with isEnd true. + end = GetBoundaryPointAtIndex(endIx, aTextNodeList, IsEndIndex::Yes); + + // 6.6. If wordStartBounded is true and matchIndex is not at a word boundary + // in searchBuffer, given the language from start’s node as the locale; or + // wordEndBounded is true and matchIndex + queryString’s length is not at a + // word boundary in searchBuffer, given the language from end’s node as the + // locale: + if ((aWordStartBounded && !IsAtWordBoundary(searchBuffer, matchIndex)) || + (aWordEndBounded && !IsAtWordBoundary(searchBuffer, endIx))) { + // 6.6.1. Set searchStart to matchIndex + 1. + searchStart = matchIndex + 1; + // 6.6.2. Set matchIndex to null. + matchIndex = -1; + } + } + // 7. Let endInset be 0. + // 8. If the last item in nodes is searchRange’s end node then set endInset + // to (searchRange’s end node's length − searchRange’s end offset) + // (endInset is the offset from the last position in the last node in the + // reverse direction. Alternatively, it is the length of the node that’s not + // included in the range.) + uint32_t endInset = + aTextNodeList.LastElement() == aSearchRange->GetEndContainer() + ? aSearchRange->GetEndContainer()->Length() - + aSearchRange->EndOffset() + : 0; + + // 9. If matchIndex + queryString’s length is greater than searchBuffer’s + // length − endInset return null. + // (If the match runs past the end of the search range, return null.) + if (matchIndex + aQuery.Length() > searchBuffer.Length() - endInset) { + return nullptr; + } + + // 10. Assert: start and end are non-null, valid boundary points in + // searchRange. + MOZ_ASSERT(start.IsSetAndValid()); + MOZ_ASSERT(end.IsSetAndValid()); + + // 11. Return a range with start start and end end. + ErrorResult rv; + RefPtr range = nsRange::Create(start, end, rv); + if (rv.Failed()) { + return nullptr; + } + + return range; +} + +RefPtr FragmentDirective::FindStringInRange(nsRange* aSearchRange, + const nsAString& aQuery, + bool aWordStartBounded, + bool aWordEndBounded) { + MOZ_ASSERT(aSearchRange); + RefPtr searchRange = aSearchRange->CloneRange(); + // 1. While searchRange is not collapsed + while (searchRange && !searchRange->Collapsed()) { + // 1.1. Let curNode be searchRange’s start node. + RefPtr curNode = searchRange->GetStartContainer(); + + // 1.2. If curNode is part of a non-searchable subtree: + if (NodeIsPartOfNonSearchableSubTree(*curNode)) { + // 1.2.1. Set searchRange’s start node to the next node, in + // shadow-including tree order, that isn’t a shadow-including descendant + // of curNode. + RefPtr next = curNode; + while ((next = next->GetNextNode())) { + if (!next->IsShadowIncludingInclusiveDescendantOf(curNode)) { + break; + } + } + if (!next) { + return nullptr; + } + // 1.2.2. Set `searchRange`s `start offset` to 0 + searchRange->SetStart(next, 0); + // 1.2.3. continue. + continue; + } + // 1.3. If curNode is not a visible TextNode: + if (!NodeIsVisibleTextNode(*curNode)) { + // 1.3.1. Set searchRange’s start node to the next node, in + // shadow-including tree order, that is not a doctype. + RefPtr next = curNode; + while ((next = next->GetNextNode())) { + if (next->NodeType() != Node_Binding::DOCUMENT_TYPE_NODE) { + break; + } + } + if (!next) { + return nullptr; + } + // 1.3.2. Set searchRange’s start offset to 0. + searchRange->SetStart(next, 0); + // 1.3.3. continue. + continue; + } + // 1.4. Let blockAncestor be the nearest block ancestor of `curNode` + RefPtr blockAncestor = GetBlockAncestorForNode(curNode); + + // 1.5. Let textNodeList be a list of Text nodes, initially empty. + nsTArray> textNodeList; + // 1.6. While curNode is a shadow-including descendant of blockAncestor and + // the position of the boundary point (curNode,0) is not after searchRange's + // end: + while (curNode && + curNode->IsShadowIncludingInclusiveDescendantOf(blockAncestor)) { + Maybe comp = nsContentUtils::ComparePoints( + curNode, 0, searchRange->GetEndContainer(), searchRange->EndOffset()); + if (comp) { + if (*comp >= 0) { + break; + } + } else { + // This means that the compared nodes are disconnected. + return nullptr; + } + // 1.6.1. If curNode has block-level display, then break. + if (NodeHasBlockLevelDisplay(*curNode)) { + break; + } + // 1.6.2. If curNode is search invisible: + if (NodeIsSearchInvisible(*curNode)) { + // 1.6.2.1. Set curNode to the next node, in shadow-including tree + // order, that isn't a shadow-including descendant of curNode. + curNode = curNode->GetNextNode(); + // 1.6.2.2. Continue. + continue; + } + // 1.6.3. If curNode is a visible text node then append it to + // textNodeList. + if (NodeIsVisibleTextNode(*curNode)) { + textNodeList.AppendElement(curNode->AsText()); + } + // 1.6.4. Set curNode to the next node in shadow-including + // tree order. + curNode = curNode->GetNextNode(); + } + // 1.7. Run the find a range from a node list steps given + // query, searchRange, textNodeList, wordStartBounded, wordEndBounded as + // input. If the resulting Range is not null, then return it. + if (RefPtr range = + FindRangeFromNodeList(searchRange, aQuery, textNodeList, + aWordStartBounded, aWordEndBounded)) { + return range; + } + + // 1.8. If curNode is null, then break. + if (!curNode) { + break; + } + + // 1.9. Assert: curNode follows searchRange's start node. + + // 1.10. Set searchRange's start to the boundary point (curNode,0). + searchRange->SetStart(curNode, 0); + } + + // 2. Return null. + return nullptr; +} +} // namespace mozilla::dom diff --git a/dom/base/FragmentDirective.h b/dom/base/FragmentDirective.h new file mode 100644 index 0000000000..8972556d6c --- /dev/null +++ b/dom/base/FragmentDirective.h @@ -0,0 +1,111 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim:set ts=2 sw=2 sts=2 et cindent: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef DOM_FRAGMENTDIRECTIVE_H_ +#define DOM_FRAGMENTDIRECTIVE_H_ + +#include "js/TypeDecls.h" +#include "mozilla/dom/BindingDeclarations.h" + +#include "mozilla/dom/fragmentdirectives_ffi_generated.h" +#include "nsCycleCollectionParticipant.h" +#include "nsStringFwd.h" +#include "nsWrapperCache.h" + +class nsINode; +class nsIURI; +class nsRange; +namespace mozilla::dom { +class Document; +class Text; + +/** + * @brief The `FragmentDirective` class is the C++ representation of the + * `Document.fragmentDirective` webidl property. + * + * This class also serves as the main interface to interact with the fragment + * directive from the C++ side. It allows to find text fragment ranges from a + * given list of `TextDirective`s using + * `FragmentDirective::FindTextFragmentsInDocument()`. + * To avoid Text Directives being applied multiple times, this class implements + * the `uninvoked directive` mechanism, which in the spec is defined to be part + * of the `Document` [0]. + * + * [0] + * https://wicg.github.io/scroll-to-text-fragment/#document-uninvoked-directives + */ +class FragmentDirective final : public nsISupports, public nsWrapperCache { + public: + NS_DECL_CYCLE_COLLECTING_ISUPPORTS + NS_DECL_CYCLE_COLLECTION_WRAPPERCACHE_CLASS(FragmentDirective) + + public: + explicit FragmentDirective(Document* aDocument); + FragmentDirective(Document* aDocument, + nsTArray&& aTextDirectives) + : mDocument(aDocument), + mUninvokedTextDirectives(std::move(aTextDirectives)) {} + + protected: + ~FragmentDirective() = default; + + public: + Document* GetParentObject() const { return mDocument; }; + + JSObject* WrapObject(JSContext* aCx, + JS::Handle aGivenProto) override; + + /** + * @brief Sets Text Directives as "uninvoked directive". + */ + void SetTextDirectives(nsTArray&& aTextDirectives) { + mUninvokedTextDirectives = std::move(aTextDirectives); + } + + /** Returns true if there are Text Directives that have not been applied to + * the `Document`. + */ + bool HasUninvokedDirectives() const { + return !mUninvokedTextDirectives.IsEmpty(); + }; + + /** Searches for the current uninvoked text directives and creates a range for + * each one that is found. + * + * When this method returns, the uninvoked directives for this document are + * cleared. + * + * This method tries to follow the specification as close as possible in how + * to find a matching range for a text directive. However, instead of using + * collator-based search, a standard case-insensitive search is used + * (`nsString::find()`). + */ + nsTArray> FindTextFragmentsInDocument(); + + /** Utility function which parses the fragment directive and removes it from + * the hash of the given URI. This operation happens in-place. + * + * If aTextDirectives is nullptr, the parsed fragment directive is discarded. + */ + static void ParseAndRemoveFragmentDirectiveFromFragment( + nsCOMPtr& aURI, + nsTArray* aTextDirectives = nullptr); + + private: + RefPtr FindRangeForTextDirective( + const TextDirective& aTextDirective); + RefPtr FindStringInRange(nsRange* aSearchRange, + const nsAString& aQuery, + bool aWordStartBounded, + bool aWordEndBounded); + + RefPtr mDocument; + nsTArray mUninvokedTextDirectives; +}; + +} // namespace mozilla::dom + +#endif // DOM_FRAGMENTDIRECTIVE_H_ diff --git a/dom/base/Link.cpp b/dom/base/Link.cpp index 296a584ed1..1465ea5773 100644 --- a/dom/base/Link.cpp +++ b/dom/base/Link.cpp @@ -6,26 +6,21 @@ #include "Link.h" -#include "mozilla/MemoryReporting.h" #include "mozilla/dom/Element.h" #include "mozilla/dom/BindContext.h" +#include "mozilla/dom/Document.h" #include "mozilla/dom/SVGAElement.h" #include "mozilla/dom/HTMLDNSPrefetch.h" #include "mozilla/IHistory.h" -#include "mozilla/StaticPrefs_layout.h" #include "nsLayoutUtils.h" -#include "nsIURL.h" #include "nsIURIMutator.h" #include "nsISizeOf.h" -#include "nsEscape.h" #include "nsGkAtoms.h" #include "nsString.h" -#include "mozAutoDocUpdate.h" #include "mozilla/Components.h" #include "nsAttrValueInlines.h" -#include "HTMLLinkElement.h" namespace mozilla::dom { @@ -133,7 +128,7 @@ nsIURI* Link::GetURI() const { return mCachedURI; } -void Link::SetProtocol(const nsAString& aProtocol) { +void Link::SetProtocol(const nsACString& aProtocol) { nsCOMPtr uri(GetURI()); if (!uri) { // Ignore failures to be compatible with NS4. @@ -146,111 +141,100 @@ void Link::SetProtocol(const nsAString& aProtocol) { SetHrefAttribute(uri); } -void Link::SetPassword(const nsAString& aPassword) { +void Link::SetPassword(const nsACString& aPassword) { nsCOMPtr uri(GetURI()); if (!uri) { // Ignore failures to be compatible with NS4. return; } - nsresult rv = NS_MutateURI(uri) - .SetPassword(NS_ConvertUTF16toUTF8(aPassword)) - .Finalize(uri); + nsresult rv = NS_MutateURI(uri).SetPassword(aPassword).Finalize(uri); if (NS_SUCCEEDED(rv)) { SetHrefAttribute(uri); } } -void Link::SetUsername(const nsAString& aUsername) { +void Link::SetUsername(const nsACString& aUsername) { nsCOMPtr uri(GetURI()); if (!uri) { // Ignore failures to be compatible with NS4. return; } - nsresult rv = NS_MutateURI(uri) - .SetUsername(NS_ConvertUTF16toUTF8(aUsername)) - .Finalize(uri); + nsresult rv = NS_MutateURI(uri).SetUsername(aUsername).Finalize(uri); if (NS_SUCCEEDED(rv)) { SetHrefAttribute(uri); } } -void Link::SetHost(const nsAString& aHost) { +void Link::SetHost(const nsACString& aHost) { nsCOMPtr uri(GetURI()); if (!uri) { // Ignore failures to be compatible with NS4. return; } - nsresult rv = - NS_MutateURI(uri).SetHostPort(NS_ConvertUTF16toUTF8(aHost)).Finalize(uri); + nsresult rv = NS_MutateURI(uri).SetHostPort(aHost).Finalize(uri); if (NS_FAILED(rv)) { return; } SetHrefAttribute(uri); } -void Link::SetHostname(const nsAString& aHostname) { +void Link::SetHostname(const nsACString& aHostname) { nsCOMPtr uri(GetURI()); if (!uri) { // Ignore failures to be compatible with NS4. return; } - nsresult rv = - NS_MutateURI(uri).SetHost(NS_ConvertUTF16toUTF8(aHostname)).Finalize(uri); + nsresult rv = NS_MutateURI(uri).SetHost(aHostname).Finalize(uri); if (NS_FAILED(rv)) { return; } SetHrefAttribute(uri); } -void Link::SetPathname(const nsAString& aPathname) { +void Link::SetPathname(const nsACString& aPathname) { nsCOMPtr uri(GetURI()); if (!uri) { // Ignore failures to be compatible with NS4. return; } - nsresult rv = NS_MutateURI(uri) - .SetFilePath(NS_ConvertUTF16toUTF8(aPathname)) - .Finalize(uri); + nsresult rv = NS_MutateURI(uri).SetFilePath(aPathname).Finalize(uri); if (NS_FAILED(rv)) { return; } SetHrefAttribute(uri); } -void Link::SetSearch(const nsAString& aSearch) { +void Link::SetSearch(const nsACString& aSearch) { nsCOMPtr uri(GetURI()); if (!uri) { // Ignore failures to be compatible with NS4. return; } - nsresult rv = - NS_MutateURI(uri).SetQuery(NS_ConvertUTF16toUTF8(aSearch)).Finalize(uri); + nsresult rv = NS_MutateURI(uri).SetQuery(aSearch).Finalize(uri); if (NS_FAILED(rv)) { return; } SetHrefAttribute(uri); } -void Link::SetPort(const nsAString& aPort) { +void Link::SetPort(const nsACString& aPort) { nsCOMPtr uri(GetURI()); if (!uri) { // Ignore failures to be compatible with NS4. return; } - nsresult rv; - nsAutoString portStr(aPort); - // nsIURI uses -1 as default value. + nsresult rv; int32_t port = -1; if (!aPort.IsEmpty()) { - port = portStr.ToInteger(&rv); + port = aPort.ToInteger(&rv); if (NS_FAILED(rv)) { return; } @@ -263,15 +247,14 @@ void Link::SetPort(const nsAString& aPort) { SetHrefAttribute(uri); } -void Link::SetHash(const nsAString& aHash) { +void Link::SetHash(const nsACString& aHash) { nsCOMPtr uri(GetURI()); if (!uri) { // Ignore failures to be compatible with NS4. return; } - nsresult rv = - NS_MutateURI(uri).SetRef(NS_ConvertUTF16toUTF8(aHash)).Finalize(uri); + nsresult rv = NS_MutateURI(uri).SetRef(aHash).Finalize(uri); if (NS_FAILED(rv)) { return; } @@ -279,121 +262,102 @@ void Link::SetHash(const nsAString& aHash) { SetHrefAttribute(uri); } -void Link::GetOrigin(nsAString& aOrigin) { +void Link::GetOrigin(nsACString& aOrigin) { aOrigin.Truncate(); - nsCOMPtr uri(GetURI()); + nsIURI* uri = GetURI(); if (!uri) { return; } - nsString origin; - nsContentUtils::GetWebExposedOriginSerialization(uri, origin); - aOrigin.Assign(origin); + nsContentUtils::GetWebExposedOriginSerialization(uri, aOrigin); } -void Link::GetProtocol(nsAString& _protocol) { - nsCOMPtr uri(GetURI()); - if (uri) { - nsAutoCString scheme; - (void)uri->GetScheme(scheme); - CopyASCIItoUTF16(scheme, _protocol); +void Link::GetProtocol(nsACString& aProtocol) { + if (nsIURI* uri = GetURI()) { + (void)uri->GetScheme(aProtocol); } - _protocol.Append(char16_t(':')); + aProtocol.Append(':'); } -void Link::GetUsername(nsAString& aUsername) { +void Link::GetUsername(nsACString& aUsername) { aUsername.Truncate(); - nsCOMPtr uri(GetURI()); + nsIURI* uri = GetURI(); if (!uri) { return; } - nsAutoCString username; - uri->GetUsername(username); - CopyASCIItoUTF16(username, aUsername); + uri->GetUsername(aUsername); } -void Link::GetPassword(nsAString& aPassword) { +void Link::GetPassword(nsACString& aPassword) { aPassword.Truncate(); - nsCOMPtr uri(GetURI()); + nsIURI* uri = GetURI(); if (!uri) { return; } - nsAutoCString password; - uri->GetPassword(password); - CopyASCIItoUTF16(password, aPassword); + uri->GetPassword(aPassword); } -void Link::GetHost(nsAString& _host) { - _host.Truncate(); +void Link::GetHost(nsACString& aHost) { + aHost.Truncate(); - nsCOMPtr uri(GetURI()); + nsIURI* uri = GetURI(); if (!uri) { // Do not throw! Not having a valid URI should result in an empty string. return; } - nsAutoCString hostport; - nsresult rv = uri->GetHostPort(hostport); - if (NS_SUCCEEDED(rv)) { - CopyUTF8toUTF16(hostport, _host); - } + uri->GetHostPort(aHost); } -void Link::GetHostname(nsAString& _hostname) { - _hostname.Truncate(); +void Link::GetHostname(nsACString& aHostname) { + aHostname.Truncate(); - nsCOMPtr uri(GetURI()); + nsIURI* uri = GetURI(); if (!uri) { // Do not throw! Not having a valid URI should result in an empty string. return; } - nsContentUtils::GetHostOrIPv6WithBrackets(uri, _hostname); + nsContentUtils::GetHostOrIPv6WithBrackets(uri, aHostname); } -void Link::GetPathname(nsAString& _pathname) { - _pathname.Truncate(); +void Link::GetPathname(nsACString& aPathname) { + aPathname.Truncate(); - nsCOMPtr uri(GetURI()); + nsIURI* uri = GetURI(); if (!uri) { // Do not throw! Not having a valid URI should result in an empty string. return; } - nsAutoCString file; - nsresult rv = uri->GetFilePath(file); - if (NS_SUCCEEDED(rv)) { - CopyUTF8toUTF16(file, _pathname); - } + uri->GetFilePath(aPathname); } -void Link::GetSearch(nsAString& _search) { - _search.Truncate(); +void Link::GetSearch(nsACString& aSearch) { + aSearch.Truncate(); - nsCOMPtr uri(GetURI()); + nsIURI* uri = GetURI(); if (!uri) { // Do not throw! Not having a valid URI or URL should result in an empty // string. return; } - nsAutoCString search; - nsresult rv = uri->GetQuery(search); - if (NS_SUCCEEDED(rv) && !search.IsEmpty()) { - _search.Assign(u'?'); - AppendUTF8toUTF16(search, _search); + nsresult rv = uri->GetQuery(aSearch); + if (NS_SUCCEEDED(rv) && !aSearch.IsEmpty()) { + aSearch.Insert('?', 0); } } -void Link::GetPort(nsAString& _port) { - _port.Truncate(); +void Link::GetPort(nsACString& aPort) { + aPort.Truncate(); - nsCOMPtr uri(GetURI()); + nsIURI* uri = GetURI(); if (!uri) { // Do not throw! Not having a valid URI should result in an empty string. return; @@ -404,27 +368,23 @@ void Link::GetPort(nsAString& _port) { // Note that failure to get the port from the URI is not necessarily a bad // thing. Some URIs do not have a port. if (NS_SUCCEEDED(rv) && port != -1) { - nsAutoString portStr; - portStr.AppendInt(port, 10); - _port.Assign(portStr); + aPort.AppendInt(port, 10); } } -void Link::GetHash(nsAString& _hash) { - _hash.Truncate(); +void Link::GetHash(nsACString& aHash) { + aHash.Truncate(); - nsCOMPtr uri(GetURI()); + nsIURI* uri = GetURI(); if (!uri) { // Do not throw! Not having a valid URI should result in an empty // string. return; } - nsAutoCString ref; - nsresult rv = uri->GetRef(ref); - if (NS_SUCCEEDED(rv) && !ref.IsEmpty()) { - _hash.Assign(char16_t('#')); - AppendUTF8toUTF16(ref, _hash); + nsresult rv = uri->GetRef(aHash); + if (NS_SUCCEEDED(rv) && !aHash.IsEmpty()) { + aHash.Insert('#', 0); } } diff --git a/dom/base/Link.h b/dom/base/Link.h index b604457126..cee1e1c5ca 100644 --- a/dom/base/Link.h +++ b/dom/base/Link.h @@ -64,25 +64,25 @@ class Link : public nsISupports { /** * Helper methods for modifying and obtaining parts of the URI of the Link. */ - void SetProtocol(const nsAString& aProtocol); - void SetUsername(const nsAString& aUsername); - void SetPassword(const nsAString& aPassword); - void SetHost(const nsAString& aHost); - void SetHostname(const nsAString& aHostname); - void SetPathname(const nsAString& aPathname); - void SetSearch(const nsAString& aSearch); - void SetPort(const nsAString& aPort); - void SetHash(const nsAString& aHash); - void GetOrigin(nsAString& aOrigin); - void GetProtocol(nsAString& _protocol); - void GetUsername(nsAString& aUsername); - void GetPassword(nsAString& aPassword); - void GetHost(nsAString& _host); - void GetHostname(nsAString& _hostname); - void GetPathname(nsAString& _pathname); - void GetSearch(nsAString& _search); - void GetPort(nsAString& _port); - void GetHash(nsAString& _hash); + void SetProtocol(const nsACString& aProtocol); + void SetUsername(const nsACString& aUsername); + void SetPassword(const nsACString& aPassword); + void SetHost(const nsACString& aHost); + void SetHostname(const nsACString& aHostname); + void SetPathname(const nsACString& aPathname); + void SetSearch(const nsACString& aSearch); + void SetPort(const nsACString& aPort); + void SetHash(const nsACString& aHash); + void GetOrigin(nsACString& aOrigin); + void GetProtocol(nsACString& aProtocol); + void GetUsername(nsACString& aUsername); + void GetPassword(nsACString& aPassword); + void GetHost(nsACString& aHost); + void GetHostname(nsACString& aHostname); + void GetPathname(nsACString& aPathname); + void GetSearch(nsACString& aSearch); + void GetPort(nsACString& aPort); + void GetHash(nsACString& aHash); /** * Invalidates any link caching, and resets the state to the default. diff --git a/dom/base/Location.cpp b/dom/base/Location.cpp index cdf25abf56..9edb9e0b6f 100644 --- a/dom/base/Location.cpp +++ b/dom/base/Location.cpp @@ -34,6 +34,7 @@ #include "mozilla/Unused.h" #include "mozilla/dom/Document.h" #include "mozilla/dom/DocumentInlines.h" +#include "mozilla/dom/FragmentDirective.h" #include "mozilla/dom/LocationBinding.h" #include "mozilla/dom/ScriptSettings.h" #include "ReferrerInfo.h" @@ -105,6 +106,9 @@ nsresult Location::GetURI(nsIURI** aURI, bool aGetInnermostURI) { } NS_ASSERTION(uri, "nsJARURI screwed up?"); + + // Remove the fragment directive from the url hash. + FragmentDirective::ParseAndRemoveFragmentDirectiveFromFragment(uri); nsCOMPtr exposableURI = net::nsIOService::CreateExposableURI(uri); exposableURI.forget(aURI); return NS_OK; @@ -549,26 +553,6 @@ void Location::Reload(bool aForceget, nsIPrincipal& aSubjectPrincipal, return aRv.Throw(NS_ERROR_FAILURE); } - if (StaticPrefs::dom_block_reload_from_resize_event_handler()) { - nsCOMPtr window = docShell->GetWindow(); - if (window && window->IsHandlingResizeEvent()) { - // location.reload() was called on a window that is handling a - // resize event. Sites do this since Netscape 4.x needed it, but - // we don't, and it's a horrible experience for nothing. In stead - // of reloading the page, just clear style data and reflow the - // page since some sites may use this trick to work around gecko - // reflow bugs, and this should have the same effect. - RefPtr doc = window->GetExtantDoc(); - - nsPresContext* pcx; - if (doc && (pcx = doc->GetPresContext())) { - pcx->RebuildAllStyleData(NS_STYLE_HINT_REFLOW, - RestyleHint::RestyleSubtree()); - } - return; - } - } - RefPtr bc = GetBrowsingContext(); if (!bc || bc->IsDiscarded()) { return; diff --git a/dom/base/PlacesEventCounts.cpp b/dom/base/PlacesEventCounts.cpp new file mode 100644 index 0000000000..be02e4c39b --- /dev/null +++ b/dom/base/PlacesEventCounts.cpp @@ -0,0 +1,58 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim:set ts=2 sw=2 sts=2 et cindent: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "PlacesEventCounts.h" +#include "mozilla/ErrorResult.h" +#include "mozilla/dom/BindingUtils.h" +#include "mozilla/dom/PlacesEventCounts.h" +#include "mozilla/dom/PlacesEventBinding.h" +#include "mozilla/dom/PlacesObserversBinding.h" + +namespace mozilla::dom { + +// Only needed for refcounted objects. +NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_0(PlacesEventCounts) +NS_IMPL_CYCLE_COLLECTING_ADDREF(PlacesEventCounts) +NS_IMPL_CYCLE_COLLECTING_RELEASE(PlacesEventCounts) +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(PlacesEventCounts) + NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY + NS_INTERFACE_MAP_ENTRY(nsISupports) +NS_INTERFACE_MAP_END + +PlacesEventCounts::PlacesEventCounts() { + ErrorResult rv; + for (auto eventType : MakeWebIDLEnumeratedRange()) { + PlacesEventCounts_Binding::MaplikeHelpers::Set( + this, NS_ConvertUTF8toUTF16(GetEnumString(eventType)), 0, rv); + if (NS_WARN_IF(rv.Failed())) { + rv.SuppressException(); + return; + } + } +} + +nsresult PlacesEventCounts::Increment(PlacesEventType aEventType) { + ErrorResult rv; + nsAutoCString eventName(GetEnumString(aEventType)); + uint64_t count = PlacesEventCounts_Binding::MaplikeHelpers::Get( + this, NS_ConvertUTF8toUTF16(eventName), rv); + if (MOZ_UNLIKELY(rv.Failed())) { + return rv.StealNSResult(); + } + PlacesEventCounts_Binding::MaplikeHelpers::Set( + this, NS_ConvertUTF8toUTF16(eventName), ++count, rv); + if (MOZ_UNLIKELY(rv.Failed())) { + return rv.StealNSResult(); + } + return NS_OK; +} + +JSObject* PlacesEventCounts::WrapObject(JSContext* aCx, + JS::Handle aGivenProto) { + return PlacesEventCounts_Binding::Wrap(aCx, this, aGivenProto); +} + +} // namespace mozilla::dom diff --git a/dom/base/PlacesEventCounts.h b/dom/base/PlacesEventCounts.h new file mode 100644 index 0000000000..69abeac7b3 --- /dev/null +++ b/dom/base/PlacesEventCounts.h @@ -0,0 +1,39 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim:set ts=2 sw=2 sts=2 et cindent: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef DOM_PLACESEVENTCOUNTS_H_ +#define DOM_PLACESEVENTCOUNTS_H_ + +#include "js/TypeDecls.h" +#include "mozilla/dom/BindingDeclarations.h" +#include "mozilla/dom/PlacesEventBinding.h" +#include "nsCycleCollectionParticipant.h" +#include "nsWrapperCache.h" + +namespace mozilla::dom { + +class PlacesEventCounts final : public nsISupports, public nsWrapperCache { + public: + NS_DECL_CYCLE_COLLECTING_ISUPPORTS + NS_DECL_CYCLE_COLLECTION_WRAPPERCACHE_CLASS(PlacesEventCounts) + + public: + PlacesEventCounts(); + + nsresult Increment(PlacesEventType aEventType); + + nsISupports* GetParentObject() const { return nullptr; }; + + JSObject* WrapObject(JSContext* aCx, + JS::Handle aGivenProto) override; + + private: + ~PlacesEventCounts() = default; +}; + +} // namespace mozilla::dom + +#endif // DOM_PLACESEVENTCOUNTS_H_ diff --git a/dom/base/PlacesObservers.cpp b/dom/base/PlacesObservers.cpp index bb92e4a072..b5211733ea 100644 --- a/dom/base/PlacesObservers.cpp +++ b/dom/base/PlacesObservers.cpp @@ -22,7 +22,7 @@ struct Flagged { Flagged(const Flagged& aOther) = default; ~Flagged() = default; - uint32_t flags; + uint32_t flags = 0; T value; }; @@ -123,6 +123,14 @@ MOZ_CAN_RUN_SCRIPT void CallListeners( } } +StaticRefPtr PlacesObservers::sCounts; +static void EnsureCountsInitialized() { + if (!PlacesObservers::sCounts) { + PlacesObservers::sCounts = new PlacesEventCounts(); + ClearOnShutdown(&PlacesObservers::sCounts); + } +} + void PlacesObservers::AddListener(GlobalObject& aGlobal, const nsTArray& aEventTypes, PlacesEventCallback& aCallback, @@ -304,7 +312,11 @@ void PlacesObservers::NotifyListeners( if (aEvents.Length() == 0) { return; } - + EnsureCountsInitialized(); + for (const auto& event : aEvents) { + DebugOnly rv = sCounts->Increment(event->Type()); + MOZ_ASSERT(NS_SUCCEEDED(rv)); + } #ifdef DEBUG if (!gNotificationQueue.IsEmpty()) { NS_WARNING( @@ -389,4 +401,10 @@ void PlacesObservers::NotifyNext() { } } +already_AddRefed PlacesObservers::Counts( + const GlobalObject& global) { + EnsureCountsInitialized(); + return do_AddRef(sCounts); +}; + } // namespace mozilla::dom diff --git a/dom/base/PlacesObservers.h b/dom/base/PlacesObservers.h index e7aca305d8..434380e11f 100644 --- a/dom/base/PlacesObservers.h +++ b/dom/base/PlacesObservers.h @@ -11,6 +11,7 @@ #include "mozilla/dom/BindingDeclarations.h" #include "mozilla/dom/PlacesObserversBinding.h" #include "mozilla/dom/PlacesEvent.h" +#include "mozilla/dom/PlacesEventCounts.h" #include "mozilla/places/INativePlacesEventCallback.h" #include "nsIWeakReferenceUtils.h" @@ -49,6 +50,9 @@ class PlacesObservers { static void NotifyListeners( const Sequence>& aEvents); + static StaticRefPtr sCounts; + static already_AddRefed Counts(const GlobalObject& global); + private: static void RemoveListener(uint32_t aFlags, PlacesEventCallback& aCallback); static void RemoveListener(uint32_t aFlags, diff --git a/dom/base/RangeBoundary.h b/dom/base/RangeBoundary.h index 2e4ab42397..4c5a70fb6a 100644 --- a/dom/base/RangeBoundary.h +++ b/dom/base/RangeBoundary.h @@ -9,6 +9,7 @@ #include "nsCOMPtr.h" #include "nsIContent.h" +#include "mozilla/dom/ShadowRoot.h" #include "mozilla/Assertions.h" #include "mozilla/Maybe.h" @@ -351,6 +352,34 @@ class RangeBoundaryBase { } public: + void NotifyParentBecomesShadowHost() { + MOZ_ASSERT(mParent); + MOZ_ASSERT(mParent->IsContainerNode(), + "Range is positioned on a text node!"); + if (!StaticPrefs::dom_shadowdom_selection_across_boundary_enabled()) { + return; + } + + if (!mIsMutationObserved) { + // RangeBoundaries that are not used in the context of a + // `MutationObserver` use the offset as main source of truth to compute + // `mRef`. Therefore, it must not be updated or invalidated. + return; + } + + if (!mRef) { + MOZ_ASSERT(mOffset.isSome() && mOffset.value() == 0, + "Invalidating offset of invalid RangeBoundary?"); + return; + } + + if (dom::ShadowRoot* shadowRoot = mParent->GetShadowRootForSelection()) { + mParent = shadowRoot; + } + + mOffset = Some(0); + } + bool IsSet() const { return mParent && (mRef || mOffset.isSome()); } bool IsSetAndValid() const { diff --git a/dom/base/RangeUtils.cpp b/dom/base/RangeUtils.cpp index 28283054b8..2383cc18aa 100644 --- a/dom/base/RangeUtils.cpp +++ b/dom/base/RangeUtils.cpp @@ -147,9 +147,10 @@ nsresult RangeUtils::CompareNodeToRange(nsINode* aNode, NS_WARN_IF(!aAbstractRange->IsPositioned())) { return NS_ERROR_INVALID_ARG; } - return CompareNodeToRangeBoundaries(aNode, aAbstractRange->StartRef(), - aAbstractRange->EndRef(), - aNodeIsBeforeRange, aNodeIsAfterRange); + return CompareNodeToRangeBoundaries( + aNode, aAbstractRange->MayCrossShadowBoundaryStartRef(), + aAbstractRange->MayCrossShadowBoundaryEndRef(), aNodeIsBeforeRange, + aNodeIsAfterRange); } template nsresult RangeUtils::CompareNodeToRangeBoundaries( diff --git a/dom/base/ScriptableContentIterator.cpp b/dom/base/ScriptableContentIterator.cpp index d10c4e68f8..ab04053c29 100644 --- a/dom/base/ScriptableContentIterator.cpp +++ b/dom/base/ScriptableContentIterator.cpp @@ -108,6 +108,22 @@ ScriptableContentIterator::InitWithRange(IteratorType aType, nsRange* aRange) { return mContentIterator->Init(aRange); } +NS_IMETHODIMP +ScriptableContentIterator::InitWithRangeAllowCrossShadowBoundary( + IteratorType aType, nsRange* aRange) { + if (aType == NOT_INITIALIZED || + (mIteratorType != NOT_INITIALIZED && aType != mIteratorType) || + aType != SUBTREE_ITERATOR) { + return NS_ERROR_INVALID_ARG; + } + + mIteratorType = aType; + MOZ_ASSERT(mIteratorType == SUBTREE_ITERATOR); + EnsureContentIterator(); + return static_cast(mContentIterator.get()) + ->InitWithAllowCrossShadowBoundary(aRange); +} + NS_IMETHODIMP ScriptableContentIterator::InitWithPositions(IteratorType aType, nsINode* aStartContainer, diff --git a/dom/base/Selection.cpp b/dom/base/Selection.cpp index 69986e6b78..7983ef98f9 100644 --- a/dom/base/Selection.cpp +++ b/dom/base/Selection.cpp @@ -22,9 +22,11 @@ #include "mozilla/CaretAssociationHint.h" #include "mozilla/ContentIterator.h" #include "mozilla/dom/Element.h" +#include "mozilla/dom/ChildIterator.h" #include "mozilla/dom/SelectionBinding.h" #include "mozilla/dom/ShadowRoot.h" #include "mozilla/dom/StaticRange.h" +#include "mozilla/dom/ShadowIncludingTreeIterator.h" #include "mozilla/ErrorResult.h" #include "mozilla/HTMLEditor.h" #include "mozilla/IntegerRange.h" @@ -778,30 +780,39 @@ NS_INTERFACE_MAP_END NS_IMPL_CYCLE_COLLECTING_ADDREF(Selection) NS_IMPL_CYCLE_COLLECTING_RELEASE_WITH_LAST_RELEASE(Selection, Disconnect()) -const RangeBoundary& Selection::AnchorRef() const { +const RangeBoundary& Selection::AnchorRef( + AllowRangeCrossShadowBoundary aAllowCrossShadowBoundary) const { if (!mAnchorFocusRange) { static RangeBoundary sEmpty; return sEmpty; } if (GetDirection() == eDirNext) { - return mAnchorFocusRange->StartRef(); + return aAllowCrossShadowBoundary == AllowRangeCrossShadowBoundary::Yes + ? mAnchorFocusRange->MayCrossShadowBoundaryStartRef() + : mAnchorFocusRange->StartRef(); } - return mAnchorFocusRange->EndRef(); + return aAllowCrossShadowBoundary == AllowRangeCrossShadowBoundary::Yes + ? mAnchorFocusRange->MayCrossShadowBoundaryEndRef() + : mAnchorFocusRange->EndRef(); } -const RangeBoundary& Selection::FocusRef() const { +const RangeBoundary& Selection::FocusRef( + AllowRangeCrossShadowBoundary aAllowCrossShadowBoundary) const { if (!mAnchorFocusRange) { static RangeBoundary sEmpty; return sEmpty; } if (GetDirection() == eDirNext) { - return mAnchorFocusRange->EndRef(); + return aAllowCrossShadowBoundary == AllowRangeCrossShadowBoundary::Yes + ? mAnchorFocusRange->MayCrossShadowBoundaryEndRef() + : mAnchorFocusRange->EndRef(); } - - return mAnchorFocusRange->StartRef(); + return aAllowCrossShadowBoundary == AllowRangeCrossShadowBoundary::Yes + ? mAnchorFocusRange->MayCrossShadowBoundaryStartRef() + : mAnchorFocusRange->StartRef(); } void Selection::SetAnchorFocusRange(size_t aIndex) { @@ -818,8 +829,8 @@ static int32_t CompareToRangeStart(const nsINode& aCompareNode, uint32_t aCompareOffset, const AbstractRange& aRange, nsContentUtils::NodeIndexCache* aCache) { - MOZ_ASSERT(aRange.GetStartContainer()); - nsINode* start = aRange.GetStartContainer(); + MOZ_ASSERT(aRange.GetMayCrossShadowBoundaryStartContainer()); + nsINode* start = aRange.GetMayCrossShadowBoundaryStartContainer(); // If the nodes that we're comparing are not in the same document, assume that // aCompareNode will fall at the end of the ranges. if (aCompareNode.GetComposedDoc() != start->GetComposedDoc() || @@ -830,8 +841,9 @@ static int32_t CompareToRangeStart(const nsINode& aCompareNode, } // The points are in the same subtree, hence there has to be an order. - return *nsContentUtils::ComparePoints(&aCompareNode, aCompareOffset, start, - aRange.StartOffset(), aCache); + return *nsContentUtils::ComparePoints( + &aCompareNode, aCompareOffset, start, + aRange.MayCrossShadowBoundaryStartOffset(), aCache); } static int32_t CompareToRangeStart(const nsINode& aCompareNode, @@ -844,7 +856,7 @@ static int32_t CompareToRangeEnd(const nsINode& aCompareNode, uint32_t aCompareOffset, const AbstractRange& aRange) { MOZ_ASSERT(aRange.IsPositioned()); - nsINode* end = aRange.GetEndContainer(); + nsINode* end = aRange.GetMayCrossShadowBoundaryEndContainer(); // If the nodes that we're comparing are not in the same document or in the // same subtree, assume that aCompareNode will fall at the end of the ranges. if (aCompareNode.GetComposedDoc() != end->GetComposedDoc() || @@ -855,8 +867,9 @@ static int32_t CompareToRangeEnd(const nsINode& aCompareNode, } // The points are in the same subtree, hence there has to be an order. - return *nsContentUtils::ComparePoints(&aCompareNode, aCompareOffset, end, - aRange.EndOffset()); + return *nsContentUtils::ComparePoints( + &aCompareNode, aCompareOffset, end, + aRange.MayCrossShadowBoundaryEndOffset()); } // static @@ -1323,7 +1336,18 @@ nsresult Selection::RemoveCollapsedRanges() { nsresult Selection::StyledRanges::RemoveCollapsedRanges() { uint32_t i = 0; while (i < mRanges.Length()) { - if (mRanges[i].mRange->Collapsed()) { + const AbstractRange* range = mRanges[i].mRange; + // If nsRange::mCrossShadowBoundaryRange exists, it means + // there's a cross boundary selection, so obviously + // we shouldn't remove this range. + const bool collapsed = + range->Collapsed() && !range->MayCrossShadowBoundary(); + // Cross boundary range should always be uncollapsed. + MOZ_ASSERT_IF( + range->MayCrossShadowBoundary(), + !range->AsDynamicRange()->CrossShadowBoundaryRangeCollapsed()); + + if (collapsed) { nsresult rv = RemoveRangeAndUnregisterSelection(*mRanges[i].mRange); NS_ENSURE_SUCCESS(rv, rv); } else { @@ -1616,7 +1640,8 @@ nsresult Selection::StyledRanges::GetIndicesForInterval( // the given interval's start point, but that range isn't collapsed (a // collapsed range should be included in the returned results). const AbstractRange* beginRange = mRanges[beginsAfterIndex].mRange; - if (beginRange->EndRef().Equals(aBeginNode, aBeginOffset) && + if (beginRange->MayCrossShadowBoundaryEndRef().Equals(aBeginNode, + aBeginOffset) && !beginRange->Collapsed()) { beginsAfterIndex++; } @@ -1627,7 +1652,8 @@ nsresult Selection::StyledRanges::GetIndicesForInterval( // included if (endsBeforeIndex < mRanges.Length()) { const AbstractRange* endRange = mRanges[endsBeforeIndex].mRange; - if (endRange->StartRef().Equals(aEndNode, aEndOffset) && + if (endRange->MayCrossShadowBoundaryStartRef().Equals(aEndNode, + aEndOffset) && endRange->Collapsed()) { endsBeforeIndex++; } @@ -1710,6 +1736,16 @@ nsresult Selection::SelectFramesOfInclusiveDescendantsOfContent( return NS_OK; } +void Selection::SelectFramesOfShadowIncludingDescendantsOfContent( + nsIContent* aContent, bool aSelected) const { + MOZ_ASSERT(aContent); + MOZ_ASSERT(StaticPrefs::dom_shadowdom_selection_across_boundary_enabled()); + for (nsINode* node : ShadowIncludingTreeIterator(*aContent)) { + nsIContent* innercontent = node->IsContent() ? node->AsContent() : nullptr; + SelectFramesOf(innercontent, aSelected); + } +} + void Selection::SelectFramesInAllRanges(nsPresContext* aPresContext) { // this method is currently only called in a user-initiated context. // therefore it is safe to assume that we are not in a Highlight selection @@ -1748,16 +1784,22 @@ nsresult Selection::SelectFrames(nsPresContext* aPresContext, if (mFrameSelection->IsInTableSelectionMode()) { const nsIContent* const commonAncestorContent = - nsIContent::FromNodeOrNull(aRange.GetClosestCommonInclusiveAncestor()); + nsIContent::FromNodeOrNull(aRange.GetClosestCommonInclusiveAncestor( + StaticPrefs::dom_select_events_textcontrols_selectstart_enabled() + ? AllowRangeCrossShadowBoundary::Yes + : AllowRangeCrossShadowBoundary::No)); nsIFrame* const frame = commonAncestorContent ? commonAncestorContent->GetPrimaryFrame() : aPresContext->PresShell()->GetRootFrame(); if (frame) { if (frame->IsTextFrame()) { - MOZ_ASSERT(commonAncestorContent == aRange.GetStartContainer()); - MOZ_ASSERT(commonAncestorContent == aRange.GetEndContainer()); + MOZ_ASSERT(commonAncestorContent == + aRange.GetMayCrossShadowBoundaryStartContainer()); + MOZ_ASSERT(commonAncestorContent == + aRange.GetMayCrossShadowBoundaryEndContainer()); static_cast(frame)->SelectionStateChanged( - aRange.StartOffset(), aRange.EndOffset(), aSelect, mSelectionType); + aRange.MayCrossShadowBoundaryStartOffset(), + aRange.MayCrossShadowBoundaryEndOffset(), aSelect, mSelectionType); } else { frame->SelectionStateChanged(); } @@ -1768,8 +1810,8 @@ nsresult Selection::SelectFrames(nsPresContext* aPresContext, // Loop through the content iterator for each content node; for each text // node, call SetSelected on it: - nsIContent* const startContent = - nsIContent::FromNodeOrNull(aRange.GetStartContainer()); + nsIContent* const startContent = nsIContent::FromNodeOrNull( + aRange.GetMayCrossShadowBoundaryStartContainer()); if (MOZ_UNLIKELY(!startContent)) { // Don't warn, bug 1055722 // XXX The range can start from a document node and such range can be @@ -1780,7 +1822,7 @@ nsresult Selection::SelectFrames(nsPresContext* aPresContext, MOZ_DIAGNOSTIC_ASSERT(startContent->IsInComposedDoc()); // We must call first one explicitly - nsINode* const endNode = aRange.GetEndContainer(); + nsINode* const endNode = aRange.GetMayCrossShadowBoundaryEndContainer(); if (NS_WARN_IF(!endNode)) { // We null-checked start node above, therefore, end node should also be // non-null here. @@ -1792,10 +1834,10 @@ nsresult Selection::SelectFrames(nsPresContext* aPresContext, // The frame could be an SVG text frame, in which case we don't treat it // as a text frame. if (frame->IsTextFrame()) { - const uint32_t startOffset = aRange.StartOffset(); - const uint32_t endOffset = endNode == startContent - ? aRange.EndOffset() - : startContent->Length(); + const uint32_t startOffset = aRange.MayCrossShadowBoundaryStartOffset(); + const uint32_t endOffset = + endNode == startContent ? aRange.MayCrossShadowBoundaryEndOffset() + : startContent->Length(); static_cast(frame)->SelectionStateChanged( startOffset, endOffset, aSelect, mSelectionType); } else { @@ -1806,7 +1848,7 @@ nsresult Selection::SelectFrames(nsPresContext* aPresContext, // If the range is in a node and the node is a leaf node, we don't need to // walk the subtree. - if (aRange.Collapsed() || + if ((aRange.Collapsed() && !aRange.MayCrossShadowBoundary()) || (startContent == endNode && !startContent->HasChildren())) { if (!isFirstContentTextNode) { SelectFramesOf(startContent, aSelect); @@ -1815,7 +1857,7 @@ nsresult Selection::SelectFrames(nsPresContext* aPresContext, } ContentSubtreeIterator subtreeIter; - subtreeIter.Init(&aRange); + subtreeIter.InitWithAllowCrossShadowBoundary(&aRange); if (isFirstContentTextNode && !subtreeIter.IsDone() && subtreeIter.GetCurrentNode() == startContent) { subtreeIter.Next(); // first content has already been handled. @@ -1825,8 +1867,12 @@ nsresult Selection::SelectFrames(nsPresContext* aPresContext, MOZ_DIAGNOSTIC_ASSERT(subtreeIter.GetCurrentNode()); if (nsIContent* const content = nsIContent::FromNodeOrNull(subtreeIter.GetCurrentNode())) { - SelectFramesOfInclusiveDescendantsOfContent(postOrderIter, content, - aSelect); + if (StaticPrefs::dom_shadowdom_selection_across_boundary_enabled()) { + SelectFramesOfShadowIncludingDescendantsOfContent(content, aSelect); + } else { + SelectFramesOfInclusiveDescendantsOfContent(postOrderIter, content, + aSelect); + } } } @@ -1839,7 +1885,7 @@ nsresult Selection::SelectFrames(nsPresContext* aPresContext, // The frame could be an SVG text frame, in which case we'll ignore it. if (frame->IsTextFrame()) { static_cast(frame)->SelectionStateChanged( - 0, aRange.EndOffset(), aSelect, mSelectionType); + 0, aRange.MayCrossShadowBoundaryEndOffset(), aSelect, mSelectionType); } } return NS_OK; @@ -1901,10 +1947,11 @@ UniquePtr Selection::LookUpSelection( if (range->IsStaticRange() && !range->AsStaticRange()->IsValid()) { continue; } - nsINode* startNode = range->GetStartContainer(); - nsINode* endNode = range->GetEndContainer(); - uint32_t startOffset = range->StartOffset(); - uint32_t endOffset = range->EndOffset(); + + nsINode* startNode = range->GetMayCrossShadowBoundaryStartContainer(); + nsINode* endNode = range->GetMayCrossShadowBoundaryEndContainer(); + uint32_t startOffset = range->MayCrossShadowBoundaryStartOffset(); + uint32_t endOffset = range->MayCrossShadowBoundaryEndOffset(); Maybe start, end; if (startNode == aContent && endNode == aContent) { @@ -2184,6 +2231,67 @@ void Selection::RemoveAllRanges(ErrorResult& aRv) { RemoveAllRangesInternal(aRv); } +already_AddRefed Selection::GetComposedRange( + const AbstractRange* aRange, + const Sequence>& aShadowRoots) const { + // If aIsEndNode is true, this method does the Step 5.1 and 5.2 + // in https://www.w3.org/TR/selection-api/#dom-selection-getcomposedranges, + // otherwise it does the Step 3.1 and 3.2. + auto reScope = [&aShadowRoots](nsINode*& aNode, uint32_t& aOffset, + bool aIsEndNode) { + MOZ_ASSERT(aNode); + while (aNode) { + const ShadowRoot* shadowRootOfNode = aNode->GetContainingShadow(); + if (!shadowRootOfNode) { + return; + } + + for (const OwningNonNull& shadowRoot : aShadowRoots) { + if (shadowRoot->IsShadowIncludingInclusiveDescendantOf( + shadowRootOfNode)) { + return; + } + } + + const nsIContent* host = aNode->GetContainingShadowHost(); + const Maybe maybeIndex = host->ComputeIndexInParentContent(); + MOZ_ASSERT(maybeIndex.isSome(), "not parent or anonymous child?"); + if (MOZ_UNLIKELY(maybeIndex.isNothing())) { + // Unlikely to happen, but still set aNode to nullptr to avoid + // leaking information about the shadow tree. + aNode = nullptr; + return; + } + aOffset = maybeIndex.value(); + if (aIsEndNode) { + aOffset += 1; + } + aNode = host->GetParentNode(); + } + }; + + nsINode* startNode = aRange->GetMayCrossShadowBoundaryStartContainer(); + uint32_t startOffset = aRange->MayCrossShadowBoundaryStartOffset(); + nsINode* endNode = aRange->GetMayCrossShadowBoundaryEndContainer(); + uint32_t endOffset = aRange->MayCrossShadowBoundaryEndOffset(); + + reScope(startNode, startOffset, false /* aIsEndNode */); + reScope(endNode, endOffset, true /* aIsEndNode */); + + RefPtr composedRange = StaticRange::Create( + startNode, startOffset, endNode, endOffset, IgnoreErrors()); + return composedRange.forget(); +} + +void Selection::GetComposedRanges( + const Sequence>& aShadowRoots, + nsTArray>& aComposedRanges) { + aComposedRanges.SetCapacity(mStyledRanges.mRanges.Length()); + for (const auto& range : mStyledRanges.mRanges) { + aComposedRanges.AppendElement(GetComposedRange(range.mRange, aShadowRoots)); + } +} + void Selection::RemoveAllRangesInternal(ErrorResult& aRv) { if (!mFrameSelection) { aRv.Throw(NS_ERROR_NOT_INITIALIZED); @@ -2501,11 +2609,15 @@ void Selection::CollapseInternal(InLimiter aInLimiter, // Hack to display the caret on the right line (bug 1237236). if (frameSelection->GetHint() == CaretAssociationHint::Before && aPoint.Container()->IsContent()) { - int32_t frameOffset; - nsTextFrame* f = do_QueryFrame(nsCaret::GetFrameAndOffset( - this, aPoint.Container(), - *aPoint.Offset(RawRangeBoundary::OffsetFilter::kValidOffsets), - &frameOffset)); + const nsCaret::CaretPosition pos{ + aPoint.Container(), + int32_t(*aPoint.Offset(RawRangeBoundary::OffsetFilter::kValidOffsets)), + frameSelection->GetHint(), frameSelection->GetCaretBidiLevel()}; + CaretFrameData frameData = nsCaret::GetFrameAndOffset(pos); + if (frameData.mFrame) { + frameSelection->SetHint(frameData.mHint); + } + nsTextFrame* f = do_QueryFrame(frameData.mFrame); if (f && f->IsAtEndOfLine() && f->HasSignificantTerminalNewline()) { // RawRangeBounary::Offset() causes computing offset if it's not been // done yet. However, it's called only when the container is a text @@ -2672,6 +2784,19 @@ AbstractRange* Selection::GetAbstractRangeAt(uint32_t aIndex) const { return mStyledRanges.mRanges.SafeElementAt(aIndex, empty).mRange; } +void Selection::GetDirection(nsAString& aDirection) const { + if (mStyledRanges.mRanges.IsEmpty() || + (mFrameSelection && (mFrameSelection->IsDoubleClickSelection() || + mFrameSelection->IsTripleClickSelection()))) { + // Empty range and double/triple clicks result a directionless selection. + aDirection.AssignLiteral("none"); + } else if (mDirection == nsDirection::eDirPrevious) { + aDirection.AssignLiteral("backward"); + } else { + aDirection.AssignLiteral("forward"); + } +} + nsRange* Selection::GetRangeAt(uint32_t aIndex) const { // This method per IDL spec returns a dynamic range. // Therefore, it must be ensured that it is only called @@ -2836,17 +2961,17 @@ void Selection::Extend(nsINode& aContainer, uint32_t aOffset, #ifdef DEBUG_SELECTION nsDirection oldDirection = GetDirection(); #endif - nsINode* anchorNode = GetAnchorNode(); - nsINode* focusNode = GetFocusNode(); - const uint32_t anchorOffset = AnchorOffset(); - const uint32_t focusOffset = FocusOffset(); + nsINode* anchorNode = GetMayCrossShadowBoundaryAnchorNode(); + nsINode* focusNode = GetMayCrossShadowBoundaryFocusNode(); + const uint32_t anchorOffset = MayCrossShadowBoundaryAnchorOffset(); + const uint32_t focusOffset = MayCrossShadowBoundaryFocusOffset(); RefPtr range = mAnchorFocusRange->CloneRange(); - nsINode* startNode = range->GetStartContainer(); - nsINode* endNode = range->GetEndContainer(); - const uint32_t startOffset = range->StartOffset(); - const uint32_t endOffset = range->EndOffset(); + nsINode* startNode = range->GetMayCrossShadowBoundaryStartContainer(); + nsINode* endNode = range->GetMayCrossShadowBoundaryEndContainer(); + const uint32_t startOffset = range->MayCrossShadowBoundaryStartOffset(); + const uint32_t endOffset = range->MayCrossShadowBoundaryEndOffset(); bool shouldClearRange = false; const Maybe anchorOldFocusOrder = nsContentUtils::ComparePoints( @@ -2882,7 +3007,8 @@ void Selection::Extend(nsINode& aContainer, uint32_t aOffset, (*anchorOldFocusOrder <= 0 && *oldFocusNewFocusOrder < 0)) { // a1,2 a,1,2 // select from 1 to 2 unless they are collapsed - range->SetEnd(aContainer, aOffset, aRv); + range->SetEnd(aContainer, aOffset, aRv, + AllowRangeCrossShadowBoundary::Yes); if (aRv.Failed()) { return; } @@ -2903,7 +3029,8 @@ void Selection::Extend(nsINode& aContainer, uint32_t aOffset, *anchorNewFocusOrder > 0) { // 2, a1 // select from 2 to 1a SetDirection(eDirPrevious); - range->SetStart(aContainer, aOffset, aRv); + range->SetStart(aContainer, aOffset, aRv, + AllowRangeCrossShadowBoundary::Yes); if (aRv.Failed()) { return; } @@ -2923,7 +3050,8 @@ void Selection::Extend(nsINode& aContainer, uint32_t aOffset, return; } - range->SetEnd(aContainer, aOffset, aRv); + range->SetEnd(aContainer, aOffset, aRv, + AllowRangeCrossShadowBoundary::Yes); if (aRv.Failed()) { return; } @@ -2933,27 +3061,33 @@ void Selection::Extend(nsINode& aContainer, uint32_t aOffset, return; } SelectFrames(presContext, *difRange, false); // deselect now - difRange->SetEnd(range->GetEndContainer(), range->EndOffset()); + difRange->SetEnd(range->GetMayCrossShadowBoundaryEndContainer(), + range->MayCrossShadowBoundaryEndOffset(), + AllowRangeCrossShadowBoundary::Yes); SelectFrames(presContext, *difRange, true); // must reselect last node // maybe more } else if (*anchorOldFocusOrder >= 0 && *anchorNewFocusOrder <= 0) { // 1,a,2 or 1a,2 or 1,a2 or 1a2 if (GetDirection() == eDirPrevious) { - res = range->SetStart(endNode, endOffset); + res = range->SetStart(endNode, endOffset, + AllowRangeCrossShadowBoundary::Yes); if (NS_FAILED(res)) { aRv.Throw(res); return; } } SetDirection(eDirNext); - range->SetEnd(aContainer, aOffset, aRv); + range->SetEnd(aContainer, aOffset, aRv, + AllowRangeCrossShadowBoundary::Yes); if (aRv.Failed()) { return; } if (focusNode != anchorNode || focusOffset != anchorOffset) { // if collapsed diff dont do anything - res = difRange->SetStart(focusNode, focusOffset); - nsresult tmp = difRange->SetEnd(anchorNode, anchorOffset); + res = difRange->SetStart(focusNode, focusOffset, + AllowRangeCrossShadowBoundary::Yes); + nsresult tmp = difRange->SetEnd(anchorNode, anchorOffset, + AllowRangeCrossShadowBoundary::Yes); if (NS_FAILED(tmp)) { res = tmp; } @@ -2987,7 +3121,8 @@ void Selection::Extend(nsINode& aContainer, uint32_t aOffset, return; } SetDirection(eDirPrevious); - range->SetStart(aContainer, aOffset, aRv); + range->SetStart(aContainer, aOffset, aRv, + AllowRangeCrossShadowBoundary::Yes); if (aRv.Failed()) { return; } @@ -2998,15 +3133,19 @@ void Selection::Extend(nsINode& aContainer, uint32_t aOffset, return; } SelectFrames(presContext, *difRange, false); - difRange->SetStart(range->GetStartContainer(), range->StartOffset()); + difRange->SetStart(range->GetMayCrossShadowBoundaryStartContainer(), + range->MayCrossShadowBoundaryStartOffset(), + AllowRangeCrossShadowBoundary::Yes); SelectFrames(presContext, *difRange, true); // must reselect last node } else if (*anchorNewFocusOrder >= 0 && *anchorOldFocusOrder <= 0) { // 2,a,1 or 2a,1 or 2,a1 or 2a1 if (GetDirection() == eDirNext) { - range->SetEnd(startNode, startOffset); + range->SetEnd(startNode, startOffset, + AllowRangeCrossShadowBoundary::Yes); } SetDirection(eDirPrevious); - range->SetStart(aContainer, aOffset, aRv); + range->SetStart(aContainer, aOffset, aRv, + AllowRangeCrossShadowBoundary::Yes); if (aRv.Failed()) { return; } @@ -3036,7 +3175,8 @@ void Selection::Extend(nsINode& aContainer, uint32_t aOffset, } else if (*oldFocusNewFocusOrder >= 0 && *anchorOldFocusOrder >= 0) { // 2,1,a or 21,a or 2,1a or 21a // select from 2 to 1 - range->SetStart(aContainer, aOffset, aRv); + range->SetStart(aContainer, aOffset, aRv, + AllowRangeCrossShadowBoundary::Yes); if (aRv.Failed()) { return; } @@ -3604,9 +3744,10 @@ void Selection::NotifySelectionListeners() { RefPtr frameSelection = mFrameSelection; - // This flag will be set to true if a selection by double click is detected. - // As soon as the selection is modified, it needs to be set to false. - frameSelection->SetIsDoubleClickSelection(false); + // This flag will be set to Double or Triple if a selection by double click or + // triple click is detected. As soon as the selection is modified, it needs to + // be reset to NotApplicable. + frameSelection->SetClickSelectionType(ClickSelectionType::NotApplicable); if (frameSelection->IsBatching()) { frameSelection->SetChangesDuringBatchingFlag(); diff --git a/dom/base/Selection.h b/dom/base/Selection.h index 9f031ab3cf..08563993ac 100644 --- a/dom/base/Selection.h +++ b/dom/base/Selection.h @@ -64,6 +64,9 @@ namespace dom { class Selection final : public nsSupportsWeakReference, public nsWrapperCache, public SupportsWeakPtr { + using AllowRangeCrossShadowBoundary = + mozilla::dom::AllowRangeCrossShadowBoundary; + protected: virtual ~Selection(); @@ -205,6 +208,10 @@ class Selection final : public nsSupportsWeakReference, nsRange* aRange, Maybe* aOutIndex, DispatchSelectstartEvent aDispatchSelectstartEvent); + already_AddRefed GetComposedRange( + const AbstractRange* aRange, + const Sequence>& aShadowRoots) const; + public: nsresult RemoveCollapsedRanges(); void Clear(nsPresContext* aPresContext); @@ -246,6 +253,8 @@ class Selection final : public nsSupportsWeakReference, // anchor and which end is focus. const nsRange* GetAnchorFocusRange() const { return mAnchorFocusRange; } + void GetDirection(nsAString& aDirection) const; + nsDirection GetDirection() const { return mDirection; } void SetDirection(nsDirection aDir) { mDirection = aDir; } @@ -321,6 +330,30 @@ class Selection final : public nsSupportsWeakReference, return offset ? *offset : 0; } + nsINode* GetMayCrossShadowBoundaryAnchorNode() const { + const RangeBoundary& anchor = AnchorRef(AllowRangeCrossShadowBoundary::Yes); + return anchor.IsSet() ? anchor.Container() : nullptr; + } + + uint32_t MayCrossShadowBoundaryAnchorOffset() const { + const RangeBoundary& anchor = AnchorRef(AllowRangeCrossShadowBoundary::Yes); + const Maybe offset = + anchor.Offset(RangeBoundary::OffsetFilter::kValidOffsets); + return offset ? *offset : 0; + } + + nsINode* GetMayCrossShadowBoundaryFocusNode() const { + const RangeBoundary& focus = FocusRef(AllowRangeCrossShadowBoundary::Yes); + return focus.IsSet() ? focus.Container() : nullptr; + } + + uint32_t MayCrossShadowBoundaryFocusOffset() const { + const RangeBoundary& focus = FocusRef(AllowRangeCrossShadowBoundary::Yes); + const Maybe offset = + focus.Offset(RangeBoundary::OffsetFilter::kValidOffsets); + return offset ? *offset : 0; + } + nsIContent* GetChildAtAnchorOffset() { const RangeBoundary& anchor = AnchorRef(); return anchor.IsSet() ? anchor.GetChildAtOffset() : nullptr; @@ -330,8 +363,12 @@ class Selection final : public nsSupportsWeakReference, return focus.IsSet() ? focus.GetChildAtOffset() : nullptr; } - const RangeBoundary& AnchorRef() const; - const RangeBoundary& FocusRef() const; + const RangeBoundary& AnchorRef( + AllowRangeCrossShadowBoundary aAllowCrossShadowBoundary = + AllowRangeCrossShadowBoundary::No) const; + const RangeBoundary& FocusRef( + AllowRangeCrossShadowBoundary aAllowCrossShadowBoundary = + AllowRangeCrossShadowBoundary::No) const; /* * IsCollapsed -- is the whole selection just one point, or unset? @@ -385,6 +422,10 @@ class Selection final : public nsSupportsWeakReference, MOZ_CAN_RUN_SCRIPT void RemoveAllRanges(mozilla::ErrorResult& aRv); + void GetComposedRanges( + const Sequence>& aShadowRoots, + nsTArray>& aComposedRanges); + /** * Whether Stringify should flush layout or not. */ @@ -810,6 +851,12 @@ class Selection final : public nsSupportsWeakReference, PostContentIterator& aPostOrderIter, nsIContent* aContent, bool aSelected) const; + /** + * https://dom.spec.whatwg.org/#concept-shadow-including-descendant + */ + void SelectFramesOfShadowIncludingDescendantsOfContent(nsIContent* aContent, + bool aSelected) const; + nsresult SelectFrames(nsPresContext* aPresContext, AbstractRange& aRange, bool aSelect) const; diff --git a/dom/base/StaticRange.cpp b/dom/base/StaticRange.cpp index 0946e8f9bf..73ff04c038 100644 --- a/dom/base/StaticRange.cpp +++ b/dom/base/StaticRange.cpp @@ -100,6 +100,13 @@ bool StaticRange::IsValid() const { return false; } + MOZ_ASSERT(mAreStartAndEndInSameTree == + (RangeUtils::ComputeRootNode(mStart.Container()) == + RangeUtils::ComputeRootNode(mEnd.Container()))); + if (!mAreStartAndEndInSameTree) { + return false; + } + const Maybe pointOrder = nsContentUtils::ComparePoints(mStart, mEnd); return pointOrder.isSome() && *pointOrder <= 0; } @@ -119,6 +126,9 @@ void StaticRange::DoSetRange(const RangeBoundaryBase& aStartBoundary, if (checkCommonAncestor) { UpdateCommonAncestorIfNecessary(); } + + mAreStartAndEndInSameTree = RangeUtils::ComputeRootNode(mStart.Container()) == + RangeUtils::ComputeRootNode(mEnd.Container()); } /* static */ diff --git a/dom/base/StaticRange.h b/dom/base/StaticRange.h index a6f677130d..af7054f843 100644 --- a/dom/base/StaticRange.h +++ b/dom/base/StaticRange.h @@ -8,6 +8,7 @@ #define mozilla_dom_StaticRange_h #include "mozilla/RangeBoundary.h" +#include "mozilla/RangeUtils.h" #include "mozilla/dom/AbstractRange.h" #include "mozilla/dom/StaticRangeBinding.h" #include "nsTArray.h" @@ -70,6 +71,21 @@ class StaticRange final : public AbstractRange { */ bool IsValid() const; + void NotifyNodeBecomesShadowHost(nsINode* aNode) { + if (aNode == mStart.Container()) { + mStart.NotifyParentBecomesShadowHost(); + } + + if (aNode == mEnd.Container()) { + mEnd.NotifyParentBecomesShadowHost(); + } + } + + private: + // Whether the start and end points are in the same tree. + // They could be in different trees, i.e, cross shadow boundaries. + bool mAreStartAndEndInSameTree = false; + protected: explicit StaticRange(nsINode* aNode) : AbstractRange(aNode, /* aIsDynamicRange = */ false) {} diff --git a/dom/base/ThirdPartyUtil.cpp b/dom/base/ThirdPartyUtil.cpp index 2c71ab3d94..0ced8aa7d8 100644 --- a/dom/base/ThirdPartyUtil.cpp +++ b/dom/base/ThirdPartyUtil.cpp @@ -200,9 +200,9 @@ ThirdPartyUtil::IsThirdPartyWindow(mozIDOMWindowProxy* aWindow, nsIURI* aURI, bool result; - // Ignore about:blank URIs here since they have no domain and attempting to - // compare against them will fail. - if (aURI && !NS_IsAboutBlank(aURI)) { + // Ignore about:blank and about:srcdoc URIs here since they have no domain + // and attempting to compare against them will fail. + if (aURI && !NS_IsAboutBlank(aURI) && !NS_IsAboutSrcdoc(aURI)) { nsCOMPtr prin; nsresult rv = GetPrincipalFromWindow(aWindow, getter_AddRefs(prin)); NS_ENSURE_SUCCESS(rv, rv); @@ -320,10 +320,10 @@ ThirdPartyUtil::IsThirdPartyChannel(nsIChannel* aChannel, nsIURI* aURI, } } - // Special consideration must be done for about:blank URIs because those - // inherit the principal from the parent context. For them, let's consider the - // principal URI. - if (NS_IsAboutBlank(channelURI)) { + // Special consideration must be done for about:blank and about:srcdoc URIs + // because those inherit the principal from the parent context. For them, + // let's consider the principal URI. + if (NS_IsAboutBlank(channelURI) || NS_IsAboutSrcdoc(channelURI)) { nsCOMPtr principalToInherit = loadInfo->FindPrincipalToInherit(aChannel); if (!principalToInherit) { diff --git a/dom/base/UseCounters.conf b/dom/base/UseCounters.conf index 86d782b476..66507b6d49 100644 --- a/dom/base/UseCounters.conf +++ b/dom/base/UseCounters.conf @@ -62,11 +62,6 @@ method DataTransfer.mozGetDataAt attribute DataTransfer.mozUserCancelled attribute DataTransfer.mozSourceNode -// Marquee events -custom onstart sets a onstart event listener -custom onbounce sets a onbounce event listener -custom onfinish sets a onfinish event listener - // Element non-standard events custom onoverflow sets an element onoverflow event listener custom onunderflow sets an element onunderflow event listener diff --git a/dom/base/crashtests/1697256.html b/dom/base/crashtests/1697256.html index 25024083e3..3d6634e952 100644 --- a/dom/base/crashtests/1697256.html +++ b/dom/base/crashtests/1697256.html @@ -4,8 +4,9 @@ + + diff --git a/dom/base/crashtests/1887963_1.html b/dom/base/crashtests/1887963_1.html new file mode 100644 index 0000000000..fbc30dc587 --- /dev/null +++ b/dom/base/crashtests/1887963_1.html @@ -0,0 +1,15 @@ + +
A
+ diff --git a/dom/base/crashtests/1887963_2.html b/dom/base/crashtests/1887963_2.html new file mode 100644 index 0000000000..53f512173b --- /dev/null +++ b/dom/base/crashtests/1887963_2.html @@ -0,0 +1,15 @@ + +
A
+ diff --git a/dom/base/crashtests/1887974.html b/dom/base/crashtests/1887974.html new file mode 100644 index 0000000000..85ffd2b02a --- /dev/null +++ b/dom/base/crashtests/1887974.html @@ -0,0 +1,18 @@ + +