Migrate to block comments

This commit is contained in:
Kevin Sawicki 2016-01-11 18:03:02 -08:00
parent 630cd091a0
commit 403870a27e
44 changed files with 538 additions and 437 deletions

View file

@ -34,13 +34,13 @@ app.setAppPath = (path) ->
app.getAppPath = ->
appPath
# Routes the events to webContents.
### Routes the events to webContents. ###
for name in ['login', 'certificate-error', 'select-client-certificate']
do (name) ->
app.on name, (event, webContents, args...) ->
webContents.emit name, event, args...
# Deprecated.
### Deprecated. ###
app.getHomeDir = deprecate 'app.getHomeDir', 'app.getPath', ->
@getPath 'home'
app.getDataPath = deprecate 'app.getDataPath', 'app.getPath', ->
@ -51,22 +51,23 @@ app.resolveProxy = deprecate 'app.resolveProxy', 'session.defaultSession.resolve
session.defaultSession.resolveProxy url, callback
deprecate.rename app, 'terminate', 'quit'
deprecate.event app, 'finish-launching', 'ready', ->
setImmediate => # give default app a chance to setup default menu.
### give default app a chance to setup default menu. ###
setImmediate =>
@emit 'finish-launching'
deprecate.event app, 'activate-with-no-open-windows', 'activate', (event, hasVisibleWindows) ->
@emit 'activate-with-no-open-windows', event if not hasVisibleWindows
deprecate.event app, 'select-certificate', 'select-client-certificate'
# Wrappers for native classes.
### Wrappers for native classes. ###
wrapDownloadItem = (downloadItem) ->
# downloadItem is an EventEmitter.
### downloadItem is an EventEmitter. ###
downloadItem.__proto__ = EventEmitter.prototype
# Deprecated.
### Deprecated. ###
deprecate.property downloadItem, 'url', 'getURL'
deprecate.property downloadItem, 'filename', 'getFilename'
deprecate.property downloadItem, 'mimeType', 'getMimeType'
deprecate.rename downloadItem, 'getUrl', 'getURL'
downloadItemBindings._setWrapDownloadItem wrapDownloadItem
# Only one App object pemitted.
### Only one App object pemitted. ###
module.exports = app

View file

@ -6,7 +6,7 @@ autoUpdater =
else
require './auto-updater/auto-updater-native'
# Deprecated.
### Deprecated. ###
deprecate.rename autoUpdater, 'setFeedUrl', 'setFeedURL'
module.exports = autoUpdater

View file

@ -28,14 +28,16 @@ class AutoUpdater extends EventEmitter
return @emitError error if error?
{releaseNotes, version} = update
# Following information is not available on Windows, so fake them.
### Following information is not available on Windows, so fake them. ###
date = new Date
url = @updateURL
@emit 'update-downloaded', {}, releaseNotes, version, date, url, => @quitAndInstall()
# Private: Emit both error object and message, this is to keep compatibility
# with Old APIs.
###
Private: Emit both error object and message, this is to keep compatibility
with Old APIs.
###
emitError: (message) ->
@emit 'error', new Error(message), message

View file

@ -2,17 +2,21 @@ fs = require 'fs'
path = require 'path'
{spawn} = require 'child_process'
appFolder = path.dirname process.execPath # i.e. my-app/app-0.1.13/
updateExe = path.resolve appFolder, '..', 'Update.exe' # i.e. my-app/Update.exe
### i.e. my-app/app-0.1.13/ ###
appFolder = path.dirname process.execPath
### i.e. my-app/Update.exe ###
updateExe = path.resolve appFolder, '..', 'Update.exe'
exeName = path.basename process.execPath
# Spawn a command and invoke the callback when it completes with an error
# and the output from standard out.
###
Spawn a command and invoke the callback when it completes with an error
and the output from standard out.
###
spawnUpdate = (args, detached, callback) ->
try
spawnedProcess = spawn updateExe, args, {detached}
catch error
# Shouldn't happen, but still guard it.
### Shouldn't happen, but still guard it. ###
process.nextTick -> callback error
return
@ -26,27 +30,27 @@ spawnUpdate = (args, detached, callback) ->
errorEmitted = true
callback error
spawnedProcess.on 'exit', (code, signal) ->
# We may have already emitted an error.
### We may have already emitted an error. ###
return if errorEmitted
# Process terminated with error.
### Process terminated with error. ###
if code isnt 0
return callback "Command failed: #{signal ? code}\n#{stderr}"
# Success.
### Success. ###
callback null, stdout
# Start an instance of the installed app.
### Start an instance of the installed app. ###
exports.processStart = (callback) ->
spawnUpdate ['--processStart', exeName], true, ->
# Download the releases specified by the URL and write new results to stdout.
### Download the releases specified by the URL and write new results to stdout. ###
exports.download = (updateURL, callback) ->
spawnUpdate ['--download', updateURL], false, (error, stdout) ->
return callback(error) if error?
try
# Last line of output is the JSON details about the releases
### Last line of output is the JSON details about the releases ###
json = stdout.trim().split('\n').pop()
update = JSON.parse(json)?.releasesToApply?.pop?()
catch
@ -54,11 +58,11 @@ exports.download = (updateURL, callback) ->
callback null, update
# Update the application to the latest remote version specified by URL.
### Update the application to the latest remote version specified by URL. ###
exports.update = (updateURL, callback) ->
spawnUpdate ['--update', updateURL], false, callback
# Is the Update.exe installed with the current application?
### Is the Update.exe installed with the current application? ###
exports.supported = ->
try
fs.accessSync updateExe, fs.R_OK

