Why Gemfury? Push, build, and install  RubyGems npm packages Python packages Maven artifacts PHP packages Go Modules Debian packages RPM packages NuGet packages

Repository URL to install this package:

Details    
atom / usr / share / atom / resources / electron.asar
Size: Mime:
|xq{"files":{"browser":{"files":{"api":{"files":{"app.js":{"size":2287,"offset":"0"},"auto-updater":{"files":{"auto-updater-native.js":{"size":223,"offset":"2461"},"auto-updater-win.js":{"size":1684,"offset":"2684"},"squirrel-update-win.js":{"size":3432,"offset":"4368"}}},"auto-updater.js":{"size":174,"offset":"2287"},"browser-window.js":{"size":4887,"offset":"7800"},"content-tracing.js":{"size":56,"offset":"12687"},"dialog.js":{"size":6151,"offset":"12743"},"exports":{"files":{"electron.js":{"size":2260,"offset":"18894"}}},"global-shortcut.js":{"size":71,"offset":"21154"},"ipc-main.js":{"size":183,"offset":"21225"},"menu-item-roles.js":{"size":4188,"offset":"21408"},"menu-item.js":{"size":2690,"offset":"25596"},"menu.js":{"size":8866,"offset":"28286"},"navigation-controller.js":{"size":5401,"offset":"37152"},"power-monitor.js":{"size":217,"offset":"42553"},"power-save-blocker.js":{"size":76,"offset":"42770"},"protocol.js":{"size":775,"offset":"42846"},"screen.js":{"size":186,"offset":"43621"},"session.js":{"size":495,"offset":"43807"},"system-preferences.js":{"size":242,"offset":"44302"},"tray.js":{"size":170,"offset":"44544"},"web-contents.js":{"size":8162,"offset":"44714"}}},"chrome-extension.js":{"size":12144,"offset":"52876"},"desktop-capturer.js":{"size":2630,"offset":"65020"},"guest-view-manager.js":{"size":6775,"offset":"67650"},"guest-window-manager.js":{"size":6814,"offset":"74425"},"init.js":{"size":4811,"offset":"81239"},"objects-registry.js":{"size":2743,"offset":"86050"},"rpc-server.js":{"size":13077,"offset":"88793"}}},"common":{"files":{"api":{"files":{"callbacks-registry.js":{"size":1491,"offset":"101870"},"clipboard.js":{"size":251,"offset":"103361"},"crash-reporter.js":{"size":2636,"offset":"103612"},"deprecate.js":{"size":2733,"offset":"106248"},"deprecations.js":{"size":231,"offset":"108981"},"exports":{"files":{"electron.js":{"size":1234,"offset":"109212"}}},"is-promise.js":{"size":320,"offset":"110446"},"native-image.js":{"size":53,"offset":"110766"},"shell.js":{"size":46,"offset":"110819"}}},"init.js":{"size":2026,"offset":"110865"},"reset-search-paths.js":{"size":1285,"offset":"112891"}}},"renderer":{"files":{"api":{"files":{"desktop-capturer.js":{"size":1438,"offset":"114176"},"exports":{"files":{"electron.js":{"size":767,"offset":"115614"}}},"ipc-renderer.js":{"size":1050,"offset":"116381"},"remote.js":{"size":10470,"offset":"117431"},"screen.js":{"size":51,"offset":"127901"},"web-frame.js":{"size":329,"offset":"127952"}}},"chrome-api.js":{"size":5241,"offset":"128281"},"content-scripts-injector.js":{"size":2103,"offset":"133522"},"extensions":{"files":{"event.js":{"size":406,"offset":"135625"},"i18n.js":{"size":2520,"offset":"136031"},"storage.js":{"size":1575,"offset":"138551"},"web-navigation.js":{"size":508,"offset":"140126"}}},"init.js":{"size":4555,"offset":"140634"},"inspector.js":{"size":2736,"offset":"145189"},"override.js":{"size":6972,"offset":"147925"},"web-view":{"files":{"guest-view-internal.js":{"size":4031,"offset":"154897"},"web-view-attributes.js":{"size":10334,"offset":"158928"},"web-view-constants.js":{"size":1182,"offset":"169262"},"web-view.js":{"size":16665,"offset":"170444"}}}}}}}'use strict'

const bindings = process.atomBinding('app')
const {app, App} = bindings

// Only one app object permitted.
module.exports = app

const electron = require('electron')
const {deprecate, Menu} = electron
const {EventEmitter} = require('events')

Object.setPrototypeOf(App.prototype, EventEmitter.prototype)

let appPath = null

Object.assign(app, {
  getAppPath () { return appPath },
  setAppPath (path) { appPath = path },
  setApplicationMenu (menu) {
    return Menu.setApplicationMenu(menu)
  },
  getApplicationMenu () {
    return Menu.getApplicationMenu()
  },
  commandLine: {
    appendSwitch: bindings.appendSwitch,
    appendArgument: bindings.appendArgument
  }
})

if (process.platform === 'darwin') {
  app.dock = {
    bounce (type = 'informational') {
      return bindings.dockBounce(type)
    },
    cancelBounce: bindings.dockCancelBounce,
    downloadFinished: bindings.dockDownloadFinished,
    setBadge: bindings.dockSetBadgeText,
    getBadge: bindings.dockGetBadgeText,
    hide: bindings.dockHide,
    show: bindings.dockShow,
    isVisible: bindings.dockIsVisible,
    setMenu: bindings.dockSetMenu,
    setIcon: bindings.dockSetIcon
  }
}

if (process.platform === 'linux') {
  app.launcher = {
    setBadgeCount: bindings.unityLauncherSetBadgeCount,
    getBadgeCount: bindings.unityLauncherGetBadgeCount,
    isCounterBadgeAvailable: bindings.unityLauncherAvailable,
    isUnityRunning: bindings.unityLauncherAvailable
  }
}

app.allowNTLMCredentialsForAllDomains = function (allow) {
  if (!process.noDeprecations) {
    deprecate.warn('app.allowNTLMCredentialsForAllDomains', 'session.allowNTLMCredentialsForDomains')
  }
  let domains = allow ? '*' : ''
  if (!this.isReady()) {
    this.commandLine.appendSwitch('auth-server-whitelist', domains)
  } else {
    electron.session.defaultSession.allowNTLMCredentialsForDomains(domains)
  }
}

// Routes the events to webContents.
const events = ['login', 'certificate-error', 'select-client-certificate']
for (let name of events) {
  app.on(name, (event, webContents, ...args) => {
    webContents.emit(name, event, ...args)
  })
}

// Wrappers for native classes.
const {DownloadItem} = process.atomBinding('download_item')
Object.setPrototypeOf(DownloadItem.prototype, EventEmitter.prototype)
if (process.platform === 'win32') {
  module.exports = require('./auto-updater/auto-updater-win')
} else {
  module.exports = require('./auto-updater/auto-updater-native')
}
const EventEmitter = require('events').EventEmitter
const {autoUpdater, AutoUpdater} = process.atomBinding('auto_updater')

Object.setPrototypeOf(AutoUpdater.prototype, EventEmitter.prototype)

module.exports = autoUpdater
'use strict'

const {app} = require('electron')
const {EventEmitter} = require('events')
const squirrelUpdate = require('./squirrel-update-win')

class AutoUpdater extends EventEmitter {
  quitAndInstall () {
    if (!this.updateAvailable) {
      return this.emitError('No update available, can\'t quit and install')
    }
    squirrelUpdate.processStart()
    app.quit()
  }

  getFeedURL () {
    return this.updateURL
  }

  setFeedURL (updateURL, headers) {
    this.updateURL = updateURL
  }

  checkForUpdates () {
    if (!this.updateURL) {
      return this.emitError('Update URL is not set')
    }
    if (!squirrelUpdate.supported()) {
      return this.emitError('Can not find Squirrel')
    }
    this.emit('checking-for-update')
    squirrelUpdate.download(this.updateURL, (error, update) => {
      if (error != null) {
        return this.emitError(error)
      }
      if (update == null) {
        return this.emit('update-not-available')
      }
      this.updateAvailable = true
      this.emit('update-available')
      squirrelUpdate.update(this.updateURL, (error) => {
        if (error != null) {
          return this.emitError(error)
        }
        const {releaseNotes, version} = update
        // Date is not available on Windows, so fake it.
        const date = new Date()
        this.emit('update-downloaded', {}, releaseNotes, version, date, this.updateURL, () => {
          this.quitAndInstall()
        })
      })
    })
  }

  // Private: Emit both error object and message, this is to keep compatibility
  // with Old APIs.
  emitError (message) {
    this.emit('error', new Error(message), message)
  }
}

module.exports = new AutoUpdater()
const fs = require('fs')
const path = require('path')
const spawn = require('child_process').spawn

// i.e. my-app/app-0.1.13/
const appFolder = path.dirname(process.execPath)

// i.e. my-app/Update.exe
const updateExe = path.resolve(appFolder, '..', 'Update.exe')
const exeName = path.basename(process.execPath)
var spawnedArgs = []
var spawnedProcess

var isSameArgs = function (args) {
  return (args.length === spawnedArgs.length) && args.every(function (e, i) {
    return e === spawnedArgs[i]
  })
}

// Spawn a command and invoke the callback when it completes with an error
// and the output from standard out.
var spawnUpdate = function (args, detached, callback) {
  var error, errorEmitted, stderr, stdout

  try {
    // Ensure we don't spawn multiple squirrel processes
    // Process spawned, same args:        Attach events to alread running process
    // Process spawned, different args:   Return with error
    // No process spawned:                Spawn new process
    if (spawnedProcess && !isSameArgs(args)) {
      return callback('AutoUpdater process with arguments ' + args + ' is already running')
    } else if (!spawnedProcess) {
      spawnedProcess = spawn(updateExe, args, {
        detached: detached
      })
      spawnedArgs = args || []
    }
  } catch (error1) {
    error = error1

    // Shouldn't happen, but still guard it.
    process.nextTick(function () {
      return callback(error)
    })
    return
  }
  stdout = ''
  stderr = ''
  spawnedProcess.stdout.on('data', function (data) {
    stdout += data
  })
  spawnedProcess.stderr.on('data', function (data) {
    stderr += data
  })
  errorEmitted = false
  spawnedProcess.on('error', function (error) {
    errorEmitted = true
    callback(error)
  })
  return spawnedProcess.on('exit', function (code, signal) {
    spawnedProcess = undefined
    spawnedArgs = []

    // We may have already emitted an error.
    if (errorEmitted) {
      return
    }

    // Process terminated with error.
    if (code !== 0) {
      return callback('Command failed: ' + (signal != null ? signal : code) + '\n' + stderr)
    }

    // Success.
    callback(null, stdout)
  })
}

// Start an instance of the installed app.
exports.processStart = function () {
  return spawnUpdate(['--processStartAndWait', exeName], true, function () {})
}

// Download the releases specified by the URL and write new results to stdout.
exports.download = function (updateURL, callback) {
  return spawnUpdate(['--download', updateURL], false, function (error, stdout) {
    var json, ref, ref1, update
    if (error != null) {
      return callback(error)
    }
    try {
      // Last line of output is the JSON details about the releases
      json = stdout.trim().split('\n').pop()
      update = (ref = JSON.parse(json)) != null ? (ref1 = ref.releasesToApply) != null ? typeof ref1.pop === 'function' ? ref1.pop() : void 0 : void 0 : void 0
    } catch (jsonError) {
      return callback('Invalid result:\n' + stdout)
    }
    return callback(null, update)
  })
}

// Update the application to the latest remote version specified by URL.
exports.update = function (updateURL, callback) {
  return spawnUpdate(['--update', updateURL], false, callback)
}

// Is the Update.exe installed with the current application?
exports.supported = function () {
  try {
    fs.accessSync(updateExe, fs.R_OK)
    return true
  } catch (error) {
    return false
  }
}
'use strict'

const {ipcMain} = require('electron')
const {EventEmitter} = require('events')
const {BrowserWindow} = process.atomBinding('window')

Object.setPrototypeOf(BrowserWindow.prototype, EventEmitter.prototype)

BrowserWindow.prototype._init = function () {
  // Avoid recursive require.
  const {app} = require('electron')

  // Simulate the application menu on platforms other than macOS.
  if (process.platform !== 'darwin') {
    const menu = app.getApplicationMenu()
    if (menu) this.setMenu(menu)
  }

  // Make new windows requested by links behave like "window.open"
  this.webContents.on('-new-window', (event, url, frameName, disposition) => {
    const options = {
      show: true,
      width: 800,
      height: 600
    }
    ipcMain.emit('ELECTRON_GUEST_WINDOW_MANAGER_WINDOW_OPEN', event, url, frameName, disposition, options)
  })

  // window.resizeTo(...)
  // window.moveTo(...)
  this.webContents.on('move', (event, size) => {
    this.setBounds(size)
  })

  // Hide the auto-hide menu when webContents is focused.
  this.webContents.on('activate', () => {
    if (process.platform !== 'darwin' && this.isMenuBarAutoHide() && this.isMenuBarVisible()) {
      this.setMenuBarVisibility(false)
    }
  })

  // Change window title to page title.
  this.webContents.on('page-title-updated', (event, title) => {
    // The page-title-updated event is not emitted immediately (see #3645), so
    // when the callback is called the BrowserWindow might have been closed.
    if (this.isDestroyed()) return

    // Route the event to BrowserWindow.
    this.emit('page-title-updated', event, title)
    if (!event.defaultPrevented) this.setTitle(title)
  })

  // Sometimes the webContents doesn't get focus when window is shown, so we
  // have to force focusing on webContents in this case. The safest way is to
  // focus it when we first start to load URL, if we do it earlier it won't
  // have effect, if we do it later we might move focus in the page.
  //
  // Though this hack is only needed on macOS when the app is launched from
  // Finder, we still do it on all platforms in case of other bugs we don't
  // know.
  this.webContents.once('load-url', function () {
    this.focus()
  })

  // Redirect focus/blur event to app instance too.
  this.on('blur', (event) => {
    app.emit('browser-window-blur', event, this)
  })
  this.on('focus', (event) => {
    app.emit('browser-window-focus', event, this)
  })

  // Subscribe to visibilityState changes and pass to renderer process.
  let isVisible = this.isVisible() && !this.isMinimized()
  const visibilityChanged = () => {
    const newState = this.isVisible() && !this.isMinimized()
    if (isVisible !== newState) {
      isVisible = newState
      this.webContents.send('ELECTRON_RENDERER_WINDOW_VISIBILITY_CHANGE', isVisible ? 'visible' : 'hidden')
    }
  }

  const visibilityEvents = ['show', 'hide', 'minimize', 'maximize', 'restore']
  for (let event of visibilityEvents) {
    this.on(event, visibilityChanged)
  }

  // Notify the creation of the window.
  app.emit('browser-window-created', {}, this)

  Object.defineProperty(this, 'devToolsWebContents', {
    enumerable: true,
    configurable: false,
    get () {
      return this.webContents.devToolsWebContents
    }
  })
}

BrowserWindow.getFocusedWindow = () => {
  for (let window of BrowserWindow.getAllWindows()) {
    if (window.isFocused()) return window
  }
  return null
}

BrowserWindow.fromWebContents = (webContents) => {
  for (let window of BrowserWindow.getAllWindows()) {
    if (window.webContents.equal(webContents)) return window
  }
}

BrowserWindow.fromDevToolsWebContents = (webContents) => {
  for (let window of BrowserWindow.getAllWindows()) {
    if (window.devToolsWebContents.equal(webContents)) return window
  }
}

// Helpers.
Object.assign(BrowserWindow.prototype, {
  loadURL (...args) {
    return this.webContents.loadURL(...args)
  },
  getURL (...args) {
    return this.webContents.getURL()
  },
  reload (...args) {
    return this.webContents.reload(...args)
  },
  send (...args) {
    return this.webContents.send(...args)
  },
  openDevTools (...args) {
    return this.webContents.openDevTools(...args)
  },
  closeDevTools () {
    return this.webContents.closeDevTools()
  },
  isDevToolsOpened () {
    return this.webContents.isDevToolsOpened()
  },
  isDevToolsFocused () {
    return this.webContents.isDevToolsFocused()
  },
  toggleDevTools () {
    return this.webContents.toggleDevTools()
  },
  inspectElement (...args) {
    return this.webContents.inspectElement(...args)
  },
  inspectServiceWorker () {
    return this.webContents.inspectServiceWorker()
  },
  showDefinitionForSelection () {
    return this.webContents.showDefinitionForSelection()
  },
  capturePage (...args) {
    return this.webContents.capturePage(...args)
  }
})

module.exports = BrowserWindow
module.exports = process.atomBinding('content_tracing')
'use strict'

const {app, BrowserWindow} = require('electron')
const binding = process.atomBinding('dialog')
const v8Util = process.atomBinding('v8_util')

var includes = [].includes

const fileDialogProperties = {
  openFile: 1 << 0,
  openDirectory: 1 << 1,
  multiSelections: 1 << 2,
  createDirectory: 1 << 3,
  showHiddenFiles: 1 << 4
}

var messageBoxTypes = ['none', 'info', 'warning', 'error', 'question']

var messageBoxOptions = {
  noLink: 1 << 0
}

var parseArgs = function (window, options, callback, ...args) {
  if (window !== null && window.constructor !== BrowserWindow) {
    // Shift.
    [callback, options, window] = [options, window, null]
  }

  if ((callback == null) && typeof options === 'function') {
    // Shift.
    [callback, options] = [options, null]
  }

  // Fallback to using very last argument as the callback function
  var lastArgument = args[args.length - 1]
  if ((callback == null) && typeof lastArgument === 'function') {
    callback = lastArgument
  }

  return [window, options, callback]
}

var checkAppInitialized = function () {
  if (!app.isReady()) {
    throw new Error('dialog module can only be used after app is ready')
  }
}

module.exports = {
  showOpenDialog: function (...args) {
    var prop, properties, value, wrappedCallback
    checkAppInitialized()
    let [window, options, callback] = parseArgs(...args)
    if (options == null) {
      options = {
        title: 'Open',
        properties: ['openFile']
      }
    }
    if (options.properties == null) {
      options.properties = ['openFile']
    }
    if (!Array.isArray(options.properties)) {
      throw new TypeError('Properties must be an array')
    }
    properties = 0
    for (prop in fileDialogProperties) {
      value = fileDialogProperties[prop]
      if (includes.call(options.properties, prop)) {
        properties |= value
      }
    }
    if (options.title == null) {
      options.title = ''
    } else if (typeof options.title !== 'string') {
      throw new TypeError('Title must be a string')
    }
    if (options.buttonLabel == null) {
      options.buttonLabel = ''
    } else if (typeof options.buttonLabel !== 'string') {
      throw new TypeError('buttonLabel must be a string')
    }
    if (options.defaultPath == null) {
      options.defaultPath = ''
    } else if (typeof options.defaultPath !== 'string') {
      throw new TypeError('Default path must be a string')
    }
    if (options.filters == null) {
      options.filters = []
    }
    wrappedCallback = typeof callback === 'function' ? function (success, result) {
      return callback(success ? result : void 0)
    } : null
    return binding.showOpenDialog(options.title, options.buttonLabel, options.defaultPath, options.filters, properties, window, wrappedCallback)
  },

  showSaveDialog: function (...args) {
    var wrappedCallback
    checkAppInitialized()
    let [window, options, callback] = parseArgs(...args)
    if (options == null) {
      options = {
        title: 'Save'
      }
    }
    if (options.title == null) {
      options.title = ''
    } else if (typeof options.title !== 'string') {
      throw new TypeError('Title must be a string')
    }
    if (options.buttonLabel == null) {
      options.buttonLabel = ''
    } else if (typeof options.buttonLabel !== 'string') {
      throw new TypeError('buttonLabel must be a string')
    }
    if (options.defaultPath == null) {
      options.defaultPath = ''
    } else if (typeof options.defaultPath !== 'string') {
      throw new TypeError('Default path must be a string')
    }
    if (options.filters == null) {
      options.filters = []
    }
    wrappedCallback = typeof callback === 'function' ? function (success, result) {
      return callback(success ? result : void 0)
    } : null
    return binding.showSaveDialog(options.title, options.buttonLabel, options.defaultPath, options.filters, window, wrappedCallback)
  },

  showMessageBox: function (...args) {
    var flags, i, j, len, messageBoxType, ref2, ref3, text
    checkAppInitialized()
    let [window, options, callback] = parseArgs(...args)
    if (options == null) {
      options = {
        type: 'none'
      }
    }
    if (options.type == null) {
      options.type = 'none'
    }
    messageBoxType = messageBoxTypes.indexOf(options.type)
    if (!(messageBoxType > -1)) {
      throw new TypeError('Invalid message box type')
    }
    if (!Array.isArray(options.buttons)) {
      throw new TypeError('Buttons must be an array')
    }
    if (options.title == null) {
      options.title = ''
    } else if (typeof options.title !== 'string') {
      throw new TypeError('Title must be a string')
    }
    if (options.message == null) {
      options.message = ''
    } else if (typeof options.message !== 'string') {
      throw new TypeError('Message must be a string')
    }
    if (options.detail == null) {
      options.detail = ''
    } else if (typeof options.detail !== 'string') {
      throw new TypeError('Detail must be a string')
    }
    if (options.icon == null) {
      options.icon = null
    }
    if (options.defaultId == null) {
      options.defaultId = -1
    }

    // Choose a default button to get selected when dialog is cancelled.
    if (options.cancelId == null) {
      options.cancelId = 0
      ref2 = options.buttons
      for (i = j = 0, len = ref2.length; j < len; i = ++j) {
        text = ref2[i]
        if ((ref3 = text.toLowerCase()) === 'cancel' || ref3 === 'no') {
          options.cancelId = i
          break
        }
      }
    }
    flags = options.noLink ? messageBoxOptions.noLink : 0
    return binding.showMessageBox(messageBoxType, options.buttons, options.defaultId, options.cancelId, flags, options.title, options.message, options.detail, options.icon, window, callback)
  },

  showErrorBox: function (...args) {
    return binding.showErrorBox(...args)
  }
}

// Mark standard asynchronous functions.
var ref1 = ['showMessageBox', 'showOpenDialog', 'showSaveDialog']
var j, len, api
for (j = 0, len = ref1.length; j < len; j++) {
  api = ref1[j]
  v8Util.setHiddenValue(module.exports[api], 'asynchronous', true)
}
const common = require('../../../common/api/exports/electron')

// Import common modules.
common.defineProperties(exports)

Object.defineProperties(exports, {
  // Browser side modules, please sort with alphabet order.
  app: {
    enumerable: true,
    get: function () {
      return require('../app')
    }
  },
  autoUpdater: {
    enumerable: true,
    get: function () {
      return require('../auto-updater')
    }
  },
  BrowserWindow: {
    enumerable: true,
    get: function () {
      return require('../browser-window')
    }
  },
  contentTracing: {
    enumerable: true,
    get: function () {
      return require('../content-tracing')
    }
  },
  dialog: {
    enumerable: true,
    get: function () {
      return require('../dialog')
    }
  },
  ipcMain: {
    enumerable: true,
    get: function () {
      return require('../ipc-main')
    }
  },
  globalShortcut: {
    enumerable: true,
    get: function () {
      return require('../global-shortcut')
    }
  },
  Menu: {
    enumerable: true,
    get: function () {
      return require('../menu')
    }
  },
  MenuItem: {
    enumerable: true,
    get: function () {
      return require('../menu-item')
    }
  },
  powerMonitor: {
    enumerable: true,
    get: function () {
      return require('../power-monitor')
    }
  },
  powerSaveBlocker: {
    enumerable: true,
    get: function () {
      return require('../power-save-blocker')
    }
  },
  protocol: {
    enumerable: true,
    get: function () {
      return require('../protocol')
    }
  },
  screen: {
    enumerable: true,
    get: function () {
      return require('../screen')
    }
  },
  session: {
    enumerable: true,
    get: function () {
      return require('../session')
    }
  },
  systemPreferences: {
    enumerable: true,
    get: function () {
      return require('../system-preferences')
    }
  },
  Tray: {
    enumerable: true,
    get: function () {
      return require('../tray')
    }
  },
  webContents: {
    enumerable: true,
    get: function () {
      return require('../web-contents')
    }
  },

  // The internal modules, invisible unless you know their names.
  NavigationController: {
    get: function () {
      return require('../navigation-controller')
    }
  }
})
module.exports = process.atomBinding('global_shortcut').globalShortcut
const EventEmitter = require('events').EventEmitter

