diff --git a/moli-renderer-v8/src/script_vm/tests/dom_elements/dom_surface/extracted/dom_interfaces_and_collections.rs b/moli-renderer-v8/src/script_vm/tests/dom_elements/dom_surface/extracted/dom_interfaces_and_collections.rs index 11543174d9..d8c89c0faa 100644 --- a/moli-renderer-v8/src/script_vm/tests/dom_elements/dom_surface/extracted/dom_interfaces_and_collections.rs +++ b/moli-renderer-v8/src/script_vm/tests/dom_elements/dom_surface/extracted/dom_interfaces_and_collections.rs @@ -2830,3 +2830,107 @@ fn reflected_dom_token_list_attributes_are_live_same_object_and_owner_scoped() { assert_eq!(result, "ok"); } + +#[test] +fn svg_svg_element_deselect_all_clears_the_owner_document_selection() { + let mut vm = new_parsed_test_vm( + "https://svg-deselect-all.test/", + "", + ); + + let result = vm + .eval( + r#" + (() => { + const assert = (condition, message) => { + if (!condition) throw new Error(message); + }; + const svgNamespace = "http://www.w3.org/2000/svg"; + const outer = document.createElementNS(svgNamespace, "svg"); + const inner = document.createElementNS(svgNamespace, "svg"); + const svgText = document.createElementNS(svgNamespace, "text"); + const htmlText = document.createElement("p"); + svgText.textContent = "SVG selection"; + htmlText.textContent = "HTML selection"; + inner.appendChild(svgText); + outer.appendChild(inner); + document.body.append(outer, htmlText); + + const descriptor = Object.getOwnPropertyDescriptor( + SVGSVGElement.prototype, + "deselectAll" + ); + assert(!!descriptor, "deselectAll descriptor"); + assert(typeof descriptor.value === "function", "deselectAll function"); + assert(descriptor.value.name === "deselectAll", "deselectAll name"); + assert(descriptor.value.length === 0, "deselectAll length"); + assert(descriptor.enumerable && descriptor.writable && descriptor.configurable, + "deselectAll descriptor flags"); + + const selection = window.getSelection(); + const select = node => { + const range = document.createRange(); + range.selectNodeContents(node); + selection.removeAllRanges(); + selection.addRange(range); + assert(selection.rangeCount === 1, "selection precondition"); + }; + + outer.deselectAll(); + assert(selection.rangeCount === 0 && selection.isCollapsed, + "empty selection stays empty"); + + select(svgText); + outer.deselectAll(); + assert(selection.rangeCount === 0 && selection.isCollapsed, + "outer svg clears SVG selection"); + + select(htmlText); + inner.deselectAll(); + assert(selection.rangeCount === 0 && selection.isCollapsed, + "inner svg clears selection outside its subtree"); + + select(svgText); + const originalDocumentGetSelection = document.getSelection; + const originalRemoveAllRanges = Selection.prototype.removeAllRanges; + document.getSelection = () => { throw new Error("observable getSelection call"); }; + Selection.prototype.removeAllRanges = () => { + throw new Error("observable removeAllRanges call"); + }; + Object.defineProperty(outer, "ownerDocument", { + value: null, + configurable: true + }); + Object.defineProperty(document, "defaultView", { + value: null, + configurable: true + }); + try { + outer.deselectAll(); + } finally { + delete outer.ownerDocument; + delete document.defaultView; + document.getSelection = originalDocumentGetSelection; + Selection.prototype.removeAllRanges = originalRemoveAllRanges; + } + assert(selection.rangeCount === 0, "deselectAll uses internal selection state"); + + let borrowed = "returned"; + try { + descriptor.value.call(document.createElement("div")); + } catch (error) { + borrowed = error && error.name; + } + assert(borrowed === "TypeError", "deselectAll receiver brand"); + + const detachedDocument = document.implementation.createHTMLDocument(""); + const detachedSvg = detachedDocument.createElementNS(svgNamespace, "svg"); + detachedSvg.deselectAll(); + return "ok"; + })() + "#, + ) + .expect("SVGSVGElement.deselectAll should evaluate"); + + assert_eq!(result, "ok"); +}