View file

@ -5,56 +5,61 @@
BrowserWindow::__proto__ = EventEmitter.prototype
BrowserWindow::_init = ->
{app} = require 'electron' # avoid recursive require.
### avoid recursive require. ###
{app} = require 'electron'
# Simulate the application menu on platforms other than OS X.
### Simulate the application menu on platforms other than OS X. ###
if process.platform isnt 'darwin'
menu = app.getApplicationMenu()
@setMenu menu if menu?
# Make new windows requested by links behave like "window.open"
### Make new windows requested by links behave like "window.open" ###
@webContents.on '-new-window', (event, url, frameName) ->
options = show: true, width: 800, height: 600
ipcMain.emit 'ATOM_SHELL_GUEST_WINDOW_MANAGER_WINDOW_OPEN', event, url, frameName, options
# window.resizeTo(...)
# window.moveTo(...)
###
window.resizeTo(...)
window.moveTo(...)
###
@webContents.on 'move', (event, size) =>
@setBounds size
# Hide the auto-hide menu when webContents is focused.
### Hide the auto-hide menu when webContents is focused. ###
@webContents.on 'activate', =>
if process.platform isnt 'darwin' and @isMenuBarAutoHide() and @isMenuBarVisible()
@setMenuBarVisibility false
# Forward the crashed event.
### Forward the crashed event. ###
@webContents.on 'crashed', =>
@emit 'crashed'
# Change window title to page title.
### Change window title to page title. ###
@webContents.on 'page-title-updated', (event, title, explicitSet) =>
@emit 'page-title-updated', event, title
@setTitle title unless event.defaultPrevented
# 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 OS X when the app is launched from
# Finder, we still do it on all platforms in case of other bugs we don't know.
###
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 OS X when the app is launched from
Finder, we still do it on all platforms in case of other bugs we don't know.
###
@webContents.once 'load-url', ->
@focus()
# Redirect focus/blur event to app instance too.
### Redirect focus/blur event to app instance too. ###
@on 'blur', (event) =>
app.emit 'browser-window-blur', event, this
@on 'focus', (event) =>
app.emit 'browser-window-focus', event, this
# Notify the creation of the window.
### Notify the creation of the window. ###
app.emit 'browser-window-created', {}, this
# Be compatible with old APIs.
### Be compatible with old APIs. ###
@webContents.on 'devtools-focused', => @emit 'devtools-focused'
@webContents.on 'devtools-opened', => @emit 'devtools-opened'
@webContents.on 'devtools-closed', => @emit 'devtools-closed'
@ -76,7 +81,7 @@ BrowserWindow.fromDevToolsWebContents = (webContents) ->
windows = BrowserWindow.getAllWindows()
return window for window in windows when window.devToolsWebContents?.equal webContents
# Helpers.
### Helpers. ###
BrowserWindow::loadURL = -> @webContents.loadURL.apply @webContents, arguments
BrowserWindow::getURL = -> @webContents.getURL()
BrowserWindow::reload = -> @webContents.reload.apply @webContents, arguments
@ -89,7 +94,7 @@ BrowserWindow::toggleDevTools = -> @webContents.toggleDevTools()
BrowserWindow::inspectElement = -> @webContents.inspectElement.apply @webContents, arguments
BrowserWindow::inspectServiceWorker = -> @webContents.inspectServiceWorker()
# Deprecated.
### Deprecated. ###
deprecate.member BrowserWindow, 'undo', 'webContents'
deprecate.member BrowserWindow, 'redo', 'webContents'
deprecate.member BrowserWindow, 'cut', 'webContents'

View file

@ -16,12 +16,12 @@ messageBoxOptions =
parseArgs = (window, options, callback) ->
unless window is null or window?.constructor is BrowserWindow
# Shift.
### Shift. ###
callback = options
options = window
window = null
if not callback? and typeof options is 'function'
# Shift.
### Shift. ###
callback = options
options = null
[window, options, callback]
@ -97,7 +97,7 @@ module.exports =
options.icon ?= null
options.defaultId ?= -1
# Choose a default button to get selected when dialog is cancelled.
### Choose a default button to get selected when dialog is cancelled. ###
unless options.cancelId?
options.cancelId = 0
for text, i in options.buttons
@ -122,6 +122,6 @@ module.exports =
showErrorBox: (args...) ->
binding.showErrorBox args...
# Mark standard asynchronous functions.
### Mark standard asynchronous functions. ###
for api in ['showMessageBox', 'showOpenDialog', 'showSaveDialog']
v8Util.setHiddenValue module.exports[api], 'asynchronous', true

View file

@ -1,10 +1,10 @@
common = require '../../../../common/api/lib/exports/electron'
# Import common modules.
### Import common modules. ###
common.defineProperties exports
Object.defineProperties exports,
# Browser side modules, please sort with alphabet order.
### Browser side modules, please sort with alphabet order. ###
app:
enumerable: true
get: -> require '../app'
@ -50,7 +50,7 @@ Object.defineProperties exports,
Tray:
enumerable: true
get: -> require '../tray'
# The internal modules, invisible unless you know their names.
### The internal modules, invisible unless you know their names. ###
NavigationController:
get: -> require '../navigation-controller'
webContents:

View file

@ -1,6 +1,6 @@
{deprecate, ipcMain} = require 'electron'
# This module is deprecated, we mirror everything from ipcMain.
### This module is deprecated, we mirror everything from ipcMain. ###
deprecate.warn 'ipc module', 'require("electron").ipcMain'
module.exports = ipcMain