module.exports = new EventEmitter()

// Do not throw exception when channel name is "error".
module.exports.on('error', () => {})
const {app} = require('electron')

const roles = {
  about: {
    get label () {
      return process.platform === 'linux' ? 'About' : `About ${app.getName()}`
    }
  },
  close: {
    label: process.platform === 'darwin' ? 'Close Window' : 'Close',
    accelerator: 'CommandOrControl+W',
    windowMethod: 'close'
  },
  copy: {
    label: 'Copy',
    accelerator: 'CommandOrControl+C',
    webContentsMethod: 'copy'
  },
  cut: {
    label: 'Cut',
    accelerator: 'CommandOrControl+X',
    webContentsMethod: 'cut'
  },
  delete: {
    label: 'Delete',
    webContentsMethod: 'delete'
  },
  front: {
    label: 'Bring All to Front'
  },
  help: {
    label: 'Help'
  },
  hide: {
    get label () {
      return `Hide ${app.getName()}`
    },
    accelerator: 'Command+H'
  },
  hideothers: {
    label: 'Hide Others',
    accelerator: 'Command+Alt+H'
  },
  minimize: {
    label: 'Minimize',
    accelerator: 'CommandOrControl+M',
    windowMethod: 'minimize'
  },
  paste: {
    label: 'Paste',
    accelerator: 'CommandOrControl+V',
    webContentsMethod: 'paste'
  },
  pasteandmatchstyle: {
    label: 'Paste and Match Style',
    accelerator: 'Shift+CommandOrControl+V',
    webContentsMethod: 'pasteAndMatchStyle'
  },
  quit: {
    get label () {
      switch (process.platform) {
        case 'darwin': return `Quit ${app.getName()}`
        case 'win32': return 'Exit'
        default: return 'Quit'
      }
    },
    accelerator: process.platform === 'win32' ? null : 'CommandOrControl+Q',
    appMethod: 'quit'
  },
  redo: {
    label: 'Redo',
    accelerator: 'Shift+CommandOrControl+Z',
    webContentsMethod: 'redo'
  },
  resetzoom: {
    label: 'Actual Size',
    accelerator: 'CommandOrControl+0',
    webContentsMethod: (webContents) => {
      webContents.setZoomLevel(0)
    }
  },
  selectall: {
    label: 'Select All',
    accelerator: 'CommandOrControl+A',
    webContentsMethod: 'selectAll'
  },
  services: {
    label: 'Services'
  },
  startspeaking: {
    label: 'Start Speaking'
  },
  stopspeaking: {
    label: 'Stop Speaking'
  },
  togglefullscreen: {
    label: 'Toggle Full Screen',
    accelerator: process.platform === 'darwin' ? 'Control+Command+F' : 'F11',
    windowMethod: (window) => {
      window.setFullScreen(!window.isFullScreen())
    }
  },
  undo: {
    label: 'Undo',
    accelerator: 'CommandOrControl+Z',
    webContentsMethod: 'undo'
  },
  unhide: {
    label: 'Show All'
  },
  window: {
    label: 'Window'
  },
  zoom: {
    label: 'Zoom'
  },
  zoomin: {
    label: 'Zoom In',
    accelerator: 'CommandOrControl+Plus',
    webContentsMethod: (webContents) => {
      webContents.getZoomLevel((zoomLevel) => {
        webContents.setZoomLevel(zoomLevel + 0.5)
      })
    }
  },
  zoomout: {
    label: 'Zoom Out',
    accelerator: 'CommandOrControl+-',
    webContentsMethod: (webContents) => {
      webContents.getZoomLevel((zoomLevel) => {
        webContents.setZoomLevel(zoomLevel - 0.5)
      })
    }
  }
}

const canExecuteRole = (role) => {
  if (!roles.hasOwnProperty(role)) return false
  if (process.platform !== 'darwin') return true
  // macOS handles all roles natively except the ones listed below
  return ['zoomin', 'zoomout', 'resetzoom'].includes(role)
}

exports.getDefaultLabel = (role) => {
  if (roles.hasOwnProperty(role)) {
    return roles[role].label
  } else {
    return ''
  }
}

exports.getDefaultAccelerator = (role) => {
  if (roles.hasOwnProperty(role)) return roles[role].accelerator
}

exports.execute = (role, focusedWindow, focusedWebContents) => {
  if (!canExecuteRole(role)) return false

  const {appMethod, webContentsMethod, windowMethod} = roles[role]

  if (appMethod) {
    app[appMethod]()
    return true
  }

  if (windowMethod && focusedWindow != null) {
    if (typeof windowMethod === 'function') {
      windowMethod(focusedWindow)
    } else {
      focusedWindow[windowMethod]()
    }
    return true
  }

  if (webContentsMethod && focusedWebContents != null) {
    if (typeof webContentsMethod === 'function') {
      webContentsMethod(focusedWebContents)
    } else {
      focusedWebContents[webContentsMethod]()
    }
    return true
  }

  return false
}
'use strict'

const roles = require('./menu-item-roles')

let nextCommandId = 0

const MenuItem = function (options) {
  const {Menu} = require('electron')

  this.selector = options.selector
  this.type = options.type
  this.role = options.role
  this.label = options.label
  this.sublabel = options.sublabel
  this.accelerator = options.accelerator
  this.icon = options.icon
  this.enabled = options.enabled
  this.visible = options.visible
  this.checked = options.checked

  this.submenu = options.submenu
  if (this.submenu != null && this.submenu.constructor !== Menu) {
    this.submenu = Menu.buildFromTemplate(this.submenu)
  }
  if (this.type == null && this.submenu != null) {
    this.type = 'submenu'
  }
  if (this.type === 'submenu' && (this.submenu == null || this.submenu.constructor !== Menu)) {
    throw new Error('Invalid submenu')
  }

  this.overrideReadOnlyProperty('type', 'normal')
  this.overrideReadOnlyProperty('role')
  this.overrideReadOnlyProperty('accelerator')
  this.overrideReadOnlyProperty('icon')
  this.overrideReadOnlyProperty('submenu')

  this.overrideProperty('label', roles.getDefaultLabel(this.role))
  this.overrideProperty('sublabel', '')
  this.overrideProperty('enabled', true)
  this.overrideProperty('visible', true)
  this.overrideProperty('checked', false)

  if (!MenuItem.types.includes(this.type)) {
    throw new Error(`Unknown menu item type: ${this.type}`)
  }

  this.overrideReadOnlyProperty('commandId', ++nextCommandId)

  const click = options.click
  this.click = (event, focusedWindow, focusedWebContents) => {
    // Manually flip the checked flags when clicked.
    if (this.type === 'checkbox' || this.type === 'radio') {
      this.checked = !this.checked
    }

    if (!roles.execute(this.role, focusedWindow, focusedWebContents)) {
      if (typeof click === 'function') {
        click(this, focusedWindow, event)
      } else if (typeof this.selector === 'string' && process.platform === 'darwin') {
        Menu.sendActionToFirstResponder(this.selector)
      }
    }
  }
}

MenuItem.types = ['normal', 'separator', 'submenu', 'checkbox', 'radio']

MenuItem.prototype.getDefaultRoleAccelerator = function () {
  return roles.getDefaultAccelerator(this.role)
}

MenuItem.prototype.overrideProperty = function (name, defaultValue) {
  if (defaultValue == null) {
    defaultValue = null
  }
  if (this[name] == null) {
    this[name] = defaultValue
  }
}

MenuItem.prototype.overrideReadOnlyProperty = function (name, defaultValue) {
  this.overrideProperty(name, defaultValue)
  Object.defineProperty(this, name, {
    enumerable: true,
    writable: false,
    value: this[name]
  })
}

module.exports = MenuItem
'use strict'

const {BrowserWindow, MenuItem, webContents} = require('electron')
const EventEmitter = require('events').EventEmitter
const v8Util = process.atomBinding('v8_util')
const bindings = process.atomBinding('menu')

// Automatically generated radio menu item's group id.
var nextGroupId = 0

// Search between separators to find a radio menu item and return its group id,
// otherwise generate a group id.
var generateGroupId = function (items, pos) {
  var i, item, j, k, ref1, ref2, ref3
  if (pos > 0) {
    for (i = j = ref1 = pos - 1; ref1 <= 0 ? j <= 0 : j >= 0; i = ref1 <= 0 ? ++j : --j) {
      item = items[i]
      if (item.type === 'radio') {
        return item.groupId
      }
      if (item.type === 'separator') {
        break
      }
    }
  } else if (pos < items.length) {
    for (i = k = ref2 = pos, ref3 = items.length - 1; ref2 <= ref3 ? k <= ref3 : k >= ref3; i = ref2 <= ref3 ? ++k : --k) {
      item = items[i]
      if (item.type === 'radio') {
        return item.groupId
      }
      if (item.type === 'separator') {
        break
      }
    }
  }
  return ++nextGroupId
}

// Returns the index of item according to |id|.
var indexOfItemById = function (items, id) {
  var i, item, j, len
  for (i = j = 0, len = items.length; j < len; i = ++j) {
    item = items[i]
    if (item.id === id) {
      return i
    }
  }
  return -1
}

// Returns the index of where to insert the item according to |position|.
var indexToInsertByPosition = function (items, position) {
  var insertIndex
  if (!position) {
    return items.length
  }
  const [query, id] = position.split('=')
  insertIndex = indexOfItemById(items, id)
  if (insertIndex === -1 && query !== 'endof') {
    console.warn("Item with id '" + id + "' is not found")
    return items.length
  }
  switch (query) {
    case 'after':
      insertIndex++
      break
    case 'endof':

      // If the |id| doesn't exist, then create a new group with the |id|.
      if (insertIndex === -1) {
        items.push({
          id: id,
          type: 'separator'
        })
        insertIndex = items.length - 1
      }

      // Find the end of the group.
      insertIndex++
      while (insertIndex < items.length && items[insertIndex].type !== 'separator') {
        insertIndex++
      }
  }
  return insertIndex
}

const Menu = bindings.Menu

Object.setPrototypeOf(Menu.prototype, EventEmitter.prototype)

Menu.prototype._init = function () {
  this.commandsMap = {}
  this.groupsMap = {}
  this.items = []
  this.delegate = {
    isCommandIdChecked: (commandId) => {
      var command = this.commandsMap[commandId]
      return command != null ? command.checked : undefined
    },
    isCommandIdEnabled: (commandId) => {
      var command = this.commandsMap[commandId]
      return command != null ? command.enabled : undefined
    },
    isCommandIdVisible: (commandId) => {
      var command = this.commandsMap[commandId]
      return command != null ? command.visible : undefined
    },
    getAcceleratorForCommandId: (commandId, useDefaultAccelerator) => {
      const command = this.commandsMap[commandId]
      if (command == null) return
      if (command.accelerator != null) return command.accelerator
      if (useDefaultAccelerator) return command.getDefaultRoleAccelerator()
    },
    getIconForCommandId: (commandId) => {
      var command = this.commandsMap[commandId]
      return command != null ? command.icon : undefined
    },
    executeCommand: (event, commandId) => {
      const command = this.commandsMap[commandId]
      if (command == null) return
      command.click(event, BrowserWindow.getFocusedWindow(), webContents.getFocusedWebContents())
    },
    menuWillShow: () => {
      // Make sure radio groups have at least one menu item seleted.
      var checked, group, id, j, len, radioItem, ref1
      ref1 = this.groupsMap
      for (id in ref1) {
        group = ref1[id]
        checked = false
        for (j = 0, len = group.length; j < len; j++) {
          radioItem = group[j]
          if (!radioItem.checked) {
            continue
          }
          checked = true
          break
        }
        if (!checked) {
          v8Util.setHiddenValue(group[0], 'checked', true)
        }
      }
    }
  }
}

Menu.prototype.popup = function (window, x, y, positioningItem) {
  if (typeof window !== 'object' || window.constructor !== BrowserWindow) {
    // Shift.
    positioningItem = y
    y = x
    x = window
    window = BrowserWindow.getFocusedWindow()
  }

  // Default to showing under mouse location.
  if (typeof x !== 'number') x = -1
  if (typeof y !== 'number') y = -1

  // Default to not highlighting any item.
  if (typeof positioningItem !== 'number') positioningItem = -1

  this.popupAt(window, x, y, positioningItem)
}

Menu.prototype.append = function (item) {
  return this.insert(this.getItemCount(), item)
}

Menu.prototype.insert = function (pos, item) {
  var base, name
  if ((item != null ? item.constructor : void 0) !== MenuItem) {
    throw new TypeError('Invalid item')
  }
  switch (item.type) {
    case 'normal':
      this.insertItem(pos, item.commandId, item.label)
      break
    case 'checkbox':
      this.insertCheckItem(pos, item.commandId, item.label)
      break
    case 'separator':
      this.insertSeparator(pos)
      break
    case 'submenu':
      this.insertSubMenu(pos, item.commandId, item.label, item.submenu)
      break
    case 'radio':
      // Grouping radio menu items.
      item.overrideReadOnlyProperty('groupId', generateGroupId(this.items, pos))
      if ((base = this.groupsMap)[name = item.groupId] == null) {
        base[name] = []
      }
      this.groupsMap[item.groupId].push(item)

      // Setting a radio menu item should flip other items in the group.
      v8Util.setHiddenValue(item, 'checked', item.checked)
      Object.defineProperty(item, 'checked', {
        enumerable: true,
        get: function () {
          return v8Util.getHiddenValue(item, 'checked')
        },
        set: () => {
          var j, len, otherItem, ref1
          ref1 = this.groupsMap[item.groupId]
          for (j = 0, len = ref1.length; j < len; j++) {
            otherItem = ref1[j]
            if (otherItem !== item) {
              v8Util.setHiddenValue(otherItem, 'checked', false)
            }
          }
          return v8Util.setHiddenValue(item, 'checked', true)
        }
      })
      this.insertRadioItem(pos, item.commandId, item.label, item.groupId)
  }
  if (item.sublabel != null) {
    this.setSublabel(pos, item.sublabel)
  }
  if (item.icon != null) {
    this.setIcon(pos, item.icon)
  }
  if (item.role != null) {
    this.setRole(pos, item.role)
  }

  // Make menu accessable to items.
  item.overrideReadOnlyProperty('menu', this)

  // Remember the items.
  this.items.splice(pos, 0, item)
  this.commandsMap[item.commandId] = item
}

// Force menuWillShow to be called
Menu.prototype._callMenuWillShow = function () {
  if (this.delegate != null) {
    this.delegate.menuWillShow()
  }
  this.items.forEach(function (item) {
    if (item.submenu != null) {
      item.submenu._callMenuWillShow()
    }
  })
}

var applicationMenu = null

Menu.setApplicationMenu = function (menu) {
  if (!(menu === null || menu.constructor === Menu)) {
    throw new TypeError('Invalid menu')
  }

  // Keep a reference.
  applicationMenu = menu
  if (process.platform === 'darwin') {
    if (menu === null) {
      return
    }
    menu._callMenuWillShow()
    bindings.setApplicationMenu(menu)
  } else {
    BrowserWindow.getAllWindows().forEach(function (window) {
      window.setMenu(menu)
    })
  }
}

Menu.getApplicationMenu = function () {
  return applicationMenu
}

Menu.sendActionToFirstResponder = bindings.sendActionToFirstResponder

Menu.buildFromTemplate = function (template) {
  var insertIndex, item, j, k, key, len, len1, menu, menuItem, positionedTemplate
  if (!Array.isArray(template)) {
    throw new TypeError('Invalid template for Menu')
  }
  positionedTemplate = []
  insertIndex = 0
  for (j = 0, len = template.length; j < len; j++) {
    item = template[j]
    if (item.position) {
      insertIndex = indexToInsertByPosition(positionedTemplate, item.position)
    } else {
      // If no |position| is specified, insert after last item.
      insertIndex++
    }
    positionedTemplate.splice(insertIndex, 0, item)
  }
  menu = new Menu()
  for (k = 0, len1 = positionedTemplate.length; k < len1; k++) {
    item = positionedTemplate[k]
    if (typeof item !== 'object') {
      throw new TypeError('Invalid template for MenuItem')
    }
    menuItem = new MenuItem(item)
    for (key in item) {
      // Preserve extra fields specified by user
      if (!menuItem.hasOwnProperty(key)) {
        menuItem[key] = item[key]
      }
    }
    menu.append(menuItem)
  }
  return menu
}

module.exports = Menu
'use strict'

const {ipcMain} = require('electron')

// The history operation in renderer is redirected to browser.
ipcMain.on('ELECTRON_NAVIGATION_CONTROLLER', function (event, method, ...args) {
  event.sender[method](...args)
})

ipcMain.on('ELECTRON_SYNC_NAVIGATION_CONTROLLER', function (event, method, ...args) {
  event.returnValue = event.sender[method](...args)
})

// JavaScript implementation of Chromium's NavigationController.
// Instead of relying on Chromium for history control, we compeletely do history
// control on user land, and only rely on WebContents.loadURL for navigation.
// This helps us avoid Chromium's various optimizations so we can ensure renderer
// process is restarted everytime.
var NavigationController = (function () {
  function NavigationController (webContents) {
    this.webContents = webContents
    this.clearHistory()

    // webContents may have already navigated to a page.
    if (this.webContents._getURL()) {
      this.currentIndex++
      this.history.push(this.webContents._getURL())
    }
    this.webContents.on('navigation-entry-commited', (event, url, inPage, replaceEntry) => {
      var currentEntry
      if (this.inPageIndex > -1 && !inPage) {
        // Navigated to a new page, clear in-page mark.
        this.inPageIndex = -1
      } else if (this.inPageIndex === -1 && inPage && !replaceEntry) {
        // Started in-page navigations.
        this.inPageIndex = this.currentIndex
      }
      if (this.pendingIndex >= 0) {
        // Go to index.
        this.currentIndex = this.pendingIndex
        this.pendingIndex = -1
        this.history[this.currentIndex] = url
      } else if (replaceEntry) {
        // Non-user initialized navigation.
        this.history[this.currentIndex] = url
      } else {
        // Normal navigation. Clear history.
        this.history = this.history.slice(0, this.currentIndex + 1)
        currentEntry = this.history[this.currentIndex]

        if (currentEntry !== url) {
          this.currentIndex++
          this.history.push(url)
        }
      }
    })
  }

  NavigationController.prototype.loadURL = function (url, options) {
    if (options == null) {
      options = {}
    }
    this.pendingIndex = -1
    this.webContents._loadURL(url, options)
    return this.webContents.emit('load-url', url, options)
  }

  NavigationController.prototype.getURL = function () {
    if (this.currentIndex === -1) {
      return ''
    } else {
      return this.history[this.currentIndex]
    }
  }

  NavigationController.prototype.stop = function () {
    this.pendingIndex = -1
    return this.webContents._stop()
  }

  NavigationController.prototype.reload = function () {
    this.pendingIndex = this.currentIndex
    return this.webContents._loadURL(this.getURL(), {})
  }

  NavigationController.prototype.reloadIgnoringCache = function () {
    this.pendingIndex = this.currentIndex
    return this.webContents._loadURL(this.getURL(), {
      extraHeaders: 'pragma: no-cache\n'
    })
  }

  NavigationController.prototype.canGoBack = function () {
    return this.getActiveIndex() > 0
  }

  NavigationController.prototype.canGoForward = function () {
    return this.getActiveIndex() < this.history.length - 1
  }

  NavigationController.prototype.canGoToIndex = function (index) {
    return index >= 0 && index < this.history.length
  }

  NavigationController.prototype.canGoToOffset = function (offset) {
    return this.canGoToIndex(this.currentIndex + offset)
  }

  NavigationController.prototype.clearHistory = function () {
    this.history = []
    this.currentIndex = -1
    this.pendingIndex = -1
    this.inPageIndex = -1
  }

  NavigationController.prototype.goBack = function () {
    if (!this.canGoBack()) {
      return
    }
    this.pendingIndex = this.getActiveIndex() - 1
    if (this.inPageIndex > -1 && this.pendingIndex >= this.inPageIndex) {
      return this.webContents._goBack()
    } else {
      return this.webContents._loadURL(this.history[this.pendingIndex], {})
    }
  }

  NavigationController.prototype.goForward = function () {
    if (!this.canGoForward()) {
      return
    }
    this.pendingIndex = this.getActiveIndex() + 1
    if (this.inPageIndex > -1 && this.pendingIndex >= this.inPageIndex) {
      return this.webContents._goForward()
    } else {
      return this.webContents._loadURL(this.history[this.pendingIndex], {})
    }
  }

  NavigationController.prototype.goToIndex = function (index) {
    if (!this.canGoToIndex(index)) {
      return
    }
    this.pendingIndex = index
    return this.webContents._loadURL(this.history[this.pendingIndex], {})
  }

  NavigationController.prototype.goToOffset = function (offset) {
    var pendingIndex
    if (!this.canGoToOffset(offset)) {
      return
    }
    pendingIndex = this.currentIndex + offset
    if (this.inPageIndex > -1 && pendingIndex >= this.inPageIndex) {
      this.pendingIndex = pendingIndex
      return this.webContents._goToOffset(offset)
    } else {
      return this.goToIndex(pendingIndex)
    }
  }

  NavigationController.prototype.getActiveIndex = function () {
    if (this.pendingIndex === -1) {
      return this.currentIndex
    } else {
      return this.pendingIndex
    }
  }

  NavigationController.prototype.length = function () {
    return this.history.length
  }

  return NavigationController
})()

module.exports = NavigationController
const {EventEmitter} = require('events')
const {powerMonitor, PowerMonitor} = process.atomBinding('power_monitor')

Object.setPrototypeOf(PowerMonitor.prototype, EventEmitter.prototype)

module.exports = powerMonitor
module.exports = process.atomBinding('power_save_blocker').powerSaveBlocker
const {app, session} = require('electron')

// Global protocol APIs.
module.exports = process.atomBinding('protocol')

// Fallback protocol APIs of default session.
Object.setPrototypeOf(module.exports, new Proxy({}, {
  get (target, property) {
    if (!app.isReady()) return

    const protocol = session.defaultSession.protocol
    if (!Object.getPrototypeOf(protocol).hasOwnProperty(property)) return

    // Returning a native function directly would throw error.
    return (...args) => protocol[property](...args)
  },

  ownKeys () {
    if (!app.isReady()) return []

    return Object.getOwnPropertyNames(Object.getPrototypeOf(session.defaultSession.protocol))
  },

  getOwnPropertyDescriptor (target) {
    return { configurable: true, enumerable: true }
  }
}))
const {EventEmitter} = require('events')
const {screen, Screen} = process.atomBinding('screen')

Object.setPrototypeOf(Screen.prototype, EventEmitter.prototype)

module.exports = screen
const {EventEmitter} = require('events')
const {app} = require('electron')
const {fromPartition, Session} = process.atomBinding('session')

