Bug 253814 - Concat with a CSSStyleSheet and shadowRoot.adoptedStyleSheets returns array in array
Summary: Concat with a CSSStyleSheet and shadowRoot.adoptedStyleSheets returns array i...
Status: RESOLVED FIXED
Alias: None
Product: WebKit
Classification: Unclassified
Component: CSS (show other bugs)
Version: Safari 16
Hardware: All All
: P2 Major
Assignee: Yusuke Suzuki
URL:
Keywords: InRadar
Depends on:
Blocks:
 
Reported: 2023-03-13 02:00 PDT by Bram Kragten
Modified: 2023-03-13 17:30 PDT (History)
5 users (show)

See Also:


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Bram Kragten 2023-03-13 02:00:59 PDT
In Safari 16.4, when concatting a CSSStyleSheet with the shadowRoot.adoptedStyleSheets, it returns a nested array instead of a flat one:

Correct:
> [new CSSStyleSheet].concat([])
< [CSSStyleSheet] (1)
> [new CSSStyleSheet].concat([new CSSStyleSheet])
< [CSSStyleSheet, CSSStyleSheet] (2)

Not correct:
> $0.shadowRoot.adoptedStyleSheets
< [CSSStyleSheet] (1)
> [new CSSStyleSheet].concat($0.shadowRoot.adoptedStyleSheets)
< [CSSStyleSheet, Array] (2)
Expected:
< [CSSStyleSheet, CSSStyleSheet] (2)

