Merge branch 'master' into chrome-storage-sync
This commit is contained in:
commit
f121f46a24
50 changed files with 605 additions and 192 deletions
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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))
|
||||
})
|
||||
}
|
||||
})
|
||||
|
|
|
@ -194,4 +194,6 @@ exports.injectTo = function (extensionId, isBackgroundPage, context) {
|
|||
setPopup () {},
|
||||
getPopup () {}
|
||||
}
|
||||
|
||||
chrome.i18n = require('./extensions/i18n.js').setup(extensionId)
|
||||
}
|
||||
|
|
84
lib/renderer/extensions/i18n.js
Normal file
84
lib/renderer/extensions/i18n.js
Normal 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)
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue