Bug 218363 - Promises returned by our DOM API have the caller's global instead of the callee's
Summary: Promises returned by our DOM API have the caller's global instead of the call...
Status: RESOLVED FIXED
Alias: None
Product: WebKit
Classification: Unclassified
Component: Bindings (show other bugs)
Version: WebKit Nightly Build
Hardware: Unspecified Unspecified
: P2 Normal
Assignee: Chris Dumez
URL:
Keywords: InRadar
: 218361 (view as bug list)
Depends on: 218468 218474
Blocks:
  Show dependency treegraph
 
Reported: 2020-10-29 16:31 PDT by Chris Dumez
Modified: 2022-01-04 16:48 PST (History)
17 users (show)

See Also:


Attachments
WIP Patch (5.85 KB, patch)
2020-10-29 16:33 PDT, Chris Dumez
no flags Details | Formatted Diff | Diff
WIP Patch (7.04 KB, patch)
2020-10-29 16:54 PDT, Chris Dumez
ews-feeder: commit-queue-
Details | Formatted Diff | Diff
WIP Patch (91.63 KB, patch)
2020-10-30 08:27 PDT, Chris Dumez
no flags Details | Formatted Diff | Diff
WIP Patch (95.21 KB, patch)
2020-10-30 09:12 PDT, Chris Dumez
no flags Details | Formatted Diff | Diff
WIP Patch (95.29 KB, patch)
2020-10-30 09:17 PDT, Chris Dumez
no flags Details | Formatted Diff | Diff
WIP Patch (98.56 KB, patch)
2020-10-30 09:25 PDT, Chris Dumez
no flags Details | Formatted Diff | Diff
Patch (109.21 KB, patch)
2020-10-30 10:22 PDT, Chris Dumez
no flags Details | Formatted Diff | Diff
Patch (110.05 KB, patch)
2020-10-30 13:42 PDT, Chris Dumez
no flags Details | Formatted Diff | Diff
Patch (110.15 KB, patch)
2020-10-30 16:17 PDT, Chris Dumez
no flags Details | Formatted Diff | Diff
Patch (111.29 KB, patch)
2020-10-30 16:59 PDT, Chris Dumez
no flags Details | Formatted Diff | Diff
Patch (111.94 KB, patch)
2020-10-30 23:15 PDT, Chris Dumez
no flags Details | Formatted Diff | Diff
Patch (110.42 KB, patch)
2020-10-30 23:18 PDT, Chris Dumez
no flags Details | Formatted Diff | Diff

Note You need to log in before you can comment on or make changes to this bug.
Description Chris Dumez 2020-10-29 16:31:51 PDT
Promises returns by our DOM API have the caller's global instead of the callee's. This is inconsistent with Blink and Gecko.
As a result, the values passed when resolving those promises also end up with the wrong global object.
Comment 1 Chris Dumez 2020-10-29 16:33:02 PDT
Created attachment 412698 [details]
WIP Patch
Comment 2 Chris Dumez 2020-10-29 16:47:24 PDT
Comment on attachment 412698 [details]
WIP Patch

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

> Source/WebCore/dom/Document.cpp:2715
> +        m_documentTaskGroup->stopAndDiscardAllTasks();*/

I need to look into this more and talk to Ryosuke but I think we may want a group per top document, rather than a group per document. This way, we would still discard all tasks on navigation.
Comment 3 Chris Dumez 2020-10-29 16:54:46 PDT
Created attachment 412699 [details]
WIP Patch
Comment 4 Chris Dumez 2020-10-30 08:27:50 PDT
Created attachment 412746 [details]
WIP Patch
Comment 5 Chris Dumez 2020-10-30 09:12:39 PDT
Created attachment 412752 [details]
WIP Patch
Comment 6 Chris Dumez 2020-10-30 09:17:03 PDT
Created attachment 412753 [details]
WIP Patch
Comment 7 EWS Watchlist 2020-10-30 09:18:03 PDT
This patch modifies the imported WPT tests. Please ensure that any changes on the tests (not coming from a WPT import) are exported to WPT. Please see https://trac.webkit.org/wiki/WPTExportProcess
Comment 8 Chris Dumez 2020-10-30 09:25:59 PDT
Created attachment 412755 [details]
WIP Patch
Comment 9 Chris Dumez 2020-10-30 10:22:56 PDT
Created attachment 412768 [details]
Patch
Comment 10 Chris Dumez 2020-10-30 13:42:05 PDT
Created attachment 412792 [details]
Patch
Comment 11 Darin Adler 2020-10-30 16:06:13 PDT
Comment on attachment 412792 [details]
Patch

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

