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
}
// JavaScript implementations of WebContents.
const binding = process.atomBinding('web_contents')
const {WebContents} = binding
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.
const webFrameMethods = [
'insertText',
@ -90,40 +102,11 @@ const webFrameMethods = [
'setZoomLevel',
'setZoomLevelLimits'
]
const webFrameMethodsWithResult = [
'getZoomFactor',
'getZoomLevel'
]
// 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)
// 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)
ipcMain.once(`ELECTRON_INTERNAL_BROWSER_ASYNC_WEB_FRAME_RESPONSE_${requestId}`, function (event, result) {
@ -138,24 +121,23 @@ WebContents.prototype._init = function () {
})
}
// Mapping webFrame methods.
for (const method of webFrameMethods) {
this[method] = function (...args) {
WebContents.prototype[method] = function (...args) {
this.send('ELECTRON_INTERNAL_RENDERER_WEB_FRAME_METHOD', method, args)
}
}
for (const method of webFrameMethodsWithResult) {
this[method] = function (...args) {
WebContents.prototype[method] = function (...args) {
const callback = args[args.length - 1]
const actualArgs = args.slice(0, args.length - 2)
syncWebFrameMethods.call(this, getNextId(), method, callback, ...actualArgs)
}
}
// Make sure webContents.executeJavaScript would run the code only when the
// webContents has been loaded.
this.executeJavaScript = function (code, hasUserGesture, callback) {
// Make sure WebContents::executeJavaScript would run the code only when the
// WebContents has been loaded.
WebContents.prototype.executeJavaScript = function (code, hasUserGesture, callback) {
const requestId = getNextId()
if (typeof hasUserGesture === 'function') {
callback = hasUserGesture
@ -170,39 +152,8 @@ WebContents.prototype._init = function () {
}
}
// 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))
})
})
this.printToPDF = function (options, callback) {
// Translate the options of printToPDF.
WebContents.prototype.printToPDF = function (options, callback) {
const printingSetting = Object.assign({}, defaultPrintingSetting)
if (options.landscape) {
printingSetting.landscape = options.landscape
@ -243,9 +194,59 @@ WebContents.prototype._init = function () {
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)
}
// JavaScript wrapper of Debugger.
const {Debugger} = process.atomBinding('debugger')
Object.setPrototypeOf(Debugger.prototype, EventEmitter.prototype)