8000 feat: expose sync js exec interface on WebFrame (try 2) by bughit · Pull Request #21371 · electron/electron · GitHub
[go: up one dir, main page]
More Web Proxy on the site http://driver.im/
Skip to content

feat: expose sync js exec interface on WebFrame (try 2) #21371

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 1 commit into from

Conversation

bughit
Copy link
Contributor
@bughit bughit commented Dec 3, 2019

Description of Change

Before the promisification (#17312) of WebFrame#executeJavaScript* their interface was async (callback) but behavior was sync (callback called before the methods returned). So they could be used in sync code (or async, you could wrap them in a promise yourself).

The switch from a callback to a promise interface made it impossible to use them in sync mode (#19161), which was a breaking change without a workaround.

This PR exposes the sync js exec methods on WebFrame, providing a workaround for code that used the original methods synchronously. In general, a sync in-process js eval makes perfect sense, because it's sync under the hood.

This attempt uses the same underlying methods WebLocalFrame::RequestExecuteScript* as the "async" versions.

Fixes #19161

Checklist

Release Notes

Notes: add sync javascript execution methods to WebFrame

@electron-cation electron-cation bot added the new-pr 🌱 PR opened recently label Dec 3, 2019
Copy link
Member
@MarshallOfSound MarshallOfSound left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I fundamentally don't think we should be exposing this as API. It is abusing an implementation details of Chromiums API. There is no way we can guarantee it will remain sync and by making this API we are committing to supporting it.

I am firmly against this API being added.

@electron/wg-api for reference


// this can only happen if the frame is not in the
// mojom::FrameLifecycleState::kRunning state
args->ThrowError("execution did not complete synchronously");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This makes no sense as an API, their is no way the app can handle this in any useful way.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have never seen the top frame not in the mojom::FrameLifecycleState::kRunning state.

Can you point out when it wouldn't be?

When a frame is in the kRunning state, execution is guaranteed to be synchronous, that's not an "implementation detail" it's a public api guarantee

  enum ScriptExecutionType {
    // Execute script synchronously, unless the page is suspended.
    kSynchronous,
    // Execute script asynchronously.
    kAsynchronous,
    // Execute script asynchronously, blocking the window.onload event.
    kAsynchronousBlockingOnload
  };

Only if the page is suspended, would execution not be synchronous.

If the top frame being suspended is an unavoidable, unpredictable possibility outside of one's control, which seems unlikely, I would agree with you. Otherwise these sync methods are useful and will work with 100% reliability and so should not be rejected.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll test it. If so, and if this is something that could be detected, the sync methods could fail early. That would not be unreasonable behavior.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd also test what happens if the executed script calls alert; that seems like it would pause the script also and prevent a synchronous return from happening.


// this can only happen if the frame is not in the
// mojom::FrameLifecycleState::kRunning state
args->ThrowError("execution did not complete synchronously");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@electron-cation electron-cation bot removed the new-pr 🌱 PR opened recently label Dec 4, 2019
@MarshallOfSound
Copy link
Member

@bughit I've come up with a middle ground that should suit your use case. Implement this API to support both callbacks and promises. Then if you really want to do this, you can implement the "fake sync" behavior in JS by using the callback.

@nornagon
Copy link
Contributor
nornagon commented Dec 5, 2019

@bughit I've come up with a middle ground that should suit your use case. Implement this API to support both callbacks and promises. Then if you really want to do this, you can implement the "fake sync" behavior in JS by using the callback.

Do you mean to support the old behavior where you could pass in a callback? Seems reasonable to me, and as a bonus old code that depends on the synchronous behavior would continue to work unchanged.

@bughit
Copy link
Contributor Author
bughit commented Dec 5, 2019

@MarshallOfSound That works for me, I am already using the "fake sync" approach in v4.

Please give more details of how this should work.

  • should I modify existing methods?
  • to take an optional callback?
  • that gets invoked if present in addition to the promise?

@MarshallOfSound
Copy link
Member

Yep exactly that. The current API should work like it currently does (return a promise). But if a callback is also provided it will be called in addition to resolving the promise

@bughit
Copy link
Contributor Author
bughit commented Dec 6, 2019

@MarshallOfSound

could use some help with callback conversion

bughit/electron@948d2a3

callback signature

  // for compatibility with the older version of this, error is after result
  using CompletionCallback =
      base::Callback<void(const v8::Local<v8::Value>& result,
                          const v8::Local<v8::Value>& error)>;

which results in a conversion error

../..\gin/converter.h(284,24): error: no member named 'FromV8' in 'gin::Converter<base::RepeatingCallback<void (const v8::Local<v8::Value> &, const v8::Local<v8
::Value> &)>, void>'
  return Converter<T>::FromV8(isolate, input, result);
         ~~~~~~~~~~~~~~^

@bughit
Copy link
Contributor Author
bughit commented Dec 6, 2019

needed "shell/common/gin_converters/callback_converter.h"

@bughit
Copy link
Contributor Author
bughit commented Dec 6, 2019

closing in favor of #21423

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

promisifying WebFrame.executeJavaScript(InIsolatedWorld) is a breaking change without a workaround
3 participants
0