Set JavaScript APIs on prototype of WebContents
authorCheng Zhao <zcbenz@gmail.com>
Tue, 2 Aug 2016 11:52:07 +0000 (20:52 +0900)
committerCheng Zhao <zcbenz@gmail.com>
Tue, 2 Aug 2016 11:52:07 +0000 (20:52 +0900)
lib/browser/api/web-contents.js

index 796f2b2..c255cd0 100644 (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,83 +102,112 @@ 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)
+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) {
+    if (callback) callback(result)
+  })
+}
 
-  // 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)
+const syncWebFrameMethods = function (requestId, method, callback, ...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) {
+    if (callback) callback(result)
+  })
+}
 
-  // 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)
-      }
-    }
+for (const method of webFrameMethods) {
+  WebContents.prototype[method] = function (...args) {
+    this.send('ELECTRON_INTERNAL_RENDERER_WEB_FRAME_METHOD', method, args)
   }
+}
 
-  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) {
-      if (callback) callback(result)
-    })
+for (const method of webFrameMethodsWithResult) {
+  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)
   }
+}
 
-  const syncWebFrameMethods = function (requestId, method, callback, ...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) {
-      if (callback) callback(result)
+// 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
+    hasUserGesture = false
+  }
+  if (this.getURL() && !this.isLoadingMainFrame()) {
+    asyncWebFrameMethods.call(this, requestId, 'executeJavaScript', callback, code, hasUserGesture)
+  } else {
+    this.once('did-finish-load', () => {
+      asyncWebFrameMethods.call(this, requestId, 'executeJavaScript', callback, code, hasUserGesture)
     })
   }
+}
 
-  // Mapping webFrame methods.
-  for (const method of webFrameMethods) {
-    this[method] = function (...args) {
-      this.send('ELECTRON_INTERNAL_RENDERER_WEB_FRAME_METHOD', method, args)
-    }
+// Translate the options of printToPDF.
+WebContents.prototype.printToPDF = function (options, callback) {
+  const printingSetting = Object.assign({}, defaultPrintingSetting)
+  if (options.landscape) {
+    printingSetting.landscape = options.landscape
+  }
+  if (options.marginsType) {
+    printingSetting.marginsType = options.marginsType
+  }
+  if (options.printSelectionOnly) {
+    printingSetting.shouldPrintSelectionOnly = options.printSelectionOnly
+  }
+  if (options.printBackground) {
+    printingSetting.shouldPrintBackgrounds = options.printBackground
   }
 
-  for (const method of webFrameMethodsWithResult) {
-    this[method] = function (...args) {
-      const callback = args[args.length - 1]
-      const actualArgs = args.slice(0, args.length - 2)
-      syncWebFrameMethods.call(this, getNextId(), method, callback, ...actualArgs)
+  if (options.pageSize) {
+    const pageSize = options.pageSize
+    if (typeof pageSize === 'object') {
+      if (!pageSize.height || !pageSize.width) {
+        return callback(new Error('Must define height and width for pageSize'))
+      }
+      // Dimensions in Microns
+      // 1 meter = 10^6 microns
+      printingSetting.mediaSize = {
+        name: 'CUSTOM',
+        custom_display_name: 'Custom',
+        height_microns: pageSize.height,
+        width_microns: pageSize.width
+      }
+    } else if (PDFPageSizes[pageSize]) {
+      printingSetting.mediaSize = PDFPageSizes[pageSize]
+    } else {
+      return callback(new Error(`Does not support pageSize with ${pageSize}`))
     }
+  } else {
+    printingSetting.mediaSize = PDFPageSizes['A4']
   }
 
-  // Make sure webContents.executeJavaScript would run the code only when the
-  // webContents has been loaded.
-  this.executeJavaScript = function (code, hasUserGesture, callback) {
-    const requestId = getNextId()
-    if (typeof hasUserGesture === 'function') {
-      callback = hasUserGesture
-      hasUserGesture = false
-    }
-    if (this.getURL() && !this.isLoadingMainFrame()) {
-      asyncWebFrameMethods.call(this, requestId, 'executeJavaScript', callback, code, hasUserGesture)
-    } else {
-      this.once('did-finish-load', () => {
-        asyncWebFrameMethods.call(this, requestId, 'executeJavaScript', callback, code, hasUserGesture)
-      })
+  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)
+      }
     }
   }
 
@@ -202,50 +243,10 @@ WebContents.prototype._init = function () {
     })
   })
 
-  this.printToPDF = function (options, callback) {
-    const printingSetting = Object.assign({}, defaultPrintingSetting)
-    if (options.landscape) {
-      printingSetting.landscape = options.landscape
-    }
-    if (options.marginsType) {
-      printingSetting.marginsType = options.marginsType
-    }
-    if (options.printSelectionOnly) {
-      printingSetting.shouldPrintSelectionOnly = options.printSelectionOnly
-    }
-    if (options.printBackground) {
-      printingSetting.shouldPrintBackgrounds = options.printBackground
-    }
-
-    if (options.pageSize) {
-      const pageSize = options.pageSize
-      if (typeof pageSize === 'object') {
-        if (!pageSize.height || !pageSize.width) {
-          return callback(new Error('Must define height and width for pageSize'))
-        }
-        // Dimensions in Microns
-        // 1 meter = 10^6 microns
-        printingSetting.mediaSize = {
-          name: 'CUSTOM',
-          custom_display_name: 'Custom',
-          height_microns: pageSize.height,
-          width_microns: pageSize.width
-        }
-      } else if (PDFPageSizes[pageSize]) {
-        printingSetting.mediaSize = PDFPageSizes[pageSize]
-      } else {
-        return callback(new Error(`Does not support pageSize with ${pageSize}`))
-      }
-    } else {
-      printingSetting.mediaSize = PDFPageSizes['A4']
-    }
-
-    this._printToPDF(printingSetting, callback)
-  }
-
   app.emit('web-contents-created', {}, this)
 }
 
+// JavaScript wrapper of Debugger.
 const {Debugger} = process.atomBinding('debugger')
 
 Object.setPrototypeOf(Debugger.prototype, EventEmitter.prototype)