// Public API.
Object.defineProperties(exports, {
  defaultSession: {
    enumerable: true,
    get () { return fromPartition('') }
  },
  fromPartition: {
    enumerable: true,
    value: fromPartition
  }
})

Object.setPrototypeOf(Session.prototype, EventEmitter.prototype)

Session.prototype._init = function () {
  app.emit('session-created', this)
}
const {EventEmitter} = require('events')
const {systemPreferences, SystemPreferences} = process.atomBinding('system_preferences')

Object.setPrototypeOf(SystemPreferences.prototype, EventEmitter.prototype)

module.exports = systemPreferences
const {EventEmitter} = require('events')
const {Tray} = process.atomBinding('tray')

Object.setPrototypeOf(Tray.prototype, EventEmitter.prototype)

module.exports = Tray
'use strict'

const {EventEmitter} = require('events')
const electron = require('electron')
const {app, ipcMain, session, NavigationController} = electron

// session is not used here, the purpose is to make sure session is initalized
// before the webContents module.
session

let nextId = 0
const getNextId = function () {
  return ++nextId
}

// Stock page sizes
const PDFPageSizes = {
  A5: {
    custom_display_name: 'A5',
    height_microns: 210000,
    name: 'ISO_A5',
    width_microns: 148000
  },
  A4: {
    custom_display_name: 'A4',
    height_microns: 297000,
    name: 'ISO_A4',
    is_default: 'true',
    width_microns: 210000
  },
  A3: {
    custom_display_name: 'A3',
    height_microns: 420000,
    name: 'ISO_A3',
    width_microns: 297000
  },
  Legal: {
    custom_display_name: 'Legal',
    height_microns: 355600,
    name: 'NA_LEGAL',
    width_microns: 215900
  },
  Letter: {
    custom_display_name: 'Letter',
    height_microns: 279400,
    name: 'NA_LETTER',
    width_microns: 215900
  },
  Tabloid: {
    height_microns: 431800,
    name: 'NA_LEDGER',
    width_microns: 279400,
    custom_display_name: 'Tabloid'
  }
}

// Default printing setting
const defaultPrintingSetting = {
  pageRage: [],
  mediaSize: {},
  landscape: false,
  color: 2,
  headerFooterEnabled: false,
  marginsType: 0,
  isFirstRequest: false,
  requestID: getNextId(),
  previewModifiable: true,
  printToPDF: true,
  printWithCloudPrint: false,
  printWithPrivet: false,
  printWithExtension: false,
  deviceName: 'Save as PDF',
  generateDraftData: true,
  fitToPageEnabled: false,
  duplex: 0,
  copies: 1,
  collate: true,
  shouldPrintBackgrounds: false,
  shouldPrintSelectionOnly: false
}

// JavaScript implementations of WebContents.
const binding = process.atomBinding('web_contents')
const {WebContents} = binding

Object.setPrototypeOf(NavigationController.prototype, EventEmitter.prototype)
Object.setPrototypeOf(WebContents.prototype, NavigationController.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',
  'setLayoutZoomLevelLimits',
  'setVisualZoomLevelLimits',
  'setZoomFactor',
  'setZoomLevel',
  // TODO(kevinsawicki): Remove in 2.0, deprecate before then with warnings
  'setZoomLevelLimits'
]
const webFrameMethodsWithResult = [
  'getZoomFactor',
  'getZoomLevel'
]

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)
  })
}

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)
  })
}

for (const method of webFrameMethods) {
  WebContents.prototype[method] = function (...args) {
    this.send('ELECTRON_INTERNAL_RENDERER_WEB_FRAME_METHOD', method, args)
  }
}

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)
  }
}

// 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)
    })
  }
}

// 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
  }

  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)
}

// Add JavaScript wrappers for WebContents class.
WebContents.prototype._init = function () {
  // The navigation controller.
  NavigationController.call(this, this)

  // Every remote callback from renderer process would add a listenter to the
  // render-view-deleted event, so ignore the listenters warning.
  this.setMaxListeners(0)

  // 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) {
    // Access Menu via electron.Menu to prevent circular require
    const menu = electron.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('page-title-updated', ...args)
    })
  })

  app.emit('web-contents-created', {}, this)
}

// JavaScript wrapper of Debugger.
const {Debugger} = process.atomBinding('debugger')

Object.setPrototypeOf(Debugger.prototype, EventEmitter.prototype)

// Public APIs.
module.exports = {
  create (options = {}) {
    return binding.create(options)
  },

  fromId (id) {
    return binding.fromId(id)
  },

  getFocusedWebContents () {
    let focused = null
    for (let contents of binding.getAllWebContents()) {
      if (!contents.isFocused()) continue
      if (focused == null) focused = contents
      // Return webview web contents which may be embedded inside another
      // web contents that is also reporting as focused
      if (contents.getType() === 'webview') return contents
    }
    return focused
  },

  getAllWebContents () {
    return binding.getAllWebContents()
  }
}
const {app, ipcMain, webContents, BrowserWindow} = require('electron')
const {getAllWebContents} = process.atomBinding('web_contents')
const renderProcessPreferences = process.atomBinding('render_process_preferences').forAllWebContents()

const {Buffer} = require('buffer')
const fs = require('fs')
const path = require('path')
const url = require('url')

// TODO(zcbenz): Remove this when we have Object.values().
const objectValues = function (object) {
  return Object.keys(object).map(function (key) { return object[key] })
}

// Mapping between extensionId(hostname) and manifest.
const manifestMap = {}  // extensionId => manifest
const manifestNameMap = {}  // name => manifest

const generateExtensionIdFromName = function (name) {
  return name.replace(/[\W_]+/g, '-').toLowerCase()
}

const isWindowOrWebView = function (webContents) {
  const type = webContents.getType()
  return type === 'window' || type === 'webview'
}

// Create or get manifest object from |srcDirectory|.
const getManifestFromPath = function (srcDirectory) {
  let manifest
  let manifestContent

  try {
    manifestContent = fs.readFileSync(path.join(srcDirectory, 'manifest.json'))
  } catch (readError) {
    console.warn(`Reading ${path.join(srcDirectory, 'manifest.json')} failed.`)
    console.warn(readError.stack || readError)
    throw readError
  }

  try {
    manifest = JSON.parse(manifestContent)
  } catch (parseError) {
    console.warn(`Parsing ${path.join(srcDirectory, 'manifest.json')} failed.`)
    console.warn(parseError.stack || parseError)
    throw parseError
  }

  if (!manifestNameMap[manifest.name]) {
    const extensionId = generateExtensionIdFromName(manifest.name)
    manifestMap[extensionId] = manifestNameMap[manifest.name] = manifest
    Object.assign(manifest, {
      srcDirectory: srcDirectory,
      extensionId: extensionId,
      // We can not use 'file://' directly because all resources in the extension
      // will be treated as relative to the root in Chrome.
      startPage: url.format({
        protocol: 'chrome-extension',
        slashes: true,
        hostname: extensionId,
        pathname: manifest.devtools_page
      })
    })
    return manifest
  } else if (manifest && manifest.name) {
    console.warn(`Attempted to load extension "${manifest.name}" that has already been loaded.`)
  }
}

// Manage the background pages.
const backgroundPages = {}

const startBackgroundPages = function (manifest) {
  if (backgroundPages[manifest.extensionId] || !manifest.background) return

  let html
  let name
  if (manifest.background.page) {
    name = manifest.background.page
    html = fs.readFileSync(path.join(manifest.srcDirectory, manifest.background.page))
  } else {
    name = '_generated_background_page.html'
    const scripts = manifest.background.scripts.map((name) => {
      return `<script src="${name}"></script>`
    }).join('')
    html = new Buffer(`<html><body>${scripts}</body></html>`)
  }

  const contents = webContents.create({
    partition: 'persist:__chrome_extension',
    isBackgroundPage: true,
    commandLineSwitches: ['--background-page']
  })
  backgroundPages[manifest.extensionId] = { html: html, webContents: contents, name: name }
  contents.loadURL(url.format({
    protocol: 'chrome-extension',
    slashes: true,
    hostname: manifest.extensionId,
    pathname: name
  }))
}

const removeBackgroundPages = function (manifest) {
  if (!backgroundPages[manifest.extensionId]) return

  backgroundPages[manifest.extensionId].webContents.destroy()
  delete backgroundPages[manifest.extensionId]
}

const sendToBackgroundPages = function (...args) {
  for (const page of objectValues(backgroundPages)) {
    page.webContents.sendToAll(...args)
  }
}

// Dispatch web contents events to Chrome APIs
const hookWebContentsEvents = function (webContents) {
  const tabId = webContents.id

  sendToBackgroundPages('CHROME_TABS_ONCREATED')

  webContents.on('will-navigate', (event, url) => {
    sendToBackgroundPages('CHROME_WEBNAVIGATION_ONBEFORENAVIGATE', {
      frameId: 0,
      parentFrameId: -1,
      processId: webContents.getId(),
      tabId: tabId,
      timeStamp: Date.now(),
      url: url
    })
  })

  webContents.on('did-navigate', (event, url) => {
    sendToBackgroundPages('CHROME_WEBNAVIGATION_ONCOMPLETED', {
      frameId: 0,
      parentFrameId: -1,
      processId: webContents.getId(),
      tabId: tabId,
      timeStamp: Date.now(),
      url: url
    })
  })

  webContents.once('destroyed', () => {
    sendToBackgroundPages('CHROME_TABS_ONREMOVED', tabId)
  })
}

// Handle the chrome.* API messages.
let nextId = 0

ipcMain.on('CHROME_RUNTIME_CONNECT', function (event, extensionId, connectInfo) {
  const page = backgroundPages[extensionId]
  if (!page) {
    console.error(`Connect to unknown extension ${extensionId}`)
    return
  }

  const portId = ++nextId
  event.returnValue = {tabId: page.webContents.id, portId: portId}

  event.sender.once('render-view-deleted', () => {
    if (page.webContents.isDestroyed()) return
    page.webContents.sendToAll(`CHROME_PORT_DISCONNECT_${portId}`)
  })
  page.webContents.sendToAll(`CHROME_RUNTIME_ONCONNECT_${extensionId}`, event.sender.id, portId, connectInfo)
})

ipcMain.on('CHROME_I18N_MANIFEST', function (event, extensionId) {
  event.returnValue = manifestMap[extensionId]
})

ipcMain.on('CHROME_RUNTIME_SENDMESSAGE', function (event, extensionId, message) {
  const page = backgroundPages[extensionId]
  if (!page) {
    console.error(`Connect to unknown extension ${extensionId}`)
    return
  }

  page.webContents.sendToAll(`CHROME_RUNTIME_ONMESSAGE_${extensionId}`, event.sender.id, message)
})

ipcMain.on('CHROME_TABS_SEND_MESSAGE', function (event, tabId, extensionId, isBackgroundPage, message) {
  const contents = webContents.fromId(tabId)
  if (!contents) {
    console.error(`Sending message to unknown tab ${tabId}`)
    return
  }

  const senderTabId = isBackgroundPage ? null : event.sender.id

  contents.sendToAll(`CHROME_RUNTIME_ONMESSAGE_${extensionId}`, senderTabId, message)
})

ipcMain.on('CHROME_TABS_EXECUTESCRIPT', function (event, requestId, tabId, extensionId, details) {
  const contents = webContents.fromId(tabId)
  if (!contents) {
    console.error(`Sending message to unknown tab ${tabId}`)
    return
  }

  let code, url
  if (details.file) {
    const manifest = manifestMap[extensionId]
    code = String(fs.readFileSync(path.join(manifest.srcDirectory, details.file)))
    url = `chrome-extension://${extensionId}${details.file}`
  } else {
    code = details.code
    url = `chrome-extension://${extensionId}/${String(Math.random()).substr(2, 8)}.js`
  }

  contents.send('CHROME_TABS_EXECUTESCRIPT', event.sender.id, requestId, extensionId, url, code)
})

// Transfer the content scripts to renderer.
const contentScripts = {}

const injectContentScripts = function (manifest) {
  if (contentScripts[manifest.name] || !manifest.content_scripts) return

  const readArrayOfFiles = function (relativePath) {
    return {
      url: `chrome-extension://${manifest.extensionId}/${relativePath}`,
      code: String(fs.readFileSync(path.join(manifest.srcDirectory, relativePath)))
    }
  }

  const contentScriptToEntry = function (script) {
    return {
      matches: script.matches,
      js: script.js.map(readArrayOfFiles),
      runAt: script.run_at || 'document_idle'
    }
  }

  try {
    const entry = {
      extensionId: manifest.extensionId,
      contentScripts: manifest.content_scripts.map(contentScriptToEntry)
    }
    contentScripts[manifest.name] = renderProcessPreferences.addEntry(entry)
  } catch (e) {
    console.error('Failed to read content scripts', e)
  }
}

const removeContentScripts = function (manifest) {
  if (!contentScripts[manifest.name]) return

  renderProcessPreferences.removeEntry(contentScripts[manifest.name])
  delete contentScripts[manifest.name]
}

// Transfer the |manifest| to a format that can be recognized by the
// |DevToolsAPI.addExtensions|.
const manifestToExtensionInfo = function (manifest) {
  return {
    startPage: manifest.startPage,
    srcDirectory: manifest.srcDirectory,
    name: manifest.name,
    exposeExperimentalAPIs: true
  }
}

// Load the extensions for the window.
const loadExtension = function (manifest) {
  startBackgroundPages(manifest)
  injectContentScripts(manifest)
}

const loadDevToolsExtensions = function (win, manifests) {
  if (!win.devToolsWebContents) return

  manifests.forEach(loadExtension)

  const extensionInfoArray = manifests.map(manifestToExtensionInfo)
  win.devToolsWebContents.executeJavaScript(`DevToolsAPI.addExtensions(${JSON.stringify(extensionInfoArray)})`)
}

app.on('web-contents-created', function (event, webContents) {
  if (!isWindowOrWebView(webContents)) return

  hookWebContentsEvents(webContents)
  webContents.on('devtools-opened', function () {
    loadDevToolsExtensions(webContents, objectValues(manifestMap))
  })
})

// The chrome-extension: can map a extension URL request to real file path.
const chromeExtensionHandler = function (request, callback) {
  const parsed = url.parse(request.url)
  if (!parsed.hostname || !parsed.path) return callback()

  const manifest = manifestMap[parsed.hostname]
  if (!manifest) return callback()

  const page = backgroundPages[parsed.hostname]
  if (page && parsed.path === `/${page.name}`) {
    return callback({
      mimeType: 'text/html',
      data: page.html
    })
  }

  fs.readFile(path.join(manifest.srcDirectory, parsed.path), function (err, content) {
    if (err) {
      return callback(-6)  // FILE_NOT_FOUND
    } else {
      return callback(content)
    }
  })
}

app.on('session-created', function (ses) {
  ses.protocol.registerBufferProtocol('chrome-extension', chromeExtensionHandler, function (error) {
    if (error) {
      console.error(`Unable to register chrome-extension protocol: ${error}`)
    }
  })
})

// The persistent path of "DevTools Extensions" preference file.
let loadedExtensionsPath = null

app.on('will-quit', function () {
  try {
    const loadedExtensions = objectValues(manifestMap).map(function (manifest) {
      return manifest.srcDirectory
    })
    if (loadedExtensions.length > 0) {
      try {
        fs.mkdirSync(path.dirname(loadedExtensionsPath))
      } catch (error) {
        // Ignore error
      }
      fs.writeFileSync(loadedExtensionsPath, JSON.stringify(loadedExtensions))
    } else {
      fs.unlinkSync(loadedExtensionsPath)
    }
  } catch (error) {
    // Ignore error
  }
})

// We can not use protocol or BrowserWindow until app is ready.
app.once('ready', function () {
  // Load persisted extensions.
  loadedExtensionsPath = path.join(app.getPath('userData'), 'DevTools Extensions')
  try {
    const loadedExtensions = JSON.parse(fs.readFileSync(loadedExtensionsPath))
    if (Array.isArray(loadedExtensions)) {
      for (const srcDirectory of loadedExtensions) {
        // Start background pages and set content scripts.
        const manifest = getManifestFromPath(srcDirectory)
        loadExtension(manifest)
      }
    }
  } catch (error) {
    // Ignore error
  }

  // The public API to add/remove extensions.
  BrowserWindow.addDevToolsExtension = function (srcDirectory) {
    const manifest = getManifestFromPath(srcDirectory)
    if (manifest) {
      loadExtension(manifest)
      for (const webContents of getAllWebContents()) {
        if (isWindowOrWebView(webContents)) {
          loadDevToolsExtensions(webContents, [manifest])
        }
      }
      return manifest.name
    }
  }

  BrowserWindow.removeDevToolsExtension = function (name) {
    const manifest = manifestNameMap[name]
    if (!manifest) return

    removeBackgroundPages(manifest)
    removeContentScripts(manifest)
    delete manifestMap[manifest.extensionId]
    delete manifestNameMap[name]
  }

  BrowserWindow.getDevToolsExtensions = function () {
    const extensions = {}
    Object.keys(manifestNameMap).forEach(function (name) {
      const manifest = manifestNameMap[name]
      extensions[name] = {name: manifest.name, version: manifest.version}
    })
    return extensions
  }
})
'use strict'

const ipcMain = require('electron').ipcMain
const desktopCapturer = process.atomBinding('desktop_capturer').desktopCapturer

var deepEqual = function (opt1, opt2) {
  return JSON.stringify(opt1) === JSON.stringify(opt2)
}

// A queue for holding all requests from renderer process.
var requestsQueue = []

ipcMain.on('ELECTRON_BROWSER_DESKTOP_CAPTURER_GET_SOURCES', function (event, captureWindow, captureScreen, thumbnailSize, id) {
  var request
  request = {
    id: id,
    options: {
      captureWindow: captureWindow,
      captureScreen: captureScreen,
      thumbnailSize: thumbnailSize
    },
    webContents: event.sender
  }
  requestsQueue.push(request)
  if (requestsQueue.length === 1) {
    desktopCapturer.startHandling(captureWindow, captureScreen, thumbnailSize)
  }

  // If the WebContents is destroyed before receiving result, just remove the
  // reference from requestsQueue to make the module not send the result to it.
  event.sender.once('destroyed', function () {
    request.webContents = null
  })
})

desktopCapturer.emit = function (event, name, sources) {
  // Receiving sources result from main process, now send them back to renderer.
  var handledRequest, i, len, ref, ref1, request, result, source, unhandledRequestsQueue
  handledRequest = requestsQueue.shift(0)
  result = (function () {
    var i, len, results
    results = []
    for (i = 0, len = sources.length; i < len; i++) {
      source = sources[i]
      results.push({
        id: source.id,
        name: source.name,
        thumbnail: source.thumbnail.toDataURL()
      })
    }
    return results
  })()
  if ((ref = handledRequest.webContents) != null) {
    ref.send('ELECTRON_RENDERER_DESKTOP_CAPTURER_RESULT_' + handledRequest.id, result)
  }

  // Check the queue to see whether there is other same request. If has, handle
  // it for reducing redunplicated `desktopCaptuer.startHandling` calls.
  unhandledRequestsQueue = []
  for (i = 0, len = requestsQueue.length; i < len; i++) {
    request = requestsQueue[i]
    if (deepEqual(handledRequest.options, request.options)) {
      if ((ref1 = request.webContents) != null) {
        ref1.send('ELECTRON_RENDERER_DESKTOP_CAPTURER_RESULT_' + request.id, result)
      }
    } else {
      unhandledRequestsQueue.push(request)
    }
  }
  requestsQueue = unhandledRequestsQueue

  // If the requestsQueue is not empty, start a new request handling.
  if (requestsQueue.length > 0) {
    const {captureWindow, captureScreen, thumbnailSize} = requestsQueue[0].options
    return desktopCapturer.startHandling(captureWindow, captureScreen, thumbnailSize)
  }
}
'use strict'

const ipcMain = require('electron').ipcMain
const webContents = require('electron').webContents

// Doesn't exist in early initialization.
let webViewManager = null

const supportedWebViewEvents = [
  'load-commit',
  'did-finish-load',
  'did-fail-load',
  'did-frame-finish-load',
  'did-start-loading',
  'did-stop-loading',
  'did-get-response-details',
  'did-get-redirect-request',
  'dom-ready',
  'console-message',
  'devtools-opened',
  'devtools-closed',
  'devtools-focused',
  'new-window',
  'will-navigate',
  'did-navigate',
  'did-navigate-in-page',
  'close',
  'crashed',
  'gpu-crashed',
  'plugin-crashed',
  'destroyed',
  'page-title-updated',
  'page-favicon-updated',
  'enter-html-full-screen',
  'leave-html-full-screen',
  'media-started-playing',
  'media-paused',
  'found-in-page',
  'did-change-theme-color',
  'update-target-url'
]

let nextInstanceId = 0
const guestInstances = {}
const embedderElementsMap = {}
const reverseEmbedderElementsMap = {}

// Moves the last element of array to the first one.
const moveLastToFirst = function (list) {
  return list.unshift(list.pop())
}

// Generate guestInstanceId.
const getNextInstanceId = function () {
  return ++nextInstanceId
}

// Create a new guest instance.
const createGuest = function (embedder, params) {
  if (webViewManager == null) {
    webViewManager = process.atomBinding('web_view_manager')
  }

  const id = getNextInstanceId(embedder)
  const guest = webContents.create({
    isGuest: true,
    partition: params.partition,
    embedder: embedder
  })
  guestInstances[id] = {
    guest: guest,
    embedder: embedder
  }

  // Destroy guest when the embedder is gone or navigated.
  const destroyEvents = ['will-destroy', 'crashed', 'did-navigate']
  const destroy = function () {
    if (guestInstances[id] != null) {
      destroyGuest(embedder, id)
    }
  }
  for (const event of destroyEvents) {
    embedder.once(event, destroy)

    // Users might also listen to the crashed event, so we must ensure the guest
    // is destroyed before users' listener gets called. It is done by moving our
    // listener to the first one in queue.
    const listeners = embedder._events[event]
    if (Array.isArray(listeners)) {
      moveLastToFirst(listeners)
    }
  }
  guest.once('destroyed', function () {
    for (const event of destroyEvents) {
      embedder.removeListener(event, destroy)
    }
  })

  // Init guest web view after attached.
  guest.once('did-attach', function () {
    let opts
    params = this.attachParams
    delete this.attachParams
    this.viewInstanceId = params.instanceId
    this.setSize({
      normal: {
        width: params.elementWidth,
        height: params.elementHeight
      },
      enableAutoSize: params.autosize,
      min: {
        width: params.minwidth,
        height: params.minheight
      },
      max: {
        width: params.maxwidth,
        height: params.maxheight
      }
    })
    if (params.src) {
      opts = {}
      if (params.httpreferrer) {
        opts.httpReferrer = params.httpreferrer
      }
      if (params.useragent) {
        opts.userAgent = params.useragent
      }
      this.loadURL(params.src, opts)
    }
    guest.allowPopups = params.allowpopups
  })

  // Dispatch events to embedder.
  const fn = function (event) {
    guest.on(event, function (_, ...args) {
      embedder.send.apply(embedder, ['ELECTRON_GUEST_VIEW_INTERNAL_DISPATCH_EVENT-' + guest.viewInstanceId, event].concat(args))
    })
  }
  for (const event of supportedWebViewEvents) {
    fn(event)
  }

  // Dispatch guest's IPC messages to embedder.
  guest.on('ipc-message-host', function (_, [channel, ...args]) {
    embedder.send.apply(embedder, ['ELECTRON_GUEST_VIEW_INTERNAL_IPC_MESSAGE-' + guest.viewInstanceId, channel].concat(args))
  })

  // Autosize.
  guest.on('size-changed', function (_, ...args) {
    embedder.send.apply(embedder, ['ELECTRON_GUEST_VIEW_INTERNAL_SIZE_CHANGED-' + guest.viewInstanceId].concat(args))
  })

  return id
}

