mirror of
https://github.com/servo/servo.git
synced 2025-08-03 04:30:10 +01:00
Auto merge of #29396 - delan:window-getter, r=jdm
HTML #named-access-on-the-window-object This patch implements [named access on the window object](https://html.spec.whatwg.org/#named-access-on-the-window-object) based on @pylbrecht’s and @yvt’s previous work (see also #27952, #25562, #21869, #19904). That work makes 130 tests pass, and I’ve made some additional changes to make [the test for the feature itself](https://wpt.live/webidl/ecmascript-binding/window-named-properties-object.html) pass too, but I haven’t worked with mozjs before, so I have no idea if I’ve gone about my changes the Right Way. In particular, this patch: * [x] applies @pylbrecht’s patch (#27952) * [x] resolves conflicts related to #29079 and #28546 (5ae0ca48f8a74e4bae04f8bc9e50252a05e4c1e1) * [x] applies @yvt’s patch to the segfault in getPrototypeIfOrdinary (1e24ffdec6e91784b76a8f02bda26e8de0420c94) * [x] reverts the change introducing Untransplantable (76dc35a2b4ddd39f588b6c0b4e9bd46346888606), since #28534 was abandoned in favour of #28536 * [x] triages the unexpected WPT results, updating expectations as needed (see below) * [x] fixes the failures in /webidl/ecmascript-binding/window-named-properties-object.html (3844f69ea9b591b766fee3537f640e7aae05bb11) * [x] fixes the failures in the [[GetOwnProperty]] subtest (75c24e9c134c531f8fc893a185683ea2e609a47e) * [x] fixes the failures in the [[DefineOwnProperty]] subtest (e4e7a14e514db75c3d6694fb1aaeb24d04c9ae81) * [x] fixes the failures in the [[HasProperty]] subtest (75c24e9c134c531f8fc893a185683ea2e609a47e) * [x] fixes the failures in the [[Get]] subtest (4bafb273d0d1d57589cede88ba2696b0e42920a9) * [x] fixes the failures in the [[Set]] (direct) subtest (e4e7a14e514db75c3d6694fb1aaeb24d04c9ae81) * [x] fixes the failures in the [[Delete]] subtest (4bafb273d0d1d57589cede88ba2696b0e42920a9) * [x] fixes the failures in the [[OwnPropertyKeys]] subtest (afcd24a2973557509438833b8fe06a24377710f1) * [x] resolves the todos and fixmes * [x] Document: handle exposed objects ×3 (ed3e37bb22276031e0423dfa8005bc79ab673913) * [x] Window::NamedGetter: document.get_elements_with_id() (618ef5744645bb14405d95177bf49aa3b98439dc) * [x] get_own_property_descriptor: check for interactions with indexed access (b906faee59aa15f716662c223fc61758c4053299) * [x] create trap: pass proper classp (618ef5744645bb14405d95177bf49aa3b98439dc) <details><summary>131× tests that now pass</summary> * **/webidl/ecmascript-binding/window-named-properties-object.html** *directly relevant to this patch* * **/FileAPI/Blob-methods-from-detached-frame.html** done #29450 #29451 * **/cors/script-304.html** * **/css/CSS2/abspos/adjacent-to-relpos-inline-in-inline-that-had-block.html** * **/css/CSS2/abspos/adjacent-to-relpos-inline-that-had-block.html** * **/css/CSS2/floats-clear/remove-block-before-self-collapsing-sibling-with-clearance.html** * **/css/CSS2/floats/computed-float-position-absolute.html** * **/css/CSS2/normal-flow/block-in-inline-hittest-float-001.html** * **/css/CSS2/normal-flow/block-in-inline-insert-018.html** * **/css/CSS2/normal-flow/margin-collapse-through-percentage-padding.html** * **/css/CSS2/positioning/abspos-change-in-inline-block.html** * **/css/CSS2/positioning/abspos-width-change-inline-container-001.html** * **/css/CSS2/positioning/detach-abspos-before-layout.html** * **/css/CSS2/positioning/relpos-percentage-left-in-scrollable-2.html** * **/css/CSS2/positioning/relpos-percentage-left-in-scrollable.html** * **/css/CSS2/stacking-context/composite-change-after-scroll-preserves-stacking-order.html** * **/css/CSS2/stacking-context/opacity-change-parent-stacking-context.html** * **/css/CSS2/stacking-context/opacity-change-twice-stacking-context.html** * **/css/CSS2/stacking-context/opacity-transition-change-parent-stacking-context.html** * **/css/css-animations/animation-base-response-002.html** * **/css/css-animations/style-animation-parsing.html** * **/css/css-backgrounds/background-clip-color-repaint.html** * **/css/css-backgrounds/background-image-gradient-currentcolor-conic-repaint.html** * **/css/css-backgrounds/background-image-gradient-currentcolor-linear-repaint.html** * **/css/css-backgrounds/background-image-gradient-currentcolor-radial-repaint.html** * **/css/css-backgrounds/background-image-none-gradient-repaint.html** * **/css/css-backgrounds/border-radius-dynamic-from-no-radius.html** * **/css/css-backgrounds/child-move-reveals-parent-background.html** * **/css/css-color/filters-under-will-change-opacity.html** * **/css/css-flexbox/anonymous-flex-item-001.html** * **/css/css-flexbox/anonymous-flex-item-003.html** * **/css/css-flexbox/dynamic-bsize-change.html** * **/css/css-flexbox/flex-aspect-ratio-img-row-016.html** * **/css/cssom/at-namespace.html** * **/css/cssom/computed-style-002.html** * **/css/cssom/getComputedStyle-display-none-002.html** * **/css/cssom/getComputedStyle-display-none-003.html** * **/css/cssom/preferred-stylesheet-reversed-order.html** * **/css/cssom-view/HTMLImageElement-x-and-y-ignore-transforms.html** * **/css/cssom-view/pt-to-px-width.html** * **/css/cssom-view/scroll-back-to-initial-position.html** * **/css/css-text-decor/text-decoration-color-recalc-002.html** * **/css/css-text-decor/text-decoration-line-grammar-error-color-dynamic-001.optional.html** * **/css/css-text-decor/text-decoration-line-spelling-error-color-dynamic-001.optional.html** * **/css/css-text/text-indent/percentage-value-intrinsic-size.html** * **/css/css-text/white-space/display-contents-remove-whitespace-change.html** * **/css/css-text/white-space/white-space-empty-text-sibling.html** * **/css/css-transforms/change-perspective-property.html** * **/css/css-transforms/change-scale-wide-range.html** * **/css/css-transforms/change-transform-origin-property.html** * **/css/css-transforms/dynamic-fixed-pos-cb-change.html** * **/css/css-transforms/individual-transform/change-rotate-property.html** * **/css/css-transforms/individual-transform/change-scale-property.html** * **/css/css-transforms/paint-order-with-transform-change.html** * **/css/css-transforms/size-change-under-backface-visibility-hidden.html** * **/css/css-transforms/subpixel-transform-changes-001.html** * **/css/css-transforms/subpixel-transform-changes-002.html** * **/css/css-transforms/subpixel-transform-changes-003.html** * **/css/css-transforms/subpixel-transform-changes-004.html** * **/css/css-transitions/transition-reparented.html** * **/css/css-ui/historical/moz-user-modify-01.html** * **/css/css-ui/historical/user-modify-01.html** * **/css/css-ui/historical/webkit-user-modify-01.html** * **/css/css-ui/resize-change-margin.html** * **/css/css-ui/text-overflow-change-color.html** * **/css/css-ui/text-overflow-ellipsis-width-001.html** * **/css/css-values/animations/line-height-lh-transition.html** * **/css/css-values/calc-nesting.html** * **/css/css-values/update-subpixel-rem-unit.html** * **/css/css-values/viewport-units-modify.html** * **/css/css-variables/css-variable-change-style-001.html** * **/css/css-variables/css-variable-change-style-002.html** * **/css/css-variables/variable-cycles.html** * **/css/selectors/attribute-selectors/style-attribute-selector.html** * **/css/selectors/i18n/lang-pseudo-class-disconnected.html** * **/css/selectors/invalidation/attribute.html** * **/css/selectors/invalidation/class-id-attr.html** * **/css/selectors/invalidation/defined.html** * **/css/selectors/invalidation/sheet-going-away-001.html** * **/css/selectors/invalidation/sibling.html** * **/css/selectors/missing-right-token.html** * **/css/selectors/not-complex.html** * **/css/selectors/not-specificity.html** * **/css/selectors/nth-of-type-namespace.html** * **/css/selectors/selectors-case-sensitive-001.html** * **/css/selectors/visited-inheritance.html** * **/custom-elements/CustomElementRegistry-constructor-and-callbacks-are-held-strongly.html** * **/dom/events/event-global-is-still-set-when-coercing-beforeunload-result.html** * **/dom/nodes/remove-and-adopt-thcrash.html** * **/html/browsers/browsing-the-web/history-traversal/history-traversal-navigate-parent-while-child-loading.html** * **/html/browsers/history/the-history-interface/history_back_cross_realm_method.html** * **/html/browsers/history/the-history-interface/history_forward_cross_realm_method.html** * **/html/browsers/history/the-history-interface/history_go_cross_realm_method.html** * **/html/browsers/the-window-object/named-access-on-the-window-object/window-named-properties.html** * **/html/dom/elements/images/bypass-cache-revalidation.html** * **/html/rendering/non-replaced-elements/tables/table-ua-stylesheet.html** * **/html/rendering/replaced-elements/attributes-for-embedded-content-and-images/img-alt-crash-001.html** * **/html/rendering/replaced-elements/embedded-content/change-src-while-not-displayed.html** * **/html/rendering/replaced-elements/embedded-content/cross-domain-iframe-in-multicol.sub.html** * **/html/rendering/replaced-elements/embedded-content/cross-domain-iframe.sub.html** * **/html/rendering/replaced-elements/embedded-content/tall-cross-domain-iframe-in-scrolled.sub.html** * **/html/rendering/widgets/the-select-element/select-invalidation.html** * **/html/semantics/document-metadata/the-base-element/base_target_does_not_affect_iframe_src_navigation.html** * **/html/semantics/document-metadata/the-base-element/base_target_does_not_affect_location_assignment.html** * **/html/semantics/embedded-content/the-embed-element/embed-document-under-content-visibility-focus.html** * **/html/semantics/embedded-content/the-embed-element/embed-document-under-content-visibility-gbcr.html** * **/html/semantics/embedded-content/the-iframe-element/iframe-network-error.sub.html** * **/html/semantics/embedded-content/the-iframe-element/srcdoc-anchor.html** * **/html/semantics/embedded-content/the-iframe-element/srcdoc-attribute-reset.html** * **/html/semantics/embedded-content/the-img-element/image-compositing-change.html** * **/html/semantics/embedded-content/the-img-element/image-compositing-large-scale-change.html** * **/html/semantics/embedded-content/the-img-element/image-loading-lazy-clip-path.html** * **/html/semantics/embedded-content/the-img-element/image-loading-lazy-zero-intersection-area.html** * **/html/semantics/embedded-content/the-img-element/image-loading-subpixel-clip.html** * **/html/semantics/forms/form-submission-0/form-double-submit-to-different-origin-frame.html** * **/html/semantics/forms/form-submission-0/form-submit-iframe-then-location-navigate.html** * **/html/semantics/forms/form-submission-0/reparent-form-during-planned-navigation-task.html** * **/html/semantics/forms/the-button-element/button-willvalidate-readonly-attribute.html** * **/html/semantics/forms/the-input-element/datetime-local-trailing-zeros.html** * **/html/semantics/forms/the-input-element/radio-morphed.html** * **/html/semantics/forms/the-progress-element/progress.html** * **/html/semantics/forms/the-select-element/select-willvalidate-readonly-attribute.html** * **/html/semantics/selectors/pseudo-classes/placeholder-shown-type-change.html** * **/html/syntax/serializing-html-fragments/initial-linefeed-pre.html** * **/html/webappapis/scripting/events/event-handler-processing-algorithm-error/frameset-element-synthetic-errorevent.html** * **/html/webappapis/scripting/events/event-handler-processing-algorithm-error/frameset-element-synthetic-event.html** * **/quirks/unitless-length/excluded-properties-001.html** * **/quirks/unitless-length/excluded-properties-003.html** * **/selection/caret/empty-elements.html** * **/selection/contenteditable/cefalse-on-boundaries.html** * **/selection/textcontrols/onselectionchange-content-attribute.html** </details> <details><summary>94× tests that improve but still fail in a way that’s clearly the subject of the test</summary> * **/css/css-animations/animation-base-response-001.html** * **/css/css-backgrounds/background-image-gradient-currentcolor-visited.html** * **/css/css-backgrounds/inheritance.sub.html** * **/css/css-backgrounds/local-attachment-content-box-scroll.html** * **/css/css-color/canvas-change-opacity.html** * **/css/css-color/color-initial-canvastext.html** * **/css/css-flexbox/flex-aspect-ratio-img-column-010.html** * **/css/css-flexbox/flex-aspect-ratio-img-row-004.html** * **/css/css-fonts/parsing/font-face-src-format.html** getter works but insertRule does nothing * **/css/css-fonts/parsing/font-face-src-list.html** as above * **/css/css-fonts/parsing/font-face-src-local.html** as above, but also delayed length change? * **/css/css-fonts/parsing/font-face-src-tech.html** as above * **/css/css-text/white-space/trailing-space-before-br-001.html** * **/css/css-transforms/3d-rendering-context-behavior.html** failures are subject of test, but also blank page? * **/css/css-transforms/individual-transform/change-translate-property.html** * **/css/css-transforms/preserve-3d-flat-grouping-properties.html** * **/css/css-transforms/transforms-support-calc.html** * **/css/css-transitions/inherit-height-transition.html** * **/css/css-ui/inheritance.html** * **/css/css-ui/parsing/outline-width-computed.html** failures are subject of test; https://drafts.csswg.org/css-values-4/#snap-a-length-as-a-border-width * **/css/css-values/lh-unit-003.html** * **/css/css-values/viewport-units-compute.html** * **/css/css-values/viewport-units-keyframes.html** * **/css/css-values/viewport-units-media-queries.html** * **/css/css-values/viewport-units-writing-mode.html** * **/css/cssom-view/elementsFromPoint-table.html** * **/css/cssom-view/scroll-behavior-default-css.html** * **/css/cssom-view/scroll-behavior-element.html** * **/css/cssom-view/scroll-behavior-main-frame-root.html** * **/css/cssom-view/scroll-behavior-main-frame-window.html** * **/css/cssom-view/scroll-behavior-smooth-navigation.html** * **/css/cssom-view/scroll-behavior-subframe-root.html** * **/css/cssom/computed-style-003.html** * **/css/cssom/computed-style-004.html** * **/css/cssom/font-variant-shorthand-serialization.html** * **/css/selectors/dir-selector-auto.html** * **/css/selectors/has-argument-with-explicit-scope.html** * **/css/selectors/has-basic.html** * **/css/selectors/has-relative-argument.html** * **/css/selectors/invalidation/attribute-or-elemental-selectors-in-has.html** * **/css/selectors/invalidation/child-indexed-pseudo-classes-in-has.html** * **/css/selectors/invalidation/dir-pseudo-class-in-has.html** * **/css/selectors/invalidation/empty-pseudo-in-has.html** * **/css/selectors/invalidation/has-in-adjacent-position.html** * **/css/selectors/invalidation/has-in-ancestor-position.html** * **/css/selectors/invalidation/has-in-parent-position.html** * **/css/selectors/invalidation/has-in-sibling-position.html** * **/css/selectors/invalidation/has-invalidation-after-removing-non-first-element.html** * **/css/selectors/invalidation/has-invalidation-for-wiping-an-element.html** * **/css/selectors/invalidation/has-sibling.html** * **/css/selectors/invalidation/has-with-not.html** * **/css/selectors/invalidation/has-with-pseudo-class.html** * **/css/selectors/invalidation/input-pseudo-classes-in-has.html** * **/css/selectors/invalidation/is-pseudo-containing-complex-in-has.html** * **/css/selectors/invalidation/lang-pseudo-class-in-has.html** * **/css/selectors/invalidation/not-pseudo-containing-complex-in-has.html** * **/css/selectors/invalidation/typed-child-indexed-pseudo-classes-in-has.html** * **/css/selectors/is-where-basic.html** * **/css/selectors/is-where-not.html** * **/html/rendering/non-replaced-elements/tables/form-in-tables.html** * /html/rendering/replaced-elements/embedded-content-rendering-rules/canvas-update- * **/html/semantics/document-metadata/the-meta-element/color-scheme/meta-color-scheme-attribute-changes.html** * **/html/semantics/document-metadata/the-meta-element/color-scheme/meta-color-scheme-normal-descendant-change.html** * **/html/semantics/document-metadata/the-meta-element/color-scheme/meta-color-scheme-remove.html** * **/html/semantics/embedded-content/the-img-element/image-loading-lazy-slow-aspect-ratio.html** * **/html/semantics/embedded-content/the-img-element/image-loading-lazy-slow.html** * **/html/semantics/interactive-elements/the-dialog-element/inertness-with-modal-dialogs-and-iframes.html** * **/html/semantics/popovers/popover-stacking.html** * **/referrer-policy/generic/inheritance/popup-inheritance-form-submission.html** * **/resource-timing/resource-reload-TAO.html** * **/selection/bidi/modify.tentative.html** * **/selection/contenteditable/modify.tentative.html** * **/selection/textcontrols/selectionchange.html** * **/css/CSS2/normal-flow/block-in-inline-client-rects-001.html** * **/css/CSS2/positioning/relpos-percentage-top-in-scrollable.html** * **/css/css-animations/computed-style-animation-parsing.html** * **/css/css-variables/variables-substitute-guaranteed-invalid.html** * **/css/cssom-view/elementsFromPoint-simple.html** * **/css/cssom/CSSStyleSheet-modify-after-removal.html** * **/css/cssom/getComputedStyle-display-none-001.html** * **/css/selectors/focus-visible-script-focus-001.html** * **/css/selectors/has-specificity.html** * **/css/selectors/invalidation/is.html** * **/css/selectors/invalidation/not-002.html** * **/dom/events/EventListener-handleEvent-cross-realm.html** * **/dom/traversal/TreeWalker-acceptNode-filter-cross-realm.html** * **/html/browsers/the-window-object/window-properties.https.html** * **/html/browsers/the-window-object/window-prototype-chain.html** * **/html/browsers/windows/nested-browsing-contexts/frameElement.sub.html** * **/html/dom/aria-element-reflection.html** * **/html/rendering/replaced-elements/embedded-content-rendering-rules/canvas-update-with-border-object-fit.html** * **/html/semantics/forms/form-submission-0/form-double-submit-multiple-targets.html** * **/html/semantics/forms/the-input-element/show-picker-cross-origin-iframe.html** * **/html/webappapis/scripting/events/compile-event-handler-lexical-scopes-form-owner.html** * **/quirks/unitless-length/excluded-properties-002.html** </details> <details><summary>22× tests with failures that looked interesting</summary> * 1× CRASH [expected ...] * **/css/css-fonts/crash-large-grapheme-cluster.html** crash is subject of test * 6× FAIL [expected PASS] /... (reftest) * **/css/css-flexbox/anonymous-flex-item-005.html** old pass was spurious * **/css/css-flexbox/anonymous-flex-item-006.html** old pass was spurious * **/css/css-text-decor/text-decoration-line-recalc.html** old pass was spurious; ‘text-decoration’ shorthand unimplemented * **/css/css-ui/appearance-auto-non-html-namespace-001.html** old pass was spurious * **/html/canvas/element/drawing-text-to-the-canvas/direction-inherit-rtl.html** old pass was spurious * **/html/canvas/element/drawing-text-to-the-canvas/direction-rtl.html** old pass was spurious * 13× TIMEOUT [expected ...] * **/css/css-fonts/font-size-monospace-adjust.html** timeout is subject of test; reftest runner bug with 1e10s transition? * **/css/cssom-view/background-change-during-smooth-scroll.html** timeout is subject of test * **/css/selectors/invalidation/has-complexity.html** timeout is subject of test * **/css/selectors/invalidation/media-loading-pseudo-classes-in-has.html** timeout is subject of test; :has() unimplemented * **/fetch/stale-while-revalidate/stale-image.html** timeout is subject of test * **/html/browsers/browsing-the-web/history-traversal/history-traversal-navigates-multiple-frames.html** timeout is subject of test * **/html/rendering/widgets/appearance/appearance-animation-001.html** timeout is subject of test; reftest runner bug with 1e10s animation? * **/html/rendering/widgets/appearance/appearance-animation-002.html** timeout is subject of test; reftest runner bug with 1e10s animation? * **/html/rendering/widgets/appearance/appearance-transition-001.html** timeout is subject of test; reftest runner bug with 1e10s transition? * **/html/rendering/widgets/appearance/appearance-transition-002.html** timeout is subject of test; reftest runner bug with 1e10s transition? * **/html/rendering/widgets/appearance/appearance-transition-003.html** timeout is subject of test; reftest runner bug with 1e10s transition? * **/resource-timing/opaque-origin.html** timeout is subject of test * **/selection/textcontrols/selectionchange-bubble.html** timeout is subject of test * **/webidl/ecmascript-binding/invalid-this-value-cross-realm.html** ‘HTMLDocument’ unimplemented </details> --- <!-- Thank you for contributing to Servo! Please replace each `[ ]` by `[X]` when the step is complete, and replace `___` with appropriate data: --> - [x] `./mach build -d` does not report any errors - [x] `./mach test-tidy` does not report any errors - [x] These changes fix #27949 (GitHub issue number if applicable) <!-- Either: --> - [x] There are tests for these changes (including but not limited to web-platform-tests/wpt#39211) OR - [ ] These changes do not require tests because ___
This commit is contained in:
commit
e0f87cbba2
267 changed files with 6147 additions and 1467 deletions
|
@ -3290,6 +3290,13 @@ rooted!(in(*cx) let mut prototype_proto = ptr::null_mut::<JSObject>());
|
|||
%s;
|
||||
assert!(!prototype_proto.is_null());""" % getPrototypeProto)]
|
||||
|
||||
if self.descriptor.hasNamedPropertiesObject():
|
||||
assert not self.haveUnscopables
|
||||
code.append(CGGeneric("""\
|
||||
rooted!(in(*cx) let mut prototype_proto_proto = prototype_proto.get());
|
||||
dom::types::%s::create_named_properties_object(cx, prototype_proto_proto.handle(), prototype_proto.handle_mut());
|
||||
assert!(!prototype_proto.is_null());""" % name))
|
||||
|
||||
properties = {
|
||||
"id": name,
|
||||
"unscopables": "unscopable_names" if self.haveUnscopables else "&[]",
|
||||
|
@ -5508,15 +5515,15 @@ class CGDOMJSProxyHandler_getOwnPropertyDescriptor(CGAbstractExternMethod):
|
|||
attrs += " | JSPROP_READONLY"
|
||||
fillDescriptor = ("set_property_descriptor(\n"
|
||||
" MutableHandle::from_raw(desc),\n"
|
||||
" result_root.handle(),\n"
|
||||
" rval.handle(),\n"
|
||||
" (%s) as u32,\n"
|
||||
" &mut *is_none\n"
|
||||
");\n"
|
||||
"return true;" % attrs)
|
||||
templateValues = {
|
||||
'jsvalRef': 'result_root.handle_mut()',
|
||||
'jsvalRef': 'rval.handle_mut()',
|
||||
'successCode': fillDescriptor,
|
||||
'pre': 'rooted!(in(*cx) let mut result_root = UndefinedValue());'
|
||||
'pre': 'rooted!(in(*cx) let mut rval = UndefinedValue());'
|
||||
}
|
||||
get += ("if let Some(index) = index {\n"
|
||||
+ " let this = UnwrapProxy(proxy);\n"
|
||||
|
@ -5524,8 +5531,7 @@ class CGDOMJSProxyHandler_getOwnPropertyDescriptor(CGAbstractExternMethod):
|
|||
+ CGIndenter(CGProxyIndexedGetter(self.descriptor, templateValues)).define() + "\n"
|
||||
+ "}\n")
|
||||
|
||||
namedGetter = self.descriptor.operations['NamedGetter']
|
||||
if namedGetter:
|
||||
if self.descriptor.supportsNamedProperties():
|
||||
attrs = []
|
||||
if not self.descriptor.interface.getExtendedAttribute("LegacyUnenumerableNamedProperties"):
|
||||
attrs.append("JSPROP_ENUMERATE")
|
||||
|
@ -5537,15 +5543,15 @@ class CGDOMJSProxyHandler_getOwnPropertyDescriptor(CGAbstractExternMethod):
|
|||
attrs = "0"
|
||||
fillDescriptor = ("set_property_descriptor(\n"
|
||||
" MutableHandle::from_raw(desc),\n"
|
||||
" result_root.handle(),\n"
|
||||
" rval.handle(),\n"
|
||||
" (%s) as u32,\n"
|
||||
" &mut *is_none\n"
|
||||
");\n"
|
||||
"return true;" % attrs)
|
||||
templateValues = {
|
||||
'jsvalRef': 'result_root.handle_mut()',
|
||||
'jsvalRef': 'rval.handle_mut()',
|
||||
'successCode': fillDescriptor,
|
||||
'pre': 'rooted!(in(*cx) let mut result_root = UndefinedValue());'
|
||||
'pre': 'rooted!(in(*cx) let mut rval = UndefinedValue());'
|
||||
}
|
||||
|
||||
# See the similar-looking in CGDOMJSProxyHandler_get for the spec quote.
|
||||
|
@ -5638,7 +5644,7 @@ class CGDOMJSProxyHandler_defineProperty(CGAbstractExternMethod):
|
|||
+ CGIndenter(CGProxyNamedSetter(self.descriptor)).define()
|
||||
+ " return (*opresult).succeed();\n"
|
||||
+ "}\n")
|
||||
elif self.descriptor.operations['NamedGetter']:
|
||||
elif self.descriptor.supportsNamedProperties():
|
||||
set += ("if id.is_string() || id.is_int() {\n"
|
||||
+ CGIndenter(CGProxyNamedGetter(self.descriptor)).define()
|
||||
+ " if result.is_some() {\n"
|
||||
|
@ -5722,7 +5728,7 @@ class CGDOMJSProxyHandler_ownPropertyKeys(CGAbstractExternMethod):
|
|||
}
|
||||
""")
|
||||
|
||||
if self.descriptor.operations['NamedGetter']:
|
||||
if self.descriptor.supportsNamedProperties():
|
||||
body += dedent(
|
||||
"""
|
||||
for name in (*unwrapped_proxy).SupportedPropertyNames() {
|
||||
|
@ -5844,11 +5850,10 @@ class CGDOMJSProxyHandler_hasOwn(CGAbstractExternMethod):
|
|||
+ " return true;\n"
|
||||
+ "}\n\n")
|
||||
|
||||
namedGetter = self.descriptor.operations['NamedGetter']
|
||||
condition = "id.is_string() || id.is_int()"
|
||||
if indexedGetter:
|
||||
condition = "index.is_none() && (%s)" % condition
|
||||
if namedGetter:
|
||||
if self.descriptor.supportsNamedProperties():
|
||||
named = """\
|
||||
if %s {
|
||||
let mut has_on_proto = false;
|
||||
|
@ -5943,8 +5948,7 @@ if !expando.is_null() {
|
|||
else:
|
||||
getIndexedOrExpando = getFromExpando + "\n"
|
||||
|
||||
namedGetter = self.descriptor.operations['NamedGetter']
|
||||
if namedGetter:
|
||||
if self.descriptor.supportsNamedProperties():
|
||||
condition = "id.is_string() || id.is_int()"
|
||||
# From step 1:
|
||||
# If O supports indexed properties and P is an array index, then:
|
||||
|
@ -6214,7 +6218,7 @@ class CGInterfaceTrait(CGThing):
|
|||
),
|
||||
rettype)
|
||||
|
||||
if descriptor.proxy:
|
||||
if descriptor.proxy or descriptor.isGlobal():
|
||||
for name, operation in descriptor.operations.items():
|
||||
if not operation or operation.isStringifier():
|
||||
continue
|
||||
|
|
|
@ -280,7 +280,8 @@ class Descriptor(DescriptorProvider):
|
|||
continue
|
||||
|
||||
def addIndexedOrNamedOperation(operation, m):
|
||||
self.proxy = True
|
||||
if not self.isGlobal():
|
||||
self.proxy = True
|
||||
if m.isIndexed():
|
||||
operation = 'Indexed' + operation
|
||||
else:
|
||||
|
@ -369,6 +370,15 @@ class Descriptor(DescriptorProvider):
|
|||
def internalNameFor(self, name):
|
||||
return self._internalNames.get(name, name)
|
||||
|
||||
def hasNamedPropertiesObject(self):
|
||||
if self.interface.isExternal():
|
||||
return False
|
||||
|
||||
return self.isGlobal() and self.supportsNamedProperties()
|
||||
|
||||
def supportsNamedProperties(self):
|
||||
return self.operations['NamedGetter'] is not None
|
||||
|
||||
def getExtendedAttributes(self, member, getter=False, setter=False):
|
||||
def maybeAppendInfallibleToAttrs(attrs, throws):
|
||||
if throws is None:
|
||||
|
|
|
@ -464,12 +464,6 @@ impl CollectionFilter for AnchorsFilter {
|
|||
}
|
||||
}
|
||||
|
||||
enum ElementLookupResult {
|
||||
None,
|
||||
One(DomRoot<Element>),
|
||||
Many,
|
||||
}
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
impl Document {
|
||||
pub fn note_node_with_dirty_descendants(&self, node: &Node) {
|
||||
|
@ -2870,73 +2864,12 @@ impl Document {
|
|||
.for_each(|(_, context)| context.send_swap_chain_present());
|
||||
}
|
||||
|
||||
// https://html.spec.whatwg.org/multipage/#dom-tree-accessors:supported-property-names
|
||||
// (This takes the filter as a method so the window named getter can use it too)
|
||||
pub fn supported_property_names_impl(
|
||||
&self,
|
||||
nameditem_filter: fn(&Node, &Atom) -> bool,
|
||||
) -> Vec<DOMString> {
|
||||
// The tricky part here is making sure we return the names in
|
||||
// tree order, without just resorting to a full tree walkthrough.
|
||||
pub fn id_map(&self) -> Ref<HashMap<Atom, Vec<Dom<Element>>>> {
|
||||
self.id_map.borrow()
|
||||
}
|
||||
|
||||
let mut first_elements_with_name: HashMap<&Atom, &Dom<Element>> = HashMap::new();
|
||||
|
||||
// Get the first-in-tree-order element for each name in the name_map
|
||||
let name_map = self.name_map.borrow();
|
||||
name_map.iter().for_each(|(name, value)| {
|
||||
if let Some(first) = value
|
||||
.iter()
|
||||
.find(|n| nameditem_filter((***n).upcast::<Node>(), &name))
|
||||
{
|
||||
first_elements_with_name.insert(name, first);
|
||||
}
|
||||
});
|
||||
|
||||
// Get the first-in-tree-order element for each name in the id_map;
|
||||
// if we already had one from the name_map, figure out which of
|
||||
// the two is first.
|
||||
let id_map = self.id_map.borrow();
|
||||
id_map.iter().for_each(|(name, value)| {
|
||||
if let Some(first) = value
|
||||
.iter()
|
||||
.find(|n| nameditem_filter((***n).upcast::<Node>(), &name))
|
||||
{
|
||||
match first_elements_with_name.get(&name) {
|
||||
None => {
|
||||
first_elements_with_name.insert(name, first);
|
||||
},
|
||||
Some(el) => {
|
||||
if *el != first && first.upcast::<Node>().is_before(el.upcast::<Node>()) {
|
||||
first_elements_with_name.insert(name, first);
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// first_elements_with_name now has our supported property names
|
||||
// as keys, and the elements to order on as values.
|
||||
let mut sortable_vec: Vec<(&Atom, &Dom<Element>)> = first_elements_with_name
|
||||
.iter()
|
||||
.map(|(k, v)| (*k, *v))
|
||||
.collect();
|
||||
sortable_vec.sort_unstable_by(|a, b| {
|
||||
if a.1 == b.1 {
|
||||
// This can happen if an img has an id different from its name,
|
||||
// spec does not say which string to put first.
|
||||
a.0.cmp(&b.0)
|
||||
} else if a.1.upcast::<Node>().is_before(b.1.upcast::<Node>()) {
|
||||
Ordering::Less
|
||||
} else {
|
||||
Ordering::Greater
|
||||
}
|
||||
});
|
||||
|
||||
// And now that they're sorted, we can return the keys
|
||||
sortable_vec
|
||||
.iter()
|
||||
.map(|(k, _v)| DOMString::from(&***k))
|
||||
.collect()
|
||||
pub fn name_map(&self) -> Ref<HashMap<Atom, Vec<Dom<Element>>>> {
|
||||
self.name_map.borrow()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3869,79 +3802,16 @@ impl Document {
|
|||
)
|
||||
}
|
||||
|
||||
// https://html.spec.whatwg.org/multipage/#dom-tree-accessors:determine-the-value-of-a-named-property
|
||||
// Support method for steps 1-3:
|
||||
// Count if there are 0, 1, or >1 elements that match the name.
|
||||
// (This takes the filter as a method so the window named getter can use it too)
|
||||
fn look_up_named_elements(
|
||||
&self,
|
||||
name: &Atom,
|
||||
nameditem_filter: fn(&Node, &Atom) -> bool,
|
||||
) -> ElementLookupResult {
|
||||
// We might match because of either id==name or name==name, so there
|
||||
// are two sets of nodes to look through, but we don't need a
|
||||
// full tree traversal.
|
||||
let id_map = self.id_map.borrow();
|
||||
let name_map = self.name_map.borrow();
|
||||
let id_vec = id_map.get(&name);
|
||||
let name_vec = name_map.get(&name);
|
||||
pub fn get_elements_with_id(&self, id: &Atom) -> Ref<[Dom<Element>]> {
|
||||
Ref::map(self.id_map.borrow(), |map| {
|
||||
map.get(id).map(|vec| &**vec).unwrap_or_default()
|
||||
})
|
||||
}
|
||||
|
||||
// If nothing can possibly have the name, exit fast
|
||||
if id_vec.is_none() && name_vec.is_none() {
|
||||
return ElementLookupResult::None;
|
||||
}
|
||||
|
||||
let one_from_id_map = if let Some(id_vec) = id_vec {
|
||||
let mut elements = id_vec
|
||||
.iter()
|
||||
.filter(|n| nameditem_filter((***n).upcast::<Node>(), &name))
|
||||
.peekable();
|
||||
if let Some(first) = elements.next() {
|
||||
if elements.peek().is_none() {
|
||||
Some(first)
|
||||
} else {
|
||||
return ElementLookupResult::Many;
|
||||
}
|
||||
} else {
|
||||
None
|
||||
}
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let one_from_name_map = if let Some(name_vec) = name_vec {
|
||||
let mut elements = name_vec
|
||||
.iter()
|
||||
.filter(|n| nameditem_filter((***n).upcast::<Node>(), &name))
|
||||
.peekable();
|
||||
if let Some(first) = elements.next() {
|
||||
if elements.peek().is_none() {
|
||||
Some(first)
|
||||
} else {
|
||||
return ElementLookupResult::Many;
|
||||
}
|
||||
} else {
|
||||
None
|
||||
}
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
// We now have two elements, or one element, or the same
|
||||
// element twice, or no elements.
|
||||
match (one_from_id_map, one_from_name_map) {
|
||||
(Some(one), None) | (None, Some(one)) => {
|
||||
ElementLookupResult::One(DomRoot::from_ref(&one))
|
||||
},
|
||||
(Some(one), Some(other)) => {
|
||||
if one == other {
|
||||
ElementLookupResult::One(DomRoot::from_ref(&one))
|
||||
} else {
|
||||
ElementLookupResult::Many
|
||||
}
|
||||
},
|
||||
(None, None) => ElementLookupResult::None,
|
||||
}
|
||||
pub fn get_elements_with_name(&self, name: &Atom) -> Ref<[Dom<Element>]> {
|
||||
Ref::map(self.name_map.borrow(), |map| {
|
||||
map.get(name).map(|vec| &**vec).unwrap_or_default()
|
||||
})
|
||||
}
|
||||
|
||||
#[allow(unrooted_must_root)]
|
||||
|
@ -4871,45 +4741,76 @@ impl DocumentMethods for Document {
|
|||
#[allow(unsafe_code)]
|
||||
// https://html.spec.whatwg.org/multipage/#dom-tree-accessors:dom-document-nameditem-filter
|
||||
fn NamedGetter(&self, _cx: JSContext, name: DOMString) -> Option<NonNull<JSObject>> {
|
||||
#[derive(JSTraceable, MallocSizeOf)]
|
||||
struct NamedElementFilter {
|
||||
name: Atom,
|
||||
if name.is_empty() {
|
||||
return None;
|
||||
}
|
||||
impl CollectionFilter for NamedElementFilter {
|
||||
fn filter(&self, elem: &Element, _root: &Node) -> bool {
|
||||
elem.upcast::<Node>().is_document_named_item(&self.name)
|
||||
let name = Atom::from(name);
|
||||
|
||||
// Step 1.
|
||||
let elements_with_name = self.get_elements_with_name(&name);
|
||||
let name_iter = elements_with_name
|
||||
.iter()
|
||||
.filter(|elem| is_named_element_with_name_attribute(elem));
|
||||
let elements_with_id = self.get_elements_with_id(&name);
|
||||
let id_iter = elements_with_id
|
||||
.iter()
|
||||
.filter(|elem| is_named_element_with_id_attribute(elem));
|
||||
let mut elements = name_iter.chain(id_iter);
|
||||
|
||||
let first = elements.next()?;
|
||||
|
||||
if elements.next().is_none() {
|
||||
// Step 2.
|
||||
if let Some(nested_window_proxy) = first
|
||||
.downcast::<HTMLIFrameElement>()
|
||||
.and_then(|iframe| iframe.GetContentWindow())
|
||||
{
|
||||
unsafe {
|
||||
return Some(NonNull::new_unchecked(
|
||||
nested_window_proxy.reflector().get_jsobject().get(),
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
// Step 3.
|
||||
unsafe {
|
||||
return Some(NonNull::new_unchecked(
|
||||
first.reflector().get_jsobject().get(),
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
let name = Atom::from(name);
|
||||
|
||||
match self.look_up_named_elements(&name, Node::is_document_named_item) {
|
||||
ElementLookupResult::None => {
|
||||
return None;
|
||||
},
|
||||
ElementLookupResult::One(element) => {
|
||||
if let Some(nested_proxy) = element
|
||||
.downcast::<HTMLIFrameElement>()
|
||||
.and_then(|iframe| iframe.GetContentWindow())
|
||||
{
|
||||
unsafe {
|
||||
return Some(NonNull::new_unchecked(
|
||||
nested_proxy.reflector().get_jsobject().get(),
|
||||
));
|
||||
}
|
||||
}
|
||||
unsafe {
|
||||
return Some(NonNull::new_unchecked(
|
||||
element.reflector().get_jsobject().get(),
|
||||
));
|
||||
}
|
||||
},
|
||||
ElementLookupResult::Many => {},
|
||||
};
|
||||
|
||||
// Step 4.
|
||||
let filter = NamedElementFilter { name: name };
|
||||
let collection = HTMLCollection::create(self.window(), self.upcast(), Box::new(filter));
|
||||
#[derive(JSTraceable, MallocSizeOf)]
|
||||
struct DocumentNamedGetter {
|
||||
name: Atom,
|
||||
}
|
||||
impl CollectionFilter for DocumentNamedGetter {
|
||||
fn filter(&self, elem: &Element, _root: &Node) -> bool {
|
||||
let type_ = match elem.upcast::<Node>().type_id() {
|
||||
NodeTypeId::Element(ElementTypeId::HTMLElement(type_)) => type_,
|
||||
_ => return false,
|
||||
};
|
||||
match type_ {
|
||||
HTMLElementTypeId::HTMLFormElement | HTMLElementTypeId::HTMLIFrameElement => {
|
||||
elem.get_name().as_ref() == Some(&self.name)
|
||||
},
|
||||
HTMLElementTypeId::HTMLImageElement => elem.get_name().map_or(false, |name| {
|
||||
name == *self.name ||
|
||||
!name.is_empty() && elem.get_id().as_ref() == Some(&self.name)
|
||||
}),
|
||||
// TODO handle <embed> and <object>; these depend on whether the element is
|
||||
// “exposed”, a concept that doesn’t fully make sense until embed/object
|
||||
// behaviour is actually implemented
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
let collection = HTMLCollection::create(
|
||||
self.window(),
|
||||
self.upcast(),
|
||||
Box::new(DocumentNamedGetter { name }),
|
||||
);
|
||||
unsafe {
|
||||
Some(NonNull::new_unchecked(
|
||||
collection.reflector().get_jsobject().get(),
|
||||
|
@ -4919,7 +4820,61 @@ impl DocumentMethods for Document {
|
|||
|
||||
// https://html.spec.whatwg.org/multipage/#dom-tree-accessors:supported-property-names
|
||||
fn SupportedPropertyNames(&self) -> Vec<DOMString> {
|
||||
self.supported_property_names_impl(Node::is_document_named_item)
|
||||
let mut names_with_first_named_element_map: HashMap<&Atom, &Element> = HashMap::new();
|
||||
|
||||
let name_map = self.name_map.borrow();
|
||||
for (name, elements) in &*name_map {
|
||||
if name.is_empty() {
|
||||
continue;
|
||||
}
|
||||
let mut name_iter = elements
|
||||
.iter()
|
||||
.filter(|elem| is_named_element_with_name_attribute(elem));
|
||||
if let Some(first) = name_iter.next() {
|
||||
names_with_first_named_element_map.insert(name, first);
|
||||
}
|
||||
}
|
||||
let id_map = self.id_map.borrow();
|
||||
for (id, elements) in &*id_map {
|
||||
if id.is_empty() {
|
||||
continue;
|
||||
}
|
||||
let mut id_iter = elements
|
||||
.iter()
|
||||
.filter(|elem| is_named_element_with_id_attribute(elem));
|
||||
if let Some(first) = id_iter.next() {
|
||||
match names_with_first_named_element_map.entry(id) {
|
||||
Vacant(entry) => drop(entry.insert(first)),
|
||||
Occupied(mut entry) => {
|
||||
if first.upcast::<Node>().is_before(entry.get().upcast()) {
|
||||
*entry.get_mut() = first;
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let mut names_with_first_named_element_vec: Vec<(&Atom, &Element)> =
|
||||
names_with_first_named_element_map
|
||||
.iter()
|
||||
.map(|(k, v)| (*k, *v))
|
||||
.collect();
|
||||
names_with_first_named_element_vec.sort_unstable_by(|a, b| {
|
||||
if a.1 == b.1 {
|
||||
// This can happen if an img has an id different from its name,
|
||||
// spec does not say which string to put first.
|
||||
a.0.cmp(&b.0)
|
||||
} else if a.1.upcast::<Node>().is_before(b.1.upcast::<Node>()) {
|
||||
Ordering::Less
|
||||
} else {
|
||||
Ordering::Greater
|
||||
}
|
||||
});
|
||||
|
||||
names_with_first_named_element_vec
|
||||
.iter()
|
||||
.map(|(k, _v)| DOMString::from(&***k))
|
||||
.collect()
|
||||
}
|
||||
|
||||
// https://html.spec.whatwg.org/multipage/#dom-document-clear
|
||||
|
@ -5394,3 +5349,26 @@ pub enum ReflowTriggerCondition {
|
|||
PendingRestyles,
|
||||
PaintPostponed,
|
||||
}
|
||||
|
||||
fn is_named_element_with_name_attribute(elem: &Element) -> bool {
|
||||
let type_ = match elem.upcast::<Node>().type_id() {
|
||||
NodeTypeId::Element(ElementTypeId::HTMLElement(type_)) => type_,
|
||||
_ => return false,
|
||||
};
|
||||
match type_ {
|
||||
HTMLElementTypeId::HTMLFormElement |
|
||||
HTMLElementTypeId::HTMLIFrameElement |
|
||||
HTMLElementTypeId::HTMLImageElement => true,
|
||||
// TODO handle <embed> and <object>; these depend on whether the element is
|
||||
// “exposed”, a concept that doesn’t fully make sense until embed/object
|
||||
// behaviour is actually implemented
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
fn is_named_element_with_id_attribute(elem: &Element) -> bool {
|
||||
// TODO handle <embed> and <object>; these depend on whether the element is
|
||||
// “exposed”, a concept that doesn’t fully make sense until embed/object
|
||||
// behaviour is actually implemented
|
||||
elem.is::<HTMLImageElement>() && elem.get_name().map_or(false, |name| !name.is_empty())
|
||||
}
|
||||
|
|
|
@ -82,7 +82,6 @@ use script_traits::UntrustedNodeAddress;
|
|||
use selectors::matching::{matches_selector_list, MatchingContext, MatchingMode};
|
||||
use selectors::parser::SelectorList;
|
||||
use servo_arc::Arc;
|
||||
use servo_atoms::Atom;
|
||||
use servo_url::ServoUrl;
|
||||
use smallvec::SmallVec;
|
||||
use std::borrow::Cow;
|
||||
|
@ -1242,34 +1241,6 @@ impl Node {
|
|||
}
|
||||
}
|
||||
|
||||
// https://html.spec.whatwg.org/multipage/#dom-document-nameditem-filter
|
||||
pub fn is_document_named_item(&self, name: &Atom) -> bool {
|
||||
let html_elem_type = match self.type_id() {
|
||||
NodeTypeId::Element(ElementTypeId::HTMLElement(type_)) => type_,
|
||||
_ => return false,
|
||||
};
|
||||
let elem = self
|
||||
.downcast::<Element>()
|
||||
.expect("Node with an Element::HTMLElement NodeTypeID must be an Element");
|
||||
match html_elem_type {
|
||||
HTMLElementTypeId::HTMLFormElement | HTMLElementTypeId::HTMLIFrameElement => {
|
||||
elem.get_name().map_or(false, |n| n == *name)
|
||||
},
|
||||
HTMLElementTypeId::HTMLImageElement =>
|
||||
// Images can match by id, but only when their name is non-empty.
|
||||
{
|
||||
elem.get_name().map_or(false, |n| {
|
||||
n == *name || elem.get_id().map_or(false, |i| i == *name)
|
||||
})
|
||||
},
|
||||
// TODO: Handle <embed> and <object>; these depend on
|
||||
// whether the element is "exposed", a concept which
|
||||
// doesn't fully make sense until embed/object behaviors
|
||||
// are actually implemented.
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_styled(&self) -> bool {
|
||||
self.style_and_layout_data.borrow().is_some()
|
||||
}
|
||||
|
|
|
@ -45,8 +45,7 @@
|
|||
optional DOMString features = "");
|
||||
//getter WindowProxy (unsigned long index);
|
||||
|
||||
// https://github.com/servo/servo/issues/14453
|
||||
// getter object (DOMString name);
|
||||
getter object (DOMString name);
|
||||
|
||||
// the user agent
|
||||
readonly attribute Navigator navigator;
|
||||
|
|
|
@ -6,6 +6,7 @@ use crate::dom::bindings::cell::{DomRefCell, Ref};
|
|||
use crate::dom::bindings::codegen::Bindings::DocumentBinding::{
|
||||
DocumentMethods, DocumentReadyState,
|
||||
};
|
||||
use crate::dom::bindings::codegen::Bindings::HTMLIFrameElementBinding::HTMLIFrameElementMethods;
|
||||
use crate::dom::bindings::codegen::Bindings::HistoryBinding::HistoryBinding::HistoryMethods;
|
||||
use crate::dom::bindings::codegen::Bindings::ImageBitmapBinding::{
|
||||
ImageBitmapOptions, ImageBitmapSource,
|
||||
|
@ -19,7 +20,7 @@ use crate::dom::bindings::codegen::Bindings::WindowBinding::{
|
|||
use crate::dom::bindings::codegen::Bindings::WindowBinding::{ScrollBehavior, ScrollToOptions};
|
||||
use crate::dom::bindings::codegen::UnionTypes::{RequestOrUSVString, StringOrFunction};
|
||||
use crate::dom::bindings::error::{Error, ErrorResult, Fallible};
|
||||
use crate::dom::bindings::inheritance::Castable;
|
||||
use crate::dom::bindings::inheritance::{Castable, ElementTypeId, HTMLElementTypeId, NodeTypeId};
|
||||
use crate::dom::bindings::num::Finite;
|
||||
use crate::dom::bindings::refcounted::Trusted;
|
||||
use crate::dom::bindings::reflector::DomObject;
|
||||
|
@ -40,6 +41,8 @@ use crate::dom::eventtarget::EventTarget;
|
|||
use crate::dom::globalscope::GlobalScope;
|
||||
use crate::dom::hashchangeevent::HashChangeEvent;
|
||||
use crate::dom::history::History;
|
||||
use crate::dom::htmlcollection::{CollectionFilter, HTMLCollection};
|
||||
use crate::dom::htmliframeelement::HTMLIFrameElement;
|
||||
use crate::dom::identityhub::Identities;
|
||||
use crate::dom::location::Location;
|
||||
use crate::dom::mediaquerylist::{MediaQueryList, MediaQueryListMatchState};
|
||||
|
@ -71,6 +74,7 @@ use crate::task_manager::TaskManager;
|
|||
use crate::task_source::{TaskSource, TaskSourceName};
|
||||
use crate::timers::{IsInterval, TimerCallback};
|
||||
use crate::webdriver_handlers::jsval_to_webdriver;
|
||||
use crate::window_named_properties;
|
||||
use app_units::Au;
|
||||
use backtrace::Backtrace;
|
||||
use base64;
|
||||
|
@ -94,7 +98,9 @@ use js::jsapi::{GCReason, StackFormat, JS_GC};
|
|||
use js::jsval::UndefinedValue;
|
||||
use js::jsval::{JSVal, NullValue};
|
||||
use js::rust::wrappers::JS_DefineProperty;
|
||||
use js::rust::{CustomAutoRooter, CustomAutoRooterGuard, HandleValue};
|
||||
use js::rust::{
|
||||
CustomAutoRooter, CustomAutoRooterGuard, HandleObject, HandleValue, MutableHandleObject,
|
||||
};
|
||||
use media::WindowGLContext;
|
||||
use msg::constellation_msg::{BrowsingContextId, PipelineId};
|
||||
use net_traits::image_cache::{ImageCache, ImageResponder, ImageResponse};
|
||||
|
@ -120,17 +126,20 @@ use script_traits::{
|
|||
use script_traits::{TimerSchedulerMsg, WebrenderIpcSender, WindowSizeData, WindowSizeType};
|
||||
use selectors::attr::CaseSensitivity;
|
||||
use servo_arc::Arc as ServoArc;
|
||||
use servo_atoms::Atom;
|
||||
use servo_geometry::{f32_rect_to_au_rect, MaxRect};
|
||||
use servo_url::{ImmutableOrigin, MutableOrigin, ServoUrl};
|
||||
use std::borrow::Cow;
|
||||
use std::borrow::ToOwned;
|
||||
use std::cell::Cell;
|
||||
use std::cmp;
|
||||
use std::collections::hash_map::Entry;
|
||||
use std::collections::{HashMap, HashSet};
|
||||
use std::default::Default;
|
||||
use std::env;
|
||||
use std::io::{stderr, stdout, Write};
|
||||
use std::mem;
|
||||
use std::ptr::NonNull;
|
||||
use std::rc::Rc;
|
||||
use std::sync::atomic::{AtomicBool, Ordering};
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
@ -1386,9 +1395,179 @@ impl WindowMethods for Window {
|
|||
fn IsSecureContext(&self) -> bool {
|
||||
self.upcast::<GlobalScope>().is_secure_context()
|
||||
}
|
||||
|
||||
// https://html.spec.whatwg.org/multipage/#named-access-on-the-window-object
|
||||
#[allow(unsafe_code)]
|
||||
fn NamedGetter(&self, _cx: JSContext, name: DOMString) -> Option<NonNull<JSObject>> {
|
||||
if name.is_empty() {
|
||||
return None;
|
||||
}
|
||||
let document = self.Document();
|
||||
|
||||
// https://html.spec.whatwg.org/multipage/#document-tree-child-browsing-context-name-property-set
|
||||
let iframes: Vec<_> = document
|
||||
.iter_iframes()
|
||||
.filter(|iframe| {
|
||||
if let Some(window) = iframe.GetContentWindow() {
|
||||
return window.get_name() == name;
|
||||
}
|
||||
false
|
||||
})
|
||||
.collect();
|
||||
|
||||
let iframe_iter = iframes.iter().map(|iframe| iframe.upcast::<Element>());
|
||||
|
||||
let name = Atom::from(&*name);
|
||||
|
||||
// Step 1.
|
||||
let elements_with_name = document.get_elements_with_name(&name);
|
||||
let name_iter = elements_with_name
|
||||
.iter()
|
||||
.map(|element| &**element)
|
||||
.filter(|elem| is_named_element_with_name_attribute(elem));
|
||||
let elements_with_id = document.get_elements_with_id(&name);
|
||||
let id_iter = elements_with_id
|
||||
.iter()
|
||||
.map(|element| &**element)
|
||||
.filter(|elem| is_named_element_with_id_attribute(elem));
|
||||
|
||||
// Step 2.
|
||||
for elem in iframe_iter.clone() {
|
||||
if let Some(nested_window_proxy) = elem
|
||||
.downcast::<HTMLIFrameElement>()
|
||||
.and_then(|iframe| iframe.GetContentWindow())
|
||||
{
|
||||
unsafe {
|
||||
return Some(NonNull::new_unchecked(
|
||||
nested_window_proxy.reflector().get_jsobject().get(),
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let mut elements = iframe_iter.chain(name_iter).chain(id_iter);
|
||||
|
||||
let first = elements.next()?;
|
||||
|
||||
if elements.next().is_none() {
|
||||
// Step 3.
|
||||
unsafe {
|
||||
return Some(NonNull::new_unchecked(
|
||||
first.reflector().get_jsobject().get(),
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
// Step 4.
|
||||
#[derive(JSTraceable, MallocSizeOf)]
|
||||
struct WindowNamedGetter {
|
||||
name: Atom,
|
||||
}
|
||||
impl CollectionFilter for WindowNamedGetter {
|
||||
fn filter(&self, elem: &Element, _root: &Node) -> bool {
|
||||
let type_ = match elem.upcast::<Node>().type_id() {
|
||||
NodeTypeId::Element(ElementTypeId::HTMLElement(type_)) => type_,
|
||||
_ => return false,
|
||||
};
|
||||
if elem.get_id().as_ref() == Some(&self.name) {
|
||||
return true;
|
||||
}
|
||||
match type_ {
|
||||
HTMLElementTypeId::HTMLEmbedElement |
|
||||
HTMLElementTypeId::HTMLFormElement |
|
||||
HTMLElementTypeId::HTMLImageElement |
|
||||
HTMLElementTypeId::HTMLObjectElement => {
|
||||
elem.get_name().as_ref() == Some(&self.name)
|
||||
},
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
let collection = HTMLCollection::create(
|
||||
self,
|
||||
document.upcast(),
|
||||
Box::new(WindowNamedGetter { name }),
|
||||
);
|
||||
unsafe {
|
||||
Some(NonNull::new_unchecked(
|
||||
collection.reflector().get_jsobject().get(),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
// https://html.spec.whatwg.org/multipage/#dom-tree-accessors:supported-property-names
|
||||
fn SupportedPropertyNames(&self) -> Vec<DOMString> {
|
||||
let mut names_with_first_named_element_map: HashMap<&Atom, &Element> = HashMap::new();
|
||||
|
||||
let document = self.Document();
|
||||
let name_map = document.name_map();
|
||||
for (name, elements) in &*name_map {
|
||||
if name.is_empty() {
|
||||
continue;
|
||||
}
|
||||
let mut name_iter = elements
|
||||
.iter()
|
||||
.filter(|elem| is_named_element_with_name_attribute(elem));
|
||||
if let Some(first) = name_iter.next() {
|
||||
names_with_first_named_element_map.insert(name, first);
|
||||
}
|
||||
}
|
||||
let id_map = document.id_map();
|
||||
for (id, elements) in &*id_map {
|
||||
if id.is_empty() {
|
||||
continue;
|
||||
}
|
||||
let mut id_iter = elements
|
||||
.iter()
|
||||
.filter(|elem| is_named_element_with_id_attribute(elem));
|
||||
if let Some(first) = id_iter.next() {
|
||||
match names_with_first_named_element_map.entry(id) {
|
||||
Entry::Vacant(entry) => drop(entry.insert(first)),
|
||||
Entry::Occupied(mut entry) => {
|
||||
if first.upcast::<Node>().is_before(entry.get().upcast()) {
|
||||
*entry.get_mut() = first;
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let mut names_with_first_named_element_vec: Vec<(&Atom, &Element)> =
|
||||
names_with_first_named_element_map
|
||||
.iter()
|
||||
.map(|(k, v)| (*k, *v))
|
||||
.collect();
|
||||
names_with_first_named_element_vec.sort_unstable_by(|a, b| {
|
||||
if a.1 == b.1 {
|
||||
// This can happen if an img has an id different from its name,
|
||||
// spec does not say which string to put first.
|
||||
a.0.cmp(&b.0)
|
||||
} else if a.1.upcast::<Node>().is_before(b.1.upcast::<Node>()) {
|
||||
cmp::Ordering::Less
|
||||
} else {
|
||||
cmp::Ordering::Greater
|
||||
}
|
||||
});
|
||||
|
||||
names_with_first_named_element_vec
|
||||
.iter()
|
||||
.map(|(k, _v)| DOMString::from(&***k))
|
||||
.collect()
|
||||
}
|
||||
}
|
||||
|
||||
impl Window {
|
||||
// https://heycam.github.io/webidl/#named-properties-object
|
||||
// https://html.spec.whatwg.org/multipage/#named-access-on-the-window-object
|
||||
#[allow(unsafe_code)]
|
||||
pub fn create_named_properties_object(
|
||||
cx: JSContext,
|
||||
proto: HandleObject,
|
||||
object: MutableHandleObject,
|
||||
) {
|
||||
window_named_properties::create(cx, proto, object)
|
||||
}
|
||||
|
||||
pub(crate) fn set_current_event(&self, event: Option<&Event>) -> Option<DomRoot<Event>> {
|
||||
let current = self
|
||||
.current_event
|
||||
|
@ -2659,3 +2838,21 @@ impl ParseErrorReporter for CSSErrorReporter {
|
|||
));
|
||||
}
|
||||
}
|
||||
|
||||
fn is_named_element_with_name_attribute(elem: &Element) -> bool {
|
||||
let type_ = match elem.upcast::<Node>().type_id() {
|
||||
NodeTypeId::Element(ElementTypeId::HTMLElement(type_)) => type_,
|
||||
_ => return false,
|
||||
};
|
||||
match type_ {
|
||||
HTMLElementTypeId::HTMLEmbedElement |
|
||||
HTMLElementTypeId::HTMLFormElement |
|
||||
HTMLElementTypeId::HTMLImageElement |
|
||||
HTMLElementTypeId::HTMLObjectElement => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
fn is_named_element_with_id_attribute(elem: &Element) -> bool {
|
||||
elem.is_html_element()
|
||||
}
|
||||
|
|
|
@ -910,7 +910,7 @@ unsafe extern "C" fn getOwnPropertyDescriptor(
|
|||
window.to_jsval(cx, val.handle_mut());
|
||||
set_property_descriptor(
|
||||
MutableHandle::from_raw(desc),
|
||||
val.handle().into(),
|
||||
val.handle(),
|
||||
attrs,
|
||||
&mut *is_none,
|
||||
);
|
||||
|
|
|
@ -110,6 +110,8 @@ mod timers;
|
|||
mod unpremultiplytable;
|
||||
#[warn(deprecated)]
|
||||
mod webdriver_handlers;
|
||||
#[warn(deprecated)]
|
||||
mod window_named_properties;
|
||||
|
||||
pub use init::init;
|
||||
pub use script_runtime::JSEngineSetup;
|
||||
|
|
260
components/script/window_named_properties.rs
Normal file
260
components/script/window_named_properties.rs
Normal file
|
@ -0,0 +1,260 @@
|
|||
/* 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/. */
|
||||
|
||||
use crate::dom::bindings::codegen::Bindings::WindowBinding::WindowMethods;
|
||||
use crate::dom::bindings::proxyhandler::set_property_descriptor;
|
||||
use crate::dom::bindings::root::Root;
|
||||
use crate::dom::bindings::utils::has_property_on_prototype;
|
||||
use crate::dom::globalscope::GlobalScope;
|
||||
use crate::dom::window::Window;
|
||||
use crate::js::conversions::ToJSValConvertible;
|
||||
use crate::script_runtime::JSContext as SafeJSContext;
|
||||
use js::conversions::jsstr_to_string;
|
||||
use js::glue::{AppendToIdVector, CreateProxyHandler, NewProxyObject, ProxyTraps};
|
||||
use js::jsapi::{GetWellKnownSymbol, JS_SetImmutablePrototype, SymbolCode, JSPROP_READONLY};
|
||||
use js::jsapi::{
|
||||
Handle, HandleObject, JSClass, JSContext, JSErrNum, MutableHandleObject, UndefinedHandleValue,
|
||||
};
|
||||
use js::jsapi::{
|
||||
HandleId, JSClass_NON_NATIVE, MutableHandle, MutableHandleIdVector, ObjectOpResult,
|
||||
PropertyDescriptor, ProxyClassExtension, ProxyClassOps, ProxyObjectOps,
|
||||
JSCLASS_DELAY_METADATA_BUILDER, JSCLASS_IS_PROXY, JSCLASS_RESERVED_SLOTS_MASK,
|
||||
JSCLASS_RESERVED_SLOTS_SHIFT,
|
||||
};
|
||||
use js::jsid::SymbolId;
|
||||
use js::jsval::UndefinedValue;
|
||||
use js::rust::IntoHandle;
|
||||
use js::rust::{Handle as RustHandle, MutableHandle as RustMutableHandle};
|
||||
use js::rust::{HandleObject as RustHandleObject, MutableHandleObject as RustMutableHandleObject};
|
||||
use libc;
|
||||
use std::ptr;
|
||||
|
||||
struct SyncWrapper(*const libc::c_void);
|
||||
#[allow(unsafe_code)]
|
||||
unsafe impl Sync for SyncWrapper {}
|
||||
|
||||
lazy_static! {
|
||||
static ref HANDLER: SyncWrapper = {
|
||||
let traps = ProxyTraps {
|
||||
enter: None,
|
||||
getOwnPropertyDescriptor: Some(get_own_property_descriptor),
|
||||
defineProperty: Some(define_property),
|
||||
ownPropertyKeys: Some(own_property_keys),
|
||||
delete_: Some(delete),
|
||||
enumerate: None,
|
||||
getPrototypeIfOrdinary: Some(get_prototype_if_ordinary),
|
||||
getPrototype: None,
|
||||
setPrototype: None,
|
||||
setImmutablePrototype: None,
|
||||
preventExtensions: Some(prevent_extensions),
|
||||
isExtensible: Some(is_extensible),
|
||||
has: None,
|
||||
get: None,
|
||||
set: None,
|
||||
call: None,
|
||||
construct: None,
|
||||
hasOwn: None,
|
||||
getOwnEnumerablePropertyKeys: None,
|
||||
nativeCall: None,
|
||||
objectClassIs: None,
|
||||
className: Some(class_name),
|
||||
fun_toString: None,
|
||||
boxedValue_unbox: None,
|
||||
defaultValue: None,
|
||||
trace: None,
|
||||
finalize: None,
|
||||
objectMoved: None,
|
||||
isCallable: None,
|
||||
isConstructor: None,
|
||||
};
|
||||
|
||||
#[allow(unsafe_code)]
|
||||
unsafe {
|
||||
SyncWrapper(CreateProxyHandler(&traps, ptr::null()))
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#[allow(unsafe_code)]
|
||||
unsafe extern "C" fn get_own_property_descriptor(
|
||||
cx: *mut JSContext,
|
||||
proxy: HandleObject,
|
||||
id: HandleId,
|
||||
desc: MutableHandle<PropertyDescriptor>,
|
||||
is_none: *mut bool,
|
||||
) -> bool {
|
||||
let cx = SafeJSContext::from_ptr(cx);
|
||||
|
||||
if id.is_symbol() {
|
||||
if id.get().asBits_ == SymbolId(GetWellKnownSymbol(*cx, SymbolCode::toStringTag)).asBits_ {
|
||||
rooted!(in(*cx) let mut rval = UndefinedValue());
|
||||
"WindowProperties".to_jsval(*cx, rval.handle_mut());
|
||||
set_property_descriptor(
|
||||
RustMutableHandle::from_raw(desc),
|
||||
rval.handle(),
|
||||
JSPROP_READONLY.into(),
|
||||
&mut *is_none,
|
||||
);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
let mut found = false;
|
||||
if !has_property_on_prototype(
|
||||
*cx,
|
||||
RustHandle::from_raw(proxy),
|
||||
RustHandle::from_raw(id),
|
||||
&mut found,
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
if found {
|
||||
return true;
|
||||
}
|
||||
|
||||
let s = if id.is_string() {
|
||||
jsstr_to_string(*cx, id.to_string())
|
||||
} else if id.is_int() {
|
||||
// If the property key is an integer index, convert it to a String too.
|
||||
// For indexed access on the window object, which may shadow this, see
|
||||
// the getOwnPropertyDescriptor trap in dom/windowproxy.rs.
|
||||
id.to_int().to_string()
|
||||
} else if id.is_symbol() {
|
||||
// Symbol properties were already handled above.
|
||||
unreachable!()
|
||||
} else {
|
||||
unimplemented!()
|
||||
};
|
||||
if s.is_empty() {
|
||||
return true;
|
||||
}
|
||||
|
||||
let window = Root::downcast::<Window>(GlobalScope::from_object(proxy.get()))
|
||||
.expect("global is not a window");
|
||||
if let Some(obj) = window.NamedGetter(cx, s.into()) {
|
||||
rooted!(in(*cx) let mut rval = UndefinedValue());
|
||||
obj.to_jsval(*cx, rval.handle_mut());
|
||||
set_property_descriptor(
|
||||
RustMutableHandle::from_raw(desc),
|
||||
rval.handle(),
|
||||
0,
|
||||
&mut *is_none,
|
||||
);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
#[allow(unsafe_code)]
|
||||
unsafe extern "C" fn own_property_keys(
|
||||
cx: *mut JSContext,
|
||||
_proxy: HandleObject,
|
||||
props: MutableHandleIdVector,
|
||||
) -> bool {
|
||||
// TODO is this all we need to return? compare with gecko:
|
||||
// https://searchfox.org/mozilla-central/rev/af78418c4b5f2c8721d1a06486cf4cf0b33e1e8d/dom/base/WindowNamedPropertiesHandler.cpp#175-232
|
||||
// see also https://github.com/whatwg/html/issues/9068
|
||||
rooted!(in(cx) let mut rooted = SymbolId(GetWellKnownSymbol(cx, SymbolCode::toStringTag)));
|
||||
AppendToIdVector(props, rooted.handle().into());
|
||||
true
|
||||
}
|
||||
|
||||
#[allow(unsafe_code)]
|
||||
unsafe extern "C" fn define_property(
|
||||
_cx: *mut JSContext,
|
||||
_proxy: HandleObject,
|
||||
_id: HandleId,
|
||||
_desc: Handle<PropertyDescriptor>,
|
||||
result: *mut ObjectOpResult,
|
||||
) -> bool {
|
||||
(*result).code_ = JSErrNum::JSMSG_CANT_DEFINE_WINDOW_NAMED_PROPERTY as usize;
|
||||
true
|
||||
}
|
||||
|
||||
#[allow(unsafe_code)]
|
||||
unsafe extern "C" fn delete(
|
||||
_cx: *mut JSContext,
|
||||
_proxy: HandleObject,
|
||||
_id: HandleId,
|
||||
result: *mut ObjectOpResult,
|
||||
) -> bool {
|
||||
(*result).code_ = JSErrNum::JSMSG_CANT_DELETE_WINDOW_NAMED_PROPERTY as usize;
|
||||
true
|
||||
}
|
||||
|
||||
#[allow(unsafe_code)]
|
||||
unsafe extern "C" fn get_prototype_if_ordinary(
|
||||
_cx: *mut JSContext,
|
||||
proxy: HandleObject,
|
||||
is_ordinary: *mut bool,
|
||||
proto: MutableHandleObject,
|
||||
) -> bool {
|
||||
*is_ordinary = true;
|
||||
proto.set(js::jsapi::GetStaticPrototype(proxy.get()));
|
||||
true
|
||||
}
|
||||
|
||||
#[allow(unsafe_code)]
|
||||
unsafe extern "C" fn prevent_extensions(
|
||||
_cx: *mut JSContext,
|
||||
_proxy: HandleObject,
|
||||
result: *mut ObjectOpResult,
|
||||
) -> bool {
|
||||
(*result).code_ = JSErrNum::JSMSG_CANT_PREVENT_EXTENSIONS as usize;
|
||||
true
|
||||
}
|
||||
|
||||
#[allow(unsafe_code)]
|
||||
unsafe extern "C" fn is_extensible(
|
||||
_cx: *mut JSContext,
|
||||
_proxy: HandleObject,
|
||||
extensible: *mut bool,
|
||||
) -> bool {
|
||||
*extensible = true;
|
||||
true
|
||||
}
|
||||
|
||||
#[allow(unsafe_code)]
|
||||
unsafe extern "C" fn class_name(_cx: *mut JSContext, _proxy: HandleObject) -> *const libc::c_char {
|
||||
&b"WindowProperties\0" as *const _ as *const _
|
||||
}
|
||||
|
||||
// Maybe this should be a DOMJSClass. See https://bugzilla.mozilla.org/show_bug.cgi?id=787070
|
||||
#[allow(unsafe_code)]
|
||||
static CLASS: JSClass = JSClass {
|
||||
name: b"WindowProperties\0" as *const u8 as *const libc::c_char,
|
||||
flags: JSClass_NON_NATIVE |
|
||||
JSCLASS_IS_PROXY |
|
||||
JSCLASS_DELAY_METADATA_BUILDER |
|
||||
((1 & JSCLASS_RESERVED_SLOTS_MASK) << JSCLASS_RESERVED_SLOTS_SHIFT), /* JSCLASS_HAS_RESERVED_SLOTS(1) */
|
||||
cOps: unsafe { &ProxyClassOps },
|
||||
spec: ptr::null(),
|
||||
ext: unsafe { &ProxyClassExtension },
|
||||
oOps: unsafe { &ProxyObjectOps },
|
||||
};
|
||||
|
||||
#[allow(unsafe_code)]
|
||||
pub fn create(
|
||||
cx: SafeJSContext,
|
||||
proto: RustHandleObject,
|
||||
mut properties_obj: RustMutableHandleObject,
|
||||
) {
|
||||
unsafe {
|
||||
properties_obj.set(NewProxyObject(
|
||||
*cx,
|
||||
HANDLER.0,
|
||||
UndefinedHandleValue,
|
||||
proto.get(),
|
||||
&CLASS,
|
||||
false,
|
||||
));
|
||||
assert!(!properties_obj.get().is_null());
|
||||
let mut succeeded = false;
|
||||
assert!(JS_SetImmutablePrototype(
|
||||
*cx,
|
||||
properties_obj.handle().into_handle(),
|
||||
&mut succeeded
|
||||
));
|
||||
assert!(succeeded);
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue