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 missing remote object error when calling remote function created in preload script (3-0-x) #15445

Merged
merged 2 commits into from Oct 31, 2018
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
22 changes: 16 additions & 6 deletions lib/browser/rpc-server.js
Expand Up @@ -148,9 +148,10 @@ const throwRPCError = function (message) {
throw error
}

const removeRemoteListenersAndLogWarning = (sender, meta, callIntoRenderer) => {
const removeRemoteListenersAndLogWarning = (sender, callIntoRenderer) => {
const location = v8Util.getHiddenValue(callIntoRenderer, 'location')
let message = `Attempting to call a function in a renderer window that has been closed or released.` +
`\nFunction provided here: ${meta.location}`
`\nFunction provided here: ${location}`

if (sender instanceof EventEmitter) {
const remoteEvents = sender.eventNames().filter((eventName) => {
Expand Down Expand Up @@ -213,14 +214,14 @@ const unwrapArgs = function (sender, contextId, args) {
return rendererFunctions.get(objectId)
}

const webContentsId = sender.getId()
let callIntoRenderer = function (...args) {
if (!sender.isDestroyed() && webContentsId === sender.getId()) {
const callIntoRenderer = function (...args) {
if (!sender.isDestroyed()) {
sender.send('ELECTRON_RENDERER_CALLBACK', contextId, meta.id, valueToMeta(sender, contextId, args))
} else {
removeRemoteListenersAndLogWarning(this, meta, callIntoRenderer)
removeRemoteListenersAndLogWarning(this, callIntoRenderer)
}
}
v8Util.setHiddenValue(callIntoRenderer, 'location', meta.location)
Object.defineProperty(callIntoRenderer, 'length', { value: meta.length })

v8Util.setRemoteCallbackFreer(callIntoRenderer, contextId, meta.id, sender)
Expand Down Expand Up @@ -261,6 +262,15 @@ const callFunction = function (event, contextId, func, caller, args) {
}
}

ipcMain.on('ELECTRON_BROWSER_WRONG_CONTEXT_ERROR', function (event, contextId, passedContextId, id) {
const objectId = [passedContextId, id]
if (!rendererFunctions.has(objectId)) {
// Do nothing if the error has already been reported before.
return
}
removeRemoteListenersAndLogWarning(event.sender, rendererFunctions.get(objectId))
})

ipcMain.on('ELECTRON_BROWSER_REQUIRE', function (event, contextId, module) {
try {
event.returnValue = valueToMeta(event.sender, contextId, process.mainModule.require(module))
Expand Down
2 changes: 2 additions & 0 deletions lib/renderer/api/remote.js
Expand Up @@ -279,6 +279,7 @@ function metaToException (meta) {
ipcRenderer.on('ELECTRON_RENDERER_CALLBACK', (event, passedContextId, id, args) => {
if (passedContextId !== contextId) {
// The invoked callback belongs to an old page in this renderer.
ipcRenderer.send('ELECTRON_BROWSER_WRONG_CONTEXT_ERROR', contextId, passedContextId, id)
return
}
callbacksRegistry.apply(id, metaToValue(args))
Expand All @@ -288,6 +289,7 @@ ipcRenderer.on('ELECTRON_RENDERER_CALLBACK', (event, passedContextId, id, args)
ipcRenderer.on('ELECTRON_RENDERER_RELEASE_CALLBACK', (event, passedContextId, id) => {
if (passedContextId !== contextId) {
// The freed callback belongs to an old page in this renderer.
ipcRenderer.send('ELECTRON_BROWSER_WRONG_CONTEXT_ERROR', contextId, passedContextId, id)
return
}
callbacksRegistry.remove(id)
Expand Down
20 changes: 20 additions & 0 deletions spec/api-remote-spec.js
Expand Up @@ -5,6 +5,7 @@ const path = require('path')
const {closeWindow} = require('./window-helpers')

const {remote} = require('electron')
const {ipcMain, BrowserWindow} = remote

const comparePaths = (path1, path2) => {
if (process.platform === 'win32') {
Expand Down Expand Up @@ -484,4 +485,23 @@ describe('remote module', () => {
}
})
})

describe('remote function in renderer', () => {
afterEach(() => {
ipcMain.removeAllListeners('done')
})

it('works when created in preload script', (done) => {
ipcMain.once('done', () => w.close())
const preload = path.join(fixtures, 'module', 'preload-remote-function.js')
w = new BrowserWindow({
show: false,
webPreferences: {
preload: preload
}
})
w.once('closed', () => done())
w.loadURL('about:blank')
})
})
})
5 changes: 5 additions & 0 deletions spec/fixtures/module/preload-remote-function.js
@@ -0,0 +1,5 @@
const { remote, ipcRenderer } = require('electron')
remote.getCurrentWindow().rendererFunc = () => {
ipcRenderer.send('done')
}
remote.getCurrentWindow().rendererFunc()
23 changes: 9 additions & 14 deletions spec/static/main.js
Expand Up @@ -313,26 +313,21 @@ ipcMain.on('disable-preload-on-next-will-attach-webview', (event, id) => {

ipcMain.on('try-emit-web-contents-event', (event, id, eventName) => {
const consoleWarn = console.warn
let warningMessage = null
const contents = webContents.fromId(id)
const listenerCountBefore = contents.listenerCount(eventName)

try {
console.warn = (message) => {
warningMessage = message
}
contents.emit(eventName, {sender: contents})
} finally {
console.warn = (warningMessage) => {
console.warn = consoleWarn
}

const listenerCountAfter = contents.listenerCount(eventName)

event.returnValue = {
warningMessage,
listenerCountBefore,
listenerCountAfter
const listenerCountAfter = contents.listenerCount(eventName)
event.returnValue = {
warningMessage,
listenerCountBefore,
listenerCountAfter
}
}

contents.emit(eventName, {sender: contents})
})

ipcMain.on('handle-uncaught-exception', (event, message) => {
Expand Down