Set JavaScript APIs on prototype of WebContents

This commit is contained in:
Cheng Zhao 2016-08-02 20:52:07 +09:00
parent 844f32aa36
commit 025034127a

View file

@ -78,11 +78,23 @@ const defaultPrintingSetting = {
shouldPrintSelectionOnly: false shouldPrintSelectionOnly: false
} }
// JavaScript implementations of WebContents.
const binding = process.atomBinding('web_contents') const binding = process.atomBinding('web_contents')
const {WebContents} = binding const {WebContents} = binding
Object.setPrototypeOf(WebContents.prototype, EventEmitter.prototype) Object.setPrototypeOf(WebContents.prototype, EventEmitter.prototype)
// WebContents::send(channel, args..)
// WebContents::sendToAll(channel, args..)
WebContents.prototype.send = function (channel, ...args) {
if (channel == null) throw new Error('Missing required channel argument')
return this._send(false, channel, args)
}
WebContents.prototype.sendToAll = function (channel, ...args) {
if (channel == null) throw new Error('Missing required channel argument')
return this._send(true, channel, args)
}
// Following methods are mapped to webFrame. // Following methods are mapped to webFrame.
const webFrameMethods = [ const webFrameMethods = [
'insertText', 'insertText',
@ -90,72 +102,42 @@ const webFrameMethods = [
'setZoomLevel', 'setZoomLevel',
'setZoomLevelLimits' 'setZoomLevelLimits'
] ]
const webFrameMethodsWithResult = [ const webFrameMethodsWithResult = [
'getZoomFactor', 'getZoomFactor',
'getZoomLevel' 'getZoomLevel'
] ]
// Add JavaScript wrappers for WebContents class. const asyncWebFrameMethods = function (requestId, method, callback, ...args) {
WebContents.prototype._init = function () {
// Every remote callback from renderer process would add a listenter to the
// render-view-deleted event, so ignore the listenters warning.
this.setMaxListeners(0)
// WebContents::send(channel, args..)
// WebContents::sendToAll(channel, args..)
const sendWrapper = (allFrames, channel, ...args) => {
if (channel == null) {
throw new Error('Missing required channel argument')
}
return this._send(allFrames, channel, args)
}
this.send = sendWrapper.bind(null, false)
this.sendToAll = sendWrapper.bind(null, true)
// The navigation controller.
const controller = new NavigationController(this)
for (const name in NavigationController.prototype) {
const method = NavigationController.prototype[name]
if (method instanceof Function) {
this[name] = function () {
return method.apply(controller, arguments)
}
}
}
const asyncWebFrameMethods = function (requestId, method, callback, ...args) {
this.send('ELECTRON_INTERNAL_RENDERER_ASYNC_WEB_FRAME_METHOD', requestId, method, args) this.send('ELECTRON_INTERNAL_RENDERER_ASYNC_WEB_FRAME_METHOD', requestId, method, args)
ipcMain.once(`ELECTRON_INTERNAL_BROWSER_ASYNC_WEB_FRAME_RESPONSE_${requestId}`, function (event, result) { ipcMain.once(`ELECTRON_INTERNAL_BROWSER_ASYNC_WEB_FRAME_RESPONSE_${requestId}`, function (event, result) {
if (callback) callback(result) if (callback) callback(result)
}) })
} }
const syncWebFrameMethods = function (requestId, method, callback, ...args) { const syncWebFrameMethods = function (requestId, method, callback, ...args) {
this.send('ELECTRON_INTERNAL_RENDERER_SYNC_WEB_FRAME_METHOD', requestId, method, args) this.send('ELECTRON_INTERNAL_RENDERER_SYNC_WEB_FRAME_METHOD', requestId, method, args)
ipcMain.once(`ELECTRON_INTERNAL_BROWSER_SYNC_WEB_FRAME_RESPONSE_${requestId}`, function (event, result) { ipcMain.once(`ELECTRON_INTERNAL_BROWSER_SYNC_WEB_FRAME_RESPONSE_${requestId}`, function (event, result) {
if (callback) callback(result) if (callback) callback(result)
}) })
} }
// Mapping webFrame methods. for (const method of webFrameMethods) {
for (const method of webFrameMethods) { WebContents.prototype[method] = function (...args) {
this[method] = function (...args) {
this.send('ELECTRON_INTERNAL_RENDERER_WEB_FRAME_METHOD', method, args) this.send('ELECTRON_INTERNAL_RENDERER_WEB_FRAME_METHOD', method, args)
} }
} }
for (const method of webFrameMethodsWithResult) { for (const method of webFrameMethodsWithResult) {
this[method] = function (...args) { WebContents.prototype[method] = function (...args) {
const callback = args[args.length - 1] const callback = args[args.length - 1]
const actualArgs = args.slice(0, args.length - 2) const actualArgs = args.slice(0, args.length - 2)
syncWebFrameMethods.call(this, getNextId(), method, callback, ...actualArgs) syncWebFrameMethods.call(this, getNextId(), method, callback, ...actualArgs)
} }
} }
// Make sure webContents.executeJavaScript would run the code only when the // Make sure WebContents::executeJavaScript would run the code only when the
// webContents has been loaded. // WebContents has been loaded.
this.executeJavaScript = function (code, hasUserGesture, callback) { WebContents.prototype.executeJavaScript = function (code, hasUserGesture, callback) {
const requestId = getNextId() const requestId = getNextId()
if (typeof hasUserGesture === 'function') { if (typeof hasUserGesture === 'function') {
callback = hasUserGesture callback = hasUserGesture
@ -168,41 +150,10 @@ WebContents.prototype._init = function () {
asyncWebFrameMethods.call(this, requestId, 'executeJavaScript', callback, code, hasUserGesture) asyncWebFrameMethods.call(this, requestId, 'executeJavaScript', callback, code, hasUserGesture)
}) })
} }
} }
// Dispatch IPC messages to the ipc module. // Translate the options of printToPDF.
this.on('ipc-message', function (event, [channel, ...args]) { WebContents.prototype.printToPDF = function (options, callback) {
ipcMain.emit(channel, event, ...args)
})
this.on('ipc-message-sync', function (event, [channel, ...args]) {
Object.defineProperty(event, 'returnValue', {
set: function (value) {
return event.sendReply(JSON.stringify(value))
},
get: function () {}
})
ipcMain.emit(channel, event, ...args)
})
// Handle context menu action request from pepper plugin.
this.on('pepper-context-menu', function (event, params) {
const menu = Menu.buildFromTemplate(params.menu)
menu.popup(params.x, params.y)
})
// The devtools requests the webContents to reload.
this.on('devtools-reload-page', function () {
this.reload()
})
// Delays the page-title-updated event to next tick.
this.on('-page-title-updated', function (...args) {
setImmediate(() => {
this.emit.apply(this, ['page-title-updated'].concat(args))
})
})
this.printToPDF = function (options, callback) {
const printingSetting = Object.assign({}, defaultPrintingSetting) const printingSetting = Object.assign({}, defaultPrintingSetting)
if (options.landscape) { if (options.landscape) {
printingSetting.landscape = options.landscape printingSetting.landscape = options.landscape
@ -241,11 +192,61 @@ WebContents.prototype._init = function () {
} }
this._printToPDF(printingSetting, callback) this._printToPDF(printingSetting, callback)
}
// Add JavaScript wrappers for WebContents class.
WebContents.prototype._init = function () {
// Every remote callback from renderer process would add a listenter to the
// render-view-deleted event, so ignore the listenters warning.
this.setMaxListeners(0)
// The navigation controller.
const controller = new NavigationController(this)
for (const name in NavigationController.prototype) {
const method = NavigationController.prototype[name]
if (method instanceof Function) {
this[name] = function () {
return method.apply(controller, arguments)
} }
}
}
// Dispatch IPC messages to the ipc module.
this.on('ipc-message', function (event, [channel, ...args]) {
ipcMain.emit(channel, event, ...args)
})
this.on('ipc-message-sync', function (event, [channel, ...args]) {
Object.defineProperty(event, 'returnValue', {
set: function (value) {
return event.sendReply(JSON.stringify(value))
},
get: function () {}
})
ipcMain.emit(channel, event, ...args)
})
// Handle context menu action request from pepper plugin.
this.on('pepper-context-menu', function (event, params) {
const menu = Menu.buildFromTemplate(params.menu)
menu.popup(params.x, params.y)
})
// The devtools requests the webContents to reload.
this.on('devtools-reload-page', function () {
this.reload()
})
// Delays the page-title-updated event to next tick.
this.on('-page-title-updated', function (...args) {
setImmediate(() => {
this.emit.apply(this, ['page-title-updated'].concat(args))
})
})
app.emit('web-contents-created', {}, this) app.emit('web-contents-created', {}, this)
} }
// JavaScript wrapper of Debugger.
const {Debugger} = process.atomBinding('debugger') const {Debugger} = process.atomBinding('debugger')
Object.setPrototypeOf(Debugger.prototype, EventEmitter.prototype) Object.setPrototypeOf(Debugger.prototype, EventEmitter.prototype)