// Attach the guest to an element of embedder.
const attachGuest = function (embedder, elementInstanceId, guestInstanceId, params) {
  let guest, key, oldGuestInstanceId, ref1, webPreferences
  guest = guestInstances[guestInstanceId].guest

  // Destroy the old guest when attaching.
  key = (embedder.getId()) + '-' + elementInstanceId
  oldGuestInstanceId = embedderElementsMap[key]
  if (oldGuestInstanceId != null) {
    // Reattachment to the same guest is not currently supported.
    if (oldGuestInstanceId === guestInstanceId) {
      return
    }
    if (guestInstances[oldGuestInstanceId] == null) {
      return
    }
    destroyGuest(embedder, oldGuestInstanceId)
  }
  webPreferences = {
    guestInstanceId: guestInstanceId,
    nodeIntegration: (ref1 = params.nodeintegration) != null ? ref1 : false,
    plugins: params.plugins,
    zoomFactor: params.zoomFactor,
    webSecurity: !params.disablewebsecurity,
    blinkFeatures: params.blinkfeatures,
    disableBlinkFeatures: params.disableblinkfeatures
  }

  if (params.preload) {
    webPreferences.preloadURL = params.preload
  }
  webViewManager.addGuest(guestInstanceId, elementInstanceId, embedder, guest, webPreferences)
  guest.attachParams = params
  embedderElementsMap[key] = guestInstanceId
  reverseEmbedderElementsMap[guestInstanceId] = key
}

// Destroy an existing guest instance.
const destroyGuest = function (embedder, id) {
  webViewManager.removeGuest(embedder, id)
  guestInstances[id].guest.destroy()
  delete guestInstances[id]

  const key = reverseEmbedderElementsMap[id]
  if (key != null) {
    delete reverseEmbedderElementsMap[id]
    return delete embedderElementsMap[key]
  }
}

ipcMain.on('ELECTRON_GUEST_VIEW_MANAGER_CREATE_GUEST', function (event, params, requestId) {
  event.sender.send('ELECTRON_RESPONSE_' + requestId, createGuest(event.sender, params))
})

ipcMain.on('ELECTRON_GUEST_VIEW_MANAGER_ATTACH_GUEST', function (event, elementInstanceId, guestInstanceId, params) {
  attachGuest(event.sender, elementInstanceId, guestInstanceId, params)
})

ipcMain.on('ELECTRON_GUEST_VIEW_MANAGER_DESTROY_GUEST', function (event, id) {
  destroyGuest(event.sender, id)
})

ipcMain.on('ELECTRON_GUEST_VIEW_MANAGER_SET_SIZE', function (event, id, params) {
  const guestInstance = guestInstances[id]
  return guestInstance != null ? guestInstance.guest.setSize(params) : void 0
})

// Returns WebContents from its guest id.
exports.getGuest = function (id) {
  const guestInstance = guestInstances[id]
  return guestInstance != null ? guestInstance.guest : void 0
}

// Returns the embedder of the guest.
exports.getEmbedder = function (id) {
  const guestInstance = guestInstances[id]
  return guestInstance != null ? guestInstance.embedder : void 0
}
'use strict'

const {BrowserWindow, ipcMain, webContents} = require('electron')
const {isSameOrigin} = process.atomBinding('v8_util')

const hasProp = {}.hasOwnProperty
const frameToGuest = {}

// Copy attribute of |parent| to |child| if it is not defined in |child|.
const mergeOptions = function (child, parent) {
  let key, value
  for (key in parent) {
    if (!hasProp.call(parent, key)) continue
    value = parent[key]
    if (!(key in child)) {
      if (typeof value === 'object') {
        child[key] = mergeOptions({}, value)
      } else {
        child[key] = value
      }
    }
  }
  return child
}

// Merge |options| with the |embedder|'s window's options.
const mergeBrowserWindowOptions = function (embedder, options) {
  if (embedder.browserWindowOptions != null) {
    // Inherit the original options if it is a BrowserWindow.
    mergeOptions(options, embedder.browserWindowOptions)
  } else {
    // Or only inherit web-preferences if it is a webview.
    if (options.webPreferences == null) {
      options.webPreferences = {}
    }
    mergeOptions(options.webPreferences, embedder.getWebPreferences())
  }

  // Disable node integration on child window if disabled on parent window
  if (embedder.getWebPreferences().nodeIntegration === false) {
    options.webPreferences.nodeIntegration = false
  }

  return options
}

// Create a new guest created by |embedder| with |options|.
const createGuest = function (embedder, url, frameName, options) {
  let guest = frameToGuest[frameName]
  if (frameName && (guest != null)) {
    guest.loadURL(url)
    return guest.id
  }

  // Remember the embedder window's id.
  if (options.webPreferences == null) {
    options.webPreferences = {}
  }
  options.webPreferences.openerId = embedder.id
  guest = new BrowserWindow(options)
  guest.loadURL(url)

  // When |embedder| is destroyed we should also destroy attached guest, and if
  // guest is closed by user then we should prevent |embedder| from double
  // closing guest.
  const guestId = guest.webContents.id

  const closedByEmbedder = function () {
    guest.removeListener('closed', closedByUser)
    guest.destroy()
  }
  const closedByUser = function () {
    embedder.send('ELECTRON_GUEST_WINDOW_MANAGER_WINDOW_CLOSED_' + guestId)
    embedder.removeListener('render-view-deleted', closedByEmbedder)
  }
  embedder.once('render-view-deleted', closedByEmbedder)
  guest.once('closed', closedByUser)

  if (frameName) {
    frameToGuest[frameName] = guest
    guest.frameName = frameName
    guest.once('closed', function () {
      delete frameToGuest[frameName]
    })
  }

  return guestId
}

const getGuestWindow = function (guestContents) {
  let guestWindow = BrowserWindow.fromWebContents(guestContents)
  if (guestWindow == null) {
    const hostContents = guestContents.hostWebContents
    if (hostContents != null) {
      guestWindow = BrowserWindow.fromWebContents(hostContents)
    }
  }
  return guestWindow
}

// Checks whether |sender| can access the |target|:
// 1. Check whether |sender| is the parent of |target|.
// 2. Check whether |sender| has node integration, if so it is allowed to
//    do anything it wants.
// 3. Check whether the origins match.
//
// However it allows a child window without node integration but with same
// origin to do anything it wants, when its opener window has node integration.
// The W3C does not have anything on this, but from my understanding of the
// security model of |window.opener|, this should be fine.
const canAccessWindow = function (sender, target) {
  return (target.getWebPreferences().openerId === sender.id) ||
         (sender.getWebPreferences().nodeIntegration === true) ||
         isSameOrigin(sender.getURL(), target.getURL())
}

// Routed window.open messages.
ipcMain.on('ELECTRON_GUEST_WINDOW_MANAGER_WINDOW_OPEN', function (event, url, frameName, disposition, options) {
  options = mergeBrowserWindowOptions(event.sender, options)
  event.sender.emit('new-window', event, url, frameName, disposition, options)
  if ((event.sender.isGuest() && !event.sender.allowPopups) || event.defaultPrevented) {
    event.returnValue = null
  } else {
    event.returnValue = createGuest(event.sender, url, frameName, options)
  }
})

ipcMain.on('ELECTRON_GUEST_WINDOW_MANAGER_WINDOW_CLOSE', function (event, guestId) {
  const guestContents = webContents.fromId(guestId)
  if (guestContents == null) return

  if (!canAccessWindow(event.sender, guestContents)) {
    console.error(`Blocked ${event.sender.getURL()} from closing its opener.`)
    return
  }

  const guestWindow = getGuestWindow(guestContents)
  if (guestWindow != null) guestWindow.destroy()
})

ipcMain.on('ELECTRON_GUEST_WINDOW_MANAGER_WINDOW_METHOD', function (event, guestId, method, ...args) {
  const guestContents = webContents.fromId(guestId)
  if (guestContents == null) {
    event.returnValue = null
    return
  }

  if (!canAccessWindow(event.sender, guestContents)) {
    console.error(`Blocked ${event.sender.getURL()} from calling ${method} on its opener.`)
    event.returnValue = null
    return
  }

  const guestWindow = getGuestWindow(guestContents)
  if (guestWindow != null) {
    event.returnValue = guestWindow[method](...args)
  } else {
    event.returnValue = null
  }
})

ipcMain.on('ELECTRON_GUEST_WINDOW_MANAGER_WINDOW_POSTMESSAGE', function (event, guestId, message, targetOrigin, sourceOrigin) {
  const guestContents = webContents.fromId(guestId)
  if (guestContents == null) return

  // The W3C does not seem to have word on how postMessage should work when the
  // origins do not match, so we do not do |canAccessWindow| check here since
  // postMessage across origins is useful and not harmful.
  if (guestContents.getURL().indexOf(targetOrigin) === 0 || targetOrigin === '*') {
    const sourceId = event.sender.id
    guestContents.send('ELECTRON_GUEST_WINDOW_POSTMESSAGE', sourceId, message, sourceOrigin)
  }
})

ipcMain.on('ELECTRON_GUEST_WINDOW_MANAGER_WEB_CONTENTS_METHOD', function (event, guestId, method, ...args) {
  const guestContents = webContents.fromId(guestId)
  if (guestContents == null) return

  if (canAccessWindow(event.sender, guestContents)) {
    guestContents[method](...args)
  } else {
    console.error(`Blocked ${event.sender.getURL()} from calling ${method} on its opener.`)
  }
})

ipcMain.on('ELECTRON_GUEST_WINDOW_MANAGER_WEB_CONTENTS_METHOD_SYNC', function (event, guestId, method, ...args) {
  const guestContents = webContents.fromId(guestId)
  if (guestContents == null) {
    event.returnValue = null
    return
  }

  if (canAccessWindow(event.sender, guestContents)) {
    event.returnValue = guestContents[method](...args)
  } else {
    console.error(`Blocked ${event.sender.getURL()} from calling ${method} on its opener.`)
    event.returnValue = null
  }
})
'use strict'

const {Buffer} = require('buffer')
const fs = require('fs')
const path = require('path')
const util = require('util')
const Module = require('module')
const v8 = require('v8')

// We modified the original process.argv to let node.js load the atom.js,
// we need to restore it here.
process.argv.splice(1, 1)

// Clear search paths.
require('../common/reset-search-paths')

// Import common settings.
require('../common/init')

var globalPaths = Module.globalPaths

// Expose public APIs.
globalPaths.push(path.join(__dirname, 'api', 'exports'))

if (process.platform === 'win32') {
  // Redirect node's console to use our own implementations, since node can not
  // handle console output when running as GUI program.
  var consoleLog = function (...args) {
    return process.log(util.format(...args) + '\n')
  }
  var streamWrite = function (chunk, encoding, callback) {
    if (Buffer.isBuffer(chunk)) {
      chunk = chunk.toString(encoding)
    }
    process.log(chunk)
    if (callback) {
      callback()
    }
    return true
  }
  console.log = console.error = console.warn = consoleLog
  process.stdout.write = process.stderr.write = streamWrite
}

// Don't quit on fatal error.
process.on('uncaughtException', function (error) {
  // Do nothing if the user has a custom uncaught exception handler.
  var dialog, message, ref, stack
  if (process.listeners('uncaughtException').length > 1) {
    return
  }

  // Show error in GUI.
  dialog = require('electron').dialog
  stack = (ref = error.stack) != null ? ref : error.name + ': ' + error.message
  message = 'Uncaught Exception:\n' + stack
  dialog.showErrorBox('A JavaScript error occurred in the main process', message)
})

// Emit 'exit' event on quit.
const {app} = require('electron')

app.on('quit', function (event, exitCode) {
  process.emit('exit', exitCode)
})

if (process.platform === 'win32') {
  // If we are a Squirrel.Windows-installed app, set app user model ID
  // so that users don't have to do this.
  //
  // Squirrel packages are always of the form:
  //
  // PACKAGE-NAME
  // - Update.exe
  // - app-VERSION
  //   - OUREXE.exe
  //
  // Squirrel itself will always set the shortcut's App User Model ID to the
  // form `com.squirrel.PACKAGE-NAME.OUREXE`. We need to call
  // app.setAppUserModelId with a matching identifier so that renderer processes
  // will inherit this value.
  const updateDotExe = path.join(path.dirname(process.execPath), '..', 'update.exe')

  if (fs.existsSync(updateDotExe)) {
    const packageDir = path.dirname(path.resolve(updateDotExe))
    const packageName = path.basename(packageDir).replace(/\s/g, '')
    const exeName = path.basename(process.execPath).replace(/\.exe$/i, '').replace(/\s/g, '')

    app.setAppUserModelId(`com.squirrel.${packageName}.${exeName}`)
  }
}

// Map process.exit to app.exit, which quits gracefully.
process.exit = app.exit

// Load the RPC server.
require('./rpc-server')

// Load the guest view manager.
require('./guest-view-manager')
require('./guest-window-manager')

// Now we try to load app's package.json.
let packagePath = null
let packageJson = null
const searchPaths = ['app', 'app.asar', 'default_app.asar']
for (packagePath of searchPaths) {
  try {
    packagePath = path.join(process.resourcesPath, packagePath)
    packageJson = require(path.join(packagePath, 'package.json'))
    break
  } catch (error) {
    continue
  }
}

if (packageJson == null) {
  process.nextTick(function () {
    return process.exit(1)
  })
  throw new Error('Unable to find a valid app')
}

// Set application's version.
if (packageJson.version != null) {
  app.setVersion(packageJson.version)
}

// Set application's name.
if (packageJson.productName != null) {
  app.setName(packageJson.productName)
} else if (packageJson.name != null) {
  app.setName(packageJson.name)
}

// Set application's desktop name.
if (packageJson.desktopName != null) {
  app.setDesktopName(packageJson.desktopName)
} else {
  app.setDesktopName((app.getName()) + '.desktop')
}

// Set v8 flags
if (packageJson.v8Flags != null) {
  v8.setFlagsFromString(packageJson.v8Flags)
}

// Set the user path according to application's name.
app.setPath('userData', path.join(app.getPath('appData'), app.getName()))
app.setPath('userCache', path.join(app.getPath('cache'), app.getName()))
app.setAppPath(packagePath)

// Load the chrome extension support.
require('./chrome-extension')

// Load internal desktop-capturer module.
require('./desktop-capturer')

// Load protocol module to ensure it is populated on app ready
require('./api/protocol')

// Set main startup script of the app.
const mainStartupScript = packageJson.main || 'index.js'

// Finally load app's main.js and transfer control to C++.
Module._load(path.join(packagePath, mainStartupScript), Module, true)
'use strict'

const v8Util = process.atomBinding('v8_util')

class ObjectsRegistry {
  constructor () {
    this.nextId = 0

    // Stores all objects by ref-counting.
    // (id) => {object, count}
    this.storage = {}

    // Stores the IDs of objects referenced by WebContents.
    // (webContentsId) => [id]
    this.owners = {}
  }

  // Register a new object and return its assigned ID. If the object is already
  // registered then the already assigned ID would be returned.
  add (webContents, obj) {
    // Get or assign an ID to the object.
    const id = this.saveToStorage(obj)

    // Add object to the set of referenced objects.
    const webContentsId = webContents.getId()
    let owner = this.owners[webContentsId]
    if (!owner) {
      owner = this.owners[webContentsId] = new Set()
      this.registerDeleteListener(webContents, webContentsId)
    }
    if (!owner.has(id)) {
      owner.add(id)
      // Increase reference count if not referenced before.
      this.storage[id].count++
    }
    return id
  }

  // Get an object according to its ID.
  get (id) {
    return this.storage[id].object
  }

  // Dereference an object according to its ID.
  remove (webContentsId, id) {
    // Dereference from the storage.
    this.dereference(id)

    // Also remove the reference in owner.
    let owner = this.owners[webContentsId]
    if (owner) {
      owner.delete(id)
    }
  }

  // Clear all references to objects refrenced by the WebContents.
  clear (webContentsId) {
    let owner = this.owners[webContentsId]
    if (!owner) return

    for (let id of owner) this.dereference(id)

    delete this.owners[webContentsId]
  }

  // Private: Saves the object into storage and assigns an ID for it.
  saveToStorage (object) {
    let id = v8Util.getHiddenValue(object, 'atomId')
    if (!id) {
      id = ++this.nextId
      this.storage[id] = {
        count: 0,
        object: object
      }
      v8Util.setHiddenValue(object, 'atomId', id)
    }
    return id
  }

  // Private: Dereference the object from store.
  dereference (id) {
    let pointer = this.storage[id]
    if (pointer == null) {
      return
    }
    pointer.count -= 1
    if (pointer.count === 0) {
      v8Util.deleteHiddenValue(pointer.object, 'atomId')
      delete this.storage[id]
    }
  }

  // Private: Clear the storage when webContents is reloaded/navigated.
  registerDeleteListener (webContents, webContentsId) {
    const listener = (event, deletedProcessId) => {
      if (deletedProcessId === webContentsId) {
        webContents.removeListener('render-view-deleted', listener)
        this.clear(webContentsId)
      }
    }
    webContents.on('render-view-deleted', listener)
  }
}

module.exports = new ObjectsRegistry()
'use strict'

const {Buffer} = require('buffer')
const electron = require('electron')
const v8Util = process.atomBinding('v8_util')
const {ipcMain, isPromise, webContents} = electron

const objectsRegistry = require('./objects-registry')

const hasProp = {}.hasOwnProperty

// The internal properties of Function.
const FUNCTION_PROPERTIES = [
  'length', 'name', 'arguments', 'caller', 'prototype'
]

// The remote functions in renderer processes.
// id => Function
let rendererFunctions = v8Util.createDoubleIDWeakMap()

// Return the description of object's members:
let getObjectMembers = function (object) {
  let names = Object.getOwnPropertyNames(object)
  // For Function, we should not override following properties even though they
  // are "own" properties.
  if (typeof object === 'function') {
    names = names.filter((name) => {
      return !FUNCTION_PROPERTIES.includes(name)
    })
  }
  // Map properties to descriptors.
  return names.map((name) => {
    let descriptor = Object.getOwnPropertyDescriptor(object, name)
    let member = {name, enumerable: descriptor.enumerable, writable: false}
    if (descriptor.get === undefined && typeof object[name] === 'function') {
      member.type = 'method'
    } else {
      if (descriptor.set || descriptor.writable) member.writable = true
      member.type = 'get'
    }
    return member
  })
}

// Return the description of object's prototype.
let getObjectPrototype = function (object) {
  let proto = Object.getPrototypeOf(object)
  if (proto === null || proto === Object.prototype) return null
  return {
    members: getObjectMembers(proto),
    proto: getObjectPrototype(proto)
  }
}

// Convert a real value into meta data.
let valueToMeta = function (sender, value, optimizeSimpleObject = false) {
  // Determine the type of value.
  const meta = { type: typeof value }
  if (meta.type === 'object') {
    // Recognize certain types of objects.
    if (value === null) {
      meta.type = 'value'
    } else if (ArrayBuffer.isView(value)) {
      meta.type = 'buffer'
    } else if (Array.isArray(value)) {
      meta.type = 'array'
    } else if (value instanceof Error) {
      meta.type = 'error'
    } else if (value instanceof Date) {
      meta.type = 'date'
    } else if (isPromise(value)) {
      meta.type = 'promise'
    } else if (hasProp.call(value, 'callee') && value.length != null) {
      // Treat the arguments object as array.
      meta.type = 'array'
    } else if (optimizeSimpleObject && v8Util.getHiddenValue(value, 'simple')) {
      // Treat simple objects as value.
      meta.type = 'value'
    }
  }

  // Fill the meta object according to value's type.
  if (meta.type === 'array') {
    meta.members = value.map((el) => valueToMeta(sender, el))
  } else if (meta.type === 'object' || meta.type === 'function') {
    meta.name = value.constructor ? value.constructor.name : ''

    // Reference the original value if it's an object, because when it's
    // passed to renderer we would assume the renderer keeps a reference of
    // it.
    meta.id = objectsRegistry.add(sender, value)
    meta.members = getObjectMembers(value)
    meta.proto = getObjectPrototype(value)
  } else if (meta.type === 'buffer') {
    meta.value = Buffer.from(value)
  } else if (meta.type === 'promise') {
    // Add default handler to prevent unhandled rejections in main process
    // Instead they should appear in the renderer process
    value.then(function () {}, function () {})

    meta.then = valueToMeta(sender, function (onFulfilled, onRejected) {
      value.then(onFulfilled, onRejected)
    })
  } else if (meta.type === 'error') {
    meta.members = plainObjectToMeta(value)

    // Error.name is not part of own properties.
    meta.members.push({
      name: 'name',
      value: value.name
    })
  } else if (meta.type === 'date') {
    meta.value = value.getTime()
  } else {
    meta.type = 'value'
    meta.value = value
  }
  return meta
}

// Convert object to meta by value.
const plainObjectToMeta = function (obj) {
  return Object.getOwnPropertyNames(obj).map(function (name) {
    return {
      name: name,
      value: obj[name]
    }
  })
}

// Convert Error into meta data.
const exceptionToMeta = function (error) {
  return {
    type: 'exception',
    message: error.message,
    stack: error.stack || error
  }
}

// Convert array of meta data from renderer into array of real values.
const unwrapArgs = function (sender, args) {
  const metaToValue = function (meta) {
    let i, len, member, ref, returnValue
    switch (meta.type) {
      case 'value':
        return meta.value
      case 'remote-object':
        return objectsRegistry.get(meta.id)
      case 'array':
        return unwrapArgs(sender, meta.value)
      case 'buffer':
        return Buffer.from(meta.value)
      case 'date':
        return new Date(meta.value)
      case 'promise':
        return Promise.resolve({
          then: metaToValue(meta.then)
        })
      case 'object': {
        let ret = {}
        Object.defineProperty(ret.constructor, 'name', { value: meta.name })

        ref = meta.members
        for (i = 0, len = ref.length; i < len; i++) {
          member = ref[i]
          ret[member.name] = metaToValue(member.value)
        }
        return ret
      }
      case 'function-with-return-value':
        returnValue = metaToValue(meta.value)
        return function () {
          return returnValue
        }
      case 'function': {
        // Merge webContentsId and meta.id, since meta.id can be the same in
        // different webContents.
        const webContentsId = sender.getId()
        const objectId = [webContentsId, meta.id]

        // Cache the callbacks in renderer.
        if (rendererFunctions.has(objectId)) {
          return rendererFunctions.get(objectId)
        }

        let callIntoRenderer = function (...args) {
          if (!sender.isDestroyed() && webContentsId === sender.getId()) {
            sender.send('ELECTRON_RENDERER_CALLBACK', meta.id, valueToMeta(sender, args))
          } else {
            throw new Error(`Attempting to call a function in a renderer window that has been closed or released. Function provided here: ${meta.location}.`)
          }
        }

        v8Util.setRemoteCallbackFreer(callIntoRenderer, meta.id, sender)
        rendererFunctions.set(objectId, callIntoRenderer)
        return callIntoRenderer
      }
      default:
        throw new TypeError(`Unknown type: ${meta.type}`)
    }
  }
  return args.map(metaToValue)
}

