feat: optional typically sync callback for WebFrame#executeJavaScript* (#21423)

This commit is contained in:
bughit 2020-03-02 19:11:40 -05:00 committed by GitHub
parent 748a917ffd
commit 84126a4f23
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 197 additions and 23 deletions

View file

@ -15,6 +15,7 @@
#include "services/service_manager/public/cpp/interface_provider.h"
#include "shell/common/api/api.mojom.h"
#include "shell/common/gin_converters/blink_converter.h"
#include "shell/common/gin_converters/callback_converter.h"
#include "shell/common/gin_helper/dictionary.h"
#include "shell/common/gin_helper/promise.h"
#include "shell/common/node_includes.h"
@ -110,32 +111,58 @@ class RenderFrameStatus final : public content::RenderFrameObserver {
class ScriptExecutionCallback : public blink::WebScriptExecutionCallback {
public:
// for compatibility with the older version of this, error is after result
using CompletionCallback =
base::OnceCallback<void(const v8::Local<v8::Value>& result,
const v8::Local<v8::Value>& error)>;
explicit ScriptExecutionCallback(
gin_helper::Promise<v8::Local<v8::Value>> promise)
: promise_(std::move(promise)) {}
gin_helper::Promise<v8::Local<v8::Value>> promise,
CompletionCallback callback)
: promise_(std::move(promise)), callback_(std::move(callback)) {}
~ScriptExecutionCallback() override = default;
void Completed(
const blink::WebVector<v8::Local<v8::Value>>& result) override {
v8::Isolate* isolate = v8::Isolate::GetCurrent();
if (!result.empty()) {
if (!result[0].IsEmpty()) {
// Right now only single results per frame is supported.
if (!callback_.is_null())
std::move(callback_).Run(result[0], v8::Undefined(isolate));
promise_.Resolve(result[0]);
} else {
promise_.RejectWithErrorMessage(
const char* error_message =
"Script failed to execute, this normally means an error "
"was thrown. Check the renderer console for the error.");
"was thrown. Check the renderer console for the error.";
if (!callback_.is_null()) {
std::move(callback_).Run(
v8::Undefined(isolate),
v8::Exception::Error(
v8::String::NewFromUtf8(isolate, error_message)
.ToLocalChecked()));
}
promise_.RejectWithErrorMessage(error_message);
}
} else {
promise_.RejectWithErrorMessage(
const char* error_message =
"WebFrame was removed before script could run. This normally means "
"the underlying frame was destroyed");
"the underlying frame was destroyed";
if (!callback_.is_null()) {
std::move(callback_).Run(
v8::Undefined(isolate),
v8::Exception::Error(v8::String::NewFromUtf8(isolate, error_message)
.ToLocalChecked()));
}
promise_.RejectWithErrorMessage(error_message);
}
delete this;
}
private:
gin_helper::Promise<v8::Local<v8::Value>> promise_;
CompletionCallback callback_;
DISALLOW_COPY_AND_ASSIGN(ScriptExecutionCallback);
};
@ -373,9 +400,14 @@ v8::Local<v8::Promise> ExecuteJavaScript(gin_helper::Arguments* args,
bool has_user_gesture = false;
args->GetNext(&has_user_gesture);
ScriptExecutionCallback::CompletionCallback completion_callback;
args->GetNext(&completion_callback);
GetRenderFrame(window)->GetWebFrame()->RequestExecuteScriptAndReturnValue(
blink::WebScriptSource(blink::WebString::FromUTF16(code)),
has_user_gesture, new ScriptExecutionCallback(std::move(promise)));
has_user_gesture,
new ScriptExecutionCallback(std::move(promise),
std::move(completion_callback)));
return handle;
}
@ -389,6 +421,16 @@ v8::Local<v8::Promise> ExecuteJavaScriptInIsolatedWorld(
gin_helper::Promise<v8::Local<v8::Value>> promise(isolate);
v8::Local<v8::Promise> handle = promise.GetHandle();
bool has_user_gesture = false;
args->GetNext(&has_user_gesture);
blink::WebLocalFrame::ScriptExecutionType scriptExecutionType =
blink::WebLocalFrame::kSynchronous;
args->GetNext(&scriptExecutionType);
ScriptExecutionCallback::CompletionCallback completion_callback;
args->GetNext(&completion_callback);
std::vector<blink::WebScriptSource> sources;
for (const auto& script : scripts) {
@ -399,7 +441,15 @@ v8::Local<v8::Promise> ExecuteJavaScriptInIsolatedWorld(
script.Get("startLine", &start_line);
if (!script.Get("code", &code)) {
promise.RejectWithErrorMessage("Invalid 'code'");
const char* error_message = "Invalid 'code'";
if (!completion_callback.is_null()) {
std::move(completion_callback)
.Run(v8::Undefined(isolate),
v8::Exception::Error(
v8::String::NewFromUtf8(isolate, error_message)
.ToLocalChecked()));
}
promise.RejectWithErrorMessage(error_message);
return handle;
}
@ -408,19 +458,14 @@ v8::Local<v8::Promise> ExecuteJavaScriptInIsolatedWorld(
blink::WebURL(GURL(url)), start_line));
}
bool has_user_gesture = false;
args->GetNext(&has_user_gesture);
blink::WebLocalFrame::ScriptExecutionType scriptExecutionType =
blink::WebLocalFrame::kSynchronous;
args->GetNext(&scriptExecutionType);
// Debugging tip: if you see a crash stack trace beginning from this call,
// then it is very likely that some exception happened when executing the
// "content_script/init.js" script.
GetRenderFrame(window)->GetWebFrame()->RequestExecuteScriptInIsolatedWorld(
world_id, &sources.front(), sources.size(), has_user_gesture,
scriptExecutionType, new ScriptExecutionCallback(std::move(promise)));
scriptExecutionType,
new ScriptExecutionCallback(std::move(promise),
std::move(completion_callback)));
return handle;
}