Bug 234282 - Remove showModalDialog-specific logic from JSDOMWindow::getOwnPropertySlot()
Summary: Remove showModalDialog-specific logic from JSDOMWindow::getOwnPropertySlot()
Status: RESOLVED FIXED
Alias: None
Product: WebKit
Classification: Unclassified
Component: DOM (show other bugs)
Version: WebKit Nightly Build
Hardware: All All
: P2 Normal
Assignee: Alexey Shvayka
URL:
Keywords: InRadar
Depends on:
Blocks:
 
Reported: 2021-12-13 18:25 PST by Mark S. Miller
Modified: 2022-01-28 16:06 PST (History)
13 users (show)

See Also:


Attachments
Patch (12.52 KB, patch)
2022-01-25 13:42 PST, Alexey Shvayka
ysuzuki: review+
ews-feeder: commit-queue-
Details | Formatted Diff | Diff

Note You need to log in before you can comment on or make changes to this bug.
Description Mark S. Miller 2021-12-13 18:25:21 PST
At https://jsfiddle.net/kryLa94b/ we have the code

console.log(Object.getOwnPropertyNames(globalThis).includes('showModalDialog'));
console.log(Object.getOwnPropertyDescriptor(globalThis, 'showModalDialog'));

The first line tests whether `'showModalDialog'` is a property of the global object. The second line gets its own property descriptor. 

If there is no such own property, as seen for example on Brave on MacOS, the answers are `false` and `undefined`, which is consistent. 

If there is such an own property, as seen for example on Safari 15.1 on MacOS 12.0.1, the answers are

```
true
{
  configurable: true,
  enumerable: true,
  value: function showModalDialog() {
    [native code]
  },
  writable: true
}
```
which is consistent.

However, on iOS 15.1, the answers are `true` and `undefined`. These are inconsistent answers. Is there such an own property or not.

See also https://github.com/endojs/endo/issues/947 where Ash Connell originally reported this bug.
Comment 1 Alexey Shvayka 2021-12-14 15:20:35 PST
Thanks for detailed report, Mark.

The issue occurs due to some complicated logic for "showModalDialog" in Window's [[GetOwnProperty]], which is not aligned with other internal methods.

