Merge branch 'master' into chrome-storage-sync

This commit is contained in:
Jessica Lord 2016-06-09 16:35:00 -07:00
commit f121f46a24
50 changed files with 605 additions and 192 deletions

View file

@ -1,5 +1,5 @@
const {app} = require('electron')
const {createProtocolObject, registerStandardSchemes} = process.atomBinding('protocol')
const {app, session} = require('electron')
const {registerStandardSchemes} = process.atomBinding('protocol')
exports.registerStandardSchemes = function (schemes) {
if (app.isReady()) {
@ -10,7 +10,7 @@ exports.registerStandardSchemes = function (schemes) {
}
app.once('ready', function () {
let protocol = createProtocolObject()
let protocol = session.defaultSession.protocol
for (let method in protocol) {
exports[method] = protocol[method].bind(protocol)
}

View file

@ -3,6 +3,7 @@ const electron = require('electron')
const bindings = process.atomBinding('session')
const PERSIST_PREFIX = 'persist:'
const Session = new EventEmitter()
// Wrapper of binding.fromPartition that checks for ready event.
const fromPartition = function (partition, persist) {
@ -14,7 +15,7 @@ const fromPartition = function (partition, persist) {
}
// Returns the Session from |partition| string.
exports.fromPartition = function (partition = '') {
Session.fromPartition = function (partition = '') {
if (partition === '') return exports.defaultSession
if (partition.startsWith(PERSIST_PREFIX)) {
@ -25,7 +26,7 @@ exports.fromPartition = function (partition = '') {
}
// Returns the default session.
Object.defineProperty(exports, 'defaultSession', {
Object.defineProperty(Session, 'defaultSession', {
enumerable: true,
get: function () {
return fromPartition('', false)
@ -35,6 +36,9 @@ Object.defineProperty(exports, 'defaultSession', {
const wrapSession = function (session) {
// Session is an EventEmitter.
Object.setPrototypeOf(session, EventEmitter.prototype)
Session.emit('session-created', session)
}
bindings._setWrapSession(wrapSession)
module.exports = Session

View file

@ -10,6 +10,14 @@ session
const binding = process.atomBinding('web_contents')
const debuggerBinding = process.atomBinding('debugger')
const WebContents = new EventEmitter()
WebContents.create = (options = {}) => {
return binding.create(options)
}
WebContents.fromId = (id) => {
return binding.fromId(id)
}
let nextId = 0
const getNextId = function () {
return ++nextId
@ -223,6 +231,8 @@ const wrapWebContents = function (webContents) {
this._printToPDF(printingSetting, callback)
}
WebContents.emit('web-contents-created', webContents)
}
binding._setWrapWebContents(wrapWebContents)
@ -235,12 +245,4 @@ const wrapDebugger = function (webContentsDebugger) {
debuggerBinding._setWrapDebugger(wrapDebugger)
module.exports = {
create (options = {}) {
return binding.create(options)
},
fromId (id) {
return binding.fromId(id)
}
}
module.exports = WebContents

View file

@ -1,4 +1,5 @@
const {app, ipcMain, protocol, webContents, BrowserWindow} = require('electron')
const {app, ipcMain, session, webContents, BrowserWindow} = require('electron')
const {getAllWebContents} = process.atomBinding('web_contents')
const renderProcessPreferences = process.atomBinding('render_process_preferences').forAllBrowserWindow()
const fs = require('fs')
@ -81,13 +82,13 @@ const removeBackgroundPages = function (manifest) {
}
// Dispatch tabs events.
const hookWindowForTabEvents = function (win) {
const tabId = win.webContents.id
const hookWebContentsForTabEvents = function (webContents) {
const tabId = webContents.id
for (const page of objectValues(backgroundPages)) {
page.webContents.sendToAll('CHROME_TABS_ONCREATED', tabId)
}
win.once('closed', () => {
webContents.once('destroyed', () => {
for (const page of objectValues(backgroundPages)) {
page.webContents.sendToAll('CHROME_TABS_ONREMOVED', tabId)
}
@ -114,6 +115,10 @@ ipcMain.on('CHROME_RUNTIME_CONNECT', function (event, extensionId, connectInfo)
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) {
@ -221,6 +226,15 @@ const loadDevToolsExtensions = function (win, manifests) {
win.devToolsWebContents.executeJavaScript(`DevToolsAPI.addExtensions(${JSON.stringify(extensionInfoArray)})`)
}
webContents.on('web-contents-created', function (webContents) {
if (webContents.getType() === 'remote') return
hookWebContentsForTabEvents(webContents)
webContents.on('devtools-opened', function () {
loadDevToolsExtensions(webContents, objectValues(manifestMap))
})
})
// The persistent path of "DevTools Extensions" preference file.
let loadedExtensionsPath = null
@ -270,10 +284,12 @@ app.once('ready', function () {
}
})
}
protocol.registerBufferProtocol('chrome-extension', chromeExtensionHandler, function (error) {
if (error) {
console.error(`Unable to register chrome-extension protocol: ${error}`)
}
session.on('session-created', function (ses) {
ses.protocol.registerBufferProtocol('chrome-extension', chromeExtensionHandler, function (error) {
if (error) {
console.error(`Unable to register chrome-extension protocol: ${error}`)
}
})
})
// Load persisted extensions.
@ -295,12 +311,15 @@ app.once('ready', function () {
BrowserWindow.addDevToolsExtension = function (srcDirectory) {
const manifest = getManifestFromPath(srcDirectory)
if (manifest) {
for (const win of BrowserWindow.getAllWindows()) {
loadDevToolsExtensions(win, [manifest])
for (const webContents of getAllWebContents()) {
if (webContents.getType() !== 'remote') {
loadDevToolsExtensions(webContents, [manifest])
}
}
return manifest.name
}
}
BrowserWindow.removeDevToolsExtension = function (name) {
const manifest = manifestNameMap[name]
if (!manifest) return
@ -310,14 +329,4 @@ app.once('ready', function () {
delete manifestMap[manifest.extensionId]
delete manifestNameMap[name]
}
// Load extensions automatically when devtools is opened.
const init = BrowserWindow.prototype._init
BrowserWindow.prototype._init = function () {
init.call(this)
hookWindowForTabEvents(this)
this.webContents.on('devtools-opened', () => {
loadDevToolsExtensions(this, objectValues(manifestMap))
})
}
})

View file

@ -194,4 +194,6 @@ exports.injectTo = function (extensionId, isBackgroundPage, context) {
setPopup () {},
getPopup () {}
}
chrome.i18n = require('./extensions/i18n.js').setup(extensionId)
}

View file

@ -0,0 +1,84 @@
// 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)
}
}
}