> Source/WebCore/bindings/js/JSDOMPromiseDeferred.h:317
> +    auto& globalObject = *JSC::jsCast<JSDOMGlobalObject*>(&lexicalGlobalObject);

What guarantees this? I guess maybe it’s obvious, but I wasn’t sure.

> Source/WebCore/bindings/js/JSDOMPromiseDeferred.h:336
> +    auto& globalObject = *JSC::jsCast<JSDOMGlobalObject*>(&lexicalGlobalObject);

Ditto.

> Source/WebCore/dom/EventLoop.cpp:82
> +    auto associatedGroup = std::exchange(m_associatedGroups, { });

Should be plural, associatedGroups.

> Source/WebCore/dom/EventLoop.h:140
> +        m_isReadyToStop = true;
> +        if (m_eventLoop)
> +            m_eventLoop->stopAssociatedGroupsIfNecessary();

Is it correct to call stopAssociatedGroupsIfNecessary even if m_isReadyToStop was already true?
Comment 12 Chris Dumez 2020-10-30 16:10:47 PDT
Comment on attachment 412792 [details]
Patch

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

>> Source/WebCore/bindings/js/JSDOMPromiseDeferred.h:317
>> +    auto& globalObject = *JSC::jsCast<JSDOMGlobalObject*>(&lexicalGlobalObject);
> 
> What guarantees this? I guess maybe it’s obvious, but I wasn’t sure.

I will confirm with Geoff.

>> Source/WebCore/dom/EventLoop.cpp:82
>> +    auto associatedGroup = std::exchange(m_associatedGroups, { });
> 
> Should be plural, associatedGroups.

Oops. Will fix.

>> Source/WebCore/dom/EventLoop.h:140
>> +            m_eventLoop->stopAssociatedGroupsIfNecessary();
> 
> Is it correct to call stopAssociatedGroupsIfNecessary even if m_isReadyToStop was already true?

I don't see how it would hurt. stopAssociatedGroupsIfNecessary() would iterate though all the groups again and check if all groups are ready to stop.