showModalDialog() was removed from Chrome in 2015 and from Firefox in 2017, so I guess we are safe to remove it for Web content only.
Comment 2 Radar WebKit Bug Importer 2021-12-20 18:26:17 PST
<rdar://problem/86749078>
Comment 3 Chris Dumez 2022-01-24 07:22:49 PST
(In reply to Alexey Shvayka from comment #1)
> Thanks for detailed report, Mark.
> 
> The issue occurs due to some complicated logic for "showModalDialog" in
> Window's [[GetOwnProperty]], which is not aligned with other internal
> methods.
> 
> showModalDialog() was removed from Chrome in 2015 and from Firefox in 2017,
> so I guess we are safe to remove it for Web content only.

Correction, we'll be safe to remove showModalDialog() ONCE we ship the <dialog> element.
Comment 4 Alexey Shvayka 2022-01-24 07:34:05 PST
(In reply to Chris Dumez from comment #3)
> (In reply to Alexey Shvayka from comment #1)
> > Thanks for detailed report, Mark.
> > 
> > The issue occurs due to some complicated logic for "showModalDialog" in
> > Window's [[GetOwnProperty]], which is not aligned with other internal
> > methods.
> > 
> > showModalDialog() was removed from Chrome in 2015 and from Firefox in 2017,
> > so I guess we are safe to remove it for Web content only.
> 
> Correction, we'll be safe to remove showModalDialog() ONCE we ship the
> <dialog> element.

Blink removed showModalDialog() in M43, while shipped <dialog> (on by default) in M37.
However, Gecko removed showModalDialog() in M55, yet still hasn't shipped the <dialog> on by default.

What am I missing?
Comment 5 Chris Dumez 2022-01-24 07:36:54 PST
(In reply to Alexey Shvayka from comment #4)
> (In reply to Chris Dumez from comment #3)
> > (In reply to Alexey Shvayka from comment #1)
> > > Thanks for detailed report, Mark.
> > > 
> > > The issue occurs due to some complicated logic for "showModalDialog" in
> > > Window's [[GetOwnProperty]], which is not aligned with other internal
> > > methods.
> > > 
> > > showModalDialog() was removed from Chrome in 2015 and from Firefox in 2017,
> > > so I guess we are safe to remove it for Web content only.
> > 
> > Correction, we'll be safe to remove showModalDialog() ONCE we ship the
> > <dialog> element.
> 
> Blink removed showModalDialog() in M43, while shipped <dialog> (on by
> default) in M37.
> However, Gecko removed showModalDialog() in M55, yet still hasn't shipped
> the <dialog> on by default.
> 
> What am I missing?

We've discussed removing showModalDialog several times in the past and every time we made the decision to delay until we have a good alternative implemented (<dialog> element). Not to say that you cannot get that particular discussion started again. I am just telling you removing showModalDialog() at this point will be controversial.

Also, I see good patches landing for <dialog> support so maybe we won't have to wait long?
Comment 6 Alexey Shvayka 2022-01-24 07:43:15 PST
(In reply to Chris Dumez from comment #5)
> (In reply to Alexey Shvayka from comment #4)
> > (In reply to Chris Dumez from comment #3)
> > > (In reply to Alexey Shvayka from comment #1)
> > > > Thanks for detailed report, Mark.
> > > > 
> > > > The issue occurs due to some complicated logic for "showModalDialog" in
> > > > Window's [[GetOwnProperty]], which is not aligned with other internal
> > > > methods.
> > > > 
> > > > showModalDialog() was removed from Chrome in 2015 and from Firefox in 2017,
> > > > so I guess we are safe to remove it for Web content only.
> > > 
> > > Correction, we'll be safe to remove showModalDialog() ONCE we ship the
> > > <dialog> element.
> > 
> > Blink removed showModalDialog() in M43, while shipped <dialog> (on by
> > default) in M37.
> > However, Gecko removed showModalDialog() in M55, yet still hasn't shipped
> > the <dialog> on by default.
> > 
> > What am I missing?
> 
> We've discussed removing showModalDialog several times in the past and every
> time we made the decision to delay until we have a good alternative
> implemented (<dialog> element). Not to say that you cannot get that
> particular discussion started again. I am just telling you removing
> showModalDialog() at this point will be controversial.
> 
> Also, I see good patches landing for <dialog> support so maybe we won't have
> to wait long?

Yeah, Tim has done great job with <dialog> implementation.

Either way, apart from web content, there might be iOS apps using it, and there is WebKit2 GTK+ API for it, meaning removing it will take some consideration even after we ship the <dialog>.

For this particular issue issue, going with [CustomEnabled] for showModalDialog() seems like a good move.
Comment 7 Mathieu Hofman 2022-01-24 08:34:03 PST
The point of this bug is regarding the consistency of `getOwnPropertyNames` and `getOwnPropertyDescriptor` for `showModalDialog`.

If iOS doesn't have that property (undefined descriptor), it should not be included in the list of global property names. That should be independent of alternative implementations for the feature, as the feature is already missing. Or am I missing something?
Comment 8 Alexey Shvayka 2022-01-24 08:54:56 PST
(In reply to Mathieu Hofman from comment #7)
> The point of this bug is regarding the consistency of `getOwnPropertyNames`
> and `getOwnPropertyDescriptor` for `showModalDialog`.
> 
> If iOS doesn't have that property (undefined descriptor), it should not be
> included in the list of global property names. That should be independent of
> alternative implementations for the feature, as the feature is already
> missing. Or am I missing something?

You are absolutely right, it's just that removing the feature completely is one way of achieving that consistency (but we are not doing that).

The other ways would be to either a) remove showModalDialog-specific code from Window's getOwnPropertyDescriptor() and expose it in some other way (like via WebIDL attribute we use for experimental features) or b) add some more method overrides for Window, including getOwnPropertyNames(), to make all the methods consistent.

I would very much like to do the a).
Comment 9 Mark S. Miller 2022-01-24 09:29:13 PST
I am puzzled about why this issue arises in the first place, and about the nature of the discussion about how to fix it. We're talking about object invariants that should be true for all objects reachable in the language, whether created by JS or by the host. As a universal regularity, it should not be the responsibility of each feature to separately decide whether to uphold the invariant. There should be a common mechanism they all use, so there would be little danger of any individual feature breaking these invariants.
Comment 10 Alexey Shvayka 2022-01-24 10:05:26 PST
(In reply to Mark S. Miller from comment #9)
> I am puzzled about why this issue arises in the first place, and about the
> nature of the discussion about how to fix it. We're talking about object
> invariants that should be true for all objects reachable in the language,
> whether created by JS or by the host. As a universal regularity, it should
> not be the responsibility of each feature to separately decide whether to
> uphold the invariant. There should be a common mechanism they all use, so
> there would be little danger of any individual feature breaking these
> invariants.

In WebKit, it's up for individual classes to ensure their overrides of ordinary methods abide to the invariants.
Some of those classes implement the spec directly, like ProxyObject, while some others override ordinary methods for performance.
While we are always happy to remove or fix these overrides (recently we resolved similar issues with RegExp legacy features and sloppy function's caller / arguments), I don't see how we can enforce the essential invariants in our code base except for rigorous testing and code review.
Comment 11 Alexey Shvayka 2022-01-24 13:18:47 PST
Another related and very troubling inconsistency in r288438 (MiniBrowser):

> window.showModalDialog
< undefined

> showModalDialog
< function showModalDialog() { [native code] }

STP 134 returns a function for both.
Comment 12 Alexey Shvayka 2022-01-25 13:42:52 PST
Created attachment 449961 [details]
Patch
Comment 13 Yusuke Suzuki 2022-01-28 12:06:02 PST
Comment on attachment 449961 [details]
Patch

View in context: https://bugs.webkit.org/attachment.cgi?id=449961&action=review

> Source/WebCore/bindings/js/JSDOMWindowBase.cpp:132
> +    putDirectCustomAccessor(vm, static_cast<JSVMClientData*>(vm.clientData)->builtinNames().showModalDialogPublicName(), CustomGetterSetter::create(vm, showModalDialogGetter, nullptr), static_cast<unsigned>(PropertyAttribute::CustomValue));

Do we need to have custom-accessor instead of just defining a function as a method?
Since it is a function (not attribute), I think we can just define it via `JSC_NATIVE_FUNCTION`.
Comment 14 Alexey Shvayka 2022-01-28 12:08:47 PST
(In reply to Yusuke Suzuki from comment #13)
> Comment on attachment 449961 [details]
> Patch
> 
> View in context:
> https://bugs.webkit.org/attachment.cgi?id=449961&action=review
> 
> > Source/WebCore/bindings/js/JSDOMWindowBase.cpp:132
> > +    putDirectCustomAccessor(vm, static_cast<JSVMClientData*>(vm.clientData)->builtinNames().showModalDialogPublicName(), CustomGetterSetter::create(vm, showModalDialogGetter, nullptr), static_cast<unsigned>(PropertyAttribute::CustomValue));
> 
> Do we need to have custom-accessor instead of just defining a function as a
> method?
> Since it is a function (not attribute), I think we can just define it via
> `JSC_NATIVE_FUNCTION`.

I believe we to do support a case when modals are initially (on Window init) disabled (function is currently `undefined` in this case) and then get enabled via an API.
Comment 15 Alexey Shvayka 2022-01-28 12:14:00 PST
(In reply to Alexey Shvayka from comment #14)
> (In reply to Yusuke Suzuki from comment #13)
> > Comment on attachment 449961 [details]
> > Patch
> > 
> > View in context:
> > https://bugs.webkit.org/attachment.cgi?id=449961&action=review
> > 
> > > Source/WebCore/bindings/js/JSDOMWindowBase.cpp:132
> > > +    putDirectCustomAccessor(vm, static_cast<JSVMClientData*>(vm.clientData)->builtinNames().showModalDialogPublicName(), CustomGetterSetter::create(vm, showModalDialogGetter, nullptr), static_cast<unsigned>(PropertyAttribute::CustomValue));
> > 
> > Do we need to have custom-accessor instead of just defining a function as a
> > method?
> > Since it is a function (not attribute), I think we can just define it via
> > `JSC_NATIVE_FUNCTION`.
> 
> I believe we to do support a case when modals are initially (on Window init)
> disabled (function is currently `undefined` in this case) and then get
> enabled via an API.

In other words, there may be some code that relies on showModalDialog() working if its non-undefined.
Comment 16 Yusuke Suzuki 2022-01-28 13:01:41 PST
Comment on attachment 449961 [details]
Patch

View in context: https://bugs.webkit.org/attachment.cgi?id=449961&action=review

Ah, I misunderstood. r=me

> Source/WebCore/bindings/js/JSDOMWindowCustom.cpp:510
> +    return encodedJSUndefined();

Use { } in new code.

> Source/WebCore/bindings/js/JSDOMWindowCustom.cpp:528
> +        return encodedJSUndefined();

Use { } in new code.
Comment 17 Alexey Shvayka 2022-01-28 13:08:41 PST
(In reply to Yusuke Suzuki from comment #16)
> Comment on attachment 449961 [details]
> Patch
> 
> View in context:
> https://bugs.webkit.org/attachment.cgi?id=449961&action=review
> 
> Ah, I misunderstood. r=me

Thank you!

> > Source/WebCore/bindings/js/JSDOMWindowCustom.cpp:510
> > +    return encodedJSUndefined();
> 
> Use { } in new code.
> 
> > Source/WebCore/bindings/js/JSDOMWindowCustom.cpp:528
> > +        return encodedJSUndefined();
> 
> Use { } in new code.

One clarification: isn't { } equivalent to encodedJSValue() (empty value), while we need to return encoded `undefined` here? Passing empty value to userland code may causes crashes, right?
Comment 18 Yusuke Suzuki 2022-01-28 13:31:55 PST
Comment on attachment 449961 [details]
Patch

View in context: https://bugs.webkit.org/attachment.cgi?id=449961&action=review

>>> Source/WebCore/bindings/js/JSDOMWindowCustom.cpp:510
>>> +    return encodedJSUndefined();
>> 
>> Use { } in new code.
> 
> One clarification: isn't { } equivalent to encodedJSValue() (empty value), while we need to return encoded `undefined` here? Passing empty value to userland code may causes crashes, right?

Ah, oops. I was thinking it is encodedJSValue(). Is there any reason using `encodedJSUndefined` over `JSValue::encode(jsUndefined())`? I rarely see `encodedJSUndefined()`.
Comment 19 Alexey Shvayka 2022-01-28 13:37:05 PST
(In reply to Yusuke Suzuki from comment #18)
> Comment on attachment 449961 [details]
> Patch
> 
> View in context:
> https://bugs.webkit.org/attachment.cgi?id=449961&action=review
> 
> >>> Source/WebCore/bindings/js/JSDOMWindowCustom.cpp:510
> >>> +    return encodedJSUndefined();
> >> 
> >> Use { } in new code.
> > 
> > One clarification: isn't { } equivalent to encodedJSValue() (empty value), while we need to return encoded `undefined` here? Passing empty value to userland code may causes crashes, right?
> 
> Ah, oops. I was thinking it is encodedJSValue(). Is there any reason using
> `encodedJSUndefined` over `JSValue::encode(jsUndefined())`? I rarely see
> `encodedJSUndefined()`.

encodedJSUndefined() is indeed very rare outside jsc.cpp / JSDollarVM.cpp / ObjC API. Will change to `JSValue::encode(jsUndefined())`.
Comment 20 Alexey Shvayka 2022-01-28 13:37:55 PST
(In reply to Yusuke Suzuki from comment #18)
> Is there any reason using `encodedJSUndefined` over `JSValue::encode(jsUndefined())`?

No reason at all, it's the same thing.
Comment 21 Alexey Shvayka 2022-01-28 16:06:01 PST
Committed r288763 (246551@trunk): <https://commits.webkit.org/246551@trunk>