Correct:
> [new CSSStyleSheet, ...$0.shadowRoot.adoptedStyleSheets]
< [CSSStyleSheet, CSSStyleSheet] (2)
Comment 1 Chris Dumez 2023-03-13 07:27:44 PDT
Question for JSC people (added in cc). Do you know what I need to do in order to make concat() behave correctly with this new IDL type?
Comment 2 Chris Dumez 2023-03-13 07:33:36 PDT
(In reply to Chris Dumez from comment #1)
> Question for JSC people (added in cc). Do you know what I need to do in
> order to make concat() behave correctly with this new IDL type?

The type of adoptedStyleSheets is a JSC::JSObservableArray, which is a subclass of JSC::JSArray.
Comment 3 Chris Dumez 2023-03-13 07:45:36 PDT
(In reply to Chris Dumez from comment #2)
> (In reply to Chris Dumez from comment #1)
> > Question for JSC people (added in cc). Do you know what I need to do in
> > order to make concat() behave correctly with this new IDL type?
> 
> The type of adoptedStyleSheets is a JSC::JSObservableArray, which is a
> subclass of JSC::JSArray.

Looks like concat relies on isJSArray():

inline bool isJSArray(JSCell* cell)
{
    ASSERT((cell->classInfo() == JSArray::info()) == (cell->type() == ArrayType));
    return cell->type() == ArrayType;
}

So `cell->type() == ArrayType` is likely false for JSC::JSObservableArray.
Comment 4 Chris Dumez 2023-03-13 07:50:50 PDT
(In reply to Chris Dumez from comment #3)
> (In reply to Chris Dumez from comment #2)
> > (In reply to Chris Dumez from comment #1)
> > > Question for JSC people (added in cc). Do you know what I need to do in
> > > order to make concat() behave correctly with this new IDL type?
> > 
> > The type of adoptedStyleSheets is a JSC::JSObservableArray, which is a
> > subclass of JSC::JSArray.
> 
> Looks like concat relies on isJSArray():
> 
> inline bool isJSArray(JSCell* cell)
> {
>     ASSERT((cell->classInfo() == JSArray::info()) == (cell->type() ==
> ArrayType));
>     return cell->type() == ArrayType;
> }
> 
> So `cell->type() == ArrayType` is likely false for JSC::JSObservableArray.

cell->type() is DerivedArrayType for JSObservableArray, not ArrayType.
Comment 5 Chris Dumez 2023-03-13 08:04:12 PDT
Ecmascript for concat [1] says to rely on IsConcatSpreadable() [2]. IsConcatSpreadable() checks the @@isConcatSpreadable symbol and then fallback to calling IsArray() [3].

ObservableArray is defined here [4].

[1] https://tc39.es/ecma262/multipage/indexed-collections.html#sec-array.prototype.concat
[2] https://tc39.es/ecma262/multipage/indexed-collections.html#sec-isconcatspreadable
[3] https://tc39.es/ecma262/multipage/abstract-operations.html#sec-isarray
[4] https://webidl.spec.whatwg.org/#es-observable-array
Comment 6 Chris Dumez 2023-03-13 08:06:44 PDT
(In reply to Chris Dumez from comment #5)
> Ecmascript for concat [1] says to rely on IsConcatSpreadable() [2].
> IsConcatSpreadable() checks the @@isConcatSpreadable symbol and then
> fallback to calling IsArray() [3].
> 
> ObservableArray is defined here [4].
> 
> [1]
> https://tc39.es/ecma262/multipage/indexed-collections.html#sec-array.
> prototype.concat
> [2]
> https://tc39.es/ecma262/multipage/indexed-collections.html#sec-
> isconcatspreadable
> [3] https://tc39.es/ecma262/multipage/abstract-operations.html#sec-isarray
> [4] https://webidl.spec.whatwg.org/#es-observable-array

From https://webidl.spec.whatwg.org/#observable-array-exotic-object:
```
An observable array exotic object is a specific type of ECMAScript Proxy exotic object which is created using the proxy traps defined in this section. They are defined in this manner because the ECMAScript specification includes special treatment for Proxy exotic objects that have Array instances as their proxy target, and we want to ensure that observable array types are exposed to ECMAScript code with this special treatment intact.
```

So I believe concat should indeed spread the observable array here. However, our implementation currently relies on isJSArray(), not isArray() (which would be what the spec says).
Comment 7 Chris Dumez 2023-03-13 09:11:43 PDT
(In reply to Chris Dumez from comment #6)
> (In reply to Chris Dumez from comment #5)
> > Ecmascript for concat [1] says to rely on IsConcatSpreadable() [2].
> > IsConcatSpreadable() checks the @@isConcatSpreadable symbol and then
> > fallback to calling IsArray() [3].
> > 
> > ObservableArray is defined here [4].
> > 
> > [1]
> > https://tc39.es/ecma262/multipage/indexed-collections.html#sec-array.
> > prototype.concat
> > [2]
> > https://tc39.es/ecma262/multipage/indexed-collections.html#sec-
> > isconcatspreadable
> > [3] https://tc39.es/ecma262/multipage/abstract-operations.html#sec-isarray
> > [4] https://webidl.spec.whatwg.org/#es-observable-array
> 
> From https://webidl.spec.whatwg.org/#observable-array-exotic-object:
> ```
> An observable array exotic object is a specific type of ECMAScript Proxy
> exotic object which is created using the proxy traps defined in this
> section. They are defined in this manner because the ECMAScript
> specification includes special treatment for Proxy exotic objects that have
> Array instances as their proxy target, and we want to ensure that observable
> array types are exposed to ECMAScript code with this special treatment
> intact.
> ```
> 
> So I believe concat should indeed spread the observable array here. However,
> our implementation currently relies on isJSArray(), not isArray() (which
> would be what the spec says).

I have tried several approaches here but I keep hitting assertions. Seems existing code is not happy dealing with a JSObservableArray, even though it is a JSArray subclass.
Comment 8 Chris Dumez 2023-03-13 10:42:24 PDT
Pull request: https://github.com/WebKit/WebKit/pull/11449
Comment 9 Radar WebKit Bug Importer 2023-03-13 15:24:35 PDT
<rdar://problem/106667776>
Comment 10 Yusuke Suzuki 2023-03-13 15:29:52 PDT
Pull request: https://github.com/WebKit/WebKit/pull/11465
Comment 11 EWS 2023-03-13 17:30:02 PDT
Committed 261604@main (9d30cd47aaab): <https://commits.webkit.org/261604@main>

Reviewed commits have been landed. Closing PR #11465 and removing active labels.