View file

@ -2,7 +2,7 @@ v8Util = process.atomBinding 'v8_util'
nextCommandId = 0
# Maps role to methods of webContents
### Maps role to methods of webContents ###
rolesMap =
undo: 'undo'
redo: 'redo'
@ -13,7 +13,7 @@ rolesMap =
minimize: 'minimize'
close: 'close'
# Maps methods that should be called directly on the BrowserWindow instance
### Maps methods that should be called directly on the BrowserWindow instance ###
methodInBrowserWindow =
minimize: true
close: true
@ -46,7 +46,7 @@ class MenuItem
@commandId = ++nextCommandId
@click = (focusedWindow) =>
# Manually flip the checked flags when clicked.
### Manually flip the checked flags when clicked. ###
@checked = !@checked if @type in ['checkbox', 'radio']
if @role and rolesMap[@role] and process.platform isnt 'darwin' and focusedWindow?

View file

@ -4,11 +4,11 @@
v8Util = process.atomBinding 'v8_util'
bindings = process.atomBinding 'menu'
# Automatically generated radio menu item's group id.
### Automatically generated radio menu item's group id. ###
nextGroupId = 0
# Search between seperators to find a radio menu item and return its group id,
# otherwise generate a group id.
### Search between seperators to find a radio menu item and return its group id, ###
### otherwise generate a group id. ###
generateGroupId = (items, pos) ->
if pos > 0
for i in [pos - 1..0]
@ -22,12 +22,12 @@ generateGroupId = (items, pos) ->
break if item.type is 'separator'
++nextGroupId
# Returns the index of item according to |id|.
### Returns the index of item according to |id|. ###
indexOfItemById = (items, id) ->
return i for item, i in items when item.id is id
-1
# Returns the index of where to insert the item according to |position|.
### Returns the index of where to insert the item according to |position|. ###
indexToInsertByPosition = (items, position) ->
return items.length unless position
@ -41,12 +41,12 @@ indexToInsertByPosition = (items, position) ->
when 'after'
insertIndex++
when 'endof'
# If the |id| doesn't exist, then create a new group with the |id|.
### If the |id| doesn't exist, then create a new group with the |id|. ###
if insertIndex is -1
items.push id: id, type: 'separator'
insertIndex = items.length - 1
# Find the end of the group.
### Find the end of the group. ###
insertIndex++
while insertIndex < items.length and items[insertIndex].type isnt 'separator'
insertIndex++
@ -69,7 +69,7 @@ Menu::_init = ->
executeCommand: (commandId) =>
@commandsMap[commandId]?.click BrowserWindow.getFocusedWindow()
menuWillShow: =>
# Make sure radio groups have at least one menu item seleted.
### Make sure radio groups have at least one menu item seleted. ###
for id, group of @groupsMap
checked = false
for radioItem in group when radioItem.checked
@ -79,7 +79,7 @@ Menu::_init = ->
Menu::popup = (window, x, y) ->
unless window?.constructor is BrowserWindow
# Shift.
### Shift. ###
y = x
x = window
window = BrowserWindow.getFocusedWindow()
@ -100,12 +100,12 @@ Menu::insert = (pos, item) ->
when 'separator' then @insertSeparator pos
when 'submenu' then @insertSubMenu pos, item.commandId, item.label, item.submenu
when 'radio'
# Grouping radio menu items.
### Grouping radio menu items. ###
item.overrideReadOnlyProperty 'groupId', generateGroupId(@items, pos)
@groupsMap[item.groupId] ?= []
@groupsMap[item.groupId].push item
# Setting a radio menu item should flip other items in the group.
### Setting a radio menu item should flip other items in the group. ###
v8Util.setHiddenValue item, 'checked', item.checked
Object.defineProperty item, 'checked',
enumerable: true
@ -121,14 +121,14 @@ Menu::insert = (pos, item) ->
@setIcon pos, item.icon if item.icon?
@setRole pos, item.role if item.role?
# Make menu accessable to items.
### Make menu accessable to items. ###
item.overrideReadOnlyProperty 'menu', this
# Remember the items.
### Remember the items. ###
@items.splice pos, 0, item
@commandsMap[item.commandId] = item
# Force menuWillShow to be called
### Force menuWillShow to be called ###
Menu::_callMenuWillShow = ->
@delegate?.menuWillShow()
item.submenu._callMenuWillShow() for item in @items when item.submenu?
@ -136,7 +136,8 @@ Menu::_callMenuWillShow = ->
applicationMenu = null
Menu.setApplicationMenu = (menu) ->
throw new TypeError('Invalid menu') unless menu is null or menu.constructor is Menu
applicationMenu = menu # Keep a reference.
### Keep a reference. ###
applicationMenu = menu
if process.platform is 'darwin'
return if menu is null
@ -160,7 +161,7 @@ Menu.buildFromTemplate = (template) ->
if item.position
insertIndex = indexToInsertByPosition positionedTemplate, item.position
else
# If no |position| is specified, insert after last item.
### If no |position| is specified, insert after last item. ###
insertIndex++
positionedTemplate.splice insertIndex, 0, item

View file