// Call a function and send reply asynchronously if it's a an asynchronous
// style function and the caller didn't pass a callback.
const callFunction = function (event, func, caller, args) {
  let funcMarkedAsync, funcName, funcPassedCallback, ref, ret
  funcMarkedAsync = v8Util.getHiddenValue(func, 'asynchronous')
  funcPassedCallback = typeof args[args.length - 1] === 'function'
  try {
    if (funcMarkedAsync && !funcPassedCallback) {
      args.push(function (ret) {
        event.returnValue = valueToMeta(event.sender, ret, true)
      })
      func.apply(caller, args)
    } else {
      ret = func.apply(caller, args)
      event.returnValue = valueToMeta(event.sender, ret, true)
    }
  } catch (error) {
    // Catch functions thrown further down in function invocation and wrap
    // them with the function name so it's easier to trace things like
    // `Error processing argument -1.`
    funcName = ((ref = func.name) != null) ? ref : 'anonymous'
    throw new Error(`Could not call remote function '${funcName}'. Check that the function signature is correct. Underlying error: ${error.message}`)
  }
}

ipcMain.on('ELECTRON_BROWSER_REQUIRE', function (event, module) {
  try {
    event.returnValue = valueToMeta(event.sender, process.mainModule.require(module))
  } catch (error) {
    event.returnValue = exceptionToMeta(error)
  }
})

ipcMain.on('ELECTRON_BROWSER_GET_BUILTIN', function (event, module) {
  try {
    event.returnValue = valueToMeta(event.sender, electron[module])
  } catch (error) {
    event.returnValue = exceptionToMeta(error)
  }
})

ipcMain.on('ELECTRON_BROWSER_GLOBAL', function (event, name) {
  try {
    event.returnValue = valueToMeta(event.sender, global[name])
  } catch (error) {
    event.returnValue = exceptionToMeta(error)
  }
})

ipcMain.on('ELECTRON_BROWSER_CURRENT_WINDOW', function (event) {
  try {
    event.returnValue = valueToMeta(event.sender, event.sender.getOwnerBrowserWindow())
  } catch (error) {
    event.returnValue = exceptionToMeta(error)
  }
})

ipcMain.on('ELECTRON_BROWSER_CURRENT_WEB_CONTENTS', function (event) {
  event.returnValue = valueToMeta(event.sender, event.sender)
})

ipcMain.on('ELECTRON_BROWSER_CONSTRUCTOR', function (event, id, args) {
  try {
    args = unwrapArgs(event.sender, args)
    let constructor = objectsRegistry.get(id)

    // Call new with array of arguments.
    // http://stackoverflow.com/questions/1606797/use-of-apply-with-new-operator-is-this-possible
    let obj = new (Function.prototype.bind.apply(constructor, [null].concat(args)))
    event.returnValue = valueToMeta(event.sender, obj)
  } catch (error) {
    event.returnValue = exceptionToMeta(error)
  }
})

ipcMain.on('ELECTRON_BROWSER_FUNCTION_CALL', function (event, id, args) {
  try {
    args = unwrapArgs(event.sender, args)
    let func = objectsRegistry.get(id)
    callFunction(event, func, global, args)
  } catch (error) {
    event.returnValue = exceptionToMeta(error)
  }
})

ipcMain.on('ELECTRON_BROWSER_MEMBER_CONSTRUCTOR', function (event, id, method, args) {
  try {
    args = unwrapArgs(event.sender, args)
    let constructor = objectsRegistry.get(id)[method]

    // Call new with array of arguments.
    let obj = new (Function.prototype.bind.apply(constructor, [null].concat(args)))
    event.returnValue = valueToMeta(event.sender, obj)
  } catch (error) {
    event.returnValue = exceptionToMeta(error)
  }
})

ipcMain.on('ELECTRON_BROWSER_MEMBER_CALL', function (event, id, method, args) {
  try {
    args = unwrapArgs(event.sender, args)
    let obj = objectsRegistry.get(id)
    callFunction(event, obj[method], obj, args)
  } catch (error) {
    event.returnValue = exceptionToMeta(error)
  }
})

ipcMain.on('ELECTRON_BROWSER_MEMBER_SET', function (event, id, name, value) {
  try {
    let obj = objectsRegistry.get(id)
    obj[name] = value
    event.returnValue = null
  } catch (error) {
    event.returnValue = exceptionToMeta(error)
  }
})

ipcMain.on('ELECTRON_BROWSER_MEMBER_GET', function (event, id, name) {
  try {
    let obj = objectsRegistry.get(id)
    event.returnValue = valueToMeta(event.sender, obj[name])
  } catch (error) {
    event.returnValue = exceptionToMeta(error)
  }
})

ipcMain.on('ELECTRON_BROWSER_DEREFERENCE', function (event, id) {
  objectsRegistry.remove(event.sender.getId(), id)
})

ipcMain.on('ELECTRON_BROWSER_GUEST_WEB_CONTENTS', function (event, guestInstanceId) {
  try {
    let guestViewManager = require('./guest-view-manager')
    event.returnValue = valueToMeta(event.sender, guestViewManager.getGuest(guestInstanceId))
  } catch (error) {
    event.returnValue = exceptionToMeta(error)
  }
})

ipcMain.on('ELECTRON_BROWSER_ASYNC_CALL_TO_GUEST_VIEW', function (event, requestId, guestInstanceId, method, ...args) {
  try {
    let guestViewManager = require('./guest-view-manager')
    let guest = guestViewManager.getGuest(guestInstanceId)
    if (requestId) {
      const responseCallback = function (result) {
        event.sender.send(`ELECTRON_RENDERER_ASYNC_CALL_TO_GUEST_VIEW_RESPONSE_${requestId}`, result)
      }
      args.push(responseCallback)
    }
    guest[method].apply(guest, args)
  } catch (error) {
    event.returnValue = exceptionToMeta(error)
  }
})

ipcMain.on('ELECTRON_BROWSER_SEND_TO', function (event, sendToAll, webContentsId, channel, ...args) {
  let contents = webContents.fromId(webContentsId)
  if (!contents) {
    console.error(`Sending message to WebContents with unknown ID ${webContentsId}`)
    return
  }

  if (sendToAll) {
    contents.sendToAll(channel, ...args)
  } else {
    contents.send(channel, ...args)
  }
})

// Implements window.alert(message, title)
ipcMain.on('ELECTRON_BROWSER_WINDOW_ALERT', function (event, message, title) {
  if (message == null) message = ''
  if (title == null) title = ''

  event.returnValue = electron.dialog.showMessageBox(event.sender.getOwnerBrowserWindow(), {
    message: `${message}`,
    title: `${title}`,
    buttons: ['OK']
  })
})

// Implements window.confirm(message, title)
ipcMain.on('ELECTRON_BROWSER_WINDOW_CONFIRM', function (event, message, title) {
  if (message == null) message = ''
  if (title == null) title = ''

  event.returnValue = !electron.dialog.showMessageBox(event.sender.getOwnerBrowserWindow(), {
    message: `${message}`,
    title: `${title}`,
    buttons: ['OK', 'Cancel'],
    cancelId: 1
  })
})

// Implements window.close()
ipcMain.on('ELECTRON_BROWSER_WINDOW_CLOSE', function (event) {
  event.sender.getOwnerBrowserWindow().close()
  event.returnValue = null
})
'use strict'

const v8Util = process.atomBinding('v8_util')

class CallbacksRegistry {
  constructor () {
    this.nextId = 0
    this.callbacks = {}
  }

  add (callback) {
    // The callback is already added.
    var filenameAndLine, id, location, match, ref, regexp, stackString
    id = v8Util.getHiddenValue(callback, 'callbackId')
    if (id != null) {
      return id
    }
    id = ++this.nextId

    // Capture the location of the function and put it in the ID string,
    // so that release errors can be tracked down easily.
    regexp = /at (.*)/gi
    stackString = (new Error()).stack
    while ((match = regexp.exec(stackString)) !== null) {
      location = match[1]
      if (location.indexOf('(native)') !== -1) {
        continue
      }
      if (location.indexOf('electron.asar') !== -1) {
        continue
      }
      ref = /([^\/^\)]*)\)?$/gi.exec(location)
      filenameAndLine = ref[1]
      break
    }
    this.callbacks[id] = callback
    v8Util.setHiddenValue(callback, 'callbackId', id)
    v8Util.setHiddenValue(callback, 'location', filenameAndLine)
    return id
  }

  get (id) {
    var ref
    return (ref = this.callbacks[id]) != null ? ref : function () {}
  }

  apply (id, ...args) {
    return this.get(id).apply(global, ...args)
  }

  remove (id) {
    const callback = this.callbacks[id]
    if (callback) {
      v8Util.deleteHiddenValue(callback, 'callbackId')
      delete this.callbacks[id]
    }
  }
}

module.exports = CallbacksRegistry
if (process.platform === 'linux' && process.type === 'renderer') {
  // On Linux we could not access clipboard in renderer process.
  module.exports = require('electron').remote.clipboard
} else {
  module.exports = process.atomBinding('clipboard')
}
'use strict'

const os = require('os')
const path = require('path')
const spawn = require('child_process').spawn
const electron = require('electron')
const binding = process.atomBinding('crash_reporter')

var CrashReporter = (function () {
  function CrashReporter () {}

  CrashReporter.prototype.start = function (options) {
    var app, args, autoSubmit, companyName, env, extra, ignoreSystemCrashHandler, start, submitURL
    if (options == null) {
      options = {}
    }
    this.productName = options.productName
    companyName = options.companyName
    submitURL = options.submitURL
    autoSubmit = options.autoSubmit
    ignoreSystemCrashHandler = options.ignoreSystemCrashHandler
    extra = options.extra

    app = (process.type === 'browser' ? electron : electron.remote).app
    if (this.productName == null) {
      this.productName = app.getName()
    }
    if (autoSubmit == null) {
      autoSubmit = true
    }
    if (ignoreSystemCrashHandler == null) {
      ignoreSystemCrashHandler = false
    }
    if (extra == null) {
      extra = {}
    }
    if (extra._productName == null) {
      extra._productName = this.productName
    }
    if (extra._companyName == null) {
      extra._companyName = companyName
    }
    if (extra._version == null) {
      extra._version = app.getVersion()
    }
    if (companyName == null) {
      throw new Error('companyName is a required option to crashReporter.start')
    }
    if (submitURL == null) {
      throw new Error('submitURL is a required option to crashReporter.start')
    }
    start = () => {
      binding.start(this.productName, companyName, submitURL, autoSubmit, ignoreSystemCrashHandler, extra)
    }
    if (process.platform === 'win32') {
      args = ['--reporter-url=' + submitURL, '--application-name=' + this.productName, '--v=1']
      env = {
        ELECTRON_INTERNAL_CRASH_SERVICE: 1
      }
      spawn(process.execPath, args, {
        env: env,
        detached: true
      })
    }
    return start()
  }

  CrashReporter.prototype.getLastCrashReport = function () {
    var reports
    reports = this.getUploadedReports()
    if (reports.length > 0) {
      return reports[0]
    } else {
      return null
    }
  }

  CrashReporter.prototype.getUploadedReports = function () {
    var log, tmpdir
    tmpdir = process.platform === 'win32' ? os.tmpdir() : '/tmp'
    log = process.platform === 'darwin' ? path.join(tmpdir, this.productName + ' Crashes') : path.join(tmpdir, this.productName + ' Crashes', 'uploads.log')
    return binding._getUploadedReports(log)
  }

  return CrashReporter
})()

module.exports = new CrashReporter()
// Deprecate a method.
const deprecate = function (oldName, newName, fn) {
  var warned
  warned = false
  return function () {
    if (!(warned || process.noDeprecation)) {
      warned = true
      deprecate.warn(oldName, newName)
    }
    return fn.apply(this, arguments)
  }
}

// The method is renamed.
deprecate.rename = function (object, oldName, newName) {
  var newMethod, warned
  warned = false
  newMethod = function () {
    if (!(warned || process.noDeprecation)) {
      warned = true
      deprecate.warn(oldName, newName)
    }
    return this[newName].apply(this, arguments)
  }
  if (typeof object === 'function') {
    object.prototype[oldName] = newMethod
  } else {
    object[oldName] = newMethod
  }
}

// Forward the method to member.
deprecate.member = function (object, method, member) {
  var warned
  warned = false
  object.prototype[method] = function () {
    if (!(warned || process.noDeprecation)) {
      warned = true
      deprecate.warn(method, member + '.' + method)
    }
    return this[member][method].apply(this[member], arguments)
  }
}

// Deprecate a property.
deprecate.property = function (object, property, method) {
  return Object.defineProperty(object, property, {
    get: function () {
      var warned
      warned = false
      if (!(warned || process.noDeprecation)) {
        warned = true
        deprecate.warn(property + ' property', method + ' method')
      }
      return this[method]()
    }
  })
}

// Deprecate an event.
deprecate.event = function (emitter, oldName, newName, fn) {
  var warned = false
  return emitter.on(newName, function (...args) {
    // there is listeners for old API.
    if (this.listenerCount(oldName) > 0) {
      if (!(warned || process.noDeprecation)) {
        warned = true
        deprecate.warn("'" + oldName + "' event", "'" + newName + "' event")
      }
      if (fn != null) {
        fn.apply(this, arguments)
      } else {
        this.emit.apply(this, [oldName].concat(args))
      }
    }
  })
}

// Print deprecation warning.
deprecate.warn = function (oldName, newName) {
  return deprecate.log(oldName + ' is deprecated. Use ' + newName + ' instead.')
}

var deprecationHandler = null

// Print deprecation message.
deprecate.log = function (message) {
  if (typeof deprecationHandler === 'function') {
    deprecationHandler(message)
  } else if (process.throwDeprecation) {
    throw new Error(message)
  } else if (process.traceDeprecation) {
    return console.trace(message)
  } else {
    return console.warn('(electron) ' + message)
  }
}

deprecate.setHandler = function (handler) {
  deprecationHandler = handler
}

deprecate.getHandler = function () {
  return deprecationHandler
}

module.exports = deprecate
'use strict'

const deprecate = require('electron').deprecate

exports.setHandler = function (deprecationHandler) {
  deprecate.setHandler(deprecationHandler)
}

exports.getHandler = function () {
  return deprecate.getHandler()
}
// Attaches properties to |exports|.
exports.defineProperties = function (exports) {
  return Object.defineProperties(exports, {
    // Common modules, please sort with alphabet order.
    clipboard: {
      // Must be enumerable, otherwise it woulde be invisible to remote module.
      enumerable: true,
      get: function () {
        return require('../clipboard')
      }
    },
    crashReporter: {
      enumerable: true,
      get: function () {
        return require('../crash-reporter')
      }
    },
    nativeImage: {
      enumerable: true,
      get: function () {
        return require('../native-image')
      }
    },
    shell: {
      enumerable: true,
      get: function () {
        return require('../shell')
      }
    },

    // The internal modules, invisible unless you know their names.
    CallbacksRegistry: {
      get: function () {
        return require('../callbacks-registry')
      }
    },
    deprecate: {
      get: function () {
        return require('../deprecate')
      }
    },
    deprecations: {
      get: function () {
        return require('../deprecations')
      }
    },
    isPromise: {
      get: function () {
        return require('../is-promise')
      }
    }
  })
}
'use strict'

module.exports = function isPromise (val) {
  return (
    val &&
    val.then &&
    val.then instanceof Function &&
    val.constructor &&
    val.constructor.reject &&
    val.constructor.reject instanceof Function &&
    val.constructor.resolve &&
    val.constructor.resolve instanceof Function
  )
}
module.exports = process.atomBinding('native_image')
module.exports = process.atomBinding('shell')
const timers = require('timers')

const {binding} = process

process.atomBinding = function (name) {
  try {
    return binding('atom_' + process.type + '_' + name)
  } catch (error) {
    if (/No such module/.test(error.message)) {
      return binding('atom_common_' + name)
    }
  }
}

// setImmediate and process.nextTick makes use of uv_check and uv_prepare to
// run the callbacks, however since we only run uv loop on requests, the
// callbacks wouldn't be called until something else activated the uv loop,
// which would delay the callbacks for arbitrary long time. So we should
// initiatively activate the uv loop once setImmediate and process.nextTick is
// called.
var wrapWithActivateUvLoop = function (func) {
  return function () {
    process.activateUvLoop()
    return func.apply(this, arguments)
  }
}

process.nextTick = wrapWithActivateUvLoop(process.nextTick)

global.setImmediate = wrapWithActivateUvLoop(timers.setImmediate)

global.clearImmediate = timers.clearImmediate

if (process.type === 'browser') {
  // setTimeout needs to update the polling timeout of the event loop, when
  // called under Chromium's event loop the node's event loop won't get a chance
  // to update the timeout, so we have to force the node's event loop to
  // recalculate the timeout in browser process.
  global.setTimeout = wrapWithActivateUvLoop(timers.setTimeout)
  global.setInterval = wrapWithActivateUvLoop(timers.setInterval)
}

if (process.platform === 'win32') {
  // Always returns EOF for stdin stream.
  const {Readable} = require('stream')
  const stdin = new Readable()
  stdin.push(null)
  process.__defineGetter__('stdin', function () {
    return stdin
  })

  // If we're running as a Windows Store app, __dirname will be set
  // to C:/Program Files/WindowsApps.
  //
  // Nobody else get's to install there, changing the path is forbidden
  // We can therefore say that we're running as appx
  if (__dirname.indexOf('\\Program Files\\WindowsApps\\') === 2) {
    process.windowsStore = true
  }
}
const path = require('path')
const Module = require('module')

// Clear Node's global search paths.
Module.globalPaths.length = 0

// Clear current and parent(init.js)'s search paths.
module.paths = []
module.parent.paths = []

// Prevent Node from adding paths outside this app to search paths.
const resourcesPathWithTrailingSlash = process.resourcesPath + path.sep
const originalNodeModulePaths = Module._nodeModulePaths
Module._nodeModulePaths = function (from) {
  const paths = originalNodeModulePaths(from)
  const fromPath = path.resolve(from) + path.sep
  // If "from" is outside the app then we do nothing.
  if (fromPath.startsWith(resourcesPathWithTrailingSlash)) {
    return paths.filter(function (candidate) {
      return candidate.startsWith(resourcesPathWithTrailingSlash)
    })
  } else {
    return paths
  }
}

// Patch Module._resolveFilename to always require the Electron API when
// require('electron') is done.
const electronPath = path.join(__dirname, '..', process.type, 'api', 'exports', 'electron.js')
const originalResolveFilename = Module._resolveFilename
Module._resolveFilename = function (request, parent, isMain) {
  if (request === 'electron') {
    return electronPath
  } else {
    return originalResolveFilename(request, parent, isMain)
  }
}
const ipcRenderer = require('electron').ipcRenderer
const nativeImage = require('electron').nativeImage

var nextId = 0
var includes = [].includes

var getNextId = function () {
  return ++nextId
}

// |options.type| can not be empty and has to include 'window' or 'screen'.
var isValid = function (options) {
  return ((options != null ? options.types : void 0) != null) && Array.isArray(options.types)
}

exports.getSources = function (options, callback) {
  var captureScreen, captureWindow, id
  if (!isValid(options)) {
    return callback(new Error('Invalid options'))
  }
  captureWindow = includes.call(options.types, 'window')
  captureScreen = includes.call(options.types, 'screen')
  if (options.thumbnailSize == null) {
    options.thumbnailSize = {
      width: 150,
      height: 150
    }
  }
  id = getNextId()
  ipcRenderer.send('ELECTRON_BROWSER_DESKTOP_CAPTURER_GET_SOURCES', captureWindow, captureScreen, options.thumbnailSize, id)
  return ipcRenderer.once('ELECTRON_RENDERER_DESKTOP_CAPTURER_RESULT_' + id, function (event, sources) {
    var source
    callback(null, (function () {
      var i, len, results
      results = []
      for (i = 0, len = sources.length; i < len; i++) {
        source = sources[i]
        results.push({
          id: source.id,
          name: source.name,
          thumbnail: nativeImage.createFromDataURL(source.thumbnail)
        })
      }
      return results
    })())
  })
}
const common = require('../../../common/api/exports/electron')

// Import common modules.
common.defineProperties(exports)

Object.defineProperties(exports, {
  // Renderer side modules, please sort with alphabet order.
  desktopCapturer: {
    enumerable: true,
    get: function () {
      return require('../desktop-capturer')
    }
  },
  ipcRenderer: {
    enumerable: true,
    get: function () {
      return require('../ipc-renderer')
    }
  },
  remote: {
    enumerable: true,
    get: function () {
      return require('../remote')
    }
  },
  screen: {
    enumerable: true,
    get: function () {
      return require('../screen')
    }
  },
  webFrame: {
    enumerable: true,
    get: function () {
      return require('../web-frame')
    }
  }
})
'use strict'

const binding = process.atomBinding('ipc')
const v8Util = process.atomBinding('v8_util')

// Created by init.js.
const ipcRenderer = v8Util.getHiddenValue(global, 'ipc')

ipcRenderer.send = function (...args) {
  return binding.send('ipc-message', args)
}

ipcRenderer.sendSync = function (...args) {
  return JSON.parse(binding.sendSync('ipc-message-sync', args))
}

ipcRenderer.sendToHost = function (...args) {
  return binding.send('ipc-message-host', args)
}

ipcRenderer.sendTo = function (webContentsId, channel, ...args) {
  if (typeof webContentsId !== 'number') {
    throw new TypeError('First argument has to be webContentsId')
  }

  ipcRenderer.send('ELECTRON_BROWSER_SEND_TO', false, webContentsId, channel, ...args)
}

ipcRenderer.sendToAll = function (webContentsId, channel, ...args) {
  if (typeof webContentsId !== 'number') {
    throw new TypeError('First argument has to be webContentsId')
  }

  ipcRenderer.send('ELECTRON_BROWSER_SEND_TO', true, webContentsId, channel, ...args)
}

module.exports = ipcRenderer
'use strict'

const {Buffer} = require('buffer')
const v8Util = process.atomBinding('v8_util')
const {ipcRenderer, isPromise, CallbacksRegistry} = require('electron')

const callbacksRegistry = new CallbacksRegistry()

const remoteObjectCache = v8Util.createIDWeakMap()

// Convert the arguments object into an array of meta data.
const wrapArgs = function (args, visited) {
  if (visited == null) {
    visited = new Set()
  }

  const valueToMeta = function (value) {
    // Check for circular reference.
    if (visited.has(value)) {
      return {
        type: 'value',
        value: null
      }
    }

    if (Array.isArray(value)) {
      visited.add(value)
      let meta = {
        type: 'array',
        value: wrapArgs(value, visited)
      }
      visited.delete(value)
      return meta
    } else if (ArrayBuffer.isView(value)) {
      return {
        type: 'buffer',
        value: Buffer.from(value)
      }
    } else if (value instanceof Date) {
      return {
        type: 'date',
        value: value.getTime()
      }
    } else if ((value != null) && typeof value === 'object') {
      if (isPromise(value)) {
        return {
          type: 'promise',
          then: valueToMeta(function (onFulfilled, onRejected) {
            value.then(onFulfilled, onRejected)
          })
        }
      } else if (v8Util.getHiddenValue(value, 'atomId')) {
        return {
          type: 'remote-object',
          id: v8Util.getHiddenValue(value, 'atomId')
        }
      }

      let meta = {
        type: 'object',
        name: value.constructor != null ? value.constructor.name : '',
        members: []
      }
      visited.add(value)
      for (let prop in value) {
        meta.members.push({
          name: prop,
          value: valueToMeta(value[prop])
        })
      }
      visited.delete(value)
      return meta
    } else if (typeof value === 'function' && v8Util.getHiddenValue(value, 'returnValue')) {
      return {
        type: 'function-with-return-value',
        value: valueToMeta(value())
      }
    } else if (typeof value === 'function') {
      return {
        type: 'function',
        id: callbacksRegistry.add(value),
        location: v8Util.getHiddenValue(value, 'location')
      }
    } else {
      return {
        type: 'value',
        value: value
      }
    }
  }
  return args.map(valueToMeta)
}

