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

fix: don't execute preload scripts for internal <iframe> in <webview> when nodeIntegrationInSubFrames is enabled #19299

Merged
merged 1 commit into from Jul 18, 2019
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
3 changes: 2 additions & 1 deletion atom/renderer/atom_render_frame_observer.cc
Expand Up @@ -111,7 +111,8 @@ void AtomRenderFrameObserver::DidCreateScriptContext(

if (should_create_isolated_context) {
CreateIsolatedWorldContext();
renderer_client_->SetupMainWorldOverrides(context, render_frame_);
if (!renderer_client_->IsWebViewFrame(context, render_frame_))
renderer_client_->SetupMainWorldOverrides(context, render_frame_);
}
}

Expand Down
3 changes: 2 additions & 1 deletion atom/renderer/atom_renderer_client.cc
Expand Up @@ -91,7 +91,8 @@ void AtomRendererClient::DidCreateScriptContext(
base::CommandLine::ForCurrentProcess()->HasSwitch(
switches::kNodeIntegrationInSubFrames);
bool should_load_node =
is_main_frame || is_devtools || allow_node_in_subframes;
(is_main_frame || is_devtools || allow_node_in_subframes) &&
!IsWebViewFrame(context, render_frame);
if (!should_load_node) {
return;
}
Expand Down
3 changes: 2 additions & 1 deletion atom/renderer/atom_sandboxed_renderer_client.cc
Expand Up @@ -190,7 +190,8 @@ void AtomSandboxedRendererClient::DidCreateScriptContext(
base::CommandLine::ForCurrentProcess()->HasSwitch(
switches::kNodeIntegrationInSubFrames);
bool should_load_preload =
is_main_frame || is_devtools || allow_node_in_sub_frames;
(is_main_frame || is_devtools || allow_node_in_sub_frames) &&
!IsWebViewFrame(context, render_frame);
if (!should_load_preload)
return;

Expand Down
24 changes: 24 additions & 0 deletions atom/renderer/renderer_client_base.cc
Expand Up @@ -314,4 +314,28 @@ v8::Local<v8::Value> RendererClientBase::RunScript(
return script->Run(context).ToLocalChecked();
}

bool RendererClientBase::IsWebViewFrame(
v8::Handle<v8::Context> context,
content::RenderFrame* render_frame) const {
auto* isolate = context->GetIsolate();

if (render_frame->IsMainFrame())
return false;

mate::Dictionary window_dict(
isolate, GetContext(render_frame->GetWebFrame(), isolate)->Global());

v8::Local<v8::Object> frame_element;
if (!window_dict.Get("frameElement", &frame_element))
return false;

mate::Dictionary frame_element_dict(isolate, frame_element);

v8::Local<v8::Object> internal;
if (!frame_element_dict.GetHidden("internal", &internal))
return false;

return !internal.IsEmpty();
}

} // namespace atom
4 changes: 4 additions & 0 deletions atom/renderer/renderer_client_base.h
Expand Up @@ -44,6 +44,10 @@ class RendererClientBase : public content::ContentRendererClient {
static v8::Local<v8::Value> RunScript(v8::Local<v8::Context> context,
v8::Local<v8::String> source);

// v8Util.getHiddenValue(window.frameElement, 'internal')
bool IsWebViewFrame(v8::Handle<v8::Context> context,
content::RenderFrame* render_frame) const;

protected:
void AddRenderBindings(v8::Isolate* isolate,
v8::Local<v8::Object> binding_object);
Expand Down
53 changes: 43 additions & 10 deletions spec/api-subframe-spec.js
Expand Up @@ -5,7 +5,7 @@ const path = require('path')
const { emittedNTimes, emittedOnce } = require('./events-helpers')
const { closeWindow } = require('./window-helpers')

const { BrowserWindow } = remote
const { BrowserWindow, ipcMain } = remote

describe('renderer nodeIntegrationInSubFrames', () => {
const generateTests = (description, webPreferences) => {
Expand All @@ -30,7 +30,7 @@ describe('renderer nodeIntegrationInSubFrames', () => {
})

it('should load preload scripts in top level iframes', async () => {
const detailsPromise = emittedNTimes(remote.ipcMain, 'preload-ran', 2)
const detailsPromise = emittedNTimes(ipcMain, 'preload-ran', 2)
w.loadFile(path.resolve(__dirname, `fixtures/sub-frames/frame-container${fixtureSuffix}.html`))
const [event1, event2] = await detailsPromise
expect(event1[0].frameId).to.not.equal(event2[0].frameId)
Expand All @@ -39,7 +39,7 @@ describe('renderer nodeIntegrationInSubFrames', () => {
})

it('should load preload scripts in nested iframes', async () => {
const detailsPromise = emittedNTimes(remote.ipcMain, 'preload-ran', 3)
const detailsPromise = emittedNTimes(ipcMain, 'preload-ran', 3)
w.loadFile(path.resolve(__dirname, `fixtures/sub-frames/frame-with-frame-container${fixtureSuffix}.html`))
const [event1, event2, event3] = await detailsPromise
expect(event1[0].frameId).to.not.equal(event2[0].frameId)
Expand All @@ -51,37 +51,37 @@ describe('renderer nodeIntegrationInSubFrames', () => {
})

it('should correctly reply to the main frame with using event.reply', async () => {
const detailsPromise = emittedNTimes(remote.ipcMain, 'preload-ran', 2)
const detailsPromise = emittedNTimes(ipcMain, 'preload-ran', 2)
w.loadFile(path.resolve(__dirname, `fixtures/sub-frames/frame-container${fixtureSuffix}.html`))
const [event1] = await detailsPromise
const pongPromise = emittedOnce(remote.ipcMain, 'preload-pong')
const pongPromise = emittedOnce(ipcMain, 'preload-pong')
event1[0].reply('preload-ping')
const details = await pongPromise
expect(details[1]).to.equal(event1[0].frameId)
})

it('should correctly reply to the sub-frames with using event.reply', async () => {
const detailsPromise = emittedNTimes(remote.ipcMain, 'preload-ran', 2)
const detailsPromise = emittedNTimes(ipcMain, 'preload-ran', 2)
w.loadFile(path.resolve(__dirname, `fixtures/sub-frames/frame-container${fixtureSuffix}.html`))
const [, event2] = await detailsPromise
const pongPromise = emittedOnce(remote.ipcMain, 'preload-pong')
const pongPromise = emittedOnce(ipcMain, 'preload-pong')
event2[0].reply('preload-ping')
const details = await pongPromise
expect(details[1]).to.equal(event2[0].frameId)
})

it('should correctly reply to the nested sub-frames with using event.reply', async () => {
const detailsPromise = emittedNTimes(remote.ipcMain, 'preload-ran', 3)
const detailsPromise = emittedNTimes(ipcMain, 'preload-ran', 3)
w.loadFile(path.resolve(__dirname, `fixtures/sub-frames/frame-with-frame-container${fixtureSuffix}.html`))
const [, , event3] = await detailsPromise
const pongPromise = emittedOnce(remote.ipcMain, 'preload-pong')
const pongPromise = emittedOnce(ipcMain, 'preload-pong')
event3[0].reply('preload-ping')
const details = await pongPromise
expect(details[1]).to.equal(event3[0].frameId)
})

it('should not expose globals in main world', async () => {
const detailsPromise = emittedNTimes(remote.ipcMain, 'preload-ran', 2)
const detailsPromise = emittedNTimes(ipcMain, 'preload-ran', 2)
w.loadFile(path.resolve(__dirname, `fixtures/sub-frames/frame-container${fixtureSuffix}.html`))
const details = await detailsPromise
const senders = details.map(event => event[0].sender)
Expand Down Expand Up @@ -151,4 +151,37 @@ describe('renderer nodeIntegrationInSubFrames', () => {
).forEach(config => {
generateTests(config.title, config.webPreferences)
})

describe('internal <iframe> inside of <webview>', () => {
let w

beforeEach(async () => {
await closeWindow(w)
w = new BrowserWindow({
show: false,
width: 400,
height: 400,
webPreferences: {
preload: path.resolve(__dirname, 'fixtures/sub-frames/webview-iframe-preload.js'),
nodeIntegrationInSubFrames: true,
webviewTag: true
}
})
})

afterEach(() => {
return closeWindow(w).then(() => {
w = null
})
})

it('should not load preload scripts', async () => {
const promisePass = emittedOnce(ipcMain, 'webview-loaded')
const promiseFail = emittedOnce(ipcMain, 'preload-in-frame').then(() => {
throw new Error('preload loaded in internal frame')
})
await w.loadURL('about:blank')
return Promise.race([promisePass, promiseFail])
})
})
})
14 changes: 14 additions & 0 deletions spec/fixtures/sub-frames/webview-iframe-preload.js
@@ -0,0 +1,14 @@
const { ipcRenderer } = require('electron')

if (process.isMainFrame) {
window.addEventListener('DOMContentLoaded', () => {
const webview = document.createElement('webview')
webview.src = 'about:blank'
webview.addEventListener('did-finish-load', () => {
ipcRenderer.send('webview-loaded')
}, { once: true })
document.body.appendChild(webview)
})
} else {
ipcRenderer.send('preload-in-frame')
}