Seems like a good idea to early return though to avoid doing work unnecessarily.
Comment 13 Chris Dumez 2020-10-30 16:14:34 PDT
(In reply to Chris Dumez from comment #12)
> Comment on attachment 412792 [details]
> Patch
> 
> View in context:
> https://bugs.webkit.org/attachment.cgi?id=412792&action=review
> 
> >> Source/WebCore/bindings/js/JSDOMPromiseDeferred.h:317
> >> +    auto& globalObject = *JSC::jsCast<JSDOMGlobalObject*>(&lexicalGlobalObject);
> > 
> > What guarantees this? I guess maybe it’s obvious, but I wasn’t sure.
> 
> I will confirm with Geoff.

FYI, we used to call callerGlobalObject() which was casting similarly internally, so I figured I did not make thing worse:

JSDOMGlobalObject& callerGlobalObject(JSGlobalObject& lexicalGlobalObject, CallFrame& callFrame)
{
    class GetCallerGlobalObjectFunctor {
    public:
        GetCallerGlobalObjectFunctor() = default;

        StackVisitor::Status operator()(StackVisitor& visitor) const
        {
            if (!m_hasSkippedFirstFrame) {
                m_hasSkippedFirstFrame = true;
                return StackVisitor::Continue;
            }

            if (auto* codeBlock = visitor->codeBlock())
                m_globalObject = codeBlock->globalObject();
            else {
                ASSERT(visitor->callee().rawPtr());
                // FIXME: Callee is not an object if the caller is Web Assembly.
                // Figure out what to do here. We can probably get the global object
                // from the top-most Wasm Instance. https://bugs.webkit.org/show_bug.cgi?id=165721
                if (visitor->callee().isCell() && visitor->callee().asCell()->isObject())
                    m_globalObject = jsCast<JSObject*>(visitor->callee().asCell())->globalObject();
            }
            return StackVisitor::Done;
        }

        JSGlobalObject* globalObject() const { return m_globalObject; }

    private:
        mutable bool m_hasSkippedFirstFrame { false };
        mutable JSGlobalObject* m_globalObject { nullptr };
    };

    GetCallerGlobalObjectFunctor iter;
    callFrame.iterate(lexicalGlobalObject.vm(), iter);
    if (iter.globalObject())
        return *jsCast<JSDOMGlobalObject*>(iter.globalObject());

    // If we cannot find JSGlobalObject in caller frames, we just return the current lexicalGlobalObject.
    return *jsCast<JSDOMGlobalObject*>(&lexicalGlobalObject);
}
Comment 14 Chris Dumez 2020-10-30 16:17:20 PDT
Created attachment 412807 [details]
Patch
Comment 15 Ryosuke Niwa 2020-10-30 16:44:00 PDT
Comment on attachment 412807 [details]
Patch

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

> Source/WebCore/dom/EventLoop.h:117
> +        if (m_eventLoop)
> +            m_eventLoop->unregisterGroup(*this);

Can we avoid de-referencing m_eventLoop twice here?

> Source/WebCore/dom/EventLoop.h:143
> +        if (m_eventLoop)
> +            m_eventLoop->stopAssociatedGroupsIfNecessary();

Ditto.

> Source/WebCore/dom/EventLoop.h:189
>      State m_state { State::Running };
> +    bool m_isReadyToStop { false };

Please just add a new value to State instead of adding a new boolean.
Comment 16 Chris Dumez 2020-10-30 16:53:25 PDT
(In reply to Ryosuke Niwa from comment #15)
> Comment on attachment 412807 [details]
> Patch
> 
> View in context:
> https://bugs.webkit.org/attachment.cgi?id=412807&action=review
> 
> > Source/WebCore/dom/EventLoop.h:117
> > +        if (m_eventLoop)
> > +            m_eventLoop->unregisterGroup(*this);
> 
> Can we avoid de-referencing m_eventLoop twice here?
> 
> > Source/WebCore/dom/EventLoop.h:143
> > +        if (m_eventLoop)
> > +            m_eventLoop->stopAssociatedGroupsIfNecessary();
> 
> Ditto.
> 
> > Source/WebCore/dom/EventLoop.h:189
> >      State m_state { State::Running };
> > +    bool m_isReadyToStop { false };
> 
> Please just add a new value to State instead of adding a new boolean.

Will fix.
Comment 17 Chris Dumez 2020-10-30 16:59:20 PDT
Created attachment 412812 [details]
Patch
Comment 18 Chris Dumez 2020-10-30 16:59:59 PDT
*** Bug 218361 has been marked as a duplicate of this bug. ***
Comment 19 Chris Dumez 2020-10-30 23:15:12 PDT
Created attachment 412834 [details]
Patch
Comment 20 Chris Dumez 2020-10-30 23:18:09 PDT
Created attachment 412835 [details]
Patch
Comment 21 EWS 2020-10-31 09:12:04 PDT
Committed r269227: <https://trac.webkit.org/changeset/269227>

All reviewed patches have been landed. Closing bug and clearing flags on attachment 412835 [details].
Comment 22 Radar WebKit Bug Importer 2020-10-31 09:13:22 PDT
<rdar://problem/70916458>