Skip to content

Commit

Permalink
fix: ensure that functions are not retained beyond their context bein…
Browse files Browse the repository at this point in the history
…g released (#23207) (#23232)
  • Loading branch information
MarshallOfSound committed Apr 22, 2020
1 parent 039be2e commit 3909001
Show file tree
Hide file tree
Showing 3 changed files with 51 additions and 2 deletions.
Expand Up @@ -92,6 +92,16 @@ void RenderFramePersistenceStore::OnDestruct() {
delete this;
}

void RenderFramePersistenceStore::WillReleaseScriptContext(
v8::Local<v8::Context> context,
int32_t world_id) {
base::EraseIf(functions_, [context](auto const& pair) {
v8::Local<v8::Context> func_owning_context =
std::get<1>(pair.second).Get(context->GetIsolate());
return func_owning_context == context;
});
}

void RenderFramePersistenceStore::CacheProxiedObject(
v8::Local<v8::Value> from,
v8::Local<v8::Value> proxy_value) {
Expand Down
Expand Up @@ -40,6 +40,8 @@ class RenderFramePersistenceStore final : public content::RenderFrameObserver {

// RenderFrameObserver implementation.
void OnDestruct() override;
void WillReleaseScriptContext(v8::Local<v8::Context> context,
int32_t world_id) override;

size_t take_func_id() { return next_func_id_++; }

Expand Down
41 changes: 39 additions & 2 deletions spec-main/api-context-bridge-spec.ts
@@ -1,6 +1,8 @@
import { contextBridge, BrowserWindow, ipcMain } from 'electron'
import { expect } from 'chai'
import * as fs from 'fs-extra'
import * as http from 'http'
import { AddressInfo } from 'net'
import * as os from 'os'
import * as path from 'path'

Expand All @@ -12,6 +14,20 @@ const fixturesPath = path.resolve(__dirname, 'fixtures', 'api', 'context-bridge'
describe('contextBridge', () => {
let w: BrowserWindow
let dir: string
let server: http.Server

before(async () => {
server = http.createServer((req, res) => {
res.setHeader('Content-Type', 'text/html')
res.end('')
})
await new Promise(resolve => server.listen(0, resolve))
})

after(async () => {
if (server) await new Promise(resolve => server.close(resolve))
server = null as any
})

afterEach(async () => {
await closeWindow(w)
Expand Down Expand Up @@ -64,7 +80,7 @@ describe('contextBridge', () => {
preload: path.resolve(tmpDir, 'preload.js')
}
})
await w.loadFile(path.resolve(fixturesPath, 'empty.html'))
await w.loadURL(`http://127.0.0.1:${(server.address() as AddressInfo).port}`)
}

const callWithBindings = async (fn: Function) => {
Expand Down Expand Up @@ -503,7 +519,28 @@ describe('contextBridge', () => {
expect(info.objectCount).to.equal(6)
})
}


if (useSandbox) {
it('should not leak the global hold on methods sent across contexts when reloading a sandboxed renderer', async () => {
await makeBindingWindow(() => {
require('electron').ipcRenderer.on('get-gc-info', e => e.sender.send('gc-info', (contextBridge as any).debugGC()))
contextBridge.exposeInMainWorld('example', {
getFunction: () => () => 123
})
require('electron').ipcRenderer.send('window-ready-for-tasking')
})
const loadPromise = emittedOnce(ipcMain, 'window-ready-for-tasking')
expect((await getGCInfo()).functionCount).to.equal(1)
await callWithBindings((root: any) => {
root.location.reload()
})
await loadPromise
// If this is ever "2" it means we leaked the exposed function and
// therefore the entire context after a reload
expect((await getGCInfo()).functionCount).to.equal(1)
})
}

it('it should not let you overwrite existing exposed things', async () => {
await makeBindingWindow(() => {
let threw = false
Expand Down

0 comments on commit 3909001

Please sign in to comment.