Skip to content
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

feat: add missing webFrameMain.executeJavaScriptInIsolatedWorld() #27195

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
11 changes: 11 additions & 0 deletions docs/api/web-frame-main.md
Expand Up @@ -85,6 +85,17 @@ In the browser window some HTML APIs like `requestFullScreen` can only be
invoked by a gesture from the user. Setting `userGesture` to `true` will remove
this limitation.

#### `frame.executeJavaScriptInIsolatedWorld(worldId, code[, userGesture])`

* `worldId` Integer - The ID of the world to run the javascript in, `0` is the default world, `999` is the world used by Electron's `contextIsolation` feature. You can provide any integer here.
* `code` String
* `userGesture` Boolean (optional) - Default is `false`.

Returns `Promise<unknown>` - A promise that resolves with the result of the executed
code or is rejected if execution throws or results in a rejected promise.

Works like `executeJavaScript` but evaluates `scripts` in an isolated context.

#### `frame.reload()`

Returns `boolean` - Whether the reload was initiated successfully. Only results in `false` when the frame has no history.
Expand Down
45 changes: 45 additions & 0 deletions shell/browser/api/electron_api_web_frame_main.cc
Expand Up @@ -108,6 +108,49 @@ v8::Local<v8::Promise> WebFrameMain::ExecuteJavaScript(
return handle;
}

v8::Local<v8::Promise> WebFrameMain::ExecuteJavaScriptInIsolatedWorld(
gin::Arguments* args,
int world_id,
const base::string16& code) {
gin_helper::Promise<base::Value> promise(args->isolate());
v8::Local<v8::Promise> handle = promise.GetHandle();

// Optional userGesture parameter
bool user_gesture;
if (!args->PeekNext().IsEmpty()) {
if (args->PeekNext()->IsBoolean()) {
args->GetNext(&user_gesture);
} else {
args->ThrowTypeError("userGesture must be a boolean");
return handle;
}
} else {
user_gesture = false;
}

if (render_frame_disposed_) {
promise.RejectWithErrorMessage(
"Render frame was disposed before WebFrameMain could be accessed");
return handle;
}

if (user_gesture) {
auto* ftn = content::FrameTreeNode::From(render_frame_);
ftn->UpdateUserActivationState(
blink::mojom::UserActivationUpdateType::kNotifyActivation,
blink::mojom::UserActivationNotificationType::kTest);
}

render_frame_->ExecuteJavaScriptForTests(
code,
base::BindOnce([](gin_helper::Promise<base::Value> promise,
base::Value value) { promise.Resolve(value); },
std::move(promise)),
world_id);

return handle;
}

bool WebFrameMain::Reload(v8::Isolate* isolate) {
if (!CheckRenderFrame())
return false;
Expand Down Expand Up @@ -222,6 +265,8 @@ gin::ObjectTemplateBuilder WebFrameMain::GetObjectTemplateBuilder(
v8::Isolate* isolate) {
return gin::Wrappable<WebFrameMain>::GetObjectTemplateBuilder(isolate)
.SetMethod("executeJavaScript", &WebFrameMain::ExecuteJavaScript)
.SetMethod("executeJavaScriptInIsolatedWorld",
&WebFrameMain::ExecuteJavaScriptInIsolatedWorld)
.SetMethod("reload", &WebFrameMain::Reload)
.SetProperty("frameTreeNodeId", &WebFrameMain::FrameTreeNodeID)
.SetProperty("name", &WebFrameMain::Name)
Expand Down
4 changes: 4 additions & 0 deletions shell/browser/api/electron_api_web_frame_main.h
Expand Up @@ -66,6 +66,10 @@ class WebFrameMain : public gin::Wrappable<WebFrameMain> {

v8::Local<v8::Promise> ExecuteJavaScript(gin::Arguments* args,
const base::string16& code);
v8::Local<v8::Promise> ExecuteJavaScriptInIsolatedWorld(
gin::Arguments* args,
int world_id,
const base::string16& code);
bool Reload(v8::Isolate* isolate);

int FrameTreeNodeID(v8::Isolate* isolate) const;
Expand Down
13 changes: 13 additions & 0 deletions spec-main/api-web-frame-main-spec.ts
Expand Up @@ -147,6 +147,19 @@ describe('webFrameMain module', () => {
});
});

describe('WebFrame.executeJavaScriptInIsolatedWorld', () => {
it('can inject code into any subframe', async () => {
const w = new BrowserWindow({ show: false, webPreferences: { contextIsolation: true } });
await w.loadFile(path.join(subframesPath, 'frame-with-frame-container.html'));
const webFrame = w.webContents.mainFrame;

const getUrl = (frame: WebFrameMain) => frame.executeJavaScriptInIsolatedWorld(999, 'location.href');
expect(await getUrl(webFrame)).to.equal(fileUrl('frame-with-frame-container.html'));
expect(await getUrl(webFrame.frames[0])).to.equal(fileUrl('frame-with-frame.html'));
expect(await getUrl(webFrame.frames[0].frames[0])).to.equal(fileUrl('frame.html'));
});
});

describe('WebFrame.reload', () => {
it('reloads a frame', async () => {
const w = new BrowserWindow({ show: false, webPreferences: { contextIsolation: true } });
Expand Down