// Populate object's members from descriptors.
// The |ref| will be kept referenced by |members|.
// This matches |getObjectMemebers| in rpc-server.
const setObjectMembers = function (ref, object, metaId, members) {
  for (let member of members) {
    if (object.hasOwnProperty(member.name)) continue

    let descriptor = { enumerable: member.enumerable }
    if (member.type === 'method') {
      const remoteMemberFunction = function (...args) {
        if (this && this.constructor === remoteMemberFunction) {
          // Constructor call.
          let ret = ipcRenderer.sendSync('ELECTRON_BROWSER_MEMBER_CONSTRUCTOR', metaId, member.name, wrapArgs(args))
          return metaToValue(ret)
        } else {
          // Call member function.
          let ret = ipcRenderer.sendSync('ELECTRON_BROWSER_MEMBER_CALL', metaId, member.name, wrapArgs(args))
          return metaToValue(ret)
        }
      }

      let descriptorFunction = proxyFunctionProperties(remoteMemberFunction, metaId, member.name)

      descriptor.get = function () {
        descriptorFunction.ref = ref  // The member should reference its object.
        return descriptorFunction
      }
      // Enable monkey-patch the method
      descriptor.set = function (value) {
        descriptorFunction = value
        return value
      }
      descriptor.configurable = true
    } else if (member.type === 'get') {
      descriptor.get = function () {
        return metaToValue(ipcRenderer.sendSync('ELECTRON_BROWSER_MEMBER_GET', metaId, member.name))
      }

      // Only set setter when it is writable.
      if (member.writable) {
        descriptor.set = function (value) {
          ipcRenderer.sendSync('ELECTRON_BROWSER_MEMBER_SET', metaId, member.name, value)
          return value
        }
      }
    }

    Object.defineProperty(object, member.name, descriptor)
  }
}

// Populate object's prototype from descriptor.
// This matches |getObjectPrototype| in rpc-server.
const setObjectPrototype = function (ref, object, metaId, descriptor) {
  if (descriptor === null) return
  let proto = {}
  setObjectMembers(ref, proto, metaId, descriptor.members)
  setObjectPrototype(ref, proto, metaId, descriptor.proto)
  Object.setPrototypeOf(object, proto)
}

// Wrap function in Proxy for accessing remote properties
const proxyFunctionProperties = function (remoteMemberFunction, metaId, name) {
  let loaded = false

  // Lazily load function properties
  const loadRemoteProperties = () => {
    if (loaded) return
    loaded = true
    const meta = ipcRenderer.sendSync('ELECTRON_BROWSER_MEMBER_GET', metaId, name)
    setObjectMembers(remoteMemberFunction, remoteMemberFunction, meta.id, meta.members)
  }

  return new Proxy(remoteMemberFunction, {
    get: (target, property, receiver) => {
      if (!target.hasOwnProperty(property)) loadRemoteProperties()
      return target[property]
    },
    ownKeys: (target) => {
      loadRemoteProperties()
      return Object.getOwnPropertyNames(target)
    },
    getOwnPropertyDescriptor: (target, property) => {
      let descriptor = Object.getOwnPropertyDescriptor(target, property)
      if (descriptor != null) return descriptor
      loadRemoteProperties()
      return Object.getOwnPropertyDescriptor(target, property)
    }
  })
}

// Convert meta data from browser into real value.
const metaToValue = function (meta) {
  var el, i, len, ref1, results, ret
  switch (meta.type) {
    case 'value':
      return meta.value
    case 'array':
      ref1 = meta.members
      results = []
      for (i = 0, len = ref1.length; i < len; i++) {
        el = ref1[i]
        results.push(metaToValue(el))
      }
      return results
    case 'buffer':
      return Buffer.from(meta.value)
    case 'promise':
      return Promise.resolve({
        then: metaToValue(meta.then)
      })
    case 'error':
      return metaToPlainObject(meta)
    case 'date':
      return new Date(meta.value)
    case 'exception':
      throw new Error(meta.message + '\n' + meta.stack)
    default:
      if (remoteObjectCache.has(meta.id)) return remoteObjectCache.get(meta.id)

      if (meta.type === 'function') {
        // A shadow class to represent the remote function object.
        let remoteFunction = function (...args) {
          if (this && this.constructor === remoteFunction) {
            // Constructor call.
            let obj = ipcRenderer.sendSync('ELECTRON_BROWSER_CONSTRUCTOR', meta.id, wrapArgs(args))
            // Returning object in constructor will replace constructed object
            // with the returned object.
            // http://stackoverflow.com/questions/1978049/what-values-can-a-constructor-return-to-avoid-returning-this
            return metaToValue(obj)
          } else {
            // Function call.
            let obj = ipcRenderer.sendSync('ELECTRON_BROWSER_FUNCTION_CALL', meta.id, wrapArgs(args))
            return metaToValue(obj)
          }
        }
        ret = remoteFunction
      } else {
        ret = {}
      }

      // Populate delegate members.
      setObjectMembers(ret, ret, meta.id, meta.members)
      // Populate delegate prototype.
      setObjectPrototype(ret, ret, meta.id, meta.proto)

      // Set constructor.name to object's name.
      Object.defineProperty(ret.constructor, 'name', { value: meta.name })

      // Track delegate object's life time, and tell the browser to clean up
      // when the object is GCed.
      v8Util.setRemoteObjectFreer(ret, meta.id)

      // Remember object's id.
      v8Util.setHiddenValue(ret, 'atomId', meta.id)
      remoteObjectCache.set(meta.id, ret)
      return ret
  }
}

// Construct a plain object from the meta.
const metaToPlainObject = function (meta) {
  var i, len, obj, ref1
  obj = (function () {
    switch (meta.type) {
      case 'error':
        return new Error()
      default:
        return {}
    }
  })()
  ref1 = meta.members
  for (i = 0, len = ref1.length; i < len; i++) {
    let {name, value} = ref1[i]
    obj[name] = value
  }
  return obj
}

// Browser calls a callback in renderer.
ipcRenderer.on('ELECTRON_RENDERER_CALLBACK', function (event, id, args) {
  callbacksRegistry.apply(id, metaToValue(args))
})

// A callback in browser is released.
ipcRenderer.on('ELECTRON_RENDERER_RELEASE_CALLBACK', function (event, id) {
  callbacksRegistry.remove(id)
})

// List all built-in modules in browser process.
const browserModules = require('../../browser/api/exports/electron')

// And add a helper receiver for each one.
for (let name of Object.getOwnPropertyNames(browserModules)) {
  Object.defineProperty(exports, name, {
    get: function () {
      return exports.getBuiltin(name)
    }
  })
}

// Get remote module.
exports.require = function (module) {
  return metaToValue(ipcRenderer.sendSync('ELECTRON_BROWSER_REQUIRE', module))
}

// Alias to remote.require('electron').xxx.
exports.getBuiltin = function (module) {
  return metaToValue(ipcRenderer.sendSync('ELECTRON_BROWSER_GET_BUILTIN', module))
}

// Get current BrowserWindow.
exports.getCurrentWindow = function () {
  return metaToValue(ipcRenderer.sendSync('ELECTRON_BROWSER_CURRENT_WINDOW'))
}

// Get current WebContents object.
exports.getCurrentWebContents = function () {
  return metaToValue(ipcRenderer.sendSync('ELECTRON_BROWSER_CURRENT_WEB_CONTENTS'))
}

// Get a global object in browser.
exports.getGlobal = function (name) {
  return metaToValue(ipcRenderer.sendSync('ELECTRON_BROWSER_GLOBAL', name))
}

// Get the process object in browser.
exports.__defineGetter__('process', function () {
  return exports.getGlobal('process')
})

// Create a funtion that will return the specifed value when called in browser.
exports.createFunctionWithReturnValue = function (returnValue) {
  const func = function () {
    return returnValue
  }
  v8Util.setHiddenValue(func, 'returnValue', true)
  return func
}

// Get the guest WebContents from guestInstanceId.
exports.getGuestWebContents = function (guestInstanceId) {
  const meta = ipcRenderer.sendSync('ELECTRON_BROWSER_GUEST_WEB_CONTENTS', guestInstanceId)
  return metaToValue(meta)
}
module.exports = require('electron').remote.screen
'use strict'

const {EventEmitter} = require('events')
const {webFrame, WebFrame} = process.atomBinding('web_frame')

// WebFrame is an EventEmitter.
Object.setPrototypeOf(WebFrame.prototype, EventEmitter.prototype)

// Lots of webview would subscribe to webFrame's events.
webFrame.setMaxListeners(0)

module.exports = webFrame
const {ipcRenderer} = require('electron')
const Event = require('./extensions/event')
const url = require('url')

let nextId = 0

class Tab {
  constructor (tabId) {
    this.id = tabId
  }
}

class MessageSender {
  constructor (tabId, extensionId) {
    this.tab = tabId ? new Tab(tabId) : null
    this.id = extensionId
    this.url = `chrome-extension://${extensionId}`
  }
}

class Port {
  constructor (tabId, portId, extensionId, name) {
    this.tabId = tabId
    this.portId = portId
    this.disconnected = false

    this.name = name
    this.onDisconnect = new Event()
    this.onMessage = new Event()
    this.sender = new MessageSender(tabId, extensionId)

    ipcRenderer.once(`CHROME_PORT_DISCONNECT_${portId}`, () => {
      this._onDisconnect()
    })
    ipcRenderer.on(`CHROME_PORT_POSTMESSAGE_${portId}`, (event, message) => {
      const sendResponse = function () { console.error('sendResponse is not implemented') }
      this.onMessage.emit(message, this.sender, sendResponse)
    })
  }

  disconnect () {
    if (this.disconnected) return

    ipcRenderer.sendToAll(this.tabId, `CHROME_PORT_DISCONNECT_${this.portId}`)
    this._onDisconnect()
  }

  postMessage (message) {
    ipcRenderer.sendToAll(this.tabId, `CHROME_PORT_POSTMESSAGE_${this.portId}`, message)
  }

  _onDisconnect () {
    this.disconnected = true
    ipcRenderer.removeAllListeners(`CHROME_PORT_POSTMESSAGE_${this.portId}`)
    this.onDisconnect.emit()
  }
}

// Inject chrome API to the |context|
exports.injectTo = function (extensionId, isBackgroundPage, context) {
  const chrome = context.chrome = context.chrome || {}

  ipcRenderer.on(`CHROME_RUNTIME_ONCONNECT_${extensionId}`, (event, tabId, portId, connectInfo) => {
    chrome.runtime.onConnect.emit(new Port(tabId, portId, extensionId, connectInfo.name))
  })

  ipcRenderer.on(`CHROME_RUNTIME_ONMESSAGE_${extensionId}`, (event, tabId, message) => {
    chrome.runtime.onMessage.emit(message, new MessageSender(tabId, extensionId))
  })

  ipcRenderer.on('CHROME_TABS_ONCREATED', (event, tabId) => {
    chrome.tabs.onCreated.emit(new Tab(tabId))
  })

  ipcRenderer.on('CHROME_TABS_ONREMOVED', (event, tabId) => {
    chrome.tabs.onRemoved.emit(tabId)
  })

  chrome.runtime = {
    id: extensionId,

    getURL: function (path) {
      return url.format({
        protocol: 'chrome-extension',
        slashes: true,
        hostname: extensionId,
        pathname: path
      })
    },

    connect (...args) {
      if (isBackgroundPage) {
        console.error('chrome.runtime.connect is not supported in background page')
        return
      }

      // Parse the optional args.
      let targetExtensionId = extensionId
      let connectInfo = {name: ''}
      if (args.length === 1) {
        connectInfo = args[0]
      } else if (args.length === 2) {
        [targetExtensionId, connectInfo] = args
      }

      const {tabId, portId} = ipcRenderer.sendSync('CHROME_RUNTIME_CONNECT', targetExtensionId, connectInfo)
      return new Port(tabId, portId, extensionId, connectInfo.name)
    },

    sendMessage (...args) {
      if (isBackgroundPage) {
        console.error('chrome.runtime.sendMessage is not supported in background page')
        return
      }

      // Parse the optional args.
      let targetExtensionId = extensionId
      let message
      if (args.length === 1) {
        message = args[0]
      } else if (args.length === 2) {
        // A case of not provide extension-id: (message, responseCallback)
        if (typeof args[1] === 'function') {
          console.error('responseCallback is not supported')
          message = args[0]
        } else {
          [targetExtensionId, message] = args
        }
      } else {
        console.error('options and responseCallback are not supported')
      }

      ipcRenderer.send('CHROME_RUNTIME_SENDMESSAGE', targetExtensionId, message)
    },

    onConnect: new Event(),
    onMessage: new Event(),
    onInstalled: new Event()
  }

  chrome.tabs = {
    executeScript (tabId, details, callback) {
      const requestId = ++nextId
      ipcRenderer.once(`CHROME_TABS_EXECUTESCRIPT_RESULT_${requestId}`, (event, result) => {
        callback([event.result])
      })
      ipcRenderer.send('CHROME_TABS_EXECUTESCRIPT', requestId, tabId, extensionId, details)
    },

    sendMessage (tabId, message, options, responseCallback) {
      if (responseCallback) {
        console.error('responseCallback is not supported')
      }
      ipcRenderer.send('CHROME_TABS_SEND_MESSAGE', tabId, extensionId, isBackgroundPage, message)
    },

    onUpdated: new Event(),
    onCreated: new Event(),
    onRemoved: new Event()
  }

  chrome.extension = {
    getURL: chrome.runtime.getURL,
    connect: chrome.runtime.connect,
    onConnect: chrome.runtime.onConnect,
    sendMessage: chrome.runtime.sendMessage,
    onMessage: chrome.runtime.onMessage
  }

  chrome.storage = require('./extensions/storage')

  chrome.pageAction = {
    show () {},
    hide () {},
    setTitle () {},
    getTitle () {},
    setIcon () {},
    setPopup () {},
    getPopup () {}
  }

  chrome.i18n = require('./extensions/i18n').setup(extensionId)
  chrome.webNavigation = require('./extensions/web-navigation').setup()
}
const {ipcRenderer} = require('electron')
const {runInThisContext} = require('vm')

// Check whether pattern matches.
// https://developer.chrome.com/extensions/match_patterns
const matchesPattern = function (pattern) {
  if (pattern === '<all_urls>') return true

  const regexp = new RegExp('^' + pattern.replace(/\*/g, '.*') + '$')
  return location.href.match(regexp)
}

// Run the code with chrome API integrated.
const runContentScript = function (extensionId, url, code) {
  const context = {}
  require('./chrome-api').injectTo(extensionId, false, context)
  const wrapper = `(function (chrome) {\n  ${code}\n  })`
  const compiledWrapper = runInThisContext(wrapper, {
    filename: url,
    lineOffset: 1,
    displayErrors: true
  })
  return compiledWrapper.call(this, context.chrome)
}

// Run injected scripts.
// https://developer.chrome.com/extensions/content_scripts
const injectContentScript = function (extensionId, script) {
  for (const match of script.matches) {
    if (!matchesPattern(match)) return
  }

  for (const {url, code} of script.js) {
    const fire = runContentScript.bind(window, extensionId, url, code)
    if (script.runAt === 'document_start') {
      process.once('document-start', fire)
    } else if (script.runAt === 'document_end') {
      process.once('document-end', fire)
    } else if (script.runAt === 'document_idle') {
      document.addEventListener('DOMContentLoaded', fire)
    }
  }
}

// Handle the request of chrome.tabs.executeJavaScript.
ipcRenderer.on('CHROME_TABS_EXECUTESCRIPT', function (event, senderWebContentsId, requestId, extensionId, url, code) {
  const result = runContentScript.call(window, extensionId, url, code)
  ipcRenderer.sendToAll(senderWebContentsId, `CHROME_TABS_EXECUTESCRIPT_RESULT_${requestId}`, result)
})

// Read the renderer process preferences.
const preferences = process.getRenderProcessPreferences()
if (preferences) {
  for (const pref of preferences) {
    if (pref.contentScripts) {
      for (const script of pref.contentScripts) {
        injectContentScript(pref.extensionId, script)
      }
    }
  }
}
class Event {
  constructor () {
    this.listeners = []
  }

  addListener (callback) {
    this.listeners.push(callback)
  }

  removeListener (callback) {
    const index = this.listeners.indexOf(callback)
    if (index !== -1) {
      this.listeners.splice(index, 1)
    }
  }

  emit (...args) {
    for (const listener of this.listeners) {
      listener(...args)
    }
  }
}

module.exports = Event
// Implementation of chrome.i18n.getMessage
// https://developer.chrome.com/extensions/i18n#method-getMessage
//
// Does not implement predefined messages:
// https://developer.chrome.com/extensions/i18n#overview-predefined

const {ipcRenderer} = require('electron')
const fs = require('fs')
const path = require('path')

let metadata

const getExtensionMetadata = (extensionId) => {
  if (!metadata) {
    metadata = ipcRenderer.sendSync('CHROME_I18N_MANIFEST', extensionId)
  }
  return metadata
}

const getMessagesPath = (extensionId, language) => {
  const metadata = getExtensionMetadata(extensionId)
  const defaultLocale = metadata.default_locale || 'en'
  const localesDirectory = path.join(metadata.srcDirectory, '_locales')
  let messagesPath = path.join(localesDirectory, language, 'messages.json')
  if (!fs.statSyncNoException(messagesPath)) {
    messagesPath = path.join(localesDirectory, defaultLocale, 'messages.json')
  }
  return messagesPath
}

const getMessages = (extensionId, language) => {
  try {
    const messagesPath = getMessagesPath(extensionId, language)
    return JSON.parse(fs.readFileSync(messagesPath)) || {}
  } catch (error) {
    return {}
  }
}

const getLanguage = () => {
  return navigator.language.replace(/-.*$/, '').toLowerCase()
}

const replaceNumberedSubstitutions = (message, substitutions) => {
  return message.replace(/\$(\d+)/, (_, number) => {
    const index = parseInt(number, 10) - 1
    return substitutions[index] || ''
  })
}

const replacePlaceholders = (message, placeholders, substitutions) => {
  if (typeof substitutions === 'string') {
    substitutions = [substitutions]
  }
  if (!Array.isArray(substitutions)) {
    substitutions = []
  }

  if (placeholders) {
    Object.keys(placeholders).forEach((name) => {
      let {content} = placeholders[name]
      content = replaceNumberedSubstitutions(content, substitutions)
      message = message.replace(new RegExp(`\\$${name}\\$`, 'gi'), content)
    })
  }

  return replaceNumberedSubstitutions(message, substitutions)
}

const getMessage = (extensionId, messageName, substitutions) => {
  const messages = getMessages(extensionId, getLanguage())
  if (messages.hasOwnProperty(messageName)) {
    const {message, placeholders} = messages[messageName]
    return replacePlaceholders(message, placeholders, substitutions)
  }
}

exports.setup = (extensionId) => {
  return {
    getMessage (messageName, substitutions) {
      return getMessage(extensionId, messageName, substitutions)
    }
  }
}
const getStorage = (storageType) => {
  const data = window.localStorage.getItem(`__chrome.storage.${storageType}__`)
  if (data != null) {
    return JSON.parse(data)
  } else {
    return {}
  }
}

const setStorage = (storageType, storage) => {
  const json = JSON.stringify(storage)
  window.localStorage.setItem(`__chrome.storage.${storageType}__`, json)
}

const scheduleCallback = (items, callback) => {
  setTimeout(function () {
    callback(items)
  })
}

const getStorageManager = (storageType) => {
  return {
    get (keys, callback) {
      const storage = getStorage(storageType)
      if (keys == null) return scheduleCallback(storage, callback)

      let defaults = {}
      switch (typeof keys) {
        case 'string':
          keys = [keys]
          break
        case 'object':
          if (!Array.isArray(keys)) {
            defaults = keys
            keys = Object.keys(keys)
          }
          break
      }
      if (keys.length === 0) return scheduleCallback({}, callback)

      let items = {}
      keys.forEach(function (key) {
        var value = storage[key]
        if (value == null) value = defaults[key]
        items[key] = value
      })
      scheduleCallback(items, callback)
    },

    set (items, callback) {
      const storage = getStorage(storageType)

      Object.keys(items).forEach(function (name) {
        storage[name] = items[name]
      })

      setStorage(storageType, storage)

      setTimeout(callback)
    }
  }
}

module.exports = {
  sync: getStorageManager('sync'),
  local: getStorageManager('local')
}
const Event = require('./event')
const {ipcRenderer} = require('electron')

class WebNavigation {
  constructor () {
    this.onBeforeNavigate = new Event()
    this.onCompleted = new Event()

    ipcRenderer.on('CHROME_WEBNAVIGATION_ONBEFORENAVIGATE', (event, details) => {
      this.onBeforeNavigate.emit(details)
    })

    ipcRenderer.on('CHROME_WEBNAVIGATION_ONCOMPLETED', (event, details) => {
      this.onCompleted.emit(details)
    })
  }
}

exports.setup = () => {
  return new WebNavigation()
}
'use strict'

const events = require('events')
const path = require('path')
const Module = require('module')

// We modified the original process.argv to let node.js load the
// atom-renderer.js, we need to restore it here.
process.argv.splice(1, 1)

// Clear search paths.
require('../common/reset-search-paths')

// Import common settings.
require('../common/init')

var globalPaths = Module.globalPaths

// Expose public APIs.
globalPaths.push(path.join(__dirname, 'api', 'exports'))

// The global variable will be used by ipc for event dispatching
var v8Util = process.atomBinding('v8_util')

v8Util.setHiddenValue(global, 'ipc', new events.EventEmitter())

// Use electron module after everything is ready.
const electron = require('electron')

// Call webFrame method.
electron.ipcRenderer.on('ELECTRON_INTERNAL_RENDERER_WEB_FRAME_METHOD', (event, method, args) => {
  electron.webFrame[method](...args)
})

electron.ipcRenderer.on('ELECTRON_INTERNAL_RENDERER_SYNC_WEB_FRAME_METHOD', (event, requestId, method, args) => {
  const result = electron.webFrame[method](...args)
  event.sender.send(`ELECTRON_INTERNAL_BROWSER_SYNC_WEB_FRAME_RESPONSE_${requestId}`, result)
})

electron.ipcRenderer.on('ELECTRON_INTERNAL_RENDERER_ASYNC_WEB_FRAME_METHOD', (event, requestId, method, args) => {
  electron.webFrame[method](...args, function (result) {
    event.sender.send(`ELECTRON_INTERNAL_BROWSER_ASYNC_WEB_FRAME_RESPONSE_${requestId}`, result)
  })
})

// Process command line arguments.
let nodeIntegration = 'false'
let preloadScript = null
let isBackgroundPage = false
for (let arg of process.argv) {
  if (arg.indexOf('--guest-instance-id=') === 0) {
    // This is a guest web view.
    process.guestInstanceId = parseInt(arg.substr(arg.indexOf('=') + 1))
  } else if (arg.indexOf('--opener-id=') === 0) {
    // This is a guest BrowserWindow.
    process.openerId = parseInt(arg.substr(arg.indexOf('=') + 1))
  } else if (arg.indexOf('--node-integration=') === 0) {
    nodeIntegration = arg.substr(arg.indexOf('=') + 1)
  } else if (arg.indexOf('--preload=') === 0) {
    preloadScript = arg.substr(arg.indexOf('=') + 1)
  } else if (arg === '--background-page') {
    isBackgroundPage = true
  }
}