@ -1,42 +1,47 @@
{ipcMain} = require 'electron'
# The history operation in renderer is redirected to browser.
### The history operation in renderer is redirected to browser. ###
ipcMain.on 'ATOM_SHELL_NAVIGATION_CONTROLLER', (event, method, args...) ->
event.sender[method] args...
ipcMain.on 'ATOM_SHELL_SYNC_NAVIGATION_CONTROLLER', (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.
###
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.
###
class NavigationController
constructor: (@webContents) ->
@clearHistory()
# webContents may have already navigated to a page.
### webContents may have already navigated to a page. ###
if @webContents._getURL()
@currentIndex++
@history.push @webContents._getURL()
@webContents.on 'navigation-entry-commited', (event, url, inPage, replaceEntry) =>
if @inPageIndex > -1 and not inPage
# Navigated to a new page, clear in-page mark.
### Navigated to a new page, clear in-page mark. ###
@inPageIndex = -1
else if @inPageIndex is -1 and inPage
# Started in-page navigations.
### Started in-page navigations. ###
@inPageIndex = @currentIndex
if @pendingIndex >= 0 # Go to index.
if @pendingIndex >= 0
### Go to index. ###
@currentIndex = @pendingIndex
@pendingIndex = -1
@history[@currentIndex] = url
else if replaceEntry # Non-user initialized navigation.
else if replaceEntry
### Non-user initialized navigation. ###
@history[@currentIndex] = url
else # Normal navigation.
@history = @history.slice 0, @currentIndex + 1 # Clear history.
else
### Normal navigation. Clear history. ###
@history = @history.slice 0, @currentIndex + 1
currentEntry = @history[@currentIndex]
if currentEntry?.url isnt url
@currentIndex++

View file

@ -4,7 +4,7 @@ throw new Error('Can not initialize protocol module before app is ready') unless
{protocol} = process.atomBinding 'protocol'
# Warn about removed APIs.
### Warn about removed APIs. ###
logAndThrow = (callback, message) ->
console.error message
if callback then callback(new Error(message)) else throw new Error(message)

View file

@ -4,7 +4,7 @@ bindings = process.atomBinding 'session'
PERSIST_PERFIX = 'persist:'
# Returns the Session from |partition| string.
### Returns the Session from |partition| string. ###
exports.fromPartition = (partition='') ->
return exports.defaultSession if partition is ''
if partition.startsWith PERSIST_PERFIX
@ -12,13 +12,13 @@ exports.fromPartition = (partition='') ->
else
bindings.fromPartition partition, true
# Returns the default session.
### Returns the default session. ###
Object.defineProperty exports, 'defaultSession',
enumerable: true
get: -> bindings.fromPartition '', false
wrapSession = (session) ->
# session is an EventEmitter.
### session is an EventEmitter. ###
session.__proto__ = EventEmitter.prototype
bindings._setWrapSession wrapSession

View file

@ -5,7 +5,7 @@
Tray::__proto__ = EventEmitter.prototype
Tray::_init = ->
# Deprecated.
### Deprecated. ###
deprecate.rename this, 'popContextMenu', 'popUpContextMenu'
deprecate.event this, 'clicked', 'click'
deprecate.event this, 'double-clicked', 'double-click'
@ -14,6 +14,7 @@ Tray::_init = ->
Tray::setContextMenu = (menu) ->
@_setContextMenu menu
@menu = menu # Keep a strong reference of menu.
### Keep a strong reference of menu. ###
@menu = menu
module.exports = Tray

View file

@ -40,28 +40,30 @@ PDFPageSize =
custom_display_name: "Tabloid"
wrapWebContents = (webContents) ->
# webContents is an EventEmitter.
### webContents is an EventEmitter. ###
webContents.__proto__ = EventEmitter.prototype
# WebContents::send(channel, args..)
### WebContents::send(channel, args..) ###
webContents.send = (channel, args...) ->
@_send channel, [args...]
# Make sure webContents.executeJavaScript would run the code only when the
# web contents has been loaded.
###
Make sure webContents.executeJavaScript would run the code only when the
web contents has been loaded.
###
webContents.executeJavaScript = (code, hasUserGesture=false) ->
if @getURL() and not @isLoading()
@_executeJavaScript code, hasUserGesture
else
webContents.once 'did-finish-load', @_executeJavaScript.bind(this, code, hasUserGesture)
# The navigation controller.
### The navigation controller. ###
controller = new NavigationController(webContents)
for name, method of NavigationController.prototype when method instanceof Function
do (name, method) ->
webContents[name] = -> method.apply controller, arguments
# Dispatch IPC messages to the ipc module.
### Dispatch IPC messages to the ipc module. ###
webContents.on 'ipc-message', (event, packed) ->
[channel, args...] = packed
ipcMain.emit channel, event, args...
@ -70,22 +72,24 @@ wrapWebContents = (webContents) ->
Object.defineProperty event, 'returnValue', set: (value) -> event.sendReply JSON.stringify(value)
ipcMain.emit channel, event, args...
# Handle context menu action request from pepper plugin.
### Handle context menu action request from pepper plugin. ###
webContents.on 'pepper-context-menu', (event, params) ->
menu = Menu.buildFromTemplate params.menu
menu.popup params.x, params.y
# This error occurs when host could not be found.
### This error occurs when host could not be found. ###
webContents.on 'did-fail-provisional-load', (args...) ->
# Calling loadURL during this event might cause crash, so delay the event
# until next tick.
###
Calling loadURL during this event might cause crash, so delay the event
until next tick.
###
setImmediate => @emit 'did-fail-load', args...
# Delays the page-title-updated event to next tick.
### Delays the page-title-updated event to next tick. ###
webContents.on '-page-title-updated', (args...) ->
setImmediate => @emit 'page-title-updated', args...
# Deprecated.
### Deprecated. ###
deprecate.rename webContents, 'loadUrl', 'loadURL'
deprecate.rename webContents, 'getUrl', 'getURL'
deprecate.event webContents, 'page-title-set', 'page-title-updated', (args...) ->

View file

@ -3,7 +3,7 @@ fs = require 'fs'
path = require 'path'
url = require 'url'
# Mapping between hostname and file path.
### Mapping between hostname and file path. ###
hostPathMap = {}
hostPathMapNextKey = 0
@ -15,14 +15,16 @@ getHostForPath = (path) ->
getPathForHost = (host) ->
hostPathMap[host]
# Cache extensionInfo.
### Cache extensionInfo. ###
extensionInfoMap = {}
getExtensionInfoFromPath = (srcDirectory) ->
manifest = JSON.parse fs.readFileSync(path.join(srcDirectory, 'manifest.json'))
unless extensionInfoMap[manifest.name]?
# We can not use 'file://' directly because all resources in the extension
# will be treated as relative to the root in Chrome.
###
We can not use 'file://' directly because all resources in the extension
will be treated as relative to the root in Chrome.
###
page = url.format
protocol: 'chrome-extension'
slashes: true
@ -35,11 +37,11 @@ getExtensionInfoFromPath = (srcDirectory) ->
exposeExperimentalAPIs: true
extensionInfoMap[manifest.name]
# The loaded extensions cache and its persistent path.
### The loaded extensions cache and its persistent path. ###
loadedExtensions = null
loadedExtensionsPath = null
# Persistent loaded extensions.
### Persistent loaded extensions. ###
{app} = electron
app.on 'will-quit', ->
try
@ -50,21 +52,21 @@ app.on 'will-quit', ->
fs.writeFileSync loadedExtensionsPath, JSON.stringify(loadedExtensions)
catch e
# We can not use protocol or BrowserWindow until app is ready.
### We can not use protocol or BrowserWindow until app is ready. ###
app.once 'ready', ->
{protocol, BrowserWindow} = electron
# Load persistented extensions.
### Load persistented extensions. ###
loadedExtensionsPath = path.join app.getPath('userData'), 'DevTools Extensions'
try
loadedExtensions = JSON.parse fs.readFileSync(loadedExtensionsPath)
loadedExtensions = [] unless Array.isArray loadedExtensions
# Preheat the extensionInfo cache.
### Preheat the extensionInfo cache. ###
getExtensionInfoFromPath srcDirectory for srcDirectory in loadedExtensions
catch e
# The chrome-extension: can map a extension URL request to real file path.
### The chrome-extension: can map a extension URL request to real file path. ###
chromeExtensionHandler = (request, callback) ->
parsed = url.parse request.url
return callback() unless parsed.hostname and parsed.path?
@ -88,7 +90,7 @@ app.once 'ready', ->
BrowserWindow.removeDevToolsExtension = (name) ->
delete extensionInfoMap[name]
# Load persistented extensions when devtools is opened.
### Load persistented extensions when devtools is opened. ###
init = BrowserWindow::_init
BrowserWindow::_init = ->
init.call this

View file

@ -4,26 +4,30 @@
deepEqual = (opt1, opt2) ->
return JSON.stringify(opt1) is JSON.stringify(opt2)
# A queue for holding all requests from renderer process.
### A queue for holding all requests from renderer process. ###
requestsQueue = []
ipcMain.on 'ATOM_BROWSER_DESKTOP_CAPTURER_GET_SOURCES', (event, captureWindow, captureScreen, thumbnailSize, id) ->
request = id: id, options: {captureWindow, captureScreen, thumbnailSize}, webContents: event.sender
requestsQueue.push request
desktopCapturer.startHandling captureWindow, captureScreen, thumbnailSize if requestsQueue.length is 1
# If the WebContents is destroyed before receiving result, just remove the
# reference from requestsQueue to make the module not send the result to it.
###
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', ->
request.webContents = null
desktopCapturer.emit = (event, name, sources) ->
# Receiving sources result from main process, now send them back to renderer.
### Receiving sources result from main process, now send them back to renderer. ###
handledRequest = requestsQueue.shift 0
result = ({ id: source.id, name: source.name, thumbnail: source.thumbnail.toDataUrl() } for source in sources)
handledRequest.webContents?.send "ATOM_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.
###
Check the queue to see whether there is other same request. If has, handle
it for reducing redunplicated `desktopCaptuer.startHandling` calls.
###
unhandledRequestsQueue = []
for request in requestsQueue
if deepEqual handledRequest.options, request.options
@ -31,7 +35,7 @@ desktopCapturer.emit = (event, name, sources) ->
else
unhandledRequestsQueue.push request
requestsQueue = unhandledRequestsQueue
# If the requestsQueue is not empty, start a new request handling.
### If the requestsQueue is not empty, start a new request handling. ###
if requestsQueue.length > 0
{captureWindow, captureScreen, thumbnailSize} = requestsQueue[0].options
desktopCapturer.startHandling captureWindow, captureScreen, thumbnailSize

View file

@ -1,6 +1,7 @@
{ipcMain, webContents} = require 'electron'
webViewManager = null # Doesn't exist in early initialization.
### Doesn't exist in early initialization. ###
webViewManager = null
supportedWebViewEvents = [
'load-commit'
@ -40,15 +41,15 @@ guestInstances = {}
embedderElementsMap = {}
reverseEmbedderElementsMap = {}
# Moves the last element of array to the first one.
### Moves the last element of array to the first one. ###
moveLastToFirst = (list) ->
list.unshift list.pop()
# Generate guestInstanceId.
### Generate guestInstanceId. ###
getNextInstanceId = (webContents) ->
++nextInstanceId
# Create a new guest instance.
### Create a new guest instance. ###
createGuest = (embedder, params) ->
webViewManager ?= process.atomBinding 'web_view_manager'
@ -56,21 +57,23 @@ createGuest = (embedder, params) ->
guest = webContents.create {isGuest: true, partition: params.partition, embedder}
guestInstances[id] = {guest, embedder}
# Destroy guest when the embedder is gone or navigated.
### Destroy guest when the embedder is gone or navigated. ###
destroyEvents = ['destroyed', 'crashed', 'did-navigate']
destroy = ->
destroyGuest embedder, id if guestInstances[id]?
for event in 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.
###
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.
###
listeners = embedder._events[event]
moveLastToFirst listeners if Array.isArray listeners
guest.once 'destroyed', ->
embedder.removeListener event, destroy for event in destroyEvents
# Init guest web view after attached.
### Init guest web view after attached. ###
guest.once 'did-attach', ->
params = @attachParams
delete @attachParams
@ -96,32 +99,32 @@ createGuest = (embedder, params) ->
guest.allowPopups = params.allowpopups
# Dispatch events to embedder.
### Dispatch events to embedder. ###
for event in supportedWebViewEvents
do (event) ->
guest.on event, (_, args...) ->
embedder.send "ATOM_SHELL_GUEST_VIEW_INTERNAL_DISPATCH_EVENT-#{guest.viewInstanceId}", event, args...
# Dispatch guest's IPC messages to embedder.
### Dispatch guest's IPC messages to embedder. ###
guest.on 'ipc-message-host', (_, packed) ->
[channel, args...] = packed
embedder.send "ATOM_SHELL_GUEST_VIEW_INTERNAL_IPC_MESSAGE-#{guest.viewInstanceId}", channel, args...
# Autosize.
### Autosize. ###
guest.on 'size-changed', (_, args...) ->
embedder.send "ATOM_SHELL_GUEST_VIEW_INTERNAL_SIZE_CHANGED-#{guest.viewInstanceId}", args...
id
# Attach the guest to an element of embedder.
### Attach the guest to an element of embedder. ###
attachGuest = (embedder, elementInstanceId, guestInstanceId, params) ->
guest = guestInstances[guestInstanceId].guest
# Destroy the old guest when attaching.
### Destroy the old guest when attaching. ###
key = "#{embedder.getId()}-#{elementInstanceId}"
oldGuestInstanceId = embedderElementsMap[key]
if oldGuestInstanceId?
# Reattachment to the same guest is not currently supported.
### Reattachment to the same guest is not currently supported. ###
return unless oldGuestInstanceId != guestInstanceId
return unless guestInstances[oldGuestInstanceId]?
@ -139,7 +142,7 @@ attachGuest = (embedder, elementInstanceId, guestInstanceId, params) ->
embedderElementsMap[key] = guestInstanceId
reverseEmbedderElementsMap[guestInstanceId] = key
# Destroy an existing guest instance.
### Destroy an existing guest instance. ###
destroyGuest = (embedder, id) ->
webViewManager.removeGuest embedder, id
guestInstances[id].guest.destroy()
@ -165,10 +168,10 @@ ipcMain.on 'ATOM_SHELL_GUEST_VIEW_MANAGER_SET_SIZE', (event, id, params) ->
ipcMain.on 'ATOM_SHELL_GUEST_VIEW_MANAGER_SET_ALLOW_TRANSPARENCY', (event, id, allowtransparency) ->
guestInstances[id]?.guest.setAllowTransparency allowtransparency
# Returns WebContents from its guest id.
### Returns WebContents from its guest id. ###
exports.getGuest = (id) ->
guestInstances[id]?.guest
# Returns the embedder of the guest.
### Returns the embedder of the guest. ###
exports.getEmbedder = (id) ->
guestInstances[id]?.embedder

View file

@ -3,7 +3,7 @@ v8Util = process.atomBinding 'v8_util'
frameToGuest = {}
# Copy attribute of |parent| to |child| if it is not defined in |child|.
### Copy attribute of |parent| to |child| if it is not defined in |child|. ###
mergeOptions = (child, parent) ->
for own key, value of parent when key not of child
if typeof value is 'object'
@ -12,34 +12,36 @@ mergeOptions = (child, parent) ->
child[key] = value
child
# Merge |options| with the |embedder|'s window's options.
### Merge |options| with the |embedder|'s window's options. ###
mergeBrowserWindowOptions = (embedder, options) ->
if embedder.browserWindowOptions?
# Inherit the original options if it is a BrowserWindow.
### Inherit the original options if it is a BrowserWindow. ###
mergeOptions options, embedder.browserWindowOptions
else
# Or only inherit web-preferences if it is a webview.
### Or only inherit web-preferences if it is a webview. ###
options.webPreferences ?= {}
mergeOptions options.webPreferences, embedder.getWebPreferences()
options
# Create a new guest created by |embedder| with |options|.
### Create a new guest created by |embedder| with |options|. ###
createGuest = (embedder, url, frameName, options) ->
guest = frameToGuest[frameName]
if frameName and guest?
guest.loadURL url
return guest.id
# Remember the embedder window's id.
### Remember the embedder window's id. ###
options.webPreferences ?= {}
options.webPreferences.openerId = BrowserWindow.fromWebContents(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.
###
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.
###
guestId = guest.id
closedByEmbedder = ->
guest.removeListener 'closed', closedByUser
@ -58,7 +60,7 @@ createGuest = (embedder, url, frameName, options) ->
guest.id
# Routed window.open messages.
### Routed window.open messages. ###
ipcMain.on 'ATOM_SHELL_GUEST_WINDOW_MANAGER_WINDOW_OPEN', (event, args...) ->
[url, frameName, options] = args
options = mergeBrowserWindowOptions event.sender, options

View file

@ -3,26 +3,28 @@ path = require 'path'
util = require 'util'
Module = require 'module'
# We modified the original process.argv to let node.js load the atom.js,
# we need to restore it here.
### 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.
### Clear search paths. ###
require path.resolve(__dirname, '..', '..', 'common', 'lib', 'reset-search-paths')
# Import common settings.
### Import common settings. ###
require path.resolve(__dirname, '..', '..', 'common', 'lib', 'init')
globalPaths = Module.globalPaths
unless process.env.ELECTRON_HIDE_INTERNAL_MODULES
globalPaths.push path.resolve(__dirname, '..', 'api', 'lib')
# Expose public APIs.
### Expose public APIs. ###
globalPaths.push path.resolve(__dirname, '..', 'api', 'lib', 'exports')
if process.platform is 'win32'
# Redirect node's console to use our own implementations, since node can not
# handle console output when running as GUI program.
###
Redirect node's console to use our own implementations, since node can not
handle console output when running as GUI program.
###
consoleLog = (args...) ->
process.log util.format(args...) + "\n"
streamWrite = (chunk, encoding, callback) ->
@ -33,40 +35,40 @@ if process.platform is 'win32'
console.log = console.error = console.warn = consoleLog
process.stdout.write = process.stderr.write = streamWrite
# Always returns EOF for stdin stream.
### Always returns EOF for stdin stream. ###
Readable = require('stream').Readable
stdin = new Readable
stdin.push null
process.__defineGetter__ 'stdin', -> stdin
# Don't quit on fatal error.
### Don't quit on fatal error. ###
process.on 'uncaughtException', (error) ->
# Do nothing if the user has a custom uncaught exception handler.
### Do nothing if the user has a custom uncaught exception handler. ###
if process.listeners('uncaughtException').length > 1
return
# Show error in GUI.
### Show error in GUI. ###
{dialog} = require 'electron'
stack = error.stack ? "#{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.
### Emit 'exit' event on quit. ###
{app} = require 'electron'
app.on 'quit', (event, exitCode) ->
process.emit 'exit', exitCode
# Map process.exit to app.exit, which quits gracefully.
### Map process.exit to app.exit, which quits gracefully. ###
process.exit = app.exit
# Load the RPC server.
### Load the RPC server. ###
require './rpc-server'
# Load the guest view manager.
### Load the guest view manager. ###
require './guest-view-manager'
require './guest-window-manager'
# Now we try to load app's package.json.
### Now we try to load app's package.json. ###
packageJson = null
searchPaths = [ 'app', 'app.asar', 'default_app' ]
@ -82,37 +84,37 @@ unless packageJson?
process.nextTick -> process.exit 1
throw new Error("Unable to find a valid app")
# Set application's version.
### Set application's version. ###
app.setVersion packageJson.version if packageJson.version?
# Set application's name.
### Set application's name. ###
if packageJson.productName?
app.setName packageJson.productName
else if packageJson.name?
app.setName packageJson.name
# Set application's desktop name.
### Set application's desktop name. ###
if packageJson.desktopName?
app.setDesktopName packageJson.desktopName
else
app.setDesktopName "#{app.getName()}.desktop"
# Chrome 42 disables NPAPI plugins by default, reenable them here
### Chrome 42 disables NPAPI plugins by default, reenable them here ###
app.commandLine.appendSwitch 'enable-npapi'
# Set the user path according to application's name.
### 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.
### Load the chrome extension support. ###
require './chrome-extension'
# Load internal desktop-capturer module.
### Load internal desktop-capturer module. ###
require './desktop-capturer'
# Set main startup script of the app.
### Set main startup script of the app. ###
mainStartupScript = packageJson.main or 'index.js'
# Finally load app's main.js and transfer control to C++.
### Finally load app's main.js and transfer control to C++. ###
Module._load path.join(packagePath, mainStartupScript), Module, true

View file

@ -6,46 +6,52 @@ class ObjectsRegistry extends EventEmitter
@setMaxListeners Number.MAX_VALUE
@nextId = 0
# Stores all objects by ref-counting.
# (id) => {object, count}
###
Stores all objects by ref-counting.
(id) => {object, count}
###
@storage = {}
# Stores the IDs of objects referenced by WebContents.
# (webContentsId) => {(id) => (count)}
###
Stores the IDs of objects referenced by WebContents.
(webContentsId) => {(id) => (count)}
###
@owners = {}
# Register a new object, the object would be kept referenced until you release
# it explicitly.
###
Register a new object, the object would be kept referenced until you release
it explicitly.
###
add: (webContentsId, obj) ->
id = @saveToStorage obj
# Remember the owner.
### Remember the owner. ###
@owners[webContentsId] ?= {}
@owners[webContentsId][id] ?= 0
@owners[webContentsId][id]++
# Returns object's id
### Returns object's id ###
id
# Get an object according to its ID.
### Get an object according to its ID. ###
get: (id) ->
@storage[id]?.object
# Dereference an object according to its ID.
### Dereference an object according to its ID. ###
remove: (webContentsId, id) ->
@dereference id, 1
# Also reduce the count in owner.
### Also reduce the count in owner. ###
pointer = @owners[webContentsId]
return unless pointer?
--pointer[id]
delete pointer[id] if pointer[id] is 0
# Clear all references to objects refrenced by the WebContents.
### Clear all references to objects refrenced by the WebContents. ###
clear: (webContentsId) ->
@emit "clear-#{webContentsId}"
return unless @owners[webContentsId]?
@dereference id, count for id, count of @owners[webContentsId]
delete @owners[webContentsId]
# Private: Saves the object into storage and assigns an ID for it.
### Private: Saves the object into storage and assigns an ID for it. ###
saveToStorage: (object) ->
id = v8Util.getHiddenValue object, 'atomId'
unless id
@ -55,7 +61,7 @@ class ObjectsRegistry extends EventEmitter
++@storage[id].count
id
# Private: Dereference the object from store.
### Private: Dereference the object from store. ###
dereference: (id, count) ->
pointer = @storage[id]
return unless pointer?

View file

@ -7,7 +7,7 @@ objectsRegistry = require './objects-registry'
v8Util = process.atomBinding 'v8_util'
{IDWeakMap} = process.atomBinding 'id_weak_map'
# Convert a real value into meta data.
### Convert a real value into meta data. ###
valueToMeta = (sender, value, optimizeSimpleObject=false) ->
meta = type: typeof value
@ -18,11 +18,11 @@ valueToMeta = (sender, value, optimizeSimpleObject=false) ->
meta.type = 'date' if value instanceof Date
meta.type = 'promise' if value?.constructor.name is 'Promise'
# Treat simple objects as value.
### Treat simple objects as value. ###
if optimizeSimpleObject and meta.type is 'object' and v8Util.getHiddenValue value, 'simple'
meta.type = 'value'
# Treat the arguments object as array.
### Treat the arguments object as array. ###
meta.type = 'array' if meta.type is 'object' and value.callee? and value.length?
if meta.type is 'array'
@ -31,9 +31,11 @@ valueToMeta = (sender, value, optimizeSimpleObject=false) ->
else if meta.type is 'object' or meta.type is 'function'
meta.name = 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.
###
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.getId(), value
meta.members = ({name, type: typeof field} for name, field of value)
@ -43,7 +45,7 @@ valueToMeta = (sender, value, optimizeSimpleObject=false) ->
meta.then = valueToMeta sender, value.then.bind(value)
else if meta.type is 'error'
meta.members = plainObjectToMeta value
# Error.name is not part of own properties.
### Error.name is not part of own properties. ###
meta.members.push {name: 'name', value: value.name}
else if meta.type is 'date'
meta.value = value.getTime()
@ -53,15 +55,15 @@ valueToMeta = (sender, value, optimizeSimpleObject=false) ->
meta
# Convert object to meta by value.
### Convert object to meta by value. ###
plainObjectToMeta = (obj) ->
Object.getOwnPropertyNames(obj).map (name) -> {name, value: obj[name]}
# Convert Error into meta data.
### Convert Error into meta data. ###
exceptionToMeta = (error) ->
type: 'exception', message: error.message, stack: (error.stack || error)
# Convert array of meta data from renderer into array of real values.
### Convert array of meta data from renderer into array of real values. ###
unwrapArgs = (sender, args) ->
metaToValue = (meta) ->
switch meta.type
@ -80,7 +82,7 @@ unwrapArgs = (sender, args) ->
returnValue = metaToValue meta.value
-> returnValue
when 'function'
# Cache the callbacks in renderer.
### Cache the callbacks in renderer. ###
unless sender.callbacks
sender.callbacks = new IDWeakMap
sender.on 'render-view-deleted', ->
@ -106,8 +108,10 @@ unwrapArgs = (sender, args) ->
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.
###
Call a function and send reply asynchronously if it's a an asynchronous
style function and the caller didn't pass a callback.
###
callFunction = (event, func, caller, args) ->
funcMarkedAsync = v8Util.getHiddenValue(func, 'asynchronous')
funcPassedCallback = typeof args[args.length - 1] is 'function'
@ -121,15 +125,17 @@ callFunction = (event, func, caller, args) ->
ret = func.apply caller, args
event.returnValue = valueToMeta event.sender, ret, true
catch e
# 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.`
###
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 = func.name ? "anonymous"
throw new Error("Could not call remote function `#{funcName}`.
Check that the function signature is correct.
Underlying error: #{e.message}")
# Send by BrowserWindow when its render view is deleted.
### Send by BrowserWindow when its render view is deleted. ###
process.on 'ATOM_BROWSER_RELEASE_RENDER_VIEW', (id) ->
objectsRegistry.clear id
@ -164,8 +170,10 @@ ipcMain.on 'ATOM_BROWSER_CONSTRUCTOR', (event, id, args) ->
try
args = unwrapArgs event.sender, args
constructor = objectsRegistry.get id
# Call new with array of arguments.
# http://stackoverflow.com/questions/1606797/use-of-apply-with-new-operator-is-this-possible
###
Call new with array of arguments.
http://stackoverflow.com/questions/1606797/use-of-apply-with-new-operator-is-this-possible
###
obj = new (Function::bind.apply(constructor, [null].concat(args)))
event.returnValue = valueToMeta event.sender, obj
catch e
@ -183,7 +191,7 @@ ipcMain.on 'ATOM_BROWSER_MEMBER_CONSTRUCTOR', (event, id, method, args) ->
try
args = unwrapArgs event.sender, args
constructor = objectsRegistry.get(id)[method]
# Call new with array of arguments.
### Call new with array of arguments. ###
obj = new (Function::bind.apply(constructor, [null].concat(args)))
event.returnValue = valueToMeta event.sender, obj
catch e