if (window.location.protocol === 'chrome-devtools:') {
  // Override some inspector APIs.
  require('./inspector')
  nodeIntegration = 'true'
} else if (window.location.protocol === 'chrome-extension:') {
  // Add implementations of chrome API.
  require('./chrome-api').injectTo(window.location.hostname, isBackgroundPage, window)
  nodeIntegration = 'false'
} else {
  // Override default web functions.
  require('./override')

  // Inject content scripts.
  require('./content-scripts-injector')

  // Load webview tag implementation.
  if (nodeIntegration === 'true' && process.guestInstanceId == null) {
    require('./web-view/web-view')
    require('./web-view/web-view-attributes')
  }
}

if (nodeIntegration === 'true') {
  // Export node bindings to global.
  global.require = require
  global.module = module

  // Set the __filename to the path of html file if it is file: protocol.
  if (window.location.protocol === 'file:') {
    var pathname = process.platform === 'win32' && window.location.pathname[0] === '/' ? window.location.pathname.substr(1) : window.location.pathname
    global.__filename = path.normalize(decodeURIComponent(pathname))
    global.__dirname = path.dirname(global.__filename)

    // Set module's filename so relative require can work as expected.
    module.filename = global.__filename

    // Also search for module under the html file.
    module.paths = module.paths.concat(Module._nodeModulePaths(global.__dirname))
  } else {
    global.__filename = __filename
    global.__dirname = __dirname
  }

  // Redirect window.onerror to uncaughtException.
  window.onerror = function (message, filename, lineno, colno, error) {
    if (global.process.listeners('uncaughtException').length > 0) {
      global.process.emit('uncaughtException', error)
      return true
    } else {
      return false
    }
  }
} else {
  // Delete Node's symbols after the Environment has been loaded.
  process.once('loaded', function () {
    delete global.process
    delete global.setImmediate
    delete global.clearImmediate
    delete global.global
  })
}

// Load the script specfied by the "preload" attribute.
if (preloadScript) {
  try {
    require(preloadScript)
  } catch (error) {
    console.error('Unable to load preload script: ' + preloadScript)
    console.error(error.stack || error.message)
  }
}
window.onload = function () {
  // Use menu API to show context menu.
  window.InspectorFrontendHost.showContextMenuAtPoint = createMenu

  // Use dialog API to override file chooser dialog.
  window.WebInspector.createFileSelectorElement = createFileSelectorElement
}

const convertToMenuTemplate = function (items) {
  return items.map(function (item) {
    const transformed = item.type === 'subMenu' ? {
      type: 'submenu',
      label: item.label,
      enabled: item.enabled,
      submenu: convertToMenuTemplate(item.subItems)
    } : item.type === 'separator' ? {
      type: 'separator'
    } : item.type === 'checkbox' ? {
      type: 'checkbox',
      label: item.label,
      enabled: item.enabled,
      checked: item.checked
    } : {
      type: 'normal',
      label: item.label,
      enabled: item.enabled
    }

    if (item.id != null) {
      transformed.click = function () {
        window.DevToolsAPI.contextMenuItemSelected(item.id)
        return window.DevToolsAPI.contextMenuCleared()
      }
    }

    return transformed
  })
}

const createMenu = function (x, y, items) {
  const {remote} = require('electron')
  const {Menu} = remote

  let template = convertToMenuTemplate(items)
  if (useEditMenuItems(x, y, template)) {
    template = getEditMenuItems()
  }
  const menu = Menu.buildFromTemplate(template)

  // The menu is expected to show asynchronously.
  setTimeout(function () {
    menu.popup(remote.getCurrentWindow())
  })
}

const useEditMenuItems = function (x, y, items) {
  return items.length === 0 && document.elementsFromPoint(x, y).some(function (element) {
    return element.nodeName === 'INPUT' || element.nodeName === 'TEXTAREA' || element.isContentEditable
  })
}

const getEditMenuItems = function () {
  return [
    {
      role: 'undo'
    },
    {
      role: 'redo'
    },
    {
      type: 'separator'
    },
    {
      role: 'cut'
    },
    {
      role: 'copy'
    },
    {
      role: 'paste'
    },
    {
      role: 'pasteandmatchstyle'
    },
    {
      role: 'delete'
    },
    {
      role: 'selectall'
    }
  ]
}

const showFileChooserDialog = function (callback) {
  const {dialog} = require('electron').remote
  const files = dialog.showOpenDialog({})
  if (files != null) {
    callback(pathToHtml5FileObject(files[0]))
  }
}

const pathToHtml5FileObject = function (path) {
  const fs = require('fs')
  const blob = new Blob([fs.readFileSync(path)])
  blob.name = path
  return blob
}

const createFileSelectorElement = function (callback) {
  const fileSelectorElement = document.createElement('span')
  fileSelectorElement.style.display = 'none'
  fileSelectorElement.click = showFileChooserDialog.bind(this, callback)
  return fileSelectorElement
}
'use strict'

const {ipcRenderer} = require('electron')

const {defineProperty} = Object

// Helper function to resolve relative url.
const a = window.top.document.createElement('a')
const resolveURL = function (url) {
  a.href = url
  return a.href
}

// Window object returned by "window.open".
const BrowserWindowProxy = (function () {
  BrowserWindowProxy.proxies = {}

  BrowserWindowProxy.getOrCreate = function (guestId) {
    let proxy = this.proxies[guestId]
    if (proxy == null) {
      proxy = new BrowserWindowProxy(guestId)
      this.proxies[guestId] = proxy
    }
    return proxy
  }

  BrowserWindowProxy.remove = function (guestId) {
    delete this.proxies[guestId]
  }

  function BrowserWindowProxy (guestId1) {
    defineProperty(this, 'guestId', {
      configurable: false,
      enumerable: true,
      writeable: false,
      value: guestId1
    })

    this.closed = false
    ipcRenderer.once('ELECTRON_GUEST_WINDOW_MANAGER_WINDOW_CLOSED_' + this.guestId, () => {
      BrowserWindowProxy.remove(this.guestId)
      this.closed = true
    })
  }

  BrowserWindowProxy.prototype.close = function () {
    ipcRenderer.send('ELECTRON_GUEST_WINDOW_MANAGER_WINDOW_CLOSE', this.guestId)
  }

  BrowserWindowProxy.prototype.focus = function () {
    ipcRenderer.send('ELECTRON_GUEST_WINDOW_MANAGER_WINDOW_METHOD', this.guestId, 'focus')
  }

  BrowserWindowProxy.prototype.blur = function () {
    ipcRenderer.send('ELECTRON_GUEST_WINDOW_MANAGER_WINDOW_METHOD', this.guestId, 'blur')
  }

  BrowserWindowProxy.prototype.print = function () {
    ipcRenderer.send('ELECTRON_GUEST_WINDOW_MANAGER_WEB_CONTENTS_METHOD', this.guestId, 'print')
  }

  defineProperty(BrowserWindowProxy.prototype, 'location', {
    get: function () {
      return ipcRenderer.sendSync('ELECTRON_GUEST_WINDOW_MANAGER_WEB_CONTENTS_METHOD_SYNC', this.guestId, 'getURL')
    },
    set: function (url) {
      url = resolveURL(url)
      return ipcRenderer.sendSync('ELECTRON_GUEST_WINDOW_MANAGER_WEB_CONTENTS_METHOD_SYNC', this.guestId, 'loadURL', url)
    }
  })

  BrowserWindowProxy.prototype.postMessage = function (message, targetOrigin) {
    if (targetOrigin == null) {
      targetOrigin = '*'
    }
    ipcRenderer.send('ELECTRON_GUEST_WINDOW_MANAGER_WINDOW_POSTMESSAGE', this.guestId, message, targetOrigin, window.location.origin)
  }

  BrowserWindowProxy.prototype['eval'] = function (...args) {
    ipcRenderer.send('ELECTRON_GUEST_WINDOW_MANAGER_WEB_CONTENTS_METHOD', this.guestId, 'executeJavaScript', ...args)
  }

  return BrowserWindowProxy
})()

if (process.guestInstanceId == null) {
  // Override default window.close.
  window.close = function () {
    ipcRenderer.sendSync('ELECTRON_BROWSER_WINDOW_CLOSE')
  }
}

// Make the browser window or guest view emit "new-window" event.
window.open = function (url, frameName, features) {
  var feature, guestId, i, j, len, len1, name, options, ref1, ref2, value
  if (frameName == null) {
    frameName = ''
  }
  if (features == null) {
    features = ''
  }
  options = {}

  const ints = ['x', 'y', 'width', 'height', 'minWidth', 'maxWidth', 'minHeight', 'maxHeight', 'zoomFactor']
  const webPreferences = ['zoomFactor', 'nodeIntegration', 'preload']
  const disposition = 'new-window'

  // Make sure to get rid of excessive whitespace in the property name
  ref1 = features.split(/,\s*/)
  for (i = 0, len = ref1.length; i < len; i++) {
    feature = ref1[i]
    ref2 = feature.split(/\s*=/)
    name = ref2[0]
    value = ref2[1]
    value = value === 'yes' || value === '1' ? true : value === 'no' || value === '0' ? false : value
    if (webPreferences.includes(name)) {
      if (options.webPreferences == null) {
        options.webPreferences = {}
      }
      options.webPreferences[name] = value
    } else {
      options[name] = value
    }
  }
  if (options.left) {
    if (options.x == null) {
      options.x = options.left
    }
  }
  if (options.top) {
    if (options.y == null) {
      options.y = options.top
    }
  }
  if (options.title == null) {
    options.title = frameName
  }
  if (options.width == null) {
    options.width = 800
  }
  if (options.height == null) {
    options.height = 600
  }

  // Resolve relative urls.
  if (url == null || url === '') {
    url = 'about:blank'
  } else {
    url = resolveURL(url)
  }
  for (j = 0, len1 = ints.length; j < len1; j++) {
    name = ints[j]
    if (options[name] != null) {
      options[name] = parseInt(options[name], 10)
    }
  }
  guestId = ipcRenderer.sendSync('ELECTRON_GUEST_WINDOW_MANAGER_WINDOW_OPEN', url, frameName, disposition, options)
  if (guestId) {
    return BrowserWindowProxy.getOrCreate(guestId)
  } else {
    return null
  }
}

window.alert = function (message, title) {
  ipcRenderer.sendSync('ELECTRON_BROWSER_WINDOW_ALERT', message, title)
}

window.confirm = function (message, title) {
  return ipcRenderer.sendSync('ELECTRON_BROWSER_WINDOW_CONFIRM', message, title)
}

// But we do not support prompt().
window.prompt = function () {
  throw new Error('prompt() is and will not be supported.')
}

if (process.openerId != null) {
  window.opener = BrowserWindowProxy.getOrCreate(process.openerId)
}

ipcRenderer.on('ELECTRON_GUEST_WINDOW_POSTMESSAGE', function (event, sourceId, message, sourceOrigin) {
  // Manually dispatch event instead of using postMessage because we also need to
  // set event.source.
  event = document.createEvent('Event')
  event.initEvent('message', false, false)
  event.data = message
  event.origin = sourceOrigin
  event.source = BrowserWindowProxy.getOrCreate(sourceId)
  window.dispatchEvent(event)
})

// Forward history operations to browser.
var sendHistoryOperation = function (...args) {
  ipcRenderer.send('ELECTRON_NAVIGATION_CONTROLLER', ...args)
}

var getHistoryOperation = function (...args) {
  return ipcRenderer.sendSync('ELECTRON_SYNC_NAVIGATION_CONTROLLER', ...args)
}

window.history.back = function () {
  sendHistoryOperation('goBack')
}

window.history.forward = function () {
  sendHistoryOperation('goForward')
}

window.history.go = function (offset) {
  sendHistoryOperation('goToOffset', offset)
}

defineProperty(window.history, 'length', {
  get: function () {
    return getHistoryOperation('length')
  }
})

// The initial visibilityState.
let cachedVisibilityState = process.argv.includes('--hidden-page') ? 'hidden' : 'visible'

// Subscribe to visibilityState changes.
ipcRenderer.on('ELECTRON_RENDERER_WINDOW_VISIBILITY_CHANGE', function (event, visibilityState) {
  if (cachedVisibilityState !== visibilityState) {
    cachedVisibilityState = visibilityState
    document.dispatchEvent(new Event('visibilitychange'))
  }
})

// Make document.hidden and document.visibilityState return the correct value.
defineProperty(document, 'hidden', {
  get: function () {
    return cachedVisibilityState !== 'visible'
  }
})

defineProperty(document, 'visibilityState', {
  get: function () {
    return cachedVisibilityState
  }
})
'use strict'

const ipcRenderer = require('electron').ipcRenderer
const webFrame = require('electron').webFrame

var requestId = 0

var WEB_VIEW_EVENTS = {
  'load-commit': ['url', 'isMainFrame'],
  'did-finish-load': [],
  'did-fail-load': ['errorCode', 'errorDescription', 'validatedURL', 'isMainFrame'],
  'did-frame-finish-load': ['isMainFrame'],
  'did-start-loading': [],
  'did-stop-loading': [],
  'did-get-response-details': ['status', 'newURL', 'originalURL', 'httpResponseCode', 'requestMethod', 'referrer', 'headers', 'resourceType'],
  'did-get-redirect-request': ['oldURL', 'newURL', 'isMainFrame'],
  'dom-ready': [],
  'console-message': ['level', 'message', 'line', 'sourceId'],
  'devtools-opened': [],
  'devtools-closed': [],
  'devtools-focused': [],
  'new-window': ['url', 'frameName', 'disposition', 'options'],
  'will-navigate': ['url'],
  'did-navigate': ['url'],
  'did-navigate-in-page': ['url', 'isMainFrame'],
  'close': [],
  'crashed': [],
  'gpu-crashed': [],
  'plugin-crashed': ['name', 'version'],
  'destroyed': [],
  'page-title-updated': ['title', 'explicitSet'],
  'page-favicon-updated': ['favicons'],
  'enter-html-full-screen': [],
  'leave-html-full-screen': [],
  'media-started-playing': [],
  'media-paused': [],
  'found-in-page': ['result'],
  'did-change-theme-color': ['themeColor'],
  'update-target-url': ['url']
}

var DEPRECATED_EVENTS = {
  'page-title-updated': 'page-title-set'
}

var dispatchEvent = function (webView, eventName, eventKey, ...args) {
  var domEvent, f, i, j, len, ref1
  if (DEPRECATED_EVENTS[eventName] != null) {
    dispatchEvent(webView, DEPRECATED_EVENTS[eventName], eventKey, ...args)
  }
  domEvent = new Event(eventName)
  ref1 = WEB_VIEW_EVENTS[eventKey]
  for (i = j = 0, len = ref1.length; j < len; i = ++j) {
    f = ref1[i]
    domEvent[f] = args[i]
  }
  webView.dispatchEvent(domEvent)
  if (eventName === 'load-commit') {
    return webView.onLoadCommit(domEvent)
  }
}

module.exports = {
  registerEvents: function (webView, viewInstanceId) {
    ipcRenderer.on('ELECTRON_GUEST_VIEW_INTERNAL_DISPATCH_EVENT-' + viewInstanceId, function (event, eventName, ...args) {
      dispatchEvent(webView, eventName, eventName, ...args)
    })

    ipcRenderer.on('ELECTRON_GUEST_VIEW_INTERNAL_IPC_MESSAGE-' + viewInstanceId, function (event, channel, ...args) {
      var domEvent = new Event('ipc-message')
      domEvent.channel = channel
      domEvent.args = args
      webView.dispatchEvent(domEvent)
    })

    return ipcRenderer.on('ELECTRON_GUEST_VIEW_INTERNAL_SIZE_CHANGED-' + viewInstanceId, function (event, ...args) {
      var domEvent, f, i, j, len, ref1
      domEvent = new Event('size-changed')
      ref1 = ['oldWidth', 'oldHeight', 'newWidth', 'newHeight']
      for (i = j = 0, len = ref1.length; j < len; i = ++j) {
        f = ref1[i]
        domEvent[f] = args[i]
      }
      webView.onSizeChanged(domEvent)
    })
  },
  deregisterEvents: function (viewInstanceId) {
    ipcRenderer.removeAllListeners('ELECTRON_GUEST_VIEW_INTERNAL_DISPATCH_EVENT-' + viewInstanceId)
    ipcRenderer.removeAllListeners('ELECTRON_GUEST_VIEW_INTERNAL_IPC_MESSAGE-' + viewInstanceId)
    return ipcRenderer.removeAllListeners('ELECTRON_GUEST_VIEW_INTERNAL_SIZE_CHANGED-' + viewInstanceId)
  },
  createGuest: function (params, callback) {
    requestId++
    ipcRenderer.send('ELECTRON_GUEST_VIEW_MANAGER_CREATE_GUEST', params, requestId)
    return ipcRenderer.once('ELECTRON_RESPONSE_' + requestId, callback)
  },
  attachGuest: function (elementInstanceId, guestInstanceId, params) {
    ipcRenderer.send('ELECTRON_GUEST_VIEW_MANAGER_ATTACH_GUEST', elementInstanceId, guestInstanceId, params)
    return webFrame.attachGuest(elementInstanceId)
  },
  destroyGuest: function (guestInstanceId) {
    return ipcRenderer.send('ELECTRON_GUEST_VIEW_MANAGER_DESTROY_GUEST', guestInstanceId)
  },
  setSize: function (guestInstanceId, params) {
    return ipcRenderer.send('ELECTRON_GUEST_VIEW_MANAGER_SET_SIZE', guestInstanceId, params)
  }
}
'use strict'

const WebViewImpl = require('./web-view')
const guestViewInternal = require('./guest-view-internal')
const webViewConstants = require('./web-view-constants')
const remote = require('electron').remote

// Helper function to resolve url set in attribute.
var a = document.createElement('a')

var resolveURL = function (url) {
  if (url === '') return ''
  a.href = url
  return a.href
}

// Attribute objects.
// Default implementation of a WebView attribute.
class WebViewAttribute {
  constructor (name, webViewImpl) {
    this.name = name
    this.value = webViewImpl.webviewNode[name] || ''
    this.webViewImpl = webViewImpl
    this.ignoreMutation = false
    this.defineProperty()
  }

  // Retrieves and returns the attribute's value.
  getValue () {
    return this.webViewImpl.webviewNode.getAttribute(this.name) || this.value
  }

  // Sets the attribute's value.
  setValue (value) {
    return this.webViewImpl.webviewNode.setAttribute(this.name, value || '')
  }

  // Changes the attribute's value without triggering its mutation handler.
  setValueIgnoreMutation (value) {
    this.ignoreMutation = true
    this.setValue(value)
    this.ignoreMutation = false
  }

  // Defines this attribute as a property on the webview node.
  defineProperty () {
    return Object.defineProperty(this.webViewImpl.webviewNode, this.name, {
      get: () => {
        return this.getValue()
      },
      set: (value) => {
        return this.setValue(value)
      },
      enumerable: true
    })
  }

  // Called when the attribute's value changes.
  handleMutation () {}
}

// An attribute that is treated as a Boolean.
class BooleanAttribute extends WebViewAttribute {
  getValue () {
    return this.webViewImpl.webviewNode.hasAttribute(this.name)
  }

  setValue (value) {
    if (!value) {
      return this.webViewImpl.webviewNode.removeAttribute(this.name)
    } else {
      return this.webViewImpl.webviewNode.setAttribute(this.name, '')
    }
  }
}

// Attribute used to define the demension limits of autosizing.
class AutosizeDimensionAttribute extends WebViewAttribute {
  getValue () {
    return parseInt(this.webViewImpl.webviewNode.getAttribute(this.name)) || 0
  }

  handleMutation () {
    if (!this.webViewImpl.guestInstanceId) {
      return
    }
    return guestViewInternal.setSize(this.webViewImpl.guestInstanceId, {
      enableAutoSize: this.webViewImpl.attributes[webViewConstants.ATTRIBUTE_AUTOSIZE].getValue(),
      min: {
        width: parseInt(this.webViewImpl.attributes[webViewConstants.ATTRIBUTE_MINWIDTH].getValue() || 0),
        height: parseInt(this.webViewImpl.attributes[webViewConstants.ATTRIBUTE_MINHEIGHT].getValue() || 0)
      },
      max: {
        width: parseInt(this.webViewImpl.attributes[webViewConstants.ATTRIBUTE_MAXWIDTH].getValue() || 0),
        height: parseInt(this.webViewImpl.attributes[webViewConstants.ATTRIBUTE_MAXHEIGHT].getValue() || 0)
      }
    })
  }
}

// Attribute that specifies whether the webview should be autosized.
class AutosizeAttribute extends BooleanAttribute {
  constructor (webViewImpl) {
    super(webViewConstants.ATTRIBUTE_AUTOSIZE, webViewImpl)
  }
}

AutosizeAttribute.prototype.handleMutation = AutosizeDimensionAttribute.prototype.handleMutation

// Attribute representing the state of the storage partition.
class PartitionAttribute extends WebViewAttribute {
  constructor (webViewImpl) {
    super(webViewConstants.ATTRIBUTE_PARTITION, webViewImpl)
    this.validPartitionId = true
  }

  handleMutation (oldValue, newValue) {
    newValue = newValue || ''

    // The partition cannot change if the webview has already navigated.
    if (!this.webViewImpl.beforeFirstNavigation) {
      window.console.error(webViewConstants.ERROR_MSG_ALREADY_NAVIGATED)
      this.setValueIgnoreMutation(oldValue)
      return
    }
    if (newValue === 'persist:') {
      this.validPartitionId = false
      return window.console.error(webViewConstants.ERROR_MSG_INVALID_PARTITION_ATTRIBUTE)
    }
  }
}

// Attribute that handles the location and navigation of the webview.
class SrcAttribute extends WebViewAttribute {
  constructor (webViewImpl) {
    super(webViewConstants.ATTRIBUTE_SRC, webViewImpl)
    this.setupMutationObserver()
  }

  getValue () {
    if (this.webViewImpl.webviewNode.hasAttribute(this.name)) {
      return resolveURL(this.webViewImpl.webviewNode.getAttribute(this.name))
    } else {
      return this.value
    }
  }

  setValueIgnoreMutation (value) {
    super.setValueIgnoreMutation(value)

    // takeRecords() is needed to clear queued up src mutations. Without it, it
    // is possible for this change to get picked up asyncronously by src's
    // mutation observer |observer|, and then get handled even though we do not
    // want to handle this mutation.
    return this.observer.takeRecords()
  }

  handleMutation (oldValue, newValue) {
    // Once we have navigated, we don't allow clearing the src attribute.
    // Once <webview> enters a navigated state, it cannot return to a
    // placeholder state.
    if (!newValue && oldValue) {
      // src attribute changes normally initiate a navigation. We suppress
      // the next src attribute handler call to avoid reloading the page
      // on every guest-initiated navigation.
      this.setValueIgnoreMutation(oldValue)
      return
    }
    return this.parse()
  }

  // The purpose of this mutation observer is to catch assignment to the src
  // attribute without any changes to its value. This is useful in the case
  // where the webview guest has crashed and navigating to the same address
  // spawns off a new process.
  setupMutationObserver () {
    var params
    this.observer = new MutationObserver((mutations) => {
      var i, len, mutation, newValue, oldValue
      for (i = 0, len = mutations.length; i < len; i++) {
        mutation = mutations[i]
        oldValue = mutation.oldValue
        newValue = this.getValue()
        if (oldValue !== newValue) {
          return
        }
        this.handleMutation(oldValue, newValue)
      }
    })
    params = {
      attributes: true,
      attributeOldValue: true,
      attributeFilter: [this.name]
    }
    return this.observer.observe(this.webViewImpl.webviewNode, params)
  }

  parse () {
    var guestContents, httpreferrer, opts, useragent
    if (!this.webViewImpl.elementAttached || !this.webViewImpl.attributes[webViewConstants.ATTRIBUTE_PARTITION].validPartitionId || !this.getValue()) {
      return
    }
    if (this.webViewImpl.guestInstanceId == null) {
      if (this.webViewImpl.beforeFirstNavigation) {
        this.webViewImpl.beforeFirstNavigation = false
        this.webViewImpl.createGuest()
      }
      return
    }

    // Navigate to |this.src|.
    opts = {}
    httpreferrer = this.webViewImpl.attributes[webViewConstants.ATTRIBUTE_HTTPREFERRER].getValue()
    if (httpreferrer) {
      opts.httpReferrer = httpreferrer
    }
    useragent = this.webViewImpl.attributes[webViewConstants.ATTRIBUTE_USERAGENT].getValue()
    if (useragent) {
      opts.userAgent = useragent
    }
    guestContents = remote.getGuestWebContents(this.webViewImpl.guestInstanceId)
    return guestContents.loadURL(this.getValue(), opts)
  }
}

// Attribute specifies HTTP referrer.
class HttpReferrerAttribute extends WebViewAttribute {
  constructor (webViewImpl) {
    super(webViewConstants.ATTRIBUTE_HTTPREFERRER, webViewImpl)
  }
}

// Attribute specifies user agent
class UserAgentAttribute extends WebViewAttribute {
  constructor (webViewImpl) {
    super(webViewConstants.ATTRIBUTE_USERAGENT, webViewImpl)
  }
}

// Attribute that set preload script.
class PreloadAttribute extends WebViewAttribute {
  constructor (webViewImpl) {
    super(webViewConstants.ATTRIBUTE_PRELOAD, webViewImpl)
  }

  getValue () {
    var preload, protocol
    if (!this.webViewImpl.webviewNode.hasAttribute(this.name)) {
      return this.value
    }
    preload = resolveURL(this.webViewImpl.webviewNode.getAttribute(this.name))
    protocol = preload.substr(0, 5)
    if (protocol !== 'file:') {
      console.error(webViewConstants.ERROR_MSG_INVALID_PRELOAD_ATTRIBUTE)
      preload = ''
    }
    return preload
  }
}

// Attribute that specifies the blink features to be enabled.
class BlinkFeaturesAttribute extends WebViewAttribute {
  constructor (webViewImpl) {
    super(webViewConstants.ATTRIBUTE_BLINKFEATURES, webViewImpl)
  }
}

// Attribute that specifies the blink features to be disabled.
class DisableBlinkFeaturesAttribute extends WebViewAttribute {
  constructor (webViewImpl) {
    super(webViewConstants.ATTRIBUTE_DISABLEBLINKFEATURES, webViewImpl)
  }
}

// Sets up all of the webview attributes.
WebViewImpl.prototype.setupWebViewAttributes = function () {
  this.attributes = {}
  this.attributes[webViewConstants.ATTRIBUTE_AUTOSIZE] = new AutosizeAttribute(this)
  this.attributes[webViewConstants.ATTRIBUTE_PARTITION] = new PartitionAttribute(this)
  this.attributes[webViewConstants.ATTRIBUTE_SRC] = new SrcAttribute(this)
  this.attributes[webViewConstants.ATTRIBUTE_HTTPREFERRER] = new HttpReferrerAttribute(this)
  this.attributes[webViewConstants.ATTRIBUTE_USERAGENT] = new UserAgentAttribute(this)
  this.attributes[webViewConstants.ATTRIBUTE_NODEINTEGRATION] = new BooleanAttribute(webViewConstants.ATTRIBUTE_NODEINTEGRATION, this)
  this.attributes[webViewConstants.ATTRIBUTE_PLUGINS] = new BooleanAttribute(webViewConstants.ATTRIBUTE_PLUGINS, this)
  this.attributes[webViewConstants.ATTRIBUTE_DISABLEWEBSECURITY] = new BooleanAttribute(webViewConstants.ATTRIBUTE_DISABLEWEBSECURITY, this)
  this.attributes[webViewConstants.ATTRIBUTE_ALLOWPOPUPS] = new BooleanAttribute(webViewConstants.ATTRIBUTE_ALLOWPOPUPS, this)
  this.attributes[webViewConstants.ATTRIBUTE_PRELOAD] = new PreloadAttribute(this)
  this.attributes[webViewConstants.ATTRIBUTE_BLINKFEATURES] = new BlinkFeaturesAttribute(this)
  this.attributes[webViewConstants.ATTRIBUTE_DISABLEBLINKFEATURES] = new DisableBlinkFeaturesAttribute(this)

  const autosizeAttributes = [webViewConstants.ATTRIBUTE_MAXHEIGHT, webViewConstants.ATTRIBUTE_MAXWIDTH, webViewConstants.ATTRIBUTE_MINHEIGHT, webViewConstants.ATTRIBUTE_MINWIDTH]
  autosizeAttributes.forEach((attribute) => {
    this.attributes[attribute] = new AutosizeDimensionAttribute(attribute, this)
  })
}
module.exports = {
  // Attributes.
  ATTRIBUTE_AUTOSIZE: 'autosize',
  ATTRIBUTE_MAXHEIGHT: 'maxheight',
  ATTRIBUTE_MAXWIDTH: 'maxwidth',
  ATTRIBUTE_MINHEIGHT: 'minheight',
  ATTRIBUTE_MINWIDTH: 'minwidth',
  ATTRIBUTE_NAME: 'name',
  ATTRIBUTE_PARTITION: 'partition',
  ATTRIBUTE_SRC: 'src',
  ATTRIBUTE_HTTPREFERRER: 'httpreferrer',
  ATTRIBUTE_NODEINTEGRATION: 'nodeintegration',
  ATTRIBUTE_PLUGINS: 'plugins',
  ATTRIBUTE_DISABLEWEBSECURITY: 'disablewebsecurity',
  ATTRIBUTE_ALLOWPOPUPS: 'allowpopups',
  ATTRIBUTE_PRELOAD: 'preload',
  ATTRIBUTE_USERAGENT: 'useragent',
  ATTRIBUTE_BLINKFEATURES: 'blinkfeatures',
  ATTRIBUTE_DISABLEBLINKFEATURES: 'disableblinkfeatures',

  // Internal attribute.
  ATTRIBUTE_INTERNALINSTANCEID: 'internalinstanceid',

  // Error messages.
  ERROR_MSG_ALREADY_NAVIGATED: 'The object has already navigated, so its partition cannot be changed.',
  ERROR_MSG_CANNOT_INJECT_SCRIPT: '<webview>: ' + 'Script cannot be injected into content until the page has loaded.',
  ERROR_MSG_INVALID_PARTITION_ATTRIBUTE: 'Invalid partition attribute.',
  ERROR_MSG_INVALID_PRELOAD_ATTRIBUTE: 'Only "file:" protocol is supported in "preload" attribute.'
}
'use strict'

const webFrame = require('electron').webFrame
const remote = require('electron').remote
const ipcRenderer = require('electron').ipcRenderer

const v8Util = process.atomBinding('v8_util')
const guestViewInternal = require('./guest-view-internal')
const webViewConstants = require('./web-view-constants')

var hasProp = {}.hasOwnProperty

// ID generator.
var nextId = 0

var getNextId = function () {
  return ++nextId
}

// Represents the internal state of the WebView node.
var WebViewImpl = (function () {
  function WebViewImpl (webviewNode) {
    var shadowRoot
    this.webviewNode = webviewNode
    v8Util.setHiddenValue(this.webviewNode, 'internal', this)
    this.attached = false
    this.elementAttached = false
    this.beforeFirstNavigation = true

    // on* Event handlers.
    this.on = {}
    this.browserPluginNode = this.createBrowserPluginNode()
    shadowRoot = this.webviewNode.createShadowRoot()
    shadowRoot.innerHTML = '<style>:host { display: flex; }</style>'
    this.setupWebViewAttributes()
    this.setupFocusPropagation()
    this.viewInstanceId = getNextId()
    shadowRoot.appendChild(this.browserPluginNode)

    // Subscribe to host's zoom level changes.
    this.onZoomLevelChanged = (zoomLevel) => {
      this.webviewNode.setZoomLevel(zoomLevel)
    }
    webFrame.on('zoom-level-changed', this.onZoomLevelChanged)

    this.onVisibilityChanged = (event, visibilityState) => {
      this.webviewNode.send('ELECTRON_RENDERER_WINDOW_VISIBILITY_CHANGE', visibilityState)
    }
    ipcRenderer.on('ELECTRON_RENDERER_WINDOW_VISIBILITY_CHANGE', this.onVisibilityChanged)
  }

  WebViewImpl.prototype.createBrowserPluginNode = function () {
    // We create BrowserPlugin as a custom element in order to observe changes
    // to attributes synchronously.
    var browserPluginNode = new WebViewImpl.BrowserPlugin()
    v8Util.setHiddenValue(browserPluginNode, 'internal', this)
    return browserPluginNode
  }

  // Resets some state upon reattaching <webview> element to the DOM.
  WebViewImpl.prototype.reset = function () {
    // Unlisten the zoom-level-changed event.
    webFrame.removeListener('zoom-level-changed', this.onZoomLevelChanged)
    ipcRenderer.removeListener('ELECTRON_RENDERER_WINDOW_VISIBILITY_CHANGE', this.onVisibilityChanged)

    // If guestInstanceId is defined then the <webview> has navigated and has
    // already picked up a partition ID. Thus, we need to reset the initialization
    // state. However, it may be the case that beforeFirstNavigation is false BUT
    // guestInstanceId has yet to be initialized. This means that we have not
    // heard back from createGuest yet. We will not reset the flag in this case so
    // that we don't end up allocating a second guest.
    if (this.guestInstanceId) {
      guestViewInternal.destroyGuest(this.guestInstanceId)
      this.webContents = null
      this.guestInstanceId = void 0
      this.beforeFirstNavigation = true
      this.attributes[webViewConstants.ATTRIBUTE_PARTITION].validPartitionId = true
    }
    this.internalInstanceId = 0
  }

  // Sets the <webview>.request property.
  WebViewImpl.prototype.setRequestPropertyOnWebViewNode = function (request) {
    return Object.defineProperty(this.webviewNode, 'request', {
      value: request,
      enumerable: true
    })
  }

  WebViewImpl.prototype.setupFocusPropagation = function () {
    if (!this.webviewNode.hasAttribute('tabIndex')) {
      // <webview> needs a tabIndex in order to be focusable.
      // TODO(fsamuel): It would be nice to avoid exposing a tabIndex attribute
      // to allow <webview> to be focusable.
      // See http://crbug.com/231664.
      this.webviewNode.setAttribute('tabIndex', -1)
    }

    // Focus the BrowserPlugin when the <webview> takes focus.
    this.webviewNode.addEventListener('focus', () => {
      this.browserPluginNode.focus()
    })

    // Blur the BrowserPlugin when the <webview> loses focus.
    this.webviewNode.addEventListener('blur', () => {
      this.browserPluginNode.blur()
    })
  }

  // This observer monitors mutations to attributes of the <webview> and
  // updates the BrowserPlugin properties accordingly. In turn, updating
  // a BrowserPlugin property will update the corresponding BrowserPlugin
  // attribute, if necessary. See BrowserPlugin::UpdateDOMAttribute for more
  // details.
  WebViewImpl.prototype.handleWebviewAttributeMutation = function (attributeName, oldValue, newValue) {
    if (!this.attributes[attributeName] || this.attributes[attributeName].ignoreMutation) {
      return
    }

    // Let the changed attribute handle its own mutation
    return this.attributes[attributeName].handleMutation(oldValue, newValue)
  }

  WebViewImpl.prototype.handleBrowserPluginAttributeMutation = function (attributeName, oldValue, newValue) {
    if (attributeName === webViewConstants.ATTRIBUTE_INTERNALINSTANCEID && !oldValue && !!newValue) {
      this.browserPluginNode.removeAttribute(webViewConstants.ATTRIBUTE_INTERNALINSTANCEID)
      this.internalInstanceId = parseInt(newValue)

      // Track when the element resizes using the element resize callback.
      webFrame.registerElementResizeCallback(this.internalInstanceId, this.onElementResize.bind(this))
      if (!this.guestInstanceId) {
        return
      }
      return guestViewInternal.attachGuest(this.internalInstanceId, this.guestInstanceId, this.buildParams())
    }
  }

  WebViewImpl.prototype.onSizeChanged = function (webViewEvent) {
    var maxHeight, maxWidth, minHeight, minWidth, newHeight, newWidth, node, width
    newWidth = webViewEvent.newWidth
    newHeight = webViewEvent.newHeight
    node = this.webviewNode
    width = node.offsetWidth

    // Check the current bounds to make sure we do not resize <webview>
    // outside of current constraints.
    maxWidth = this.attributes[webViewConstants.ATTRIBUTE_MAXWIDTH].getValue() | width
    maxHeight = this.attributes[webViewConstants.ATTRIBUTE_MAXHEIGHT].getValue() | width
    minWidth = this.attributes[webViewConstants.ATTRIBUTE_MINWIDTH].getValue() | width
    minHeight = this.attributes[webViewConstants.ATTRIBUTE_MINHEIGHT].getValue() | width
    minWidth = Math.min(minWidth, maxWidth)
    minHeight = Math.min(minHeight, maxHeight)
    if (!this.attributes[webViewConstants.ATTRIBUTE_AUTOSIZE].getValue() || (newWidth >= minWidth && newWidth <= maxWidth && newHeight >= minHeight && newHeight <= maxHeight)) {
      node.style.width = newWidth + 'px'
      node.style.height = newHeight + 'px'

      // Only fire the DOM event if the size of the <webview> has actually
      // changed.
      return this.dispatchEvent(webViewEvent)
    }
  }

  WebViewImpl.prototype.onElementResize = function (newSize) {
    // Dispatch the 'resize' event.
    var resizeEvent
    resizeEvent = new Event('resize', {
      bubbles: true
    })

    // Using client size values, because when a webview is transformed `newSize`
    // is incorrect
    newSize.width = this.webviewNode.clientWidth
    newSize.height = this.webviewNode.clientHeight

    resizeEvent.newWidth = newSize.width
    resizeEvent.newHeight = newSize.height
    this.dispatchEvent(resizeEvent)
    if (this.guestInstanceId) {
      return guestViewInternal.setSize(this.guestInstanceId, {
        normal: newSize
      })
    }
  }

  WebViewImpl.prototype.createGuest = function () {
    return guestViewInternal.createGuest(this.buildParams(), (event, guestInstanceId) => {
      this.attachWindow(guestInstanceId)
    })
  }

  WebViewImpl.prototype.dispatchEvent = function (webViewEvent) {
    return this.webviewNode.dispatchEvent(webViewEvent)
  }

  // Adds an 'on<event>' property on the webview, which can be used to set/unset
  // an event handler.
  WebViewImpl.prototype.setupEventProperty = function (eventName) {
    var propertyName
    propertyName = 'on' + eventName.toLowerCase()
    return Object.defineProperty(this.webviewNode, propertyName, {
      get: () => {
        return this.on[propertyName]
      },
      set: (value) => {
        if (this.on[propertyName]) {
          this.webviewNode.removeEventListener(eventName, this.on[propertyName])
        }
        this.on[propertyName] = value
        if (value) {
          return this.webviewNode.addEventListener(eventName, value)
        }
      },
      enumerable: true
    })
  }

  // Updates state upon loadcommit.
  WebViewImpl.prototype.onLoadCommit = function (webViewEvent) {
    var newValue, oldValue
    oldValue = this.webviewNode.getAttribute(webViewConstants.ATTRIBUTE_SRC)
    newValue = webViewEvent.url
    if (webViewEvent.isMainFrame && (oldValue !== newValue)) {
      // Touching the src attribute triggers a navigation. To avoid
      // triggering a page reload on every guest-initiated navigation,
      // we do not handle this mutation.
      return this.attributes[webViewConstants.ATTRIBUTE_SRC].setValueIgnoreMutation(newValue)
    }
  }

  WebViewImpl.prototype.onAttach = function (storagePartitionId) {
    return this.attributes[webViewConstants.ATTRIBUTE_PARTITION].setValue(storagePartitionId)
  }

  WebViewImpl.prototype.buildParams = function () {
    var attribute, attributeName, css, elementRect, params, ref1
    params = {
      instanceId: this.viewInstanceId,
      userAgentOverride: this.userAgentOverride,
      zoomFactor: webFrame.getZoomFactor()
    }
    ref1 = this.attributes
    for (attributeName in ref1) {
      if (!hasProp.call(ref1, attributeName)) continue
      attribute = ref1[attributeName]
      params[attributeName] = attribute.getValue()
    }

    // When the WebView is not participating in layout (display:none)
    // then getBoundingClientRect() would report a width and height of 0.
    // However, in the case where the WebView has a fixed size we can
    // use that value to initially size the guest so as to avoid a relayout of
    // the on display:block.
    css = window.getComputedStyle(this.webviewNode, null)
    elementRect = this.webviewNode.getBoundingClientRect()
    params.elementWidth = parseInt(elementRect.width) || parseInt(css.getPropertyValue('width'))
    params.elementHeight = parseInt(elementRect.height) || parseInt(css.getPropertyValue('height'))
    return params
  }

  WebViewImpl.prototype.attachWindow = function (guestInstanceId) {
    this.guestInstanceId = guestInstanceId
    this.webContents = remote.getGuestWebContents(this.guestInstanceId)
    if (!this.internalInstanceId) {
      return true
    }
    return guestViewInternal.attachGuest(this.internalInstanceId, this.guestInstanceId, this.buildParams())
  }

  return WebViewImpl
})()

// Registers browser plugin <object> custom element.
var registerBrowserPluginElement = function () {
  var proto = Object.create(HTMLObjectElement.prototype)
  proto.createdCallback = function () {
    this.setAttribute('type', 'application/browser-plugin')
    this.setAttribute('id', 'browser-plugin-' + getNextId())

    // The <object> node fills in the <webview> container.
    this.style.flex = '1 1 auto'
  }
  proto.attributeChangedCallback = function (name, oldValue, newValue) {
    var internal
    internal = v8Util.getHiddenValue(this, 'internal')
    if (!internal) {
      return
    }
    return internal.handleBrowserPluginAttributeMutation(name, oldValue, newValue)
  }
  proto.attachedCallback = function () {
    // Load the plugin immediately.
    return this.nonExistentAttribute
  }
  WebViewImpl.BrowserPlugin = webFrame.registerEmbedderCustomElement('browserplugin', {
    'extends': 'object',
    prototype: proto
  })
  delete proto.createdCallback
  delete proto.attachedCallback
  delete proto.detachedCallback
  return delete proto.attributeChangedCallback
}

// Registers <webview> custom element.
var registerWebViewElement = function () {
  var createBlockHandler, createNonBlockHandler, i, j, len, len1, m, methods, nonblockMethods, proto
  proto = Object.create(HTMLObjectElement.prototype)
  proto.createdCallback = function () {
    return new WebViewImpl(this)
  }
  proto.attributeChangedCallback = function (name, oldValue, newValue) {
    var internal
    internal = v8Util.getHiddenValue(this, 'internal')
    if (!internal) {
      return
    }
    return internal.handleWebviewAttributeMutation(name, oldValue, newValue)
  }
  proto.detachedCallback = function () {
    var internal
    internal = v8Util.getHiddenValue(this, 'internal')
    if (!internal) {
      return
    }
    guestViewInternal.deregisterEvents(internal.viewInstanceId)
    internal.elementAttached = false
    return internal.reset()
  }
  proto.attachedCallback = function () {
    var internal
    internal = v8Util.getHiddenValue(this, 'internal')
    if (!internal) {
      return
    }
    if (!internal.elementAttached) {
      guestViewInternal.registerEvents(internal, internal.viewInstanceId)
      internal.elementAttached = true
      return internal.attributes[webViewConstants.ATTRIBUTE_SRC].parse()
    }
  }

  // Public-facing API methods.
  methods = [
    'getURL',
    'loadURL',
    'getTitle',
    'isLoading',
    'isLoadingMainFrame',
    'isWaitingForResponse',
    'stop',
    'reload',
    'reloadIgnoringCache',
    'canGoBack',
    'canGoForward',
    'canGoToOffset',
    'clearHistory',
    'goBack',
    'goForward',
    'goToIndex',
    'goToOffset',
    'isCrashed',
    'setUserAgent',
    'getUserAgent',
    'openDevTools',
    'closeDevTools',
    'isDevToolsOpened',
    'isDevToolsFocused',
    'inspectElement',
    'setAudioMuted',
    'isAudioMuted',
    'undo',
    'redo',
    'cut',
    'copy',
    'paste',
    'pasteAndMatchStyle',
    'delete',
    'selectAll',
    'unselect',
    'replace',
    'replaceMisspelling',
    'findInPage',
    'stopFindInPage',
    'getId',
    'downloadURL',
    'inspectServiceWorker',
    'print',
    'printToPDF',
    'showDefinitionForSelection',
    'capturePage'
  ]
  nonblockMethods = [
    'insertCSS',
    'insertText',
    'send',
    'sendInputEvent',
    'setLayoutZoomLevelLimits',
    'setVisualZoomLevelLimits',
    'setZoomFactor',
    'setZoomLevel',
    // TODO(kevinsawicki): Remove in 2.0, deprecate before then with warnings
    'setZoomLevelLimits'
  ]

  // Forward proto.foo* method calls to WebViewImpl.foo*.
  createBlockHandler = function (m) {
    return function (...args) {
      const internal = v8Util.getHiddenValue(this, 'internal')
      if (internal.webContents) {
        return internal.webContents[m](...args)
      } else {
        throw new Error(`Cannot call ${m} because the webContents is unavailable. The WebView must be attached to the DOM and the dom-ready event emitted before this method can be called.`)
      }
    }
  }
  for (i = 0, len = methods.length; i < len; i++) {
    m = methods[i]
    proto[m] = createBlockHandler(m)
  }
  createNonBlockHandler = function (m) {
    return function (...args) {
      const internal = v8Util.getHiddenValue(this, 'internal')
      return ipcRenderer.send('ELECTRON_BROWSER_ASYNC_CALL_TO_GUEST_VIEW', null, internal.guestInstanceId, m, ...args)
    }
  }
  for (j = 0, len1 = nonblockMethods.length; j < len1; j++) {
    m = nonblockMethods[j]
    proto[m] = createNonBlockHandler(m)
  }

  proto.executeJavaScript = function (code, hasUserGesture, callback) {
    var internal = v8Util.getHiddenValue(this, 'internal')
    if (typeof hasUserGesture === 'function') {
      callback = hasUserGesture
      hasUserGesture = false
    }
    let requestId = getNextId()
    ipcRenderer.send('ELECTRON_BROWSER_ASYNC_CALL_TO_GUEST_VIEW', requestId, internal.guestInstanceId, 'executeJavaScript', code, hasUserGesture)
    ipcRenderer.once(`ELECTRON_RENDERER_ASYNC_CALL_TO_GUEST_VIEW_RESPONSE_${requestId}`, function (event, result) {
      if (callback) callback(result)
    })
  }

  // WebContents associated with this webview.
  proto.getWebContents = function () {
    var internal = v8Util.getHiddenValue(this, 'internal')
    return internal.webContents
  }

  window.WebView = webFrame.registerEmbedderCustomElement('webview', {
    prototype: proto
  })

  // Delete the callbacks so developers cannot call them and produce unexpected
  // behavior.
  delete proto.createdCallback
  delete proto.attachedCallback
  delete proto.detachedCallback
  return delete proto.attributeChangedCallback
}

var useCapture = true

var listener = function (event) {
  if (document.readyState === 'loading') {
    return
  }
  registerBrowserPluginElement()
  registerWebViewElement()
  return window.removeEventListener(event.type, listener, useCapture)
}

window.addEventListener('readystatechange', listener, true)

module.exports = WebViewImpl