build: enable JS semicolons (#22783)
This commit is contained in:
parent
24e21467b9
commit
5d657dece4
354 changed files with 21512 additions and 21510 deletions
|
@ -1,20 +1,20 @@
|
|||
const { hasSwitch } = process.electronBinding('command_line')
|
||||
const binding = process.electronBinding('context_bridge')
|
||||
const { hasSwitch } = process.electronBinding('command_line');
|
||||
const binding = process.electronBinding('context_bridge');
|
||||
|
||||
const contextIsolationEnabled = hasSwitch('context-isolation')
|
||||
const contextIsolationEnabled = hasSwitch('context-isolation');
|
||||
|
||||
const checkContextIsolationEnabled = () => {
|
||||
if (!contextIsolationEnabled) throw new Error('contextBridge API can only be used when contextIsolation is enabled')
|
||||
}
|
||||
if (!contextIsolationEnabled) throw new Error('contextBridge API can only be used when contextIsolation is enabled');
|
||||
};
|
||||
|
||||
const contextBridge = {
|
||||
exposeInMainWorld: (key: string, api: Record<string, any>) => {
|
||||
checkContextIsolationEnabled()
|
||||
return binding.exposeAPIInMainWorld(key, api)
|
||||
checkContextIsolationEnabled();
|
||||
return binding.exposeAPIInMainWorld(key, api);
|
||||
},
|
||||
debugGC: () => binding._debugGCMaps({})
|
||||
}
|
||||
};
|
||||
|
||||
if (!binding._debugGCMaps) delete contextBridge.debugGC
|
||||
if (!binding._debugGCMaps) delete contextBridge.debugGC;
|
||||
|
||||
export default contextBridge
|
||||
export default contextBridge;
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
'use strict'
|
||||
'use strict';
|
||||
|
||||
const CrashReporter = require('@electron/internal/common/crash-reporter')
|
||||
const ipcRendererUtils = require('@electron/internal/renderer/ipc-renderer-internal-utils')
|
||||
const CrashReporter = require('@electron/internal/common/crash-reporter');
|
||||
const ipcRendererUtils = require('@electron/internal/renderer/ipc-renderer-internal-utils');
|
||||
|
||||
class CrashReporterRenderer extends CrashReporter {
|
||||
init (options) {
|
||||
return ipcRendererUtils.invokeSync('ELECTRON_CRASH_REPORTER_INIT', options)
|
||||
return ipcRendererUtils.invokeSync('ELECTRON_CRASH_REPORTER_INIT', options);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = new CrashReporterRenderer()
|
||||
module.exports = new CrashReporterRenderer();
|
||||
|
|
|
@ -1,39 +1,39 @@
|
|||
import { nativeImage } from 'electron'
|
||||
import { ipcRendererInternal } from '@electron/internal/renderer/ipc-renderer-internal'
|
||||
import { nativeImage } from 'electron';
|
||||
import { ipcRendererInternal } from '@electron/internal/renderer/ipc-renderer-internal';
|
||||
|
||||
const { hasSwitch } = process.electronBinding('command_line')
|
||||
const { hasSwitch } = process.electronBinding('command_line');
|
||||
|
||||
// |options.types| can't be empty and must be an array
|
||||
function isValid (options: Electron.SourcesOptions) {
|
||||
const types = options ? options.types : undefined
|
||||
return Array.isArray(types)
|
||||
const types = options ? options.types : undefined;
|
||||
return Array.isArray(types);
|
||||
}
|
||||
|
||||
const enableStacks = hasSwitch('enable-api-filtering-logging')
|
||||
const enableStacks = hasSwitch('enable-api-filtering-logging');
|
||||
|
||||
function getCurrentStack () {
|
||||
const target = {}
|
||||
const target = {};
|
||||
if (enableStacks) {
|
||||
Error.captureStackTrace(target, getCurrentStack)
|
||||
Error.captureStackTrace(target, getCurrentStack);
|
||||
}
|
||||
return (target as any).stack
|
||||
return (target as any).stack;
|
||||
}
|
||||
|
||||
export async function getSources (options: Electron.SourcesOptions) {
|
||||
if (!isValid(options)) throw new Error('Invalid options')
|
||||
if (!isValid(options)) throw new Error('Invalid options');
|
||||
|
||||
const captureWindow = options.types.includes('window')
|
||||
const captureScreen = options.types.includes('screen')
|
||||
const captureWindow = options.types.includes('window');
|
||||
const captureScreen = options.types.includes('screen');
|
||||
|
||||
const { thumbnailSize = { width: 150, height: 150 } } = options
|
||||
const { fetchWindowIcons = false } = options
|
||||
const { thumbnailSize = { width: 150, height: 150 } } = options;
|
||||
const { fetchWindowIcons = false } = options;
|
||||
|
||||
const sources = await ipcRendererInternal.invoke<ElectronInternal.GetSourcesResult[]>('ELECTRON_BROWSER_DESKTOP_CAPTURER_GET_SOURCES', {
|
||||
captureWindow,
|
||||
captureScreen,
|
||||
thumbnailSize,
|
||||
fetchWindowIcons
|
||||
} as ElectronInternal.GetSourcesOptions, getCurrentStack())
|
||||
} as ElectronInternal.GetSourcesOptions, getCurrentStack());
|
||||
|
||||
return sources.map(source => ({
|
||||
id: source.id,
|
||||
|
@ -41,5 +41,5 @@ export async function getSources (options: Electron.SourcesOptions) {
|
|||
thumbnail: nativeImage.createFromDataURL(source.thumbnail),
|
||||
display_id: source.display_id,
|
||||
appIcon: source.appIcon ? nativeImage.createFromDataURL(source.appIcon) : null
|
||||
}))
|
||||
}));
|
||||
}
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
import { defineProperties } from '@electron/internal/common/define-properties'
|
||||
import { commonModuleList } from '@electron/internal/common/api/module-list'
|
||||
import { rendererModuleList } from '@electron/internal/renderer/api/module-list'
|
||||
import { defineProperties } from '@electron/internal/common/define-properties';
|
||||
import { commonModuleList } from '@electron/internal/common/api/module-list';
|
||||
import { rendererModuleList } from '@electron/internal/renderer/api/module-list';
|
||||
|
||||
module.exports = {}
|
||||
module.exports = {};
|
||||
|
||||
defineProperties(module.exports, commonModuleList)
|
||||
defineProperties(module.exports, rendererModuleList)
|
||||
defineProperties(module.exports, commonModuleList);
|
||||
defineProperties(module.exports, rendererModuleList);
|
||||
|
|
|
@ -1,36 +1,36 @@
|
|||
const { ipc } = process.electronBinding('ipc')
|
||||
const v8Util = process.electronBinding('v8_util')
|
||||
const { ipc } = process.electronBinding('ipc');
|
||||
const v8Util = process.electronBinding('v8_util');
|
||||
|
||||
// Created by init.js.
|
||||
const ipcRenderer = v8Util.getHiddenValue<Electron.IpcRenderer>(global, 'ipc')
|
||||
const internal = false
|
||||
const ipcRenderer = v8Util.getHiddenValue<Electron.IpcRenderer>(global, 'ipc');
|
||||
const internal = false;
|
||||
|
||||
ipcRenderer.send = function (channel, ...args) {
|
||||
return ipc.send(internal, channel, args)
|
||||
}
|
||||
return ipc.send(internal, channel, args);
|
||||
};
|
||||
|
||||
ipcRenderer.sendSync = function (channel, ...args) {
|
||||
return ipc.sendSync(internal, channel, args)[0]
|
||||
}
|
||||
return ipc.sendSync(internal, channel, args)[0];
|
||||
};
|
||||
|
||||
ipcRenderer.sendToHost = function (channel, ...args) {
|
||||
return ipc.sendToHost(channel, args)
|
||||
}
|
||||
return ipc.sendToHost(channel, args);
|
||||
};
|
||||
|
||||
ipcRenderer.sendTo = function (webContentsId, channel, ...args) {
|
||||
return ipc.sendTo(internal, false, webContentsId, channel, args)
|
||||
}
|
||||
return ipc.sendTo(internal, false, webContentsId, channel, args);
|
||||
};
|
||||
|
||||
ipcRenderer.invoke = async function (channel, ...args) {
|
||||
const { error, result } = await ipc.invoke(internal, channel, args)
|
||||
const { error, result } = await ipc.invoke(internal, channel, args);
|
||||
if (error) {
|
||||
throw new Error(`Error invoking remote method '${channel}': ${error}`)
|
||||
throw new Error(`Error invoking remote method '${channel}': ${error}`);
|
||||
}
|
||||
return result
|
||||
}
|
||||
return result;
|
||||
};
|
||||
|
||||
ipcRenderer.postMessage = function (channel: string, message: any, transferables: any) {
|
||||
return ipc.postMessage(channel, message, transferables)
|
||||
}
|
||||
return ipc.postMessage(channel, message, transferables);
|
||||
};
|
||||
|
||||
export default ipcRenderer
|
||||
export default ipcRenderer;
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
const features = process.electronBinding('features')
|
||||
const v8Util = process.electronBinding('v8_util')
|
||||
const features = process.electronBinding('features');
|
||||
const v8Util = process.electronBinding('v8_util');
|
||||
|
||||
const enableRemoteModule = v8Util.getHiddenValue<boolean>(global, 'enableRemoteModule')
|
||||
const enableRemoteModule = v8Util.getHiddenValue<boolean>(global, 'enableRemoteModule');
|
||||
|
||||
// Renderer side modules, please sort alphabetically.
|
||||
export const rendererModuleList: ElectronInternal.ModuleEntry[] = [
|
||||
|
@ -9,12 +9,12 @@ export const rendererModuleList: ElectronInternal.ModuleEntry[] = [
|
|||
{ name: 'crashReporter', loader: () => require('./crash-reporter') },
|
||||
{ name: 'ipcRenderer', loader: () => require('./ipc-renderer') },
|
||||
{ name: 'webFrame', loader: () => require('./web-frame') }
|
||||
]
|
||||
];
|
||||
|
||||
if (features.isDesktopCapturerEnabled()) {
|
||||
rendererModuleList.push({ name: 'desktopCapturer', loader: () => require('./desktop-capturer') })
|
||||
rendererModuleList.push({ name: 'desktopCapturer', loader: () => require('./desktop-capturer') });
|
||||
}
|
||||
|
||||
if (features.isRemoteModuleEnabled() && enableRemoteModule) {
|
||||
rendererModuleList.push({ name: 'remote', loader: () => require('./remote') })
|
||||
rendererModuleList.push({ name: 'remote', loader: () => require('./remote') });
|
||||
}
|
||||
|
|
|
@ -1,26 +1,26 @@
|
|||
'use strict'
|
||||
'use strict';
|
||||
|
||||
const v8Util = process.electronBinding('v8_util')
|
||||
const { hasSwitch } = process.electronBinding('command_line')
|
||||
const v8Util = process.electronBinding('v8_util');
|
||||
const { hasSwitch } = process.electronBinding('command_line');
|
||||
|
||||
const { CallbacksRegistry } = require('@electron/internal/renderer/remote/callbacks-registry')
|
||||
const { isPromise, isSerializableObject } = require('@electron/internal/common/type-utils')
|
||||
const { ipcRendererInternal } = require('@electron/internal/renderer/ipc-renderer-internal')
|
||||
const { CallbacksRegistry } = require('@electron/internal/renderer/remote/callbacks-registry');
|
||||
const { isPromise, isSerializableObject } = require('@electron/internal/common/type-utils');
|
||||
const { ipcRendererInternal } = require('@electron/internal/renderer/ipc-renderer-internal');
|
||||
|
||||
const callbacksRegistry = new CallbacksRegistry()
|
||||
const remoteObjectCache = v8Util.createIDWeakMap()
|
||||
const callbacksRegistry = new CallbacksRegistry();
|
||||
const remoteObjectCache = v8Util.createIDWeakMap();
|
||||
|
||||
// An unique ID that can represent current context.
|
||||
const contextId = v8Util.getHiddenValue(global, 'contextId')
|
||||
const contextId = v8Util.getHiddenValue(global, 'contextId');
|
||||
|
||||
// Notify the main process when current context is going to be released.
|
||||
// Note that when the renderer process is destroyed, the message may not be
|
||||
// sent, we also listen to the "render-view-deleted" event in the main process
|
||||
// to guard that situation.
|
||||
process.on('exit', () => {
|
||||
const command = 'ELECTRON_BROWSER_CONTEXT_RELEASE'
|
||||
ipcRendererInternal.send(command, contextId)
|
||||
})
|
||||
const command = 'ELECTRON_BROWSER_CONTEXT_RELEASE';
|
||||
ipcRendererInternal.send(command, contextId);
|
||||
});
|
||||
|
||||
// Convert the arguments object into an array of meta data.
|
||||
function wrapArgs (args, visited = new Set()) {
|
||||
|
@ -30,182 +30,182 @@ function wrapArgs (args, visited = new Set()) {
|
|||
return {
|
||||
type: 'value',
|
||||
value: null
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
if (Array.isArray(value)) {
|
||||
visited.add(value)
|
||||
visited.add(value);
|
||||
const meta = {
|
||||
type: 'array',
|
||||
value: wrapArgs(value, visited)
|
||||
}
|
||||
visited.delete(value)
|
||||
return meta
|
||||
};
|
||||
visited.delete(value);
|
||||
return meta;
|
||||
} else if (value instanceof Buffer) {
|
||||
return {
|
||||
type: 'buffer',
|
||||
value
|
||||
}
|
||||
};
|
||||
} else if (isSerializableObject(value)) {
|
||||
return {
|
||||
type: 'value',
|
||||
value
|
||||
}
|
||||
};
|
||||
} else if (typeof value === 'object') {
|
||||
if (isPromise(value)) {
|
||||
return {
|
||||
type: 'promise',
|
||||
then: valueToMeta(function (onFulfilled, onRejected) {
|
||||
value.then(onFulfilled, onRejected)
|
||||
value.then(onFulfilled, onRejected);
|
||||
})
|
||||
}
|
||||
};
|
||||
} else if (v8Util.getHiddenValue(value, 'atomId')) {
|
||||
return {
|
||||
type: 'remote-object',
|
||||
id: v8Util.getHiddenValue(value, 'atomId')
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
const meta = {
|
||||
type: 'object',
|
||||
name: value.constructor ? value.constructor.name : '',
|
||||
members: []
|
||||
}
|
||||
visited.add(value)
|
||||
};
|
||||
visited.add(value);
|
||||
for (const prop in value) { // eslint-disable-line guard-for-in
|
||||
meta.members.push({
|
||||
name: prop,
|
||||
value: valueToMeta(value[prop])
|
||||
})
|
||||
});
|
||||
}
|
||||
visited.delete(value)
|
||||
return meta
|
||||
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'),
|
||||
length: value.length
|
||||
}
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
type: 'value',
|
||||
value
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
return args.map(valueToMeta)
|
||||
};
|
||||
return args.map(valueToMeta);
|
||||
}
|
||||
|
||||
// Populate object's members from descriptors.
|
||||
// The |ref| will be kept referenced by |members|.
|
||||
// This matches |getObjectMemebers| in rpc-server.
|
||||
function setObjectMembers (ref, object, metaId, members) {
|
||||
if (!Array.isArray(members)) return
|
||||
if (!Array.isArray(members)) return;
|
||||
|
||||
for (const member of members) {
|
||||
if (Object.prototype.hasOwnProperty.call(object, member.name)) continue
|
||||
if (Object.prototype.hasOwnProperty.call(object, member.name)) continue;
|
||||
|
||||
const descriptor = { enumerable: member.enumerable }
|
||||
const descriptor = { enumerable: member.enumerable };
|
||||
if (member.type === 'method') {
|
||||
const remoteMemberFunction = function (...args) {
|
||||
let command
|
||||
let command;
|
||||
if (this && this.constructor === remoteMemberFunction) {
|
||||
command = 'ELECTRON_BROWSER_MEMBER_CONSTRUCTOR'
|
||||
command = 'ELECTRON_BROWSER_MEMBER_CONSTRUCTOR';
|
||||
} else {
|
||||
command = 'ELECTRON_BROWSER_MEMBER_CALL'
|
||||
command = 'ELECTRON_BROWSER_MEMBER_CALL';
|
||||
}
|
||||
const ret = ipcRendererInternal.sendSync(command, contextId, metaId, member.name, wrapArgs(args))
|
||||
return metaToValue(ret)
|
||||
}
|
||||
const ret = ipcRendererInternal.sendSync(command, contextId, metaId, member.name, wrapArgs(args));
|
||||
return metaToValue(ret);
|
||||
};
|
||||
|
||||
let descriptorFunction = proxyFunctionProperties(remoteMemberFunction, metaId, member.name)
|
||||
let descriptorFunction = proxyFunctionProperties(remoteMemberFunction, metaId, member.name);
|
||||
|
||||
descriptor.get = () => {
|
||||
descriptorFunction.ref = ref // The member should reference its object.
|
||||
return descriptorFunction
|
||||
}
|
||||
descriptorFunction.ref = ref; // The member should reference its object.
|
||||
return descriptorFunction;
|
||||
};
|
||||
// Enable monkey-patch the method
|
||||
descriptor.set = (value) => {
|
||||
descriptorFunction = value
|
||||
return value
|
||||
}
|
||||
descriptor.configurable = true
|
||||
descriptorFunction = value;
|
||||
return value;
|
||||
};
|
||||
descriptor.configurable = true;
|
||||
} else if (member.type === 'get') {
|
||||
descriptor.get = () => {
|
||||
const command = 'ELECTRON_BROWSER_MEMBER_GET'
|
||||
const meta = ipcRendererInternal.sendSync(command, contextId, metaId, member.name)
|
||||
return metaToValue(meta)
|
||||
}
|
||||
const command = 'ELECTRON_BROWSER_MEMBER_GET';
|
||||
const meta = ipcRendererInternal.sendSync(command, contextId, metaId, member.name);
|
||||
return metaToValue(meta);
|
||||
};
|
||||
|
||||
if (member.writable) {
|
||||
descriptor.set = (value) => {
|
||||
const args = wrapArgs([value])
|
||||
const command = 'ELECTRON_BROWSER_MEMBER_SET'
|
||||
const meta = ipcRendererInternal.sendSync(command, contextId, metaId, member.name, args)
|
||||
if (meta != null) metaToValue(meta)
|
||||
return value
|
||||
}
|
||||
const args = wrapArgs([value]);
|
||||
const command = 'ELECTRON_BROWSER_MEMBER_SET';
|
||||
const meta = ipcRendererInternal.sendSync(command, contextId, metaId, member.name, args);
|
||||
if (meta != null) metaToValue(meta);
|
||||
return value;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
Object.defineProperty(object, member.name, descriptor)
|
||||
Object.defineProperty(object, member.name, descriptor);
|
||||
}
|
||||
}
|
||||
|
||||
// Populate object's prototype from descriptor.
|
||||
// This matches |getObjectPrototype| in rpc-server.
|
||||
function setObjectPrototype (ref, object, metaId, descriptor) {
|
||||
if (descriptor === null) return
|
||||
const proto = {}
|
||||
setObjectMembers(ref, proto, metaId, descriptor.members)
|
||||
setObjectPrototype(ref, proto, metaId, descriptor.proto)
|
||||
Object.setPrototypeOf(object, proto)
|
||||
if (descriptor === null) return;
|
||||
const 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
|
||||
function proxyFunctionProperties (remoteMemberFunction, metaId, name) {
|
||||
let loaded = false
|
||||
let loaded = false;
|
||||
|
||||
// Lazily load function properties
|
||||
const loadRemoteProperties = () => {
|
||||
if (loaded) return
|
||||
loaded = true
|
||||
const command = 'ELECTRON_BROWSER_MEMBER_GET'
|
||||
const meta = ipcRendererInternal.sendSync(command, contextId, metaId, name)
|
||||
setObjectMembers(remoteMemberFunction, remoteMemberFunction, meta.id, meta.members)
|
||||
}
|
||||
if (loaded) return;
|
||||
loaded = true;
|
||||
const command = 'ELECTRON_BROWSER_MEMBER_GET';
|
||||
const meta = ipcRendererInternal.sendSync(command, contextId, metaId, name);
|
||||
setObjectMembers(remoteMemberFunction, remoteMemberFunction, meta.id, meta.members);
|
||||
};
|
||||
|
||||
return new Proxy(remoteMemberFunction, {
|
||||
set: (target, property, value, receiver) => {
|
||||
if (property !== 'ref') loadRemoteProperties()
|
||||
target[property] = value
|
||||
return true
|
||||
if (property !== 'ref') loadRemoteProperties();
|
||||
target[property] = value;
|
||||
return true;
|
||||
},
|
||||
get: (target, property, receiver) => {
|
||||
if (!Object.prototype.hasOwnProperty.call(target, property)) loadRemoteProperties()
|
||||
const value = target[property]
|
||||
if (!Object.prototype.hasOwnProperty.call(target, property)) loadRemoteProperties();
|
||||
const value = target[property];
|
||||
if (property === 'toString' && typeof value === 'function') {
|
||||
return value.bind(target)
|
||||
return value.bind(target);
|
||||
}
|
||||
return value
|
||||
return value;
|
||||
},
|
||||
ownKeys: (target) => {
|
||||
loadRemoteProperties()
|
||||
return Object.getOwnPropertyNames(target)
|
||||
loadRemoteProperties();
|
||||
return Object.getOwnPropertyNames(target);
|
||||
},
|
||||
getOwnPropertyDescriptor: (target, property) => {
|
||||
const descriptor = Object.getOwnPropertyDescriptor(target, property)
|
||||
if (descriptor) return descriptor
|
||||
loadRemoteProperties()
|
||||
return Object.getOwnPropertyDescriptor(target, property)
|
||||
const descriptor = Object.getOwnPropertyDescriptor(target, property);
|
||||
if (descriptor) return descriptor;
|
||||
loadRemoteProperties();
|
||||
return Object.getOwnPropertyDescriptor(target, property);
|
||||
}
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
// Convert meta data from browser into real value.
|
||||
|
@ -216,143 +216,143 @@ function metaToValue (meta) {
|
|||
buffer: () => Buffer.from(meta.value.buffer, meta.value.byteOffset, meta.value.byteLength),
|
||||
promise: () => Promise.resolve({ then: metaToValue(meta.then) }),
|
||||
error: () => metaToError(meta),
|
||||
exception: () => { throw metaToError(meta.value) }
|
||||
}
|
||||
exception: () => { throw metaToError(meta.value); }
|
||||
};
|
||||
|
||||
if (Object.prototype.hasOwnProperty.call(types, meta.type)) {
|
||||
return types[meta.type]()
|
||||
return types[meta.type]();
|
||||
} else {
|
||||
let ret
|
||||
let ret;
|
||||
if (remoteObjectCache.has(meta.id)) {
|
||||
v8Util.addRemoteObjectRef(contextId, meta.id)
|
||||
return remoteObjectCache.get(meta.id)
|
||||
v8Util.addRemoteObjectRef(contextId, meta.id);
|
||||
return remoteObjectCache.get(meta.id);
|
||||
}
|
||||
|
||||
// A shadow class to represent the remote function object.
|
||||
if (meta.type === 'function') {
|
||||
const remoteFunction = function (...args) {
|
||||
let command
|
||||
let command;
|
||||
if (this && this.constructor === remoteFunction) {
|
||||
command = 'ELECTRON_BROWSER_CONSTRUCTOR'
|
||||
command = 'ELECTRON_BROWSER_CONSTRUCTOR';
|
||||
} else {
|
||||
command = 'ELECTRON_BROWSER_FUNCTION_CALL'
|
||||
command = 'ELECTRON_BROWSER_FUNCTION_CALL';
|
||||
}
|
||||
const obj = ipcRendererInternal.sendSync(command, contextId, meta.id, wrapArgs(args))
|
||||
return metaToValue(obj)
|
||||
}
|
||||
ret = remoteFunction
|
||||
const obj = ipcRendererInternal.sendSync(command, contextId, meta.id, wrapArgs(args));
|
||||
return metaToValue(obj);
|
||||
};
|
||||
ret = remoteFunction;
|
||||
} else {
|
||||
ret = {}
|
||||
ret = {};
|
||||
}
|
||||
|
||||
setObjectMembers(ret, ret, meta.id, meta.members)
|
||||
setObjectPrototype(ret, ret, meta.id, meta.proto)
|
||||
Object.defineProperty(ret.constructor, 'name', { value: meta.name })
|
||||
setObjectMembers(ret, ret, meta.id, meta.members);
|
||||
setObjectPrototype(ret, ret, meta.id, meta.proto);
|
||||
Object.defineProperty(ret.constructor, 'name', { value: meta.name });
|
||||
|
||||
// Track delegate obj's lifetime & tell browser to clean up when object is GCed.
|
||||
v8Util.setRemoteObjectFreer(ret, contextId, meta.id)
|
||||
v8Util.setHiddenValue(ret, 'atomId', meta.id)
|
||||
v8Util.addRemoteObjectRef(contextId, meta.id)
|
||||
remoteObjectCache.set(meta.id, ret)
|
||||
return ret
|
||||
v8Util.setRemoteObjectFreer(ret, contextId, meta.id);
|
||||
v8Util.setHiddenValue(ret, 'atomId', meta.id);
|
||||
v8Util.addRemoteObjectRef(contextId, meta.id);
|
||||
remoteObjectCache.set(meta.id, ret);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
function metaToError (meta) {
|
||||
const obj = meta.value
|
||||
const obj = meta.value;
|
||||
for (const { name, value } of meta.members) {
|
||||
obj[name] = metaToValue(value)
|
||||
obj[name] = metaToValue(value);
|
||||
}
|
||||
return obj
|
||||
return obj;
|
||||
}
|
||||
|
||||
function handleMessage (channel, handler) {
|
||||
ipcRendererInternal.on(channel, (event, passedContextId, id, ...args) => {
|
||||
if (passedContextId === contextId) {
|
||||
handler(id, ...args)
|
||||
handler(id, ...args);
|
||||
} else {
|
||||
// Message sent to an un-exist context, notify the error to main process.
|
||||
ipcRendererInternal.send('ELECTRON_BROWSER_WRONG_CONTEXT_ERROR', contextId, passedContextId, id)
|
||||
ipcRendererInternal.send('ELECTRON_BROWSER_WRONG_CONTEXT_ERROR', contextId, passedContextId, id);
|
||||
}
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
const enableStacks = hasSwitch('enable-api-filtering-logging')
|
||||
const enableStacks = hasSwitch('enable-api-filtering-logging');
|
||||
|
||||
function getCurrentStack () {
|
||||
const target = {}
|
||||
const target = {};
|
||||
if (enableStacks) {
|
||||
Error.captureStackTrace(target, getCurrentStack)
|
||||
Error.captureStackTrace(target, getCurrentStack);
|
||||
}
|
||||
return target.stack
|
||||
return target.stack;
|
||||
}
|
||||
|
||||
// Browser calls a callback in renderer.
|
||||
handleMessage('ELECTRON_RENDERER_CALLBACK', (id, args) => {
|
||||
callbacksRegistry.apply(id, metaToValue(args))
|
||||
})
|
||||
callbacksRegistry.apply(id, metaToValue(args));
|
||||
});
|
||||
|
||||
// A callback in browser is released.
|
||||
handleMessage('ELECTRON_RENDERER_RELEASE_CALLBACK', (id) => {
|
||||
callbacksRegistry.remove(id)
|
||||
})
|
||||
callbacksRegistry.remove(id);
|
||||
});
|
||||
|
||||
exports.require = (module) => {
|
||||
const command = 'ELECTRON_BROWSER_REQUIRE'
|
||||
const meta = ipcRendererInternal.sendSync(command, contextId, module, getCurrentStack())
|
||||
return metaToValue(meta)
|
||||
}
|
||||
const command = 'ELECTRON_BROWSER_REQUIRE';
|
||||
const meta = ipcRendererInternal.sendSync(command, contextId, module, getCurrentStack());
|
||||
return metaToValue(meta);
|
||||
};
|
||||
|
||||
// Alias to remote.require('electron').xxx.
|
||||
exports.getBuiltin = (module) => {
|
||||
const command = 'ELECTRON_BROWSER_GET_BUILTIN'
|
||||
const meta = ipcRendererInternal.sendSync(command, contextId, module, getCurrentStack())
|
||||
return metaToValue(meta)
|
||||
}
|
||||
const command = 'ELECTRON_BROWSER_GET_BUILTIN';
|
||||
const meta = ipcRendererInternal.sendSync(command, contextId, module, getCurrentStack());
|
||||
return metaToValue(meta);
|
||||
};
|
||||
|
||||
exports.getCurrentWindow = () => {
|
||||
const command = 'ELECTRON_BROWSER_CURRENT_WINDOW'
|
||||
const meta = ipcRendererInternal.sendSync(command, contextId, getCurrentStack())
|
||||
return metaToValue(meta)
|
||||
}
|
||||
const command = 'ELECTRON_BROWSER_CURRENT_WINDOW';
|
||||
const meta = ipcRendererInternal.sendSync(command, contextId, getCurrentStack());
|
||||
return metaToValue(meta);
|
||||
};
|
||||
|
||||
// Get current WebContents object.
|
||||
exports.getCurrentWebContents = () => {
|
||||
const command = 'ELECTRON_BROWSER_CURRENT_WEB_CONTENTS'
|
||||
const meta = ipcRendererInternal.sendSync(command, contextId, getCurrentStack())
|
||||
return metaToValue(meta)
|
||||
}
|
||||
const command = 'ELECTRON_BROWSER_CURRENT_WEB_CONTENTS';
|
||||
const meta = ipcRendererInternal.sendSync(command, contextId, getCurrentStack());
|
||||
return metaToValue(meta);
|
||||
};
|
||||
|
||||
// Get a global object in browser.
|
||||
exports.getGlobal = (name) => {
|
||||
const command = 'ELECTRON_BROWSER_GLOBAL'
|
||||
const meta = ipcRendererInternal.sendSync(command, contextId, name, getCurrentStack())
|
||||
return metaToValue(meta)
|
||||
}
|
||||
const command = 'ELECTRON_BROWSER_GLOBAL';
|
||||
const meta = ipcRendererInternal.sendSync(command, contextId, name, getCurrentStack());
|
||||
return metaToValue(meta);
|
||||
};
|
||||
|
||||
// Get the process object in browser.
|
||||
Object.defineProperty(exports, 'process', {
|
||||
get: () => exports.getGlobal('process')
|
||||
})
|
||||
});
|
||||
|
||||
// Create a function that will return the specified value when called in browser.
|
||||
exports.createFunctionWithReturnValue = (returnValue) => {
|
||||
const func = () => returnValue
|
||||
v8Util.setHiddenValue(func, 'returnValue', true)
|
||||
return func
|
||||
}
|
||||
const func = () => returnValue;
|
||||
v8Util.setHiddenValue(func, 'returnValue', true);
|
||||
return func;
|
||||
};
|
||||
|
||||
const addBuiltinProperty = (name) => {
|
||||
Object.defineProperty(exports, name, {
|
||||
get: () => exports.getBuiltin(name)
|
||||
})
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const { commonModuleList } = require('@electron/internal/common/api/module-list')
|
||||
const browserModules = commonModuleList.concat(require('@electron/internal/browser/api/module-keys'))
|
||||
const { commonModuleList } = require('@electron/internal/common/api/module-list');
|
||||
const browserModules = commonModuleList.concat(require('@electron/internal/browser/api/module-keys'));
|
||||
|
||||
// And add a helper receiver for each one.
|
||||
browserModules
|
||||
.filter((m) => !m.private)
|
||||
.map((m) => m.name)
|
||||
.forEach(addBuiltinProperty)
|
||||
.forEach(addBuiltinProperty);
|
||||
|
|
|
@ -1,49 +1,49 @@
|
|||
import { EventEmitter } from 'events'
|
||||
import { EventEmitter } from 'events';
|
||||
|
||||
const binding = process.electronBinding('web_frame')
|
||||
const binding = process.electronBinding('web_frame');
|
||||
|
||||
class WebFrame extends EventEmitter {
|
||||
constructor (public context: Window) {
|
||||
super()
|
||||
super();
|
||||
|
||||
// Lots of webview would subscribe to webFrame's events.
|
||||
this.setMaxListeners(0)
|
||||
this.setMaxListeners(0);
|
||||
}
|
||||
|
||||
findFrameByRoutingId (...args: Array<any>) {
|
||||
return getWebFrame(binding._findFrameByRoutingId(this.context, ...args))
|
||||
return getWebFrame(binding._findFrameByRoutingId(this.context, ...args));
|
||||
}
|
||||
|
||||
getFrameForSelector (...args: Array<any>) {
|
||||
return getWebFrame(binding._getFrameForSelector(this.context, ...args))
|
||||
return getWebFrame(binding._getFrameForSelector(this.context, ...args));
|
||||
}
|
||||
|
||||
findFrameByName (...args: Array<any>) {
|
||||
return getWebFrame(binding._findFrameByName(this.context, ...args))
|
||||
return getWebFrame(binding._findFrameByName(this.context, ...args));
|
||||
}
|
||||
|
||||
get opener () {
|
||||
return getWebFrame(binding._getOpener(this.context))
|
||||
return getWebFrame(binding._getOpener(this.context));
|
||||
}
|
||||
|
||||
get parent () {
|
||||
return getWebFrame(binding._getParent(this.context))
|
||||
return getWebFrame(binding._getParent(this.context));
|
||||
}
|
||||
|
||||
get top () {
|
||||
return getWebFrame(binding._getTop(this.context))
|
||||
return getWebFrame(binding._getTop(this.context));
|
||||
}
|
||||
|
||||
get firstChild () {
|
||||
return getWebFrame(binding._getFirstChild(this.context))
|
||||
return getWebFrame(binding._getFirstChild(this.context));
|
||||
}
|
||||
|
||||
get nextSibling () {
|
||||
return getWebFrame(binding._getNextSibling(this.context))
|
||||
return getWebFrame(binding._getNextSibling(this.context));
|
||||
}
|
||||
|
||||
get routingId () {
|
||||
return binding._getRoutingId(this.context)
|
||||
return binding._getRoutingId(this.context);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -53,17 +53,17 @@ for (const name in binding) {
|
|||
// TODO(felixrieseberg): Once we can type web_frame natives, we could
|
||||
// use a neat `keyof` here
|
||||
(WebFrame as any).prototype[name] = function (...args: Array<any>) {
|
||||
return binding[name](this.context, ...args)
|
||||
}
|
||||
return binding[name](this.context, ...args);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// Helper to return WebFrame or null depending on context.
|
||||
// TODO(zcbenz): Consider returning same WebFrame for the same frame.
|
||||
function getWebFrame (context: Window) {
|
||||
return context ? new WebFrame(context) : null
|
||||
return context ? new WebFrame(context) : null;
|
||||
}
|
||||
|
||||
const _webFrame = new WebFrame(window)
|
||||
const _webFrame = new WebFrame(window);
|
||||
|
||||
export default _webFrame
|
||||
export default _webFrame;
|
||||
|
|
|
@ -1,14 +1,14 @@
|
|||
import { ipcRendererInternal } from '@electron/internal/renderer/ipc-renderer-internal'
|
||||
import * as ipcRendererUtils from '@electron/internal/renderer/ipc-renderer-internal-utils'
|
||||
import * as url from 'url'
|
||||
import { ipcRendererInternal } from '@electron/internal/renderer/ipc-renderer-internal';
|
||||
import * as ipcRendererUtils from '@electron/internal/renderer/ipc-renderer-internal-utils';
|
||||
import * as url from 'url';
|
||||
|
||||
import { Event } from '@electron/internal/renderer/extensions/event'
|
||||
import { Event } from '@electron/internal/renderer/extensions/event';
|
||||
|
||||
class Tab {
|
||||
public id: number
|
||||
|
||||
constructor (tabId: number) {
|
||||
this.id = tabId
|
||||
this.id = tabId;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -18,9 +18,9 @@ class MessageSender {
|
|||
public url: string
|
||||
|
||||
constructor (tabId: number, extensionId: string) {
|
||||
this.tab = tabId ? new Tab(tabId) : null
|
||||
this.id = extensionId
|
||||
this.url = `chrome-extension://${extensionId}`
|
||||
this.tab = tabId ? new Tab(tabId) : null;
|
||||
this.id = extensionId;
|
||||
this.url = `chrome-extension://${extensionId}`;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -31,69 +31,69 @@ class Port {
|
|||
public sender: MessageSender
|
||||
|
||||
constructor (public tabId: number, public portId: number, extensionId: string, public name: string) {
|
||||
this.onDisconnect = new Event()
|
||||
this.onMessage = new Event()
|
||||
this.sender = new MessageSender(tabId, extensionId)
|
||||
this.onDisconnect = new Event();
|
||||
this.onMessage = new Event();
|
||||
this.sender = new MessageSender(tabId, extensionId);
|
||||
|
||||
ipcRendererInternal.once(`CHROME_PORT_DISCONNECT_${portId}`, () => {
|
||||
this._onDisconnect()
|
||||
})
|
||||
this._onDisconnect();
|
||||
});
|
||||
|
||||
ipcRendererInternal.on(`CHROME_PORT_POSTMESSAGE_${portId}`, (
|
||||
_event: Electron.Event, message: any
|
||||
) => {
|
||||
const sendResponse = function () { console.error('sendResponse is not implemented') }
|
||||
this.onMessage.emit(JSON.parse(message), this.sender, sendResponse)
|
||||
})
|
||||
const sendResponse = function () { console.error('sendResponse is not implemented'); };
|
||||
this.onMessage.emit(JSON.parse(message), this.sender, sendResponse);
|
||||
});
|
||||
}
|
||||
|
||||
disconnect () {
|
||||
if (this.disconnected) return
|
||||
if (this.disconnected) return;
|
||||
|
||||
ipcRendererInternal.sendToAll(this.tabId, `CHROME_PORT_DISCONNECT_${this.portId}`)
|
||||
this._onDisconnect()
|
||||
ipcRendererInternal.sendToAll(this.tabId, `CHROME_PORT_DISCONNECT_${this.portId}`);
|
||||
this._onDisconnect();
|
||||
}
|
||||
|
||||
postMessage (message: any) {
|
||||
ipcRendererInternal.sendToAll(this.tabId, `CHROME_PORT_POSTMESSAGE_${this.portId}`, JSON.stringify(message))
|
||||
ipcRendererInternal.sendToAll(this.tabId, `CHROME_PORT_POSTMESSAGE_${this.portId}`, JSON.stringify(message));
|
||||
}
|
||||
|
||||
_onDisconnect () {
|
||||
this.disconnected = true
|
||||
ipcRendererInternal.removeAllListeners(`CHROME_PORT_POSTMESSAGE_${this.portId}`)
|
||||
this.onDisconnect.emit()
|
||||
this.disconnected = true;
|
||||
ipcRendererInternal.removeAllListeners(`CHROME_PORT_POSTMESSAGE_${this.portId}`);
|
||||
this.onDisconnect.emit();
|
||||
}
|
||||
}
|
||||
|
||||
// Inject chrome API to the |context|
|
||||
export function injectTo (extensionId: string, context: any) {
|
||||
if (process.electronBinding('features').isExtensionsEnabled()) {
|
||||
throw new Error('Attempted to load JS chrome-extension polyfill with //extensions support enabled')
|
||||
throw new Error('Attempted to load JS chrome-extension polyfill with //extensions support enabled');
|
||||
}
|
||||
|
||||
const chrome = context.chrome = context.chrome || {}
|
||||
const chrome = context.chrome = context.chrome || {};
|
||||
|
||||
ipcRendererInternal.on(`CHROME_RUNTIME_ONCONNECT_${extensionId}`, (
|
||||
_event: Electron.Event, tabId: number, portId: number, connectInfo: { name: string }
|
||||
) => {
|
||||
chrome.runtime.onConnect.emit(new Port(tabId, portId, extensionId, connectInfo.name))
|
||||
})
|
||||
chrome.runtime.onConnect.emit(new Port(tabId, portId, extensionId, connectInfo.name));
|
||||
});
|
||||
|
||||
ipcRendererUtils.handle(`CHROME_RUNTIME_ONMESSAGE_${extensionId}`, (
|
||||
_event: Electron.Event, tabId: number, message: string
|
||||
) => {
|
||||
return new Promise(resolve => {
|
||||
chrome.runtime.onMessage.emit(message, new MessageSender(tabId, extensionId), resolve)
|
||||
})
|
||||
})
|
||||
chrome.runtime.onMessage.emit(message, new MessageSender(tabId, extensionId), resolve);
|
||||
});
|
||||
});
|
||||
|
||||
ipcRendererInternal.on('CHROME_TABS_ONCREATED', (_event: Electron.Event, tabId: number) => {
|
||||
chrome.tabs.onCreated.emit(new Tab(tabId))
|
||||
})
|
||||
chrome.tabs.onCreated.emit(new Tab(tabId));
|
||||
});
|
||||
|
||||
ipcRendererInternal.on('CHROME_TABS_ONREMOVED', (_event: Electron.Event, tabId: number) => {
|
||||
chrome.tabs.onRemoved.emit(tabId)
|
||||
})
|
||||
chrome.tabs.onRemoved.emit(tabId);
|
||||
});
|
||||
|
||||
chrome.runtime = {
|
||||
id: extensionId,
|
||||
|
@ -105,69 +105,69 @@ export function injectTo (extensionId: string, context: any) {
|
|||
slashes: true,
|
||||
hostname: extensionId,
|
||||
pathname: path
|
||||
})
|
||||
});
|
||||
},
|
||||
|
||||
// https://developer.chrome.com/extensions/runtime#method-getManifest
|
||||
getManifest: function () {
|
||||
const manifest = ipcRendererUtils.invokeSync('CHROME_EXTENSION_MANIFEST', extensionId)
|
||||
return manifest
|
||||
const manifest = ipcRendererUtils.invokeSync('CHROME_EXTENSION_MANIFEST', extensionId);
|
||||
return manifest;
|
||||
},
|
||||
|
||||
// https://developer.chrome.com/extensions/runtime#method-connect
|
||||
connect (...args: Array<any>) {
|
||||
// Parse the optional args.
|
||||
let targetExtensionId = extensionId
|
||||
let connectInfo = { name: '' }
|
||||
let targetExtensionId = extensionId;
|
||||
let connectInfo = { name: '' };
|
||||
if (args.length === 1) {
|
||||
if (typeof args[0] === 'string') {
|
||||
targetExtensionId = args[0]
|
||||
targetExtensionId = args[0];
|
||||
} else {
|
||||
connectInfo = args[0]
|
||||
connectInfo = args[0];
|
||||
}
|
||||
} else if (args.length === 2) {
|
||||
[targetExtensionId, connectInfo] = args
|
||||
[targetExtensionId, connectInfo] = args;
|
||||
}
|
||||
|
||||
const { tabId, portId } = ipcRendererUtils.invokeSync('CHROME_RUNTIME_CONNECT', targetExtensionId, connectInfo)
|
||||
return new Port(tabId, portId, extensionId, connectInfo.name)
|
||||
const { tabId, portId } = ipcRendererUtils.invokeSync('CHROME_RUNTIME_CONNECT', targetExtensionId, connectInfo);
|
||||
return new Port(tabId, portId, extensionId, connectInfo.name);
|
||||
},
|
||||
|
||||
// https://developer.chrome.com/extensions/runtime#method-sendMessage
|
||||
sendMessage (...args: Array<any>) {
|
||||
// Parse the optional args.
|
||||
const targetExtensionId = extensionId
|
||||
let message: string
|
||||
let options: Object | undefined
|
||||
let responseCallback: Chrome.Tabs.SendMessageCallback = () => {}
|
||||
const targetExtensionId = extensionId;
|
||||
let message: string;
|
||||
let options: Object | undefined;
|
||||
let responseCallback: Chrome.Tabs.SendMessageCallback = () => {};
|
||||
|
||||
if (typeof args[args.length - 1] === 'function') {
|
||||
responseCallback = args.pop()
|
||||
responseCallback = args.pop();
|
||||
}
|
||||
|
||||
if (args.length === 1) {
|
||||
[message] = args
|
||||
[message] = args;
|
||||
} else if (args.length === 2) {
|
||||
if (typeof args[0] === 'string') {
|
||||
[extensionId, message] = args
|
||||
[extensionId, message] = args;
|
||||
} else {
|
||||
[message, options] = args
|
||||
[message, options] = args;
|
||||
}
|
||||
} else {
|
||||
[extensionId, message, options] = args
|
||||
[extensionId, message, options] = args;
|
||||
}
|
||||
|
||||
if (options) {
|
||||
console.error('options are not supported')
|
||||
console.error('options are not supported');
|
||||
}
|
||||
|
||||
ipcRendererInternal.invoke('CHROME_RUNTIME_SEND_MESSAGE', targetExtensionId, message).then(responseCallback)
|
||||
ipcRendererInternal.invoke('CHROME_RUNTIME_SEND_MESSAGE', targetExtensionId, message).then(responseCallback);
|
||||
},
|
||||
|
||||
onConnect: new Event(),
|
||||
onMessage: new Event(),
|
||||
onInstalled: new Event()
|
||||
}
|
||||
};
|
||||
|
||||
chrome.tabs = {
|
||||
// https://developer.chrome.com/extensions/tabs#method-executeScript
|
||||
|
@ -177,7 +177,7 @@ export function injectTo (extensionId: string, context: any) {
|
|||
resultCallback: Chrome.Tabs.ExecuteScriptCallback = () => {}
|
||||
) {
|
||||
ipcRendererInternal.invoke('CHROME_TABS_EXECUTE_SCRIPT', tabId, extensionId, details)
|
||||
.then((result: any) => resultCallback([result]))
|
||||
.then((result: any) => resultCallback([result]));
|
||||
},
|
||||
|
||||
// https://developer.chrome.com/extensions/tabs#method-sendMessage
|
||||
|
@ -187,13 +187,13 @@ export function injectTo (extensionId: string, context: any) {
|
|||
_options: Chrome.Tabs.SendMessageDetails,
|
||||
responseCallback: Chrome.Tabs.SendMessageCallback = () => {}
|
||||
) {
|
||||
ipcRendererInternal.invoke('CHROME_TABS_SEND_MESSAGE', tabId, extensionId, message).then(responseCallback)
|
||||
ipcRendererInternal.invoke('CHROME_TABS_SEND_MESSAGE', tabId, extensionId, message).then(responseCallback);
|
||||
},
|
||||
|
||||
onUpdated: new Event(),
|
||||
onCreated: new Event(),
|
||||
onRemoved: new Event()
|
||||
}
|
||||
};
|
||||
|
||||
chrome.extension = {
|
||||
getURL: chrome.runtime.getURL,
|
||||
|
@ -201,9 +201,9 @@ export function injectTo (extensionId: string, context: any) {
|
|||
onConnect: chrome.runtime.onConnect,
|
||||
sendMessage: chrome.runtime.sendMessage,
|
||||
onMessage: chrome.runtime.onMessage
|
||||
}
|
||||
};
|
||||
|
||||
chrome.storage = require('@electron/internal/renderer/extensions/storage').setup(extensionId)
|
||||
chrome.storage = require('@electron/internal/renderer/extensions/storage').setup(extensionId);
|
||||
|
||||
chrome.pageAction = {
|
||||
show () {},
|
||||
|
@ -213,14 +213,14 @@ export function injectTo (extensionId: string, context: any) {
|
|||
setIcon () {},
|
||||
setPopup () {},
|
||||
getPopup () {}
|
||||
}
|
||||
};
|
||||
|
||||
chrome.i18n = require('@electron/internal/renderer/extensions/i18n').setup(extensionId)
|
||||
chrome.webNavigation = require('@electron/internal/renderer/extensions/web-navigation').setup()
|
||||
chrome.i18n = require('@electron/internal/renderer/extensions/i18n').setup(extensionId);
|
||||
chrome.webNavigation = require('@electron/internal/renderer/extensions/web-navigation').setup();
|
||||
|
||||
// Electron has no concept of a browserAction but we should stub these APIs for compatibility
|
||||
chrome.browserAction = {
|
||||
setIcon () {},
|
||||
setPopup () {}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
import { webFrame } from 'electron'
|
||||
import { webFrame } from 'electron';
|
||||
|
||||
import * as ipcRendererUtils from '@electron/internal/renderer/ipc-renderer-internal-utils'
|
||||
import * as ipcRendererUtils from '@electron/internal/renderer/ipc-renderer-internal-utils';
|
||||
|
||||
const v8Util = process.electronBinding('v8_util')
|
||||
const v8Util = process.electronBinding('v8_util');
|
||||
|
||||
const IsolatedWorldIDs = {
|
||||
/**
|
||||
|
@ -10,93 +10,93 @@ const IsolatedWorldIDs = {
|
|||
* electron_render_frame_observer.h
|
||||
*/
|
||||
ISOLATED_WORLD_EXTENSIONS: 1 << 20
|
||||
}
|
||||
};
|
||||
|
||||
let isolatedWorldIds = IsolatedWorldIDs.ISOLATED_WORLD_EXTENSIONS
|
||||
const extensionWorldId: {[key: string]: number | undefined} = {}
|
||||
let isolatedWorldIds = IsolatedWorldIDs.ISOLATED_WORLD_EXTENSIONS;
|
||||
const extensionWorldId: {[key: string]: number | undefined} = {};
|
||||
|
||||
// https://cs.chromium.org/chromium/src/extensions/renderer/script_injection.cc?type=cs&sq=package:chromium&g=0&l=52
|
||||
const getIsolatedWorldIdForInstance = () => {
|
||||
// TODO(samuelmaddock): allocate and cleanup IDs
|
||||
return isolatedWorldIds++
|
||||
}
|
||||
return isolatedWorldIds++;
|
||||
};
|
||||
|
||||
const escapePattern = function (pattern: string) {
|
||||
return pattern.replace(/[\\^$+?.()|[\]{}]/g, '\\$&')
|
||||
}
|
||||
return pattern.replace(/[\\^$+?.()|[\]{}]/g, '\\$&');
|
||||
};
|
||||
|
||||
// Check whether pattern matches.
|
||||
// https://developer.chrome.com/extensions/match_patterns
|
||||
const matchesPattern = function (pattern: string) {
|
||||
if (pattern === '<all_urls>') return true
|
||||
const regexp = new RegExp(`^${pattern.split('*').map(escapePattern).join('.*')}$`)
|
||||
const url = `${location.protocol}//${location.host}${location.pathname}`
|
||||
return url.match(regexp)
|
||||
}
|
||||
if (pattern === '<all_urls>') return true;
|
||||
const regexp = new RegExp(`^${pattern.split('*').map(escapePattern).join('.*')}$`);
|
||||
const url = `${location.protocol}//${location.host}${location.pathname}`;
|
||||
return url.match(regexp);
|
||||
};
|
||||
|
||||
// Run the code with chrome API integrated.
|
||||
const runContentScript = function (this: any, extensionId: string, url: string, code: string) {
|
||||
// Assign unique world ID to each extension
|
||||
const worldId = extensionWorldId[extensionId] ||
|
||||
(extensionWorldId[extensionId] = getIsolatedWorldIdForInstance())
|
||||
(extensionWorldId[extensionId] = getIsolatedWorldIdForInstance());
|
||||
|
||||
// store extension ID for content script to read in isolated world
|
||||
v8Util.setHiddenValue(global, `extension-${worldId}`, extensionId)
|
||||
v8Util.setHiddenValue(global, `extension-${worldId}`, extensionId);
|
||||
|
||||
webFrame.setIsolatedWorldInfo(worldId, {
|
||||
name: `${extensionId} [${worldId}]`
|
||||
// TODO(samuelmaddock): read `content_security_policy` from extension manifest
|
||||
// csp: manifest.content_security_policy,
|
||||
})
|
||||
});
|
||||
|
||||
const sources = [{ code, url }]
|
||||
return webFrame.executeJavaScriptInIsolatedWorld(worldId, sources)
|
||||
}
|
||||
const sources = [{ code, url }];
|
||||
return webFrame.executeJavaScriptInIsolatedWorld(worldId, sources);
|
||||
};
|
||||
|
||||
const runAllContentScript = function (scripts: Array<Electron.InjectionBase>, extensionId: string) {
|
||||
for (const { url, code } of scripts) {
|
||||
runContentScript.call(window, extensionId, url, code)
|
||||
runContentScript.call(window, extensionId, url, code);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const runStylesheet = function (this: any, url: string, code: string) {
|
||||
webFrame.insertCSS(code)
|
||||
}
|
||||
webFrame.insertCSS(code);
|
||||
};
|
||||
|
||||
const runAllStylesheet = function (css: Array<Electron.InjectionBase>) {
|
||||
for (const { url, code } of css) {
|
||||
runStylesheet.call(window, url, code)
|
||||
runStylesheet.call(window, url, code);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Run injected scripts.
|
||||
// https://developer.chrome.com/extensions/content_scripts
|
||||
const injectContentScript = function (extensionId: string, script: Electron.ContentScript) {
|
||||
if (!process.isMainFrame && !script.allFrames) return
|
||||
if (!script.matches.some(matchesPattern)) return
|
||||
if (!process.isMainFrame && !script.allFrames) return;
|
||||
if (!script.matches.some(matchesPattern)) return;
|
||||
|
||||
if (script.js) {
|
||||
const fire = runAllContentScript.bind(window, script.js, extensionId)
|
||||
const fire = runAllContentScript.bind(window, script.js, extensionId);
|
||||
if (script.runAt === 'document_start') {
|
||||
process.once('document-start', fire)
|
||||
process.once('document-start', fire);
|
||||
} else if (script.runAt === 'document_end') {
|
||||
process.once('document-end', fire)
|
||||
process.once('document-end', fire);
|
||||
} else {
|
||||
document.addEventListener('DOMContentLoaded', fire)
|
||||
document.addEventListener('DOMContentLoaded', fire);
|
||||
}
|
||||
}
|
||||
|
||||
if (script.css) {
|
||||
const fire = runAllStylesheet.bind(window, script.css)
|
||||
const fire = runAllStylesheet.bind(window, script.css);
|
||||
if (script.runAt === 'document_start') {
|
||||
process.once('document-start', fire)
|
||||
process.once('document-start', fire);
|
||||
} else if (script.runAt === 'document_end') {
|
||||
process.once('document-end', fire)
|
||||
process.once('document-end', fire);
|
||||
} else {
|
||||
document.addEventListener('DOMContentLoaded', fire)
|
||||
document.addEventListener('DOMContentLoaded', fire);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Handle the request of chrome.tabs.executeJavaScript.
|
||||
ipcRendererUtils.handle('CHROME_TABS_EXECUTE_SCRIPT', function (
|
||||
|
@ -105,15 +105,15 @@ ipcRendererUtils.handle('CHROME_TABS_EXECUTE_SCRIPT', function (
|
|||
url: string,
|
||||
code: string
|
||||
) {
|
||||
return runContentScript.call(window, extensionId, url, code)
|
||||
})
|
||||
return runContentScript.call(window, extensionId, url, code);
|
||||
});
|
||||
|
||||
module.exports = (entries: Electron.ContentScriptEntry[]) => {
|
||||
for (const entry of entries) {
|
||||
if (entry.contentScripts) {
|
||||
for (const script of entry.contentScripts) {
|
||||
injectContentScript(entry.extensionId, script)
|
||||
injectContentScript(entry.extensionId, script);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
|
@ -2,19 +2,19 @@ export class Event {
|
|||
private listeners: Function[] = []
|
||||
|
||||
addListener (callback: Function) {
|
||||
this.listeners.push(callback)
|
||||
this.listeners.push(callback);
|
||||
}
|
||||
|
||||
removeListener (callback: Function) {
|
||||
const index = this.listeners.indexOf(callback)
|
||||
const index = this.listeners.indexOf(callback);
|
||||
if (index !== -1) {
|
||||
this.listeners.splice(index, 1)
|
||||
this.listeners.splice(index, 1);
|
||||
}
|
||||
}
|
||||
|
||||
emit (...args: any[]) {
|
||||
for (const listener of this.listeners) {
|
||||
listener(...args)
|
||||
listener(...args);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
// Does not implement predefined messages:
|
||||
// https://developer.chrome.com/extensions/i18n#overview-predefined
|
||||
|
||||
import * as ipcRendererUtils from '@electron/internal/renderer/ipc-renderer-internal-utils'
|
||||
import * as ipcRendererUtils from '@electron/internal/renderer/ipc-renderer-internal-utils';
|
||||
|
||||
interface Placeholder {
|
||||
content: string;
|
||||
|
@ -13,48 +13,48 @@ interface Placeholder {
|
|||
|
||||
const getMessages = (extensionId: number) => {
|
||||
try {
|
||||
const data = ipcRendererUtils.invokeSync<string>('CHROME_GET_MESSAGES', extensionId)
|
||||
return JSON.parse(data) || {}
|
||||
const data = ipcRendererUtils.invokeSync<string>('CHROME_GET_MESSAGES', extensionId);
|
||||
return JSON.parse(data) || {};
|
||||
} catch {
|
||||
return {}
|
||||
return {};
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const replaceNumberedSubstitutions = (message: string, substitutions: string[]) => {
|
||||
return message.replace(/\$(\d+)/, (_, number) => {
|
||||
const index = parseInt(number, 10) - 1
|
||||
return substitutions[index] || ''
|
||||
})
|
||||
}
|
||||
const index = parseInt(number, 10) - 1;
|
||||
return substitutions[index] || '';
|
||||
});
|
||||
};
|
||||
|
||||
const replacePlaceholders = (message: string, placeholders: Record<string, Placeholder>, substitutions: string[] | string) => {
|
||||
if (typeof substitutions === 'string') substitutions = [substitutions]
|
||||
if (!Array.isArray(substitutions)) substitutions = []
|
||||
if (typeof substitutions === 'string') substitutions = [substitutions];
|
||||
if (!Array.isArray(substitutions)) substitutions = [];
|
||||
|
||||
if (placeholders) {
|
||||
Object.keys(placeholders).forEach((name: string) => {
|
||||
let { content } = placeholders[name]
|
||||
const substitutionsArray = Array.isArray(substitutions) ? substitutions : []
|
||||
content = replaceNumberedSubstitutions(content, substitutionsArray)
|
||||
message = message.replace(new RegExp(`\\$${name}\\$`, 'gi'), content)
|
||||
})
|
||||
let { content } = placeholders[name];
|
||||
const substitutionsArray = Array.isArray(substitutions) ? substitutions : [];
|
||||
content = replaceNumberedSubstitutions(content, substitutionsArray);
|
||||
message = message.replace(new RegExp(`\\$${name}\\$`, 'gi'), content);
|
||||
});
|
||||
}
|
||||
|
||||
return replaceNumberedSubstitutions(message, substitutions)
|
||||
}
|
||||
return replaceNumberedSubstitutions(message, substitutions);
|
||||
};
|
||||
|
||||
const getMessage = (extensionId: number, messageName: string, substitutions: string[]) => {
|
||||
const messages = getMessages(extensionId)
|
||||
const messages = getMessages(extensionId);
|
||||
if (Object.prototype.hasOwnProperty.call(messages, messageName)) {
|
||||
const { message, placeholders } = messages[messageName]
|
||||
return replacePlaceholders(message, placeholders, substitutions)
|
||||
const { message, placeholders } = messages[messageName];
|
||||
return replacePlaceholders(message, placeholders, substitutions);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
exports.setup = (extensionId: number) => {
|
||||
return {
|
||||
getMessage (messageName: string, substitutions: string[]) {
|
||||
return getMessage(extensionId, messageName, substitutions)
|
||||
return getMessage(extensionId, messageName, substitutions);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
|
|
|
@ -1,86 +1,86 @@
|
|||
import { ipcRendererInternal } from '@electron/internal/renderer/ipc-renderer-internal'
|
||||
import { ipcRendererInternal } from '@electron/internal/renderer/ipc-renderer-internal';
|
||||
|
||||
const getStorage = (storageType: string, extensionId: number, callback: Function) => {
|
||||
if (typeof callback !== 'function') throw new TypeError('No callback provided')
|
||||
if (typeof callback !== 'function') throw new TypeError('No callback provided');
|
||||
|
||||
ipcRendererInternal.invoke<string>('CHROME_STORAGE_READ', storageType, extensionId)
|
||||
.then(data => {
|
||||
if (data !== null) {
|
||||
callback(JSON.parse(data))
|
||||
callback(JSON.parse(data));
|
||||
} else {
|
||||
// Disabled due to false positive in StandardJS
|
||||
// eslint-disable-next-line standard/no-callback-literal
|
||||
callback({})
|
||||
callback({});
|
||||
}
|
||||
})
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const setStorage = (storageType: string, extensionId: number, storage: Record<string, any>, callback: Function) => {
|
||||
const json = JSON.stringify(storage)
|
||||
const json = JSON.stringify(storage);
|
||||
ipcRendererInternal.invoke('CHROME_STORAGE_WRITE', storageType, extensionId, json)
|
||||
.then(() => {
|
||||
if (callback) callback()
|
||||
})
|
||||
}
|
||||
if (callback) callback();
|
||||
});
|
||||
};
|
||||
|
||||
const getStorageManager = (storageType: string, extensionId: number) => {
|
||||
return {
|
||||
get (keys: string[], callback: Function) {
|
||||
getStorage(storageType, extensionId, (storage: Record<string, any>) => {
|
||||
if (keys == null) return callback(storage)
|
||||
if (keys == null) return callback(storage);
|
||||
|
||||
let defaults: Record<string, any> = {}
|
||||
let defaults: Record<string, any> = {};
|
||||
switch (typeof keys) {
|
||||
case 'string':
|
||||
keys = [keys]
|
||||
break
|
||||
keys = [keys];
|
||||
break;
|
||||
case 'object':
|
||||
if (!Array.isArray(keys)) {
|
||||
defaults = keys
|
||||
keys = Object.keys(keys)
|
||||
defaults = keys;
|
||||
keys = Object.keys(keys);
|
||||
}
|
||||
break
|
||||
break;
|
||||
}
|
||||
|
||||
// Disabled due to false positive in StandardJS
|
||||
// eslint-disable-next-line standard/no-callback-literal
|
||||
if (keys.length === 0) return callback({})
|
||||
if (keys.length === 0) return callback({});
|
||||
|
||||
const items: Record<string, any> = {}
|
||||
const items: Record<string, any> = {};
|
||||
keys.forEach((key: string) => {
|
||||
let value = storage[key]
|
||||
if (value == null) value = defaults[key]
|
||||
items[key] = value
|
||||
})
|
||||
callback(items)
|
||||
})
|
||||
let value = storage[key];
|
||||
if (value == null) value = defaults[key];
|
||||
items[key] = value;
|
||||
});
|
||||
callback(items);
|
||||
});
|
||||
},
|
||||
|
||||
set (items: Record<string, any>, callback: Function) {
|
||||
getStorage(storageType, extensionId, (storage: Record<string, any>) => {
|
||||
Object.keys(items).forEach(name => { storage[name] = items[name] })
|
||||
setStorage(storageType, extensionId, storage, callback)
|
||||
})
|
||||
Object.keys(items).forEach(name => { storage[name] = items[name]; });
|
||||
setStorage(storageType, extensionId, storage, callback);
|
||||
});
|
||||
},
|
||||
|
||||
remove (keys: string[], callback: Function) {
|
||||
getStorage(storageType, extensionId, (storage: Record<string, any>) => {
|
||||
if (!Array.isArray(keys)) keys = [keys]
|
||||
if (!Array.isArray(keys)) keys = [keys];
|
||||
keys.forEach((key: string) => {
|
||||
delete storage[key]
|
||||
})
|
||||
delete storage[key];
|
||||
});
|
||||
|
||||
setStorage(storageType, extensionId, storage, callback)
|
||||
})
|
||||
setStorage(storageType, extensionId, storage, callback);
|
||||
});
|
||||
},
|
||||
|
||||
clear (callback: Function) {
|
||||
setStorage(storageType, extensionId, {}, callback)
|
||||
setStorage(storageType, extensionId, {}, callback);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
export const setup = (extensionId: number) => ({
|
||||
sync: getStorageManager('sync', extensionId),
|
||||
local: getStorageManager('local', extensionId)
|
||||
})
|
||||
});
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { Event } from '@electron/internal/renderer/extensions/event'
|
||||
import { ipcRendererInternal } from '@electron/internal/renderer/ipc-renderer-internal'
|
||||
import { Event } from '@electron/internal/renderer/extensions/event';
|
||||
import { ipcRendererInternal } from '@electron/internal/renderer/ipc-renderer-internal';
|
||||
|
||||
class WebNavigation {
|
||||
private onBeforeNavigate = new Event()
|
||||
|
@ -7,13 +7,13 @@ class WebNavigation {
|
|||
|
||||
constructor () {
|
||||
ipcRendererInternal.on('CHROME_WEBNAVIGATION_ONBEFORENAVIGATE', (event: Electron.IpcRendererEvent, details: any) => {
|
||||
this.onBeforeNavigate.emit(details)
|
||||
})
|
||||
this.onBeforeNavigate.emit(details);
|
||||
});
|
||||
|
||||
ipcRendererInternal.on('CHROME_WEBNAVIGATION_ONCOMPLETED', (event: Electron.IpcRendererEvent, details: any) => {
|
||||
this.onCompleted.emit(details)
|
||||
})
|
||||
this.onCompleted.emit(details);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export const setup = () => new WebNavigation()
|
||||
export const setup = () => new WebNavigation();
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { EventEmitter } from 'events'
|
||||
import * as path from 'path'
|
||||
import { EventEmitter } from 'events';
|
||||
import * as path from 'path';
|
||||
|
||||
const Module = require('module')
|
||||
const Module = require('module');
|
||||
|
||||
// Make sure globals like "process" and "global" are always available in preload
|
||||
// scripts even after they are deleted in "loaded" script.
|
||||
|
@ -23,42 +23,42 @@ Module.wrapper = [
|
|||
// code to override "process" and "Buffer" with local variables.
|
||||
'return function (exports, require, module, __filename, __dirname) { ',
|
||||
'\n}.call(this, exports, require, module, __filename, __dirname); });'
|
||||
]
|
||||
];
|
||||
|
||||
// We modified the original process.argv to let node.js load the
|
||||
// init.js, we need to restore it here.
|
||||
process.argv.splice(1, 1)
|
||||
process.argv.splice(1, 1);
|
||||
|
||||
// Clear search paths.
|
||||
|
||||
require('../common/reset-search-paths')
|
||||
require('../common/reset-search-paths');
|
||||
|
||||
// Import common settings.
|
||||
require('@electron/internal/common/init')
|
||||
require('@electron/internal/common/init');
|
||||
|
||||
// The global variable will be used by ipc for event dispatching
|
||||
const v8Util = process.electronBinding('v8_util')
|
||||
const v8Util = process.electronBinding('v8_util');
|
||||
|
||||
const ipcEmitter = new EventEmitter()
|
||||
const ipcInternalEmitter = new EventEmitter()
|
||||
v8Util.setHiddenValue(global, 'ipc', ipcEmitter)
|
||||
v8Util.setHiddenValue(global, 'ipc-internal', ipcInternalEmitter)
|
||||
const ipcEmitter = new EventEmitter();
|
||||
const ipcInternalEmitter = new EventEmitter();
|
||||
v8Util.setHiddenValue(global, 'ipc', ipcEmitter);
|
||||
v8Util.setHiddenValue(global, 'ipc-internal', ipcInternalEmitter);
|
||||
|
||||
v8Util.setHiddenValue(global, 'ipcNative', {
|
||||
onMessage (internal: boolean, channel: string, ports: any[], args: any[], senderId: number) {
|
||||
const sender = internal ? ipcInternalEmitter : ipcEmitter
|
||||
sender.emit(channel, { sender, senderId, ports }, ...args)
|
||||
const sender = internal ? ipcInternalEmitter : ipcEmitter;
|
||||
sender.emit(channel, { sender, senderId, ports }, ...args);
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
// Use electron module after everything is ready.
|
||||
const { ipcRendererInternal } = require('@electron/internal/renderer/ipc-renderer-internal')
|
||||
const ipcRendererUtils = require('@electron/internal/renderer/ipc-renderer-internal-utils')
|
||||
const { webFrameInit } = require('@electron/internal/renderer/web-frame-init')
|
||||
webFrameInit()
|
||||
const { ipcRendererInternal } = require('@electron/internal/renderer/ipc-renderer-internal');
|
||||
const ipcRendererUtils = require('@electron/internal/renderer/ipc-renderer-internal-utils');
|
||||
const { webFrameInit } = require('@electron/internal/renderer/web-frame-init');
|
||||
webFrameInit();
|
||||
|
||||
// Process command line arguments.
|
||||
const { hasSwitch, getSwitchValue } = process.electronBinding('command_line')
|
||||
const { hasSwitch, getSwitchValue } = process.electronBinding('command_line');
|
||||
|
||||
const parseOption = function<T> (
|
||||
name: string, defaultValue: T, converter?: (value: string) => T
|
||||
|
@ -69,105 +69,105 @@ const parseOption = function<T> (
|
|||
? converter(getSwitchValue(name))
|
||||
: getSwitchValue(name)
|
||||
)
|
||||
: defaultValue
|
||||
}
|
||||
: defaultValue;
|
||||
};
|
||||
|
||||
const contextIsolation = hasSwitch('context-isolation')
|
||||
const nodeIntegration = hasSwitch('node-integration')
|
||||
const webviewTag = hasSwitch('webview-tag')
|
||||
const isHiddenPage = hasSwitch('hidden-page')
|
||||
const usesNativeWindowOpen = hasSwitch('native-window-open')
|
||||
const rendererProcessReuseEnabled = hasSwitch('disable-electron-site-instance-overrides')
|
||||
const contextIsolation = hasSwitch('context-isolation');
|
||||
const nodeIntegration = hasSwitch('node-integration');
|
||||
const webviewTag = hasSwitch('webview-tag');
|
||||
const isHiddenPage = hasSwitch('hidden-page');
|
||||
const usesNativeWindowOpen = hasSwitch('native-window-open');
|
||||
const rendererProcessReuseEnabled = hasSwitch('disable-electron-site-instance-overrides');
|
||||
|
||||
const preloadScript = parseOption('preload', null)
|
||||
const preloadScripts = parseOption('preload-scripts', [], value => value.split(path.delimiter)) as string[]
|
||||
const appPath = parseOption('app-path', null)
|
||||
const guestInstanceId = parseOption('guest-instance-id', null, value => parseInt(value))
|
||||
const openerId = parseOption('opener-id', null, value => parseInt(value))
|
||||
const preloadScript = parseOption('preload', null);
|
||||
const preloadScripts = parseOption('preload-scripts', [], value => value.split(path.delimiter)) as string[];
|
||||
const appPath = parseOption('app-path', null);
|
||||
const guestInstanceId = parseOption('guest-instance-id', null, value => parseInt(value));
|
||||
const openerId = parseOption('opener-id', null, value => parseInt(value));
|
||||
|
||||
// The arguments to be passed to isolated world.
|
||||
const isolatedWorldArgs = { ipcRendererInternal, guestInstanceId, isHiddenPage, openerId, usesNativeWindowOpen, rendererProcessReuseEnabled }
|
||||
const isolatedWorldArgs = { ipcRendererInternal, guestInstanceId, isHiddenPage, openerId, usesNativeWindowOpen, rendererProcessReuseEnabled };
|
||||
|
||||
// The webContents preload script is loaded after the session preload scripts.
|
||||
if (preloadScript) {
|
||||
preloadScripts.push(preloadScript)
|
||||
preloadScripts.push(preloadScript);
|
||||
}
|
||||
|
||||
switch (window.location.protocol) {
|
||||
case 'devtools:': {
|
||||
// Override some inspector APIs.
|
||||
require('@electron/internal/renderer/inspector')
|
||||
break
|
||||
require('@electron/internal/renderer/inspector');
|
||||
break;
|
||||
}
|
||||
case 'chrome-extension:': {
|
||||
// Inject the chrome.* APIs that chrome extensions require
|
||||
if (!process.electronBinding('features').isExtensionsEnabled()) {
|
||||
require('@electron/internal/renderer/chrome-api').injectTo(window.location.hostname, window)
|
||||
require('@electron/internal/renderer/chrome-api').injectTo(window.location.hostname, window);
|
||||
}
|
||||
break
|
||||
break;
|
||||
}
|
||||
case 'chrome:':
|
||||
break
|
||||
break;
|
||||
default: {
|
||||
// Override default web functions.
|
||||
const { windowSetup } = require('@electron/internal/renderer/window-setup')
|
||||
windowSetup(guestInstanceId, openerId, isHiddenPage, usesNativeWindowOpen, rendererProcessReuseEnabled)
|
||||
const { windowSetup } = require('@electron/internal/renderer/window-setup');
|
||||
windowSetup(guestInstanceId, openerId, isHiddenPage, usesNativeWindowOpen, rendererProcessReuseEnabled);
|
||||
|
||||
// Inject content scripts.
|
||||
if (!process.electronBinding('features').isExtensionsEnabled()) {
|
||||
const contentScripts = ipcRendererUtils.invokeSync('ELECTRON_GET_CONTENT_SCRIPTS') as Electron.ContentScriptEntry[]
|
||||
require('@electron/internal/renderer/content-scripts-injector')(contentScripts)
|
||||
const contentScripts = ipcRendererUtils.invokeSync('ELECTRON_GET_CONTENT_SCRIPTS') as Electron.ContentScriptEntry[];
|
||||
require('@electron/internal/renderer/content-scripts-injector')(contentScripts);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Load webview tag implementation.
|
||||
if (process.isMainFrame) {
|
||||
const { webViewInit } = require('@electron/internal/renderer/web-view/web-view-init')
|
||||
webViewInit(contextIsolation, webviewTag, guestInstanceId)
|
||||
const { webViewInit } = require('@electron/internal/renderer/web-view/web-view-init');
|
||||
webViewInit(contextIsolation, webviewTag, guestInstanceId);
|
||||
}
|
||||
|
||||
// Pass the arguments to isolatedWorld.
|
||||
if (contextIsolation) {
|
||||
v8Util.setHiddenValue(global, 'isolated-world-args', isolatedWorldArgs)
|
||||
v8Util.setHiddenValue(global, 'isolated-world-args', isolatedWorldArgs);
|
||||
}
|
||||
|
||||
if (nodeIntegration) {
|
||||
// Export node bindings to global.
|
||||
const { makeRequireFunction } = __non_webpack_require__('internal/modules/cjs/helpers') // eslint-disable-line
|
||||
global.module = new Module('electron/js2c/renderer_init')
|
||||
global.require = makeRequireFunction(global.module)
|
||||
global.module = new Module('electron/js2c/renderer_init');
|
||||
global.require = makeRequireFunction(global.module);
|
||||
|
||||
// Set the __filename to the path of html file if it is file: protocol.
|
||||
if (window.location.protocol === 'file:') {
|
||||
const location = window.location
|
||||
let pathname = location.pathname
|
||||
const location = window.location;
|
||||
let pathname = location.pathname;
|
||||
|
||||
if (process.platform === 'win32') {
|
||||
if (pathname[0] === '/') pathname = pathname.substr(1)
|
||||
if (pathname[0] === '/') pathname = pathname.substr(1);
|
||||
|
||||
const isWindowsNetworkSharePath = location.hostname.length > 0 && process.resourcesPath.startsWith('\\')
|
||||
const isWindowsNetworkSharePath = location.hostname.length > 0 && process.resourcesPath.startsWith('\\');
|
||||
if (isWindowsNetworkSharePath) {
|
||||
pathname = `//${location.host}/${pathname}`
|
||||
pathname = `//${location.host}/${pathname}`;
|
||||
}
|
||||
}
|
||||
|
||||
global.__filename = path.normalize(decodeURIComponent(pathname))
|
||||
global.__dirname = path.dirname(global.__filename)
|
||||
global.__filename = path.normalize(decodeURIComponent(pathname));
|
||||
global.__dirname = path.dirname(global.__filename);
|
||||
|
||||
// Set module's filename so relative require can work as expected.
|
||||
global.module.filename = global.__filename
|
||||
global.module.filename = global.__filename;
|
||||
|
||||
// Also search for module under the html file.
|
||||
global.module.paths = Module._nodeModulePaths(global.__dirname)
|
||||
global.module.paths = Module._nodeModulePaths(global.__dirname);
|
||||
} else {
|
||||
// For backwards compatibility we fake these two paths here
|
||||
global.__filename = path.join(process.resourcesPath, 'electron.asar', 'renderer', 'init.js')
|
||||
global.__dirname = path.join(process.resourcesPath, 'electron.asar', 'renderer')
|
||||
global.__filename = path.join(process.resourcesPath, 'electron.asar', 'renderer', 'init.js');
|
||||
global.__dirname = path.join(process.resourcesPath, 'electron.asar', 'renderer');
|
||||
|
||||
if (appPath) {
|
||||
// Search for module under the app directory
|
||||
global.module.paths = Module._nodeModulePaths(appPath)
|
||||
global.module.paths = Module._nodeModulePaths(appPath);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -177,42 +177,42 @@ if (nodeIntegration) {
|
|||
// We do not want to add `uncaughtException` to our definitions
|
||||
// because we don't want anyone else (anywhere) to throw that kind
|
||||
// of error.
|
||||
global.process.emit('uncaughtException' as any, error as any)
|
||||
return true
|
||||
global.process.emit('uncaughtException' as any, error as any);
|
||||
return true;
|
||||
} else {
|
||||
return false
|
||||
return false;
|
||||
}
|
||||
}
|
||||
};
|
||||
} else {
|
||||
// Delete Node's symbols after the Environment has been loaded in a
|
||||
// non context-isolated environment
|
||||
if (!contextIsolation) {
|
||||
process.once('loaded', function () {
|
||||
delete global.process
|
||||
delete global.Buffer
|
||||
delete global.setImmediate
|
||||
delete global.clearImmediate
|
||||
delete global.global
|
||||
delete global.root
|
||||
delete global.GLOBAL
|
||||
})
|
||||
delete global.process;
|
||||
delete global.Buffer;
|
||||
delete global.setImmediate;
|
||||
delete global.clearImmediate;
|
||||
delete global.global;
|
||||
delete global.root;
|
||||
delete global.GLOBAL;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Load the preload scripts.
|
||||
for (const preloadScript of preloadScripts) {
|
||||
try {
|
||||
Module._load(preloadScript)
|
||||
Module._load(preloadScript);
|
||||
} catch (error) {
|
||||
console.error(`Unable to load preload script: ${preloadScript}`)
|
||||
console.error(error)
|
||||
console.error(`Unable to load preload script: ${preloadScript}`);
|
||||
console.error(error);
|
||||
|
||||
ipcRendererInternal.send('ELECTRON_BROWSER_PRELOAD_ERROR', preloadScript, error)
|
||||
ipcRendererInternal.send('ELECTRON_BROWSER_PRELOAD_ERROR', preloadScript, error);
|
||||
}
|
||||
}
|
||||
|
||||
// Warn about security issues
|
||||
if (process.isMainFrame) {
|
||||
const { securityWarnings } = require('@electron/internal/renderer/security-warnings')
|
||||
securityWarnings(nodeIntegration)
|
||||
const { securityWarnings } = require('@electron/internal/renderer/security-warnings');
|
||||
securityWarnings(nodeIntegration);
|
||||
}
|
||||
|
|
|
@ -1,61 +1,61 @@
|
|||
import { ipcRendererInternal } from '@electron/internal/renderer/ipc-renderer-internal'
|
||||
import * as ipcRendererUtils from '@electron/internal/renderer/ipc-renderer-internal-utils'
|
||||
import { ipcRendererInternal } from '@electron/internal/renderer/ipc-renderer-internal';
|
||||
import * as ipcRendererUtils from '@electron/internal/renderer/ipc-renderer-internal-utils';
|
||||
|
||||
window.onload = function () {
|
||||
// Use menu API to show context menu.
|
||||
window.InspectorFrontendHost!.showContextMenuAtPoint = createMenu
|
||||
window.InspectorFrontendHost!.showContextMenuAtPoint = createMenu;
|
||||
|
||||
// correct for Chromium returning undefined for filesystem
|
||||
window.Persistence!.FileSystemWorkspaceBinding.completeURL = completeURL
|
||||
window.Persistence!.FileSystemWorkspaceBinding.completeURL = completeURL;
|
||||
|
||||
// Use dialog API to override file chooser dialog.
|
||||
window.UI!.createFileSelectorElement = createFileSelectorElement
|
||||
}
|
||||
window.UI!.createFileSelectorElement = createFileSelectorElement;
|
||||
};
|
||||
|
||||
// Extra / is needed as a result of MacOS requiring absolute paths
|
||||
function completeURL (project: string, path: string) {
|
||||
project = 'file:///'
|
||||
return `${project}${path}`
|
||||
project = 'file:///';
|
||||
return `${project}${path}`;
|
||||
}
|
||||
|
||||
// The DOM implementation expects (message?: string) => boolean
|
||||
(window.confirm as any) = function (message: string, title: string) {
|
||||
return ipcRendererUtils.invokeSync('ELECTRON_INSPECTOR_CONFIRM', message, title) as boolean
|
||||
}
|
||||
return ipcRendererUtils.invokeSync('ELECTRON_INSPECTOR_CONFIRM', message, title) as boolean;
|
||||
};
|
||||
|
||||
const useEditMenuItems = function (x: number, y: number, items: ContextMenuItem[]) {
|
||||
return items.length === 0 && document.elementsFromPoint(x, y).some(function (element) {
|
||||
return element.nodeName === 'INPUT' ||
|
||||
element.nodeName === 'TEXTAREA' ||
|
||||
(element as HTMLElement).isContentEditable
|
||||
})
|
||||
}
|
||||
(element as HTMLElement).isContentEditable;
|
||||
});
|
||||
};
|
||||
|
||||
const createMenu = function (x: number, y: number, items: ContextMenuItem[]) {
|
||||
const isEditMenu = useEditMenuItems(x, y, items)
|
||||
const isEditMenu = useEditMenuItems(x, y, items);
|
||||
ipcRendererInternal.invoke<number>('ELECTRON_INSPECTOR_CONTEXT_MENU', items, isEditMenu).then(id => {
|
||||
if (typeof id === 'number') {
|
||||
window.DevToolsAPI!.contextMenuItemSelected(id)
|
||||
window.DevToolsAPI!.contextMenuItemSelected(id);
|
||||
}
|
||||
window.DevToolsAPI!.contextMenuCleared()
|
||||
})
|
||||
}
|
||||
window.DevToolsAPI!.contextMenuCleared();
|
||||
});
|
||||
};
|
||||
|
||||
const showFileChooserDialog = function (callback: (blob: File) => void) {
|
||||
ipcRendererInternal.invoke<[ string, any ]>('ELECTRON_INSPECTOR_SELECT_FILE').then(([path, data]) => {
|
||||
if (path && data) {
|
||||
callback(dataToHtml5FileObject(path, data))
|
||||
callback(dataToHtml5FileObject(path, data));
|
||||
}
|
||||
})
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const dataToHtml5FileObject = function (path: string, data: any) {
|
||||
return new File([data], path)
|
||||
}
|
||||
return new File([data], path);
|
||||
};
|
||||
|
||||
const createFileSelectorElement = function (this: any, callback: () => void) {
|
||||
const fileSelectorElement = document.createElement('span')
|
||||
fileSelectorElement.style.display = 'none'
|
||||
fileSelectorElement.click = showFileChooserDialog.bind(this, callback)
|
||||
return fileSelectorElement
|
||||
}
|
||||
const fileSelectorElement = document.createElement('span');
|
||||
fileSelectorElement.style.display = 'none';
|
||||
fileSelectorElement.click = showFileChooserDialog.bind(this, callback);
|
||||
return fileSelectorElement;
|
||||
};
|
||||
|
|
|
@ -1,24 +1,24 @@
|
|||
import { ipcRendererInternal } from '@electron/internal/renderer/ipc-renderer-internal'
|
||||
import { ipcRendererInternal } from '@electron/internal/renderer/ipc-renderer-internal';
|
||||
|
||||
type IPCHandler = (event: Electron.IpcRendererEvent, ...args: any[]) => any
|
||||
|
||||
export const handle = function <T extends IPCHandler> (channel: string, handler: T) {
|
||||
ipcRendererInternal.on(channel, async (event, requestId, ...args) => {
|
||||
const replyChannel = `${channel}_RESPONSE_${requestId}`
|
||||
const replyChannel = `${channel}_RESPONSE_${requestId}`;
|
||||
try {
|
||||
event.sender.send(replyChannel, null, await handler(event, ...args))
|
||||
event.sender.send(replyChannel, null, await handler(event, ...args));
|
||||
} catch (error) {
|
||||
event.sender.send(replyChannel, error)
|
||||
event.sender.send(replyChannel, error);
|
||||
}
|
||||
})
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
export function invokeSync<T> (command: string, ...args: any[]): T {
|
||||
const [error, result] = ipcRendererInternal.sendSync(command, ...args)
|
||||
const [error, result] = ipcRendererInternal.sendSync(command, ...args);
|
||||
|
||||
if (error) {
|
||||
throw error
|
||||
throw error;
|
||||
} else {
|
||||
return result
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,30 +1,30 @@
|
|||
const { ipc } = process.electronBinding('ipc')
|
||||
const v8Util = process.electronBinding('v8_util')
|
||||
const { ipc } = process.electronBinding('ipc');
|
||||
const v8Util = process.electronBinding('v8_util');
|
||||
|
||||
// Created by init.js.
|
||||
export const ipcRendererInternal = v8Util.getHiddenValue<Electron.IpcRendererInternal>(global, 'ipc-internal')
|
||||
const internal = true
|
||||
export const ipcRendererInternal = v8Util.getHiddenValue<Electron.IpcRendererInternal>(global, 'ipc-internal');
|
||||
const internal = true;
|
||||
|
||||
ipcRendererInternal.send = function (channel, ...args) {
|
||||
return ipc.send(internal, channel, args)
|
||||
}
|
||||
return ipc.send(internal, channel, args);
|
||||
};
|
||||
|
||||
ipcRendererInternal.sendSync = function (channel, ...args) {
|
||||
return ipc.sendSync(internal, channel, args)[0]
|
||||
}
|
||||
return ipc.sendSync(internal, channel, args)[0];
|
||||
};
|
||||
|
||||
ipcRendererInternal.sendTo = function (webContentsId, channel, ...args) {
|
||||
return ipc.sendTo(internal, false, webContentsId, channel, args)
|
||||
}
|
||||
return ipc.sendTo(internal, false, webContentsId, channel, args);
|
||||
};
|
||||
|
||||
ipcRendererInternal.sendToAll = function (webContentsId, channel, ...args) {
|
||||
return ipc.sendTo(internal, true, webContentsId, channel, args)
|
||||
}
|
||||
return ipc.sendTo(internal, true, webContentsId, channel, args);
|
||||
};
|
||||
|
||||
ipcRendererInternal.invoke = async function<T> (channel: string, ...args: any[]) {
|
||||
const { error, result } = await ipc.invoke<T>(internal, channel, args)
|
||||
const { error, result } = await ipc.invoke<T>(internal, channel, args);
|
||||
if (error) {
|
||||
throw new Error(`Error invoking remote method '${channel}': ${error}`)
|
||||
throw new Error(`Error invoking remote method '${channel}': ${error}`);
|
||||
}
|
||||
return result
|
||||
}
|
||||
return result;
|
||||
};
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
const v8Util = process.electronBinding('v8_util')
|
||||
const v8Util = process.electronBinding('v8_util');
|
||||
|
||||
export class CallbacksRegistry {
|
||||
private nextId: number = 0
|
||||
|
@ -6,50 +6,50 @@ export class CallbacksRegistry {
|
|||
|
||||
add (callback: Function) {
|
||||
// The callback is already added.
|
||||
let id = v8Util.getHiddenValue<number>(callback, 'callbackId')
|
||||
if (id != null) return id
|
||||
let id = v8Util.getHiddenValue<number>(callback, 'callbackId');
|
||||
if (id != null) return id;
|
||||
|
||||
id = this.nextId += 1
|
||||
id = this.nextId += 1;
|
||||
|
||||
// Capture the location of the function and put it in the ID string,
|
||||
// so that release errors can be tracked down easily.
|
||||
const regexp = /at (.*)/gi
|
||||
const stackString = (new Error()).stack
|
||||
if (!stackString) return
|
||||
const regexp = /at (.*)/gi;
|
||||
const stackString = (new Error()).stack;
|
||||
if (!stackString) return;
|
||||
|
||||
let filenameAndLine
|
||||
let match
|
||||
let filenameAndLine;
|
||||
let match;
|
||||
|
||||
while ((match = regexp.exec(stackString)) !== null) {
|
||||
const location = match[1]
|
||||
if (location.includes('(native)')) continue
|
||||
if (location.includes('(<anonymous>)')) continue
|
||||
if (location.includes('electron/js2c')) continue
|
||||
const location = match[1];
|
||||
if (location.includes('(native)')) continue;
|
||||
if (location.includes('(<anonymous>)')) continue;
|
||||
if (location.includes('electron/js2c')) continue;
|
||||
|
||||
const ref = /([^/^)]*)\)?$/gi.exec(location)
|
||||
if (ref) filenameAndLine = ref![1]
|
||||
break
|
||||
const ref = /([^/^)]*)\)?$/gi.exec(location);
|
||||
if (ref) filenameAndLine = ref![1];
|
||||
break;
|
||||
}
|
||||
|
||||
this.callbacks.set(id, callback)
|
||||
v8Util.setHiddenValue(callback, 'callbackId', id)
|
||||
v8Util.setHiddenValue(callback, 'location', filenameAndLine)
|
||||
return id
|
||||
this.callbacks.set(id, callback);
|
||||
v8Util.setHiddenValue(callback, 'callbackId', id);
|
||||
v8Util.setHiddenValue(callback, 'location', filenameAndLine);
|
||||
return id;
|
||||
}
|
||||
|
||||
get (id: number) {
|
||||
return this.callbacks.get(id) || function () {}
|
||||
return this.callbacks.get(id) || function () {};
|
||||
}
|
||||
|
||||
apply (id: number, ...args: any[]) {
|
||||
return this.get(id).apply(global, ...args)
|
||||
return this.get(id).apply(global, ...args);
|
||||
}
|
||||
|
||||
remove (id: number) {
|
||||
const callback = this.callbacks.get(id)
|
||||
const callback = this.callbacks.get(id);
|
||||
if (callback) {
|
||||
v8Util.deleteHiddenValue(callback, 'callbackId')
|
||||
this.callbacks.delete(id)
|
||||
v8Util.deleteHiddenValue(callback, 'callbackId');
|
||||
this.callbacks.delete(id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
import { webFrame } from 'electron'
|
||||
import { ipcRendererInternal } from '@electron/internal/renderer/ipc-renderer-internal'
|
||||
import { webFrame } from 'electron';
|
||||
import { ipcRendererInternal } from '@electron/internal/renderer/ipc-renderer-internal';
|
||||
|
||||
let shouldLog: boolean | null = null
|
||||
let shouldLog: boolean | null = null;
|
||||
|
||||
const { platform, execPath, env } = process
|
||||
const { platform, execPath, env } = process;
|
||||
|
||||
/**
|
||||
* This method checks if a security message should be logged.
|
||||
|
@ -15,37 +15,37 @@ const { platform, execPath, env } = process
|
|||
*/
|
||||
const shouldLogSecurityWarnings = function (): boolean {
|
||||
if (shouldLog !== null) {
|
||||
return shouldLog
|
||||
return shouldLog;
|
||||
}
|
||||
|
||||
switch (platform) {
|
||||
case 'darwin':
|
||||
shouldLog = execPath.endsWith('MacOS/Electron') ||
|
||||
execPath.includes('Electron.app/Contents/Frameworks/')
|
||||
break
|
||||
execPath.includes('Electron.app/Contents/Frameworks/');
|
||||
break;
|
||||
case 'freebsd':
|
||||
case 'linux':
|
||||
shouldLog = execPath.endsWith('/electron')
|
||||
break
|
||||
shouldLog = execPath.endsWith('/electron');
|
||||
break;
|
||||
case 'win32':
|
||||
shouldLog = execPath.endsWith('\\electron.exe')
|
||||
break
|
||||
shouldLog = execPath.endsWith('\\electron.exe');
|
||||
break;
|
||||
default:
|
||||
shouldLog = false
|
||||
shouldLog = false;
|
||||
}
|
||||
|
||||
if ((env && env.ELECTRON_DISABLE_SECURITY_WARNINGS) ||
|
||||
(window && window.ELECTRON_DISABLE_SECURITY_WARNINGS)) {
|
||||
shouldLog = false
|
||||
shouldLog = false;
|
||||
}
|
||||
|
||||
if ((env && env.ELECTRON_ENABLE_SECURITY_WARNINGS) ||
|
||||
(window && window.ELECTRON_ENABLE_SECURITY_WARNINGS)) {
|
||||
shouldLog = true
|
||||
shouldLog = true;
|
||||
}
|
||||
|
||||
return shouldLog
|
||||
}
|
||||
return shouldLog;
|
||||
};
|
||||
|
||||
/**
|
||||
* Checks if the current window is remote.
|
||||
|
@ -54,9 +54,9 @@ const shouldLogSecurityWarnings = function (): boolean {
|
|||
*/
|
||||
const getIsRemoteProtocol = function () {
|
||||
if (window && window.location && window.location.protocol) {
|
||||
return /^(http|ftp)s?/gi.test(window.location.protocol)
|
||||
return /^(http|ftp)s?/gi.test(window.location.protocol);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Checks if the current window is from localhost.
|
||||
|
@ -65,11 +65,11 @@ const getIsRemoteProtocol = function () {
|
|||
*/
|
||||
const isLocalhost = function () {
|
||||
if (!window || !window.location) {
|
||||
return false
|
||||
return false;
|
||||
}
|
||||
|
||||
return window.location.hostname === 'localhost'
|
||||
}
|
||||
return window.location.hostname === 'localhost';
|
||||
};
|
||||
|
||||
/**
|
||||
* Tries to determine whether a CSP without `unsafe-eval` is set.
|
||||
|
@ -79,17 +79,17 @@ const isLocalhost = function () {
|
|||
const isUnsafeEvalEnabled = function () {
|
||||
return webFrame.executeJavaScript(`(${(() => {
|
||||
try {
|
||||
new Function('') // eslint-disable-line no-new,no-new-func
|
||||
new Function(''); // eslint-disable-line no-new,no-new-func
|
||||
} catch {
|
||||
return false
|
||||
return false;
|
||||
}
|
||||
return true
|
||||
}).toString()})()`, false)
|
||||
}
|
||||
return true;
|
||||
}).toString()})()`, false);
|
||||
};
|
||||
|
||||
const moreInformation = `\nFor more information and help, consult
|
||||
https://electronjs.org/docs/tutorial/security.\nThis warning will not show up
|
||||
once the app is packaged.`
|
||||
once the app is packaged.`;
|
||||
|
||||
/**
|
||||
* #1 Only load secure content
|
||||
|
@ -99,7 +99,7 @@ once the app is packaged.`
|
|||
*/
|
||||
const warnAboutInsecureResources = function () {
|
||||
if (!window || !window.performance || !window.performance.getEntriesByType) {
|
||||
return
|
||||
return;
|
||||
}
|
||||
|
||||
const resources = window.performance
|
||||
|
@ -107,20 +107,20 @@ const warnAboutInsecureResources = function () {
|
|||
.filter(({ name }) => /^(http|ftp):/gi.test(name || ''))
|
||||
.filter(({ name }) => new URL(name).hostname !== 'localhost')
|
||||
.map(({ name }) => `- ${name}`)
|
||||
.join('\n')
|
||||
.join('\n');
|
||||
|
||||
if (!resources || resources.length === 0) {
|
||||
return
|
||||
return;
|
||||
}
|
||||
|
||||
const warning = `This renderer process loads resources using insecure
|
||||
protocols. This exposes users of this app to unnecessary security risks.
|
||||
Consider loading the following resources over HTTPS or FTPS. \n${resources}
|
||||
\n${moreInformation}`
|
||||
\n${moreInformation}`;
|
||||
|
||||
console.warn('%cElectron Security Warning (Insecure Resources)',
|
||||
'font-weight: bold;', warning)
|
||||
}
|
||||
'font-weight: bold;', warning);
|
||||
};
|
||||
|
||||
/**
|
||||
* #2 on the checklist: Disable the Node.js integration in all renderers that
|
||||
|
@ -129,17 +129,17 @@ const warnAboutInsecureResources = function () {
|
|||
* Logs a warning message about Node integration.
|
||||
*/
|
||||
const warnAboutNodeWithRemoteContent = function (nodeIntegration: boolean) {
|
||||
if (!nodeIntegration || isLocalhost()) return
|
||||
if (!nodeIntegration || isLocalhost()) return;
|
||||
|
||||
if (getIsRemoteProtocol()) {
|
||||
const warning = `This renderer process has Node.js integration enabled
|
||||
and attempted to load remote content from '${window.location}'. This
|
||||
exposes users of this app to severe security risks.\n${moreInformation}`
|
||||
exposes users of this app to severe security risks.\n${moreInformation}`;
|
||||
|
||||
console.warn('%cElectron Security Warning (Node.js Integration with Remote Content)',
|
||||
'font-weight: bold;', warning)
|
||||
'font-weight: bold;', warning);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Currently missing since it has ramifications and is still experimental:
|
||||
// #3 Enable context isolation in all renderers that display remote content
|
||||
|
@ -153,14 +153,14 @@ const warnAboutNodeWithRemoteContent = function (nodeIntegration: boolean) {
|
|||
* Logs a warning message about disabled webSecurity.
|
||||
*/
|
||||
const warnAboutDisabledWebSecurity = function (webPreferences?: Electron.WebPreferences) {
|
||||
if (!webPreferences || webPreferences.webSecurity !== false) return
|
||||
if (!webPreferences || webPreferences.webSecurity !== false) return;
|
||||
|
||||
const warning = `This renderer process has "webSecurity" disabled. This
|
||||
exposes users of this app to severe security risks.\n${moreInformation}`
|
||||
exposes users of this app to severe security risks.\n${moreInformation}`;
|
||||
|
||||
console.warn('%cElectron Security Warning (Disabled webSecurity)',
|
||||
'font-weight: bold;', warning)
|
||||
}
|
||||
'font-weight: bold;', warning);
|
||||
};
|
||||
|
||||
/**
|
||||
* #6 on the checklist: Define a Content-Security-Policy and use restrictive
|
||||
|
@ -170,16 +170,16 @@ const warnAboutDisabledWebSecurity = function (webPreferences?: Electron.WebPref
|
|||
*/
|
||||
const warnAboutInsecureCSP = function () {
|
||||
isUnsafeEvalEnabled().then((enabled) => {
|
||||
if (!enabled) return
|
||||
if (!enabled) return;
|
||||
|
||||
const warning = `This renderer process has either no Content Security
|
||||
Policy set or a policy with "unsafe-eval" enabled. This exposes users of
|
||||
this app to unnecessary security risks.\n${moreInformation}`
|
||||
this app to unnecessary security risks.\n${moreInformation}`;
|
||||
|
||||
console.warn('%cElectron Security Warning (Insecure Content-Security-Policy)',
|
||||
'font-weight: bold;', warning)
|
||||
})
|
||||
}
|
||||
'font-weight: bold;', warning);
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* #7 on the checklist: Do not set allowRunningInsecureContent to true
|
||||
|
@ -187,15 +187,15 @@ const warnAboutInsecureCSP = function () {
|
|||
* Logs a warning message about disabled webSecurity.
|
||||
*/
|
||||
const warnAboutInsecureContentAllowed = function (webPreferences?: Electron.WebPreferences) {
|
||||
if (!webPreferences || !webPreferences.allowRunningInsecureContent) return
|
||||
if (!webPreferences || !webPreferences.allowRunningInsecureContent) return;
|
||||
|
||||
const warning = `This renderer process has "allowRunningInsecureContent"
|
||||
enabled. This exposes users of this app to severe security risks.\n
|
||||
${moreInformation}`
|
||||
${moreInformation}`;
|
||||
|
||||
console.warn('%cElectron Security Warning (allowRunningInsecureContent)',
|
||||
'font-weight: bold;', warning)
|
||||
}
|
||||
'font-weight: bold;', warning);
|
||||
};
|
||||
|
||||
/**
|
||||
* #8 on the checklist: Do not enable experimental features
|
||||
|
@ -204,16 +204,16 @@ const warnAboutInsecureContentAllowed = function (webPreferences?: Electron.WebP
|
|||
*/
|
||||
const warnAboutExperimentalFeatures = function (webPreferences?: Electron.WebPreferences) {
|
||||
if (!webPreferences || (!webPreferences.experimentalFeatures)) {
|
||||
return
|
||||
return;
|
||||
}
|
||||
|
||||
const warning = `This renderer process has "experimentalFeatures" enabled.
|
||||
This exposes users of this app to some security risk. If you do not need
|
||||
this feature, you should disable it.\n${moreInformation}`
|
||||
this feature, you should disable it.\n${moreInformation}`;
|
||||
|
||||
console.warn('%cElectron Security Warning (experimentalFeatures)',
|
||||
'font-weight: bold;', warning)
|
||||
}
|
||||
'font-weight: bold;', warning);
|
||||
};
|
||||
|
||||
/**
|
||||
* #9 on the checklist: Do not use enableBlinkFeatures
|
||||
|
@ -224,16 +224,16 @@ const warnAboutEnableBlinkFeatures = function (webPreferences?: Electron.WebPref
|
|||
if (!webPreferences ||
|
||||
!Object.prototype.hasOwnProperty.call(webPreferences, 'enableBlinkFeatures') ||
|
||||
(webPreferences.enableBlinkFeatures && webPreferences.enableBlinkFeatures.length === 0)) {
|
||||
return
|
||||
return;
|
||||
}
|
||||
|
||||
const warning = `This renderer process has additional "enableBlinkFeatures"
|
||||
enabled. This exposes users of this app to some security risk. If you do not
|
||||
need this feature, you should disable it.\n${moreInformation}`
|
||||
need this feature, you should disable it.\n${moreInformation}`;
|
||||
|
||||
console.warn('%cElectron Security Warning (enableBlinkFeatures)',
|
||||
'font-weight: bold;', warning)
|
||||
}
|
||||
'font-weight: bold;', warning);
|
||||
};
|
||||
|
||||
/**
|
||||
* #10 on the checklist: Do Not Use allowpopups
|
||||
|
@ -242,21 +242,21 @@ const warnAboutEnableBlinkFeatures = function (webPreferences?: Electron.WebPref
|
|||
*/
|
||||
const warnAboutAllowedPopups = function () {
|
||||
if (document && document.querySelectorAll) {
|
||||
const domElements = document.querySelectorAll('[allowpopups]')
|
||||
const domElements = document.querySelectorAll('[allowpopups]');
|
||||
|
||||
if (!domElements || domElements.length === 0) {
|
||||
return
|
||||
return;
|
||||
}
|
||||
|
||||
const warning = `A <webview> has "allowpopups" set to true. This exposes
|
||||
users of this app to some security risk, since popups are just
|
||||
BrowserWindows. If you do not need this feature, you should disable it.\n
|
||||
${moreInformation}`
|
||||
${moreInformation}`;
|
||||
|
||||
console.warn('%cElectron Security Warning (allowpopups)',
|
||||
'font-weight: bold;', warning)
|
||||
'font-weight: bold;', warning);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Currently missing since we can't easily programmatically check for it:
|
||||
// #11 Verify WebView Options Before Creation
|
||||
|
@ -268,19 +268,19 @@ const warnAboutAllowedPopups = function () {
|
|||
// Logs a warning message about the remote module
|
||||
|
||||
const warnAboutRemoteModuleWithRemoteContent = function (webPreferences?: Electron.WebPreferences) {
|
||||
if (!webPreferences || isLocalhost()) return
|
||||
const remoteModuleEnabled = webPreferences.enableRemoteModule != null ? !!webPreferences.enableRemoteModule : true
|
||||
if (!remoteModuleEnabled) return
|
||||
if (!webPreferences || isLocalhost()) return;
|
||||
const remoteModuleEnabled = webPreferences.enableRemoteModule != null ? !!webPreferences.enableRemoteModule : true;
|
||||
if (!remoteModuleEnabled) return;
|
||||
|
||||
if (getIsRemoteProtocol()) {
|
||||
const warning = `This renderer process has "enableRemoteModule" enabled
|
||||
and attempted to load remote content from '${window.location}'. This
|
||||
exposes users of this app to unnecessary security risks.\n${moreInformation}`
|
||||
exposes users of this app to unnecessary security risks.\n${moreInformation}`;
|
||||
|
||||
console.warn('%cElectron Security Warning (enableRemoteModule)',
|
||||
'font-weight: bold;', warning)
|
||||
'font-weight: bold;', warning);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Currently missing since we can't easily programmatically check for it:
|
||||
// #16 Filter the `remote` module
|
||||
|
@ -288,31 +288,31 @@ const warnAboutRemoteModuleWithRemoteContent = function (webPreferences?: Electr
|
|||
const logSecurityWarnings = function (
|
||||
webPreferences: Electron.WebPreferences | undefined, nodeIntegration: boolean
|
||||
) {
|
||||
warnAboutNodeWithRemoteContent(nodeIntegration)
|
||||
warnAboutDisabledWebSecurity(webPreferences)
|
||||
warnAboutInsecureResources()
|
||||
warnAboutInsecureContentAllowed(webPreferences)
|
||||
warnAboutExperimentalFeatures(webPreferences)
|
||||
warnAboutEnableBlinkFeatures(webPreferences)
|
||||
warnAboutInsecureCSP()
|
||||
warnAboutAllowedPopups()
|
||||
warnAboutRemoteModuleWithRemoteContent(webPreferences)
|
||||
}
|
||||
warnAboutNodeWithRemoteContent(nodeIntegration);
|
||||
warnAboutDisabledWebSecurity(webPreferences);
|
||||
warnAboutInsecureResources();
|
||||
warnAboutInsecureContentAllowed(webPreferences);
|
||||
warnAboutExperimentalFeatures(webPreferences);
|
||||
warnAboutEnableBlinkFeatures(webPreferences);
|
||||
warnAboutInsecureCSP();
|
||||
warnAboutAllowedPopups();
|
||||
warnAboutRemoteModuleWithRemoteContent(webPreferences);
|
||||
};
|
||||
|
||||
const getWebPreferences = async function () {
|
||||
try {
|
||||
return ipcRendererInternal.invoke<Electron.WebPreferences>('ELECTRON_BROWSER_GET_LAST_WEB_PREFERENCES')
|
||||
return ipcRendererInternal.invoke<Electron.WebPreferences>('ELECTRON_BROWSER_GET_LAST_WEB_PREFERENCES');
|
||||
} catch (error) {
|
||||
console.warn(`getLastWebPreferences() failed: ${error}`)
|
||||
console.warn(`getLastWebPreferences() failed: ${error}`);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export function securityWarnings (nodeIntegration: boolean) {
|
||||
const loadHandler = async function () {
|
||||
if (shouldLogSecurityWarnings()) {
|
||||
const webPreferences = await getWebPreferences()
|
||||
logSecurityWarnings(webPreferences, nodeIntegration)
|
||||
const webPreferences = await getWebPreferences();
|
||||
logSecurityWarnings(webPreferences, nodeIntegration);
|
||||
}
|
||||
}
|
||||
window.addEventListener('load', loadHandler, { once: true })
|
||||
};
|
||||
window.addEventListener('load', loadHandler, { once: true });
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { webFrame, WebFrame } from 'electron'
|
||||
import * as ipcRendererUtils from '@electron/internal/renderer/ipc-renderer-internal-utils'
|
||||
import { webFrame, WebFrame } from 'electron';
|
||||
import * as ipcRendererUtils from '@electron/internal/renderer/ipc-renderer-internal-utils';
|
||||
|
||||
// All keys of WebFrame that extend Function
|
||||
type WebFrameMethod = {
|
||||
|
@ -15,6 +15,6 @@ export const webFrameInit = () => {
|
|||
// The TypeScript compiler cannot handle the sheer number of
|
||||
// call signatures here and simply gives up. Incorrect invocations
|
||||
// will be caught by "keyof WebFrameMethod" though.
|
||||
return (webFrame[method] as any)(...args)
|
||||
})
|
||||
}
|
||||
return (webFrame[method] as any)(...args);
|
||||
});
|
||||
};
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { webFrame, IpcMessageEvent } from 'electron'
|
||||
import { ipcRendererInternal } from '@electron/internal/renderer/ipc-renderer-internal'
|
||||
import { webFrame, IpcMessageEvent } from 'electron';
|
||||
import { ipcRendererInternal } from '@electron/internal/renderer/ipc-renderer-internal';
|
||||
|
||||
import { WebViewImpl } from '@electron/internal/renderer/web-view/web-view-impl'
|
||||
import { WebViewImpl } from '@electron/internal/renderer/web-view/web-view-impl';
|
||||
|
||||
const WEB_VIEW_EVENTS: Record<string, Array<string>> = {
|
||||
'load-commit': ['url', 'isMainFrame'],
|
||||
|
@ -37,76 +37,76 @@ const WEB_VIEW_EVENTS: Record<string, Array<string>> = {
|
|||
'found-in-page': ['result'],
|
||||
'did-change-theme-color': ['themeColor'],
|
||||
'update-target-url': ['url']
|
||||
}
|
||||
};
|
||||
|
||||
const DEPRECATED_EVENTS: Record<string, string> = {
|
||||
'page-title-updated': 'page-title-set'
|
||||
}
|
||||
};
|
||||
|
||||
const dispatchEvent = function (
|
||||
webView: WebViewImpl, eventName: string, eventKey: string, ...args: Array<any>
|
||||
) {
|
||||
if (DEPRECATED_EVENTS[eventName] != null) {
|
||||
dispatchEvent(webView, DEPRECATED_EVENTS[eventName], eventKey, ...args)
|
||||
dispatchEvent(webView, DEPRECATED_EVENTS[eventName], eventKey, ...args);
|
||||
}
|
||||
|
||||
const domEvent = new Event(eventName) as ElectronInternal.WebViewEvent
|
||||
const domEvent = new Event(eventName) as ElectronInternal.WebViewEvent;
|
||||
WEB_VIEW_EVENTS[eventKey].forEach((prop, index) => {
|
||||
(domEvent as any)[prop] = args[index]
|
||||
})
|
||||
(domEvent as any)[prop] = args[index];
|
||||
});
|
||||
|
||||
webView.dispatchEvent(domEvent)
|
||||
webView.dispatchEvent(domEvent);
|
||||
|
||||
if (eventName === 'load-commit') {
|
||||
webView.onLoadCommit(domEvent)
|
||||
webView.onLoadCommit(domEvent);
|
||||
} else if (eventName === 'focus-change') {
|
||||
webView.onFocusChange()
|
||||
webView.onFocusChange();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export function registerEvents (webView: WebViewImpl, viewInstanceId: number) {
|
||||
ipcRendererInternal.on(`ELECTRON_GUEST_VIEW_INTERNAL_DESTROY_GUEST-${viewInstanceId}`, function () {
|
||||
webView.guestInstanceId = undefined
|
||||
webView.reset()
|
||||
const domEvent = new Event('destroyed')
|
||||
webView.dispatchEvent(domEvent)
|
||||
})
|
||||
webView.guestInstanceId = undefined;
|
||||
webView.reset();
|
||||
const domEvent = new Event('destroyed');
|
||||
webView.dispatchEvent(domEvent);
|
||||
});
|
||||
|
||||
ipcRendererInternal.on(`ELECTRON_GUEST_VIEW_INTERNAL_DISPATCH_EVENT-${viewInstanceId}`, function (event, eventName, ...args) {
|
||||
dispatchEvent(webView, eventName, eventName, ...args)
|
||||
})
|
||||
dispatchEvent(webView, eventName, eventName, ...args);
|
||||
});
|
||||
|
||||
ipcRendererInternal.on(`ELECTRON_GUEST_VIEW_INTERNAL_IPC_MESSAGE-${viewInstanceId}`, function (event, channel, ...args) {
|
||||
const domEvent = new Event('ipc-message') as IpcMessageEvent
|
||||
domEvent.channel = channel
|
||||
domEvent.args = args
|
||||
const domEvent = new Event('ipc-message') as IpcMessageEvent;
|
||||
domEvent.channel = channel;
|
||||
domEvent.args = args;
|
||||
|
||||
webView.dispatchEvent(domEvent)
|
||||
})
|
||||
webView.dispatchEvent(domEvent);
|
||||
});
|
||||
}
|
||||
|
||||
export function deregisterEvents (viewInstanceId: number) {
|
||||
ipcRendererInternal.removeAllListeners(`ELECTRON_GUEST_VIEW_INTERNAL_DESTROY_GUEST-${viewInstanceId}`)
|
||||
ipcRendererInternal.removeAllListeners(`ELECTRON_GUEST_VIEW_INTERNAL_DISPATCH_EVENT-${viewInstanceId}`)
|
||||
ipcRendererInternal.removeAllListeners(`ELECTRON_GUEST_VIEW_INTERNAL_IPC_MESSAGE-${viewInstanceId}`)
|
||||
ipcRendererInternal.removeAllListeners(`ELECTRON_GUEST_VIEW_INTERNAL_DESTROY_GUEST-${viewInstanceId}`);
|
||||
ipcRendererInternal.removeAllListeners(`ELECTRON_GUEST_VIEW_INTERNAL_DISPATCH_EVENT-${viewInstanceId}`);
|
||||
ipcRendererInternal.removeAllListeners(`ELECTRON_GUEST_VIEW_INTERNAL_IPC_MESSAGE-${viewInstanceId}`);
|
||||
}
|
||||
|
||||
export function createGuest (params: Record<string, any>): Promise<number> {
|
||||
return ipcRendererInternal.invoke('ELECTRON_GUEST_VIEW_MANAGER_CREATE_GUEST', params)
|
||||
return ipcRendererInternal.invoke('ELECTRON_GUEST_VIEW_MANAGER_CREATE_GUEST', params);
|
||||
}
|
||||
|
||||
export function attachGuest (
|
||||
elementInstanceId: number, guestInstanceId: number, params: Record<string, any>, contentWindow: Window
|
||||
) {
|
||||
const embedderFrameId = (webFrame as ElectronInternal.WebFrameInternal).getWebFrameId(contentWindow)
|
||||
const embedderFrameId = (webFrame as ElectronInternal.WebFrameInternal).getWebFrameId(contentWindow);
|
||||
if (embedderFrameId < 0) { // this error should not happen.
|
||||
throw new Error('Invalid embedder frame')
|
||||
throw new Error('Invalid embedder frame');
|
||||
}
|
||||
ipcRendererInternal.invoke('ELECTRON_GUEST_VIEW_MANAGER_ATTACH_GUEST', embedderFrameId, elementInstanceId, guestInstanceId, params)
|
||||
ipcRendererInternal.invoke('ELECTRON_GUEST_VIEW_MANAGER_ATTACH_GUEST', embedderFrameId, elementInstanceId, guestInstanceId, params);
|
||||
}
|
||||
|
||||
export const guestViewInternalModule = {
|
||||
deregisterEvents,
|
||||
createGuest,
|
||||
attachGuest
|
||||
}
|
||||
};
|
||||
|
|
|
@ -1,15 +1,15 @@
|
|||
import { ipcRendererInternal } from '@electron/internal/renderer/ipc-renderer-internal'
|
||||
import { WebViewImpl } from '@electron/internal/renderer/web-view/web-view-impl'
|
||||
import { WEB_VIEW_CONSTANTS } from '@electron/internal/renderer/web-view/web-view-constants'
|
||||
import { ipcRendererInternal } from '@electron/internal/renderer/ipc-renderer-internal';
|
||||
import { WebViewImpl } from '@electron/internal/renderer/web-view/web-view-impl';
|
||||
import { WEB_VIEW_CONSTANTS } from '@electron/internal/renderer/web-view/web-view-constants';
|
||||
|
||||
// Helper function to resolve url set in attribute.
|
||||
const a = document.createElement('a')
|
||||
const a = document.createElement('a');
|
||||
|
||||
const resolveURL = function (url?: string | null) {
|
||||
if (!url) return ''
|
||||
a.href = url
|
||||
return a.href
|
||||
}
|
||||
if (!url) return '';
|
||||
a.href = url;
|
||||
return a.href;
|
||||
};
|
||||
|
||||
interface MutationHandler {
|
||||
handleMutation (_oldValue: any, _newValue: any): any;
|
||||
|
@ -22,40 +22,40 @@ class WebViewAttribute implements MutationHandler {
|
|||
public ignoreMutation = false;
|
||||
|
||||
constructor (public name: string, public webViewImpl: WebViewImpl) {
|
||||
this.name = name
|
||||
this.value = (webViewImpl.webviewNode as Record<string, any>)[name] || ''
|
||||
this.webViewImpl = webViewImpl
|
||||
this.defineProperty()
|
||||
this.name = name;
|
||||
this.value = (webViewImpl.webviewNode as Record<string, any>)[name] || '';
|
||||
this.webViewImpl = webViewImpl;
|
||||
this.defineProperty();
|
||||
}
|
||||
|
||||
// Retrieves and returns the attribute's value.
|
||||
public getValue () {
|
||||
return this.webViewImpl.webviewNode.getAttribute(this.name) || this.value
|
||||
return this.webViewImpl.webviewNode.getAttribute(this.name) || this.value;
|
||||
}
|
||||
|
||||
// Sets the attribute's value.
|
||||
public setValue (value: any) {
|
||||
this.webViewImpl.webviewNode.setAttribute(this.name, value || '')
|
||||
this.webViewImpl.webviewNode.setAttribute(this.name, value || '');
|
||||
}
|
||||
|
||||
// Changes the attribute's value without triggering its mutation handler.
|
||||
public setValueIgnoreMutation (value: any) {
|
||||
this.ignoreMutation = true
|
||||
this.setValue(value)
|
||||
this.ignoreMutation = false
|
||||
this.ignoreMutation = true;
|
||||
this.setValue(value);
|
||||
this.ignoreMutation = false;
|
||||
}
|
||||
|
||||
// Defines this attribute as a property on the webview node.
|
||||
public defineProperty () {
|
||||
return Object.defineProperty(this.webViewImpl.webviewNode, this.name, {
|
||||
get: () => {
|
||||
return this.getValue()
|
||||
return this.getValue();
|
||||
},
|
||||
set: (value) => {
|
||||
return this.setValue(value)
|
||||
return this.setValue(value);
|
||||
},
|
||||
enumerable: true
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
// Called when the attribute's value changes.
|
||||
|
@ -65,14 +65,14 @@ class WebViewAttribute implements MutationHandler {
|
|||
// An attribute that is treated as a Boolean.
|
||||
class BooleanAttribute extends WebViewAttribute {
|
||||
getValue () {
|
||||
return this.webViewImpl.webviewNode.hasAttribute(this.name)
|
||||
return this.webViewImpl.webviewNode.hasAttribute(this.name);
|
||||
}
|
||||
|
||||
setValue (value: boolean) {
|
||||
if (value) {
|
||||
this.webViewImpl.webviewNode.setAttribute(this.name, '')
|
||||
this.webViewImpl.webviewNode.setAttribute(this.name, '');
|
||||
} else {
|
||||
this.webViewImpl.webviewNode.removeAttribute(this.name)
|
||||
this.webViewImpl.webviewNode.removeAttribute(this.name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -82,21 +82,21 @@ class PartitionAttribute extends WebViewAttribute {
|
|||
public validPartitionId = true
|
||||
|
||||
constructor (public webViewImpl: WebViewImpl) {
|
||||
super(WEB_VIEW_CONSTANTS.ATTRIBUTE_PARTITION, webViewImpl)
|
||||
super(WEB_VIEW_CONSTANTS.ATTRIBUTE_PARTITION, webViewImpl);
|
||||
}
|
||||
|
||||
public handleMutation = (oldValue: any, newValue: any) => {
|
||||
newValue = newValue || ''
|
||||
newValue = newValue || '';
|
||||
|
||||
// The partition cannot change if the webview has already navigated.
|
||||
if (!this.webViewImpl.beforeFirstNavigation) {
|
||||
console.error(WEB_VIEW_CONSTANTS.ERROR_MSG_ALREADY_NAVIGATED)
|
||||
this.setValueIgnoreMutation(oldValue)
|
||||
return
|
||||
console.error(WEB_VIEW_CONSTANTS.ERROR_MSG_ALREADY_NAVIGATED);
|
||||
this.setValueIgnoreMutation(oldValue);
|
||||
return;
|
||||
}
|
||||
if (newValue === 'persist:') {
|
||||
this.validPartitionId = false
|
||||
console.error(WEB_VIEW_CONSTANTS.ERROR_MSG_INVALID_PARTITION_ATTRIBUTE)
|
||||
this.validPartitionId = false;
|
||||
console.error(WEB_VIEW_CONSTANTS.ERROR_MSG_INVALID_PARTITION_ATTRIBUTE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -106,26 +106,26 @@ class SrcAttribute extends WebViewAttribute {
|
|||
public observer!: MutationObserver;
|
||||
|
||||
constructor (public webViewImpl: WebViewImpl) {
|
||||
super(WEB_VIEW_CONSTANTS.ATTRIBUTE_SRC, webViewImpl)
|
||||
this.setupMutationObserver()
|
||||
super(WEB_VIEW_CONSTANTS.ATTRIBUTE_SRC, webViewImpl);
|
||||
this.setupMutationObserver();
|
||||
}
|
||||
|
||||
public getValue () {
|
||||
if (this.webViewImpl.webviewNode.hasAttribute(this.name)) {
|
||||
return resolveURL(this.webViewImpl.webviewNode.getAttribute(this.name))
|
||||
return resolveURL(this.webViewImpl.webviewNode.getAttribute(this.name));
|
||||
} else {
|
||||
return this.value
|
||||
return this.value;
|
||||
}
|
||||
}
|
||||
|
||||
public setValueIgnoreMutation (value: any) {
|
||||
super.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.
|
||||
this.observer.takeRecords()
|
||||
this.observer.takeRecords();
|
||||
}
|
||||
|
||||
public handleMutation = (oldValue: any, newValue: any) => {
|
||||
|
@ -136,10 +136,10 @@ class SrcAttribute extends WebViewAttribute {
|
|||
// 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
|
||||
this.setValueIgnoreMutation(oldValue);
|
||||
return;
|
||||
}
|
||||
this.parse()
|
||||
this.parse();
|
||||
}
|
||||
|
||||
// The purpose of this mutation observer is to catch assignment to the src
|
||||
|
@ -149,144 +149,144 @@ class SrcAttribute extends WebViewAttribute {
|
|||
public setupMutationObserver () {
|
||||
this.observer = new MutationObserver((mutations) => {
|
||||
for (const mutation of mutations) {
|
||||
const { oldValue } = mutation
|
||||
const newValue = this.getValue()
|
||||
const { oldValue } = mutation;
|
||||
const newValue = this.getValue();
|
||||
if (oldValue !== newValue) {
|
||||
return
|
||||
return;
|
||||
}
|
||||
this.handleMutation(oldValue, newValue)
|
||||
this.handleMutation(oldValue, newValue);
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
const params = {
|
||||
attributes: true,
|
||||
attributeOldValue: true,
|
||||
attributeFilter: [this.name]
|
||||
}
|
||||
};
|
||||
|
||||
this.observer.observe(this.webViewImpl.webviewNode, params)
|
||||
this.observer.observe(this.webViewImpl.webviewNode, params);
|
||||
}
|
||||
|
||||
public parse () {
|
||||
if (!this.webViewImpl.elementAttached || !this.webViewImpl.attributes[WEB_VIEW_CONSTANTS.ATTRIBUTE_PARTITION].validPartitionId || !this.getValue()) {
|
||||
return
|
||||
return;
|
||||
}
|
||||
if (this.webViewImpl.guestInstanceId == null) {
|
||||
if (this.webViewImpl.beforeFirstNavigation) {
|
||||
this.webViewImpl.beforeFirstNavigation = false
|
||||
this.webViewImpl.createGuest()
|
||||
this.webViewImpl.beforeFirstNavigation = false;
|
||||
this.webViewImpl.createGuest();
|
||||
}
|
||||
return
|
||||
return;
|
||||
}
|
||||
|
||||
// Navigate to |this.src|.
|
||||
const opts: Record<string, string> = {}
|
||||
const opts: Record<string, string> = {};
|
||||
|
||||
const httpreferrer = this.webViewImpl.attributes[WEB_VIEW_CONSTANTS.ATTRIBUTE_HTTPREFERRER].getValue()
|
||||
const httpreferrer = this.webViewImpl.attributes[WEB_VIEW_CONSTANTS.ATTRIBUTE_HTTPREFERRER].getValue();
|
||||
if (httpreferrer) {
|
||||
opts.httpReferrer = httpreferrer
|
||||
opts.httpReferrer = httpreferrer;
|
||||
}
|
||||
|
||||
const useragent = this.webViewImpl.attributes[WEB_VIEW_CONSTANTS.ATTRIBUTE_USERAGENT].getValue()
|
||||
const useragent = this.webViewImpl.attributes[WEB_VIEW_CONSTANTS.ATTRIBUTE_USERAGENT].getValue();
|
||||
if (useragent) {
|
||||
opts.userAgent = useragent
|
||||
opts.userAgent = useragent;
|
||||
}
|
||||
|
||||
const guestInstanceId = this.webViewImpl.guestInstanceId
|
||||
const method = 'loadURL'
|
||||
const args = [this.getValue(), opts]
|
||||
const guestInstanceId = this.webViewImpl.guestInstanceId;
|
||||
const method = 'loadURL';
|
||||
const args = [this.getValue(), opts];
|
||||
|
||||
ipcRendererInternal.invoke('ELECTRON_GUEST_VIEW_MANAGER_CALL', guestInstanceId, method, args)
|
||||
ipcRendererInternal.invoke('ELECTRON_GUEST_VIEW_MANAGER_CALL', guestInstanceId, method, args);
|
||||
}
|
||||
}
|
||||
|
||||
// Attribute specifies HTTP referrer.
|
||||
class HttpReferrerAttribute extends WebViewAttribute {
|
||||
constructor (webViewImpl: WebViewImpl) {
|
||||
super(WEB_VIEW_CONSTANTS.ATTRIBUTE_HTTPREFERRER, webViewImpl)
|
||||
super(WEB_VIEW_CONSTANTS.ATTRIBUTE_HTTPREFERRER, webViewImpl);
|
||||
}
|
||||
}
|
||||
|
||||
// Attribute specifies user agent
|
||||
class UserAgentAttribute extends WebViewAttribute {
|
||||
constructor (webViewImpl: WebViewImpl) {
|
||||
super(WEB_VIEW_CONSTANTS.ATTRIBUTE_USERAGENT, webViewImpl)
|
||||
super(WEB_VIEW_CONSTANTS.ATTRIBUTE_USERAGENT, webViewImpl);
|
||||
}
|
||||
}
|
||||
|
||||
// Attribute that set preload script.
|
||||
class PreloadAttribute extends WebViewAttribute {
|
||||
constructor (webViewImpl: WebViewImpl) {
|
||||
super(WEB_VIEW_CONSTANTS.ATTRIBUTE_PRELOAD, webViewImpl)
|
||||
super(WEB_VIEW_CONSTANTS.ATTRIBUTE_PRELOAD, webViewImpl);
|
||||
}
|
||||
|
||||
public getValue () {
|
||||
if (!this.webViewImpl.webviewNode.hasAttribute(this.name)) {
|
||||
return this.value
|
||||
return this.value;
|
||||
}
|
||||
|
||||
let preload = resolveURL(this.webViewImpl.webviewNode.getAttribute(this.name))
|
||||
const protocol = preload.substr(0, 5)
|
||||
let preload = resolveURL(this.webViewImpl.webviewNode.getAttribute(this.name));
|
||||
const protocol = preload.substr(0, 5);
|
||||
|
||||
if (protocol !== 'file:') {
|
||||
console.error(WEB_VIEW_CONSTANTS.ERROR_MSG_INVALID_PRELOAD_ATTRIBUTE)
|
||||
preload = ''
|
||||
console.error(WEB_VIEW_CONSTANTS.ERROR_MSG_INVALID_PRELOAD_ATTRIBUTE);
|
||||
preload = '';
|
||||
}
|
||||
|
||||
return preload
|
||||
return preload;
|
||||
}
|
||||
}
|
||||
|
||||
// Attribute that specifies the blink features to be enabled.
|
||||
class BlinkFeaturesAttribute extends WebViewAttribute {
|
||||
constructor (webViewImpl: WebViewImpl) {
|
||||
super(WEB_VIEW_CONSTANTS.ATTRIBUTE_BLINKFEATURES, webViewImpl)
|
||||
super(WEB_VIEW_CONSTANTS.ATTRIBUTE_BLINKFEATURES, webViewImpl);
|
||||
}
|
||||
}
|
||||
|
||||
// Attribute that specifies the blink features to be disabled.
|
||||
class DisableBlinkFeaturesAttribute extends WebViewAttribute {
|
||||
constructor (webViewImpl: WebViewImpl) {
|
||||
super(WEB_VIEW_CONSTANTS.ATTRIBUTE_DISABLEBLINKFEATURES, webViewImpl)
|
||||
super(WEB_VIEW_CONSTANTS.ATTRIBUTE_DISABLEBLINKFEATURES, webViewImpl);
|
||||
}
|
||||
}
|
||||
|
||||
// Attribute that specifies the web preferences to be enabled.
|
||||
class WebPreferencesAttribute extends WebViewAttribute {
|
||||
constructor (webViewImpl: WebViewImpl) {
|
||||
super(WEB_VIEW_CONSTANTS.ATTRIBUTE_WEBPREFERENCES, webViewImpl)
|
||||
super(WEB_VIEW_CONSTANTS.ATTRIBUTE_WEBPREFERENCES, webViewImpl);
|
||||
}
|
||||
}
|
||||
|
||||
class EnableRemoteModuleAttribute extends WebViewAttribute {
|
||||
constructor (webViewImpl: WebViewImpl) {
|
||||
super(WEB_VIEW_CONSTANTS.ATTRIBUTE_ENABLEREMOTEMODULE, webViewImpl)
|
||||
super(WEB_VIEW_CONSTANTS.ATTRIBUTE_ENABLEREMOTEMODULE, webViewImpl);
|
||||
}
|
||||
|
||||
public getValue () {
|
||||
return this.webViewImpl.webviewNode.getAttribute(this.name) !== 'false'
|
||||
return this.webViewImpl.webviewNode.getAttribute(this.name) !== 'false';
|
||||
}
|
||||
|
||||
public setValue (value: any) {
|
||||
this.webViewImpl.webviewNode.setAttribute(this.name, value ? 'true' : 'false')
|
||||
this.webViewImpl.webviewNode.setAttribute(this.name, value ? 'true' : 'false');
|
||||
}
|
||||
}
|
||||
|
||||
// Sets up all of the webview attributes.
|
||||
WebViewImpl.prototype.setupWebViewAttributes = function () {
|
||||
this.attributes = {}
|
||||
this.attributes[WEB_VIEW_CONSTANTS.ATTRIBUTE_PARTITION] = new PartitionAttribute(this)
|
||||
this.attributes[WEB_VIEW_CONSTANTS.ATTRIBUTE_SRC] = new SrcAttribute(this)
|
||||
this.attributes[WEB_VIEW_CONSTANTS.ATTRIBUTE_HTTPREFERRER] = new HttpReferrerAttribute(this)
|
||||
this.attributes[WEB_VIEW_CONSTANTS.ATTRIBUTE_USERAGENT] = new UserAgentAttribute(this)
|
||||
this.attributes[WEB_VIEW_CONSTANTS.ATTRIBUTE_NODEINTEGRATION] = new BooleanAttribute(WEB_VIEW_CONSTANTS.ATTRIBUTE_NODEINTEGRATION, this)
|
||||
this.attributes[WEB_VIEW_CONSTANTS.ATTRIBUTE_NODEINTEGRATIONINSUBFRAMES] = new BooleanAttribute(WEB_VIEW_CONSTANTS.ATTRIBUTE_NODEINTEGRATIONINSUBFRAMES, this)
|
||||
this.attributes[WEB_VIEW_CONSTANTS.ATTRIBUTE_PLUGINS] = new BooleanAttribute(WEB_VIEW_CONSTANTS.ATTRIBUTE_PLUGINS, this)
|
||||
this.attributes[WEB_VIEW_CONSTANTS.ATTRIBUTE_DISABLEWEBSECURITY] = new BooleanAttribute(WEB_VIEW_CONSTANTS.ATTRIBUTE_DISABLEWEBSECURITY, this)
|
||||
this.attributes[WEB_VIEW_CONSTANTS.ATTRIBUTE_ALLOWPOPUPS] = new BooleanAttribute(WEB_VIEW_CONSTANTS.ATTRIBUTE_ALLOWPOPUPS, this)
|
||||
this.attributes[WEB_VIEW_CONSTANTS.ATTRIBUTE_ENABLEREMOTEMODULE] = new EnableRemoteModuleAttribute(this)
|
||||
this.attributes[WEB_VIEW_CONSTANTS.ATTRIBUTE_PRELOAD] = new PreloadAttribute(this)
|
||||
this.attributes[WEB_VIEW_CONSTANTS.ATTRIBUTE_BLINKFEATURES] = new BlinkFeaturesAttribute(this)
|
||||
this.attributes[WEB_VIEW_CONSTANTS.ATTRIBUTE_DISABLEBLINKFEATURES] = new DisableBlinkFeaturesAttribute(this)
|
||||
this.attributes[WEB_VIEW_CONSTANTS.ATTRIBUTE_WEBPREFERENCES] = new WebPreferencesAttribute(this)
|
||||
}
|
||||
this.attributes = {};
|
||||
this.attributes[WEB_VIEW_CONSTANTS.ATTRIBUTE_PARTITION] = new PartitionAttribute(this);
|
||||
this.attributes[WEB_VIEW_CONSTANTS.ATTRIBUTE_SRC] = new SrcAttribute(this);
|
||||
this.attributes[WEB_VIEW_CONSTANTS.ATTRIBUTE_HTTPREFERRER] = new HttpReferrerAttribute(this);
|
||||
this.attributes[WEB_VIEW_CONSTANTS.ATTRIBUTE_USERAGENT] = new UserAgentAttribute(this);
|
||||
this.attributes[WEB_VIEW_CONSTANTS.ATTRIBUTE_NODEINTEGRATION] = new BooleanAttribute(WEB_VIEW_CONSTANTS.ATTRIBUTE_NODEINTEGRATION, this);
|
||||
this.attributes[WEB_VIEW_CONSTANTS.ATTRIBUTE_NODEINTEGRATIONINSUBFRAMES] = new BooleanAttribute(WEB_VIEW_CONSTANTS.ATTRIBUTE_NODEINTEGRATIONINSUBFRAMES, this);
|
||||
this.attributes[WEB_VIEW_CONSTANTS.ATTRIBUTE_PLUGINS] = new BooleanAttribute(WEB_VIEW_CONSTANTS.ATTRIBUTE_PLUGINS, this);
|
||||
this.attributes[WEB_VIEW_CONSTANTS.ATTRIBUTE_DISABLEWEBSECURITY] = new BooleanAttribute(WEB_VIEW_CONSTANTS.ATTRIBUTE_DISABLEWEBSECURITY, this);
|
||||
this.attributes[WEB_VIEW_CONSTANTS.ATTRIBUTE_ALLOWPOPUPS] = new BooleanAttribute(WEB_VIEW_CONSTANTS.ATTRIBUTE_ALLOWPOPUPS, this);
|
||||
this.attributes[WEB_VIEW_CONSTANTS.ATTRIBUTE_ENABLEREMOTEMODULE] = new EnableRemoteModuleAttribute(this);
|
||||
this.attributes[WEB_VIEW_CONSTANTS.ATTRIBUTE_PRELOAD] = new PreloadAttribute(this);
|
||||
this.attributes[WEB_VIEW_CONSTANTS.ATTRIBUTE_BLINKFEATURES] = new BlinkFeaturesAttribute(this);
|
||||
this.attributes[WEB_VIEW_CONSTANTS.ATTRIBUTE_DISABLEBLINKFEATURES] = new DisableBlinkFeaturesAttribute(this);
|
||||
this.attributes[WEB_VIEW_CONSTANTS.ATTRIBUTE_WEBPREFERENCES] = new WebPreferencesAttribute(this);
|
||||
};
|
||||
|
|
|
@ -8,12 +8,12 @@
|
|||
// which runs in browserify environment instead of Node environment, all native
|
||||
// modules must be passed from outside, all included files must be plain JS.
|
||||
|
||||
import { WEB_VIEW_CONSTANTS } from '@electron/internal/renderer/web-view/web-view-constants'
|
||||
import { WebViewImpl as IWebViewImpl, webViewImplModule } from '@electron/internal/renderer/web-view/web-view-impl'
|
||||
import { WEB_VIEW_CONSTANTS } from '@electron/internal/renderer/web-view/web-view-constants';
|
||||
import { WebViewImpl as IWebViewImpl, webViewImplModule } from '@electron/internal/renderer/web-view/web-view-impl';
|
||||
|
||||
// Return a WebViewElement class that is defined in this context.
|
||||
const defineWebViewElement = (v8Util: NodeJS.V8UtilBinding, webViewImpl: typeof webViewImplModule) => {
|
||||
const { guestViewInternal, WebViewImpl } = webViewImpl
|
||||
const { guestViewInternal, WebViewImpl } = webViewImpl;
|
||||
return class WebViewElement extends HTMLElement {
|
||||
public internalInstanceId?: number;
|
||||
|
||||
|
@ -33,45 +33,45 @@ const defineWebViewElement = (v8Util: NodeJS.V8UtilBinding, webViewImpl: typeof
|
|||
WEB_VIEW_CONSTANTS.ATTRIBUTE_BLINKFEATURES,
|
||||
WEB_VIEW_CONSTANTS.ATTRIBUTE_DISABLEBLINKFEATURES,
|
||||
WEB_VIEW_CONSTANTS.ATTRIBUTE_WEBPREFERENCES
|
||||
]
|
||||
];
|
||||
}
|
||||
|
||||
constructor () {
|
||||
super()
|
||||
v8Util.setHiddenValue(this, 'internal', new WebViewImpl(this))
|
||||
super();
|
||||
v8Util.setHiddenValue(this, 'internal', new WebViewImpl(this));
|
||||
}
|
||||
|
||||
connectedCallback () {
|
||||
const internal = v8Util.getHiddenValue<IWebViewImpl>(this, 'internal')
|
||||
const internal = v8Util.getHiddenValue<IWebViewImpl>(this, 'internal');
|
||||
if (!internal) {
|
||||
return
|
||||
return;
|
||||
}
|
||||
if (!internal.elementAttached) {
|
||||
guestViewInternal.registerEvents(internal, internal.viewInstanceId)
|
||||
internal.elementAttached = true
|
||||
internal.attributes[WEB_VIEW_CONSTANTS.ATTRIBUTE_SRC].parse()
|
||||
guestViewInternal.registerEvents(internal, internal.viewInstanceId);
|
||||
internal.elementAttached = true;
|
||||
internal.attributes[WEB_VIEW_CONSTANTS.ATTRIBUTE_SRC].parse();
|
||||
}
|
||||
}
|
||||
|
||||
attributeChangedCallback (name: string, oldValue: any, newValue: any) {
|
||||
const internal = v8Util.getHiddenValue<IWebViewImpl>(this, 'internal')
|
||||
const internal = v8Util.getHiddenValue<IWebViewImpl>(this, 'internal');
|
||||
if (internal) {
|
||||
internal.handleWebviewAttributeMutation(name, oldValue, newValue)
|
||||
internal.handleWebviewAttributeMutation(name, oldValue, newValue);
|
||||
}
|
||||
}
|
||||
|
||||
disconnectedCallback () {
|
||||
const internal = v8Util.getHiddenValue<IWebViewImpl>(this, 'internal')
|
||||
const internal = v8Util.getHiddenValue<IWebViewImpl>(this, 'internal');
|
||||
if (!internal) {
|
||||
return
|
||||
return;
|
||||
}
|
||||
guestViewInternal.deregisterEvents(internal.viewInstanceId)
|
||||
internal.elementAttached = false
|
||||
this.internalInstanceId = 0
|
||||
internal.reset()
|
||||
guestViewInternal.deregisterEvents(internal.viewInstanceId);
|
||||
internal.elementAttached = false;
|
||||
this.internalInstanceId = 0;
|
||||
internal.reset();
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
// Register <webview> custom element.
|
||||
const registerWebViewElement = (v8Util: NodeJS.V8UtilBinding, webViewImpl: typeof webViewImplModule) => {
|
||||
|
@ -79,40 +79,40 @@ const registerWebViewElement = (v8Util: NodeJS.V8UtilBinding, webViewImpl: typeo
|
|||
// eslint-disable-next-line
|
||||
const WebViewElement = defineWebViewElement(v8Util, webViewImpl) as unknown as typeof ElectronInternal.WebViewElement
|
||||
|
||||
webViewImpl.setupMethods(WebViewElement)
|
||||
webViewImpl.setupMethods(WebViewElement);
|
||||
|
||||
// The customElements.define has to be called in a special scope.
|
||||
const webFrame = webViewImpl.webFrame as ElectronInternal.WebFrameInternal
|
||||
const webFrame = webViewImpl.webFrame as ElectronInternal.WebFrameInternal;
|
||||
webFrame.allowGuestViewElementDefinition(window, () => {
|
||||
window.customElements.define('webview', WebViewElement);
|
||||
(window as any).WebView = WebViewElement
|
||||
(window as any).WebView = WebViewElement;
|
||||
|
||||
// Delete the callbacks so developers cannot call them and produce unexpected
|
||||
// behavior.
|
||||
delete WebViewElement.prototype.connectedCallback
|
||||
delete WebViewElement.prototype.disconnectedCallback
|
||||
delete WebViewElement.prototype.attributeChangedCallback
|
||||
delete WebViewElement.prototype.connectedCallback;
|
||||
delete WebViewElement.prototype.disconnectedCallback;
|
||||
delete WebViewElement.prototype.attributeChangedCallback;
|
||||
|
||||
// Now that |observedAttributes| has been retrieved, we can hide it from
|
||||
// user code as well.
|
||||
// TypeScript is concerned that we're deleting a read-only attribute
|
||||
delete (WebViewElement as any).observedAttributes
|
||||
})
|
||||
}
|
||||
delete (WebViewElement as any).observedAttributes;
|
||||
});
|
||||
};
|
||||
|
||||
// Prepare to register the <webview> element.
|
||||
export const setupWebView = (v8Util: NodeJS.V8UtilBinding, webViewImpl: typeof webViewImplModule) => {
|
||||
const useCapture = true
|
||||
const useCapture = true;
|
||||
const listener = (event: Event) => {
|
||||
if (document.readyState === 'loading') {
|
||||
return
|
||||
return;
|
||||
}
|
||||
|
||||
webViewImpl.setupAttributes()
|
||||
registerWebViewElement(v8Util, webViewImpl)
|
||||
webViewImpl.setupAttributes();
|
||||
registerWebViewElement(v8Util, webViewImpl);
|
||||
|
||||
window.removeEventListener(event.type, listener, useCapture)
|
||||
}
|
||||
window.removeEventListener(event.type, listener, useCapture);
|
||||
};
|
||||
|
||||
window.addEventListener('readystatechange', listener, useCapture)
|
||||
}
|
||||
window.addEventListener('readystatechange', listener, useCapture);
|
||||
};
|
||||
|
|
|
@ -1,21 +1,21 @@
|
|||
import * as electron from 'electron'
|
||||
import * as electron from 'electron';
|
||||
|
||||
import { ipcRendererInternal } from '@electron/internal/renderer/ipc-renderer-internal'
|
||||
import * as ipcRendererUtils from '@electron/internal/renderer/ipc-renderer-internal-utils'
|
||||
import * as guestViewInternal from '@electron/internal/renderer/web-view/guest-view-internal'
|
||||
import { WEB_VIEW_CONSTANTS } from '@electron/internal/renderer/web-view/web-view-constants'
|
||||
import { syncMethods, asyncMethods, properties } from '@electron/internal/common/web-view-methods'
|
||||
import { deserialize } from '@electron/internal/common/type-utils'
|
||||
const { webFrame } = electron
|
||||
import { ipcRendererInternal } from '@electron/internal/renderer/ipc-renderer-internal';
|
||||
import * as ipcRendererUtils from '@electron/internal/renderer/ipc-renderer-internal-utils';
|
||||
import * as guestViewInternal from '@electron/internal/renderer/web-view/guest-view-internal';
|
||||
import { WEB_VIEW_CONSTANTS } from '@electron/internal/renderer/web-view/web-view-constants';
|
||||
import { syncMethods, asyncMethods, properties } from '@electron/internal/common/web-view-methods';
|
||||
import { deserialize } from '@electron/internal/common/type-utils';
|
||||
const { webFrame } = electron;
|
||||
|
||||
const v8Util = process.electronBinding('v8_util')
|
||||
const v8Util = process.electronBinding('v8_util');
|
||||
|
||||
// ID generator.
|
||||
let nextId = 0
|
||||
let nextId = 0;
|
||||
|
||||
const getNextId = function () {
|
||||
return ++nextId
|
||||
}
|
||||
return ++nextId;
|
||||
};
|
||||
|
||||
// Represents the internal state of the WebView node.
|
||||
export class WebViewImpl {
|
||||
|
@ -38,29 +38,29 @@ export class WebViewImpl {
|
|||
|
||||
constructor (public webviewNode: HTMLElement) {
|
||||
// Create internal iframe element.
|
||||
this.internalElement = this.createInternalElement()
|
||||
const shadowRoot = this.webviewNode.attachShadow({ mode: 'open' })
|
||||
shadowRoot.innerHTML = '<!DOCTYPE html><style type="text/css">:host { display: flex; }</style>'
|
||||
this.setupWebViewAttributes()
|
||||
this.viewInstanceId = getNextId()
|
||||
shadowRoot.appendChild(this.internalElement)
|
||||
this.internalElement = this.createInternalElement();
|
||||
const shadowRoot = this.webviewNode.attachShadow({ mode: 'open' });
|
||||
shadowRoot.innerHTML = '<!DOCTYPE html><style type="text/css">:host { display: flex; }</style>';
|
||||
this.setupWebViewAttributes();
|
||||
this.viewInstanceId = getNextId();
|
||||
shadowRoot.appendChild(this.internalElement);
|
||||
|
||||
// Provide access to contentWindow.
|
||||
Object.defineProperty(this.webviewNode, 'contentWindow', {
|
||||
get: () => {
|
||||
return this.internalElement.contentWindow
|
||||
return this.internalElement.contentWindow;
|
||||
},
|
||||
enumerable: true
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
createInternalElement () {
|
||||
const iframeElement = document.createElement('iframe')
|
||||
iframeElement.style.flex = '1 1 auto'
|
||||
iframeElement.style.width = '100%'
|
||||
iframeElement.style.border = '0'
|
||||
v8Util.setHiddenValue(iframeElement, 'internal', this)
|
||||
return iframeElement
|
||||
const iframeElement = document.createElement('iframe');
|
||||
iframeElement.style.flex = '1 1 auto';
|
||||
iframeElement.style.width = '100%';
|
||||
iframeElement.style.border = '0';
|
||||
v8Util.setHiddenValue(iframeElement, 'internal', this);
|
||||
return iframeElement;
|
||||
}
|
||||
|
||||
// Resets some state upon reattaching <webview> element to the DOM.
|
||||
|
@ -72,20 +72,20 @@ export class WebViewImpl {
|
|||
// 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) {
|
||||
this.guestInstanceId = undefined
|
||||
this.guestInstanceId = undefined;
|
||||
}
|
||||
|
||||
this.beforeFirstNavigation = true
|
||||
this.attributes[WEB_VIEW_CONSTANTS.ATTRIBUTE_PARTITION].validPartitionId = true
|
||||
this.beforeFirstNavigation = true;
|
||||
this.attributes[WEB_VIEW_CONSTANTS.ATTRIBUTE_PARTITION].validPartitionId = true;
|
||||
|
||||
// Since attachment swaps a local frame for a remote frame, we need our
|
||||
// internal iframe element to be local again before we can reattach.
|
||||
const newFrame = this.createInternalElement()
|
||||
const oldFrame = this.internalElement
|
||||
this.internalElement = newFrame
|
||||
const newFrame = this.createInternalElement();
|
||||
const oldFrame = this.internalElement;
|
||||
this.internalElement = newFrame;
|
||||
|
||||
if (oldFrame && oldFrame.parentNode) {
|
||||
oldFrame.parentNode.replaceChild(newFrame, oldFrame)
|
||||
oldFrame.parentNode.replaceChild(newFrame, oldFrame);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -96,179 +96,179 @@ export class WebViewImpl {
|
|||
// details.
|
||||
handleWebviewAttributeMutation (attributeName: string, oldValue: any, newValue: any) {
|
||||
if (!this.attributes[attributeName] || this.attributes[attributeName].ignoreMutation) {
|
||||
return
|
||||
return;
|
||||
}
|
||||
|
||||
// Let the changed attribute handle its own mutation
|
||||
this.attributes[attributeName].handleMutation(oldValue, newValue)
|
||||
this.attributes[attributeName].handleMutation(oldValue, newValue);
|
||||
}
|
||||
|
||||
onElementResize () {
|
||||
const resizeEvent = new Event('resize') as ElectronInternal.WebFrameResizeEvent
|
||||
resizeEvent.newWidth = this.webviewNode.clientWidth
|
||||
resizeEvent.newHeight = this.webviewNode.clientHeight
|
||||
this.dispatchEvent(resizeEvent)
|
||||
const resizeEvent = new Event('resize') as ElectronInternal.WebFrameResizeEvent;
|
||||
resizeEvent.newWidth = this.webviewNode.clientWidth;
|
||||
resizeEvent.newHeight = this.webviewNode.clientHeight;
|
||||
this.dispatchEvent(resizeEvent);
|
||||
}
|
||||
|
||||
createGuest () {
|
||||
guestViewInternal.createGuest(this.buildParams()).then(guestInstanceId => {
|
||||
this.attachGuestInstance(guestInstanceId)
|
||||
})
|
||||
this.attachGuestInstance(guestInstanceId);
|
||||
});
|
||||
}
|
||||
|
||||
dispatchEvent (webViewEvent: Electron.Event) {
|
||||
this.webviewNode.dispatchEvent(webViewEvent)
|
||||
this.webviewNode.dispatchEvent(webViewEvent);
|
||||
}
|
||||
|
||||
// Adds an 'on<event>' property on the webview, which can be used to set/unset
|
||||
// an event handler.
|
||||
setupEventProperty (eventName: string) {
|
||||
const propertyName = `on${eventName.toLowerCase()}`
|
||||
const propertyName = `on${eventName.toLowerCase()}`;
|
||||
return Object.defineProperty(this.webviewNode, propertyName, {
|
||||
get: () => {
|
||||
return this.on[propertyName]
|
||||
return this.on[propertyName];
|
||||
},
|
||||
set: (value) => {
|
||||
if (this.on[propertyName]) {
|
||||
this.webviewNode.removeEventListener(eventName, this.on[propertyName])
|
||||
this.webviewNode.removeEventListener(eventName, this.on[propertyName]);
|
||||
}
|
||||
this.on[propertyName] = value
|
||||
this.on[propertyName] = value;
|
||||
if (value) {
|
||||
return this.webviewNode.addEventListener(eventName, value)
|
||||
return this.webviewNode.addEventListener(eventName, value);
|
||||
}
|
||||
},
|
||||
enumerable: true
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
// Updates state upon loadcommit.
|
||||
onLoadCommit (webViewEvent: ElectronInternal.WebViewEvent) {
|
||||
const oldValue = this.webviewNode.getAttribute(WEB_VIEW_CONSTANTS.ATTRIBUTE_SRC)
|
||||
const newValue = webViewEvent.url
|
||||
const oldValue = this.webviewNode.getAttribute(WEB_VIEW_CONSTANTS.ATTRIBUTE_SRC);
|
||||
const 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.
|
||||
this.attributes[WEB_VIEW_CONSTANTS.ATTRIBUTE_SRC].setValueIgnoreMutation(newValue)
|
||||
this.attributes[WEB_VIEW_CONSTANTS.ATTRIBUTE_SRC].setValueIgnoreMutation(newValue);
|
||||
}
|
||||
}
|
||||
|
||||
// Emits focus/blur events.
|
||||
onFocusChange () {
|
||||
const hasFocus = document.activeElement === this.webviewNode
|
||||
const hasFocus = document.activeElement === this.webviewNode;
|
||||
if (hasFocus !== this.hasFocus) {
|
||||
this.hasFocus = hasFocus
|
||||
this.dispatchEvent(new Event(hasFocus ? 'focus' : 'blur'))
|
||||
this.hasFocus = hasFocus;
|
||||
this.dispatchEvent(new Event(hasFocus ? 'focus' : 'blur'));
|
||||
}
|
||||
}
|
||||
|
||||
onAttach (storagePartitionId: number) {
|
||||
return this.attributes[WEB_VIEW_CONSTANTS.ATTRIBUTE_PARTITION].setValue(storagePartitionId)
|
||||
return this.attributes[WEB_VIEW_CONSTANTS.ATTRIBUTE_PARTITION].setValue(storagePartitionId);
|
||||
}
|
||||
|
||||
buildParams () {
|
||||
const params: Record<string, any> = {
|
||||
instanceId: this.viewInstanceId,
|
||||
userAgentOverride: this.userAgentOverride
|
||||
}
|
||||
};
|
||||
|
||||
for (const attributeName in this.attributes) {
|
||||
if (Object.prototype.hasOwnProperty.call(this.attributes, attributeName)) {
|
||||
params[attributeName] = this.attributes[attributeName].getValue()
|
||||
params[attributeName] = this.attributes[attributeName].getValue();
|
||||
}
|
||||
}
|
||||
|
||||
return params
|
||||
return params;
|
||||
}
|
||||
|
||||
attachGuestInstance (guestInstanceId: number) {
|
||||
if (!this.elementAttached) {
|
||||
// The element could be detached before we got response from browser.
|
||||
return
|
||||
return;
|
||||
}
|
||||
this.internalInstanceId = getNextId()
|
||||
this.guestInstanceId = guestInstanceId
|
||||
this.internalInstanceId = getNextId();
|
||||
this.guestInstanceId = guestInstanceId;
|
||||
|
||||
guestViewInternal.attachGuest(
|
||||
this.internalInstanceId,
|
||||
this.guestInstanceId,
|
||||
this.buildParams(),
|
||||
this.internalElement.contentWindow!
|
||||
)
|
||||
);
|
||||
|
||||
// ResizeObserver is a browser global not recognized by "standard".
|
||||
/* globals ResizeObserver */
|
||||
// TODO(zcbenz): Should we deprecate the "resize" event? Wait, it is not
|
||||
// even documented.
|
||||
this.resizeObserver = new ResizeObserver(this.onElementResize.bind(this))
|
||||
this.resizeObserver.observe(this.internalElement)
|
||||
this.resizeObserver = new ResizeObserver(this.onElementResize.bind(this));
|
||||
this.resizeObserver.observe(this.internalElement);
|
||||
}
|
||||
}
|
||||
|
||||
export const setupAttributes = () => {
|
||||
require('@electron/internal/renderer/web-view/web-view-attributes')
|
||||
}
|
||||
require('@electron/internal/renderer/web-view/web-view-attributes');
|
||||
};
|
||||
|
||||
// I wish eslint wasn't so stupid, but it is
|
||||
// eslint-disable-next-line
|
||||
export const setupMethods = (WebViewElement: typeof ElectronInternal.WebViewElement) => {
|
||||
WebViewElement.prototype.getWebContentsId = function () {
|
||||
const internal = v8Util.getHiddenValue<WebViewImpl>(this, 'internal')
|
||||
const internal = v8Util.getHiddenValue<WebViewImpl>(this, 'internal');
|
||||
if (!internal.guestInstanceId) {
|
||||
throw new Error('The WebView must be attached to the DOM and the dom-ready event emitted before this method can be called.')
|
||||
throw new Error('The WebView must be attached to the DOM and the dom-ready event emitted before this method can be called.');
|
||||
}
|
||||
return internal.guestInstanceId
|
||||
}
|
||||
return internal.guestInstanceId;
|
||||
};
|
||||
|
||||
// Focusing the webview should move page focus to the underlying iframe.
|
||||
WebViewElement.prototype.focus = function () {
|
||||
this.contentWindow.focus()
|
||||
}
|
||||
this.contentWindow.focus();
|
||||
};
|
||||
|
||||
// Forward proto.foo* method calls to WebViewImpl.foo*.
|
||||
const createBlockHandler = function (method: string) {
|
||||
return function (this: ElectronInternal.WebViewElement, ...args: Array<any>) {
|
||||
return ipcRendererUtils.invokeSync('ELECTRON_GUEST_VIEW_MANAGER_CALL', this.getWebContentsId(), method, args)
|
||||
}
|
||||
}
|
||||
return ipcRendererUtils.invokeSync('ELECTRON_GUEST_VIEW_MANAGER_CALL', this.getWebContentsId(), method, args);
|
||||
};
|
||||
};
|
||||
|
||||
for (const method of syncMethods) {
|
||||
(WebViewElement.prototype as Record<string, any>)[method] = createBlockHandler(method)
|
||||
(WebViewElement.prototype as Record<string, any>)[method] = createBlockHandler(method);
|
||||
}
|
||||
|
||||
const createNonBlockHandler = function (method: string) {
|
||||
return function (this: ElectronInternal.WebViewElement, ...args: Array<any>) {
|
||||
return ipcRendererInternal.invoke('ELECTRON_GUEST_VIEW_MANAGER_CALL', this.getWebContentsId(), method, args)
|
||||
}
|
||||
}
|
||||
return ipcRendererInternal.invoke('ELECTRON_GUEST_VIEW_MANAGER_CALL', this.getWebContentsId(), method, args);
|
||||
};
|
||||
};
|
||||
|
||||
for (const method of asyncMethods) {
|
||||
(WebViewElement.prototype as Record<string, any>)[method] = createNonBlockHandler(method)
|
||||
(WebViewElement.prototype as Record<string, any>)[method] = createNonBlockHandler(method);
|
||||
}
|
||||
|
||||
WebViewElement.prototype.capturePage = async function (...args) {
|
||||
return deserialize(await ipcRendererInternal.invoke('ELECTRON_GUEST_VIEW_MANAGER_CAPTURE_PAGE', this.getWebContentsId(), args))
|
||||
}
|
||||
return deserialize(await ipcRendererInternal.invoke('ELECTRON_GUEST_VIEW_MANAGER_CAPTURE_PAGE', this.getWebContentsId(), args));
|
||||
};
|
||||
|
||||
const createPropertyGetter = function (property: string) {
|
||||
return function (this: ElectronInternal.WebViewElement) {
|
||||
return ipcRendererUtils.invokeSync('ELECTRON_GUEST_VIEW_MANAGER_PROPERTY_GET', this.getWebContentsId(), property)
|
||||
}
|
||||
}
|
||||
return ipcRendererUtils.invokeSync('ELECTRON_GUEST_VIEW_MANAGER_PROPERTY_GET', this.getWebContentsId(), property);
|
||||
};
|
||||
};
|
||||
|
||||
const createPropertySetter = function (property: string) {
|
||||
return function (this: ElectronInternal.WebViewElement, arg: any) {
|
||||
return ipcRendererUtils.invokeSync('ELECTRON_GUEST_VIEW_MANAGER_PROPERTY_SET', this.getWebContentsId(), property, arg)
|
||||
}
|
||||
}
|
||||
return ipcRendererUtils.invokeSync('ELECTRON_GUEST_VIEW_MANAGER_PROPERTY_SET', this.getWebContentsId(), property, arg);
|
||||
};
|
||||
};
|
||||
|
||||
for (const property of properties) {
|
||||
Object.defineProperty(WebViewElement.prototype, property, {
|
||||
get: createPropertyGetter(property) as any,
|
||||
set: createPropertySetter(property)
|
||||
})
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export const webViewImplModule = {
|
||||
setupAttributes,
|
||||
|
@ -276,4 +276,4 @@ export const webViewImplModule = {
|
|||
guestViewInternal,
|
||||
webFrame,
|
||||
WebViewImpl
|
||||
}
|
||||
};
|
||||
|
|
|
@ -1,18 +1,18 @@
|
|||
import { ipcRendererInternal } from '@electron/internal/renderer/ipc-renderer-internal'
|
||||
import { ipcRendererInternal } from '@electron/internal/renderer/ipc-renderer-internal';
|
||||
|
||||
const v8Util = process.electronBinding('v8_util')
|
||||
const v8Util = process.electronBinding('v8_util');
|
||||
|
||||
function handleFocusBlur (guestInstanceId: number) {
|
||||
// Note that while Chromium content APIs have observer for focus/blur, they
|
||||
// unfortunately do not work for webview.
|
||||
|
||||
window.addEventListener('focus', () => {
|
||||
ipcRendererInternal.send('ELECTRON_GUEST_VIEW_MANAGER_FOCUS_CHANGE', true, guestInstanceId)
|
||||
})
|
||||
ipcRendererInternal.send('ELECTRON_GUEST_VIEW_MANAGER_FOCUS_CHANGE', true, guestInstanceId);
|
||||
});
|
||||
|
||||
window.addEventListener('blur', () => {
|
||||
ipcRendererInternal.send('ELECTRON_GUEST_VIEW_MANAGER_FOCUS_CHANGE', false, guestInstanceId)
|
||||
})
|
||||
ipcRendererInternal.send('ELECTRON_GUEST_VIEW_MANAGER_FOCUS_CHANGE', false, guestInstanceId);
|
||||
});
|
||||
}
|
||||
|
||||
export function webViewInit (
|
||||
|
@ -20,17 +20,17 @@ export function webViewInit (
|
|||
) {
|
||||
// Don't allow recursive `<webview>`.
|
||||
if (webviewTag && guestInstanceId == null) {
|
||||
const { webViewImplModule } = require('@electron/internal/renderer/web-view/web-view-impl')
|
||||
const { webViewImplModule } = require('@electron/internal/renderer/web-view/web-view-impl');
|
||||
if (contextIsolation) {
|
||||
v8Util.setHiddenValue(window, 'web-view-impl', webViewImplModule)
|
||||
v8Util.setHiddenValue(window, 'web-view-impl', webViewImplModule);
|
||||
} else {
|
||||
const { setupWebView } = require('@electron/internal/renderer/web-view/web-view-element')
|
||||
setupWebView(v8Util, webViewImplModule)
|
||||
const { setupWebView } = require('@electron/internal/renderer/web-view/web-view-element');
|
||||
setupWebView(v8Util, webViewImplModule);
|
||||
}
|
||||
}
|
||||
|
||||
if (guestInstanceId) {
|
||||
// Report focus/blur events of webview to browser.
|
||||
handleFocusBlur(guestInstanceId)
|
||||
handleFocusBlur(guestInstanceId);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,12 +7,12 @@
|
|||
|
||||
// Rip global off of window (which is also global) so that webpack doesn't
|
||||
// auto replace it with a looped reference to this file
|
||||
const _global = (self as any || window as any).global as NodeJS.Global
|
||||
const process = _global.process
|
||||
const Buffer = _global.Buffer
|
||||
const _global = (self as any || window as any).global as NodeJS.Global;
|
||||
const process = _global.process;
|
||||
const Buffer = _global.Buffer;
|
||||
|
||||
export {
|
||||
_global,
|
||||
process,
|
||||
Buffer
|
||||
}
|
||||
};
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { ipcRendererInternal } from '@electron/internal/renderer/ipc-renderer-internal'
|
||||
import * as ipcRendererUtils from '@electron/internal/renderer/ipc-renderer-internal-utils'
|
||||
import { ipcRendererInternal } from '@electron/internal/renderer/ipc-renderer-internal';
|
||||
import * as ipcRendererUtils from '@electron/internal/renderer/ipc-renderer-internal-utils';
|
||||
|
||||
// This file implements the following APIs:
|
||||
// - window.history.back()
|
||||
|
@ -19,29 +19,29 @@ import * as ipcRendererUtils from '@electron/internal/renderer/ipc-renderer-inte
|
|||
// - document.visibilityState
|
||||
|
||||
// Helper function to resolve relative url.
|
||||
const resolveURL = (url: string, base: string) => new URL(url, base).href
|
||||
const resolveURL = (url: string, base: string) => new URL(url, base).href;
|
||||
|
||||
// Use this method to ensure values expected as strings in the main process
|
||||
// are convertible to strings in the renderer process. This ensures exceptions
|
||||
// converting values to strings are thrown in this process.
|
||||
const toString = (value: any) => {
|
||||
return value != null ? `${value}` : value
|
||||
}
|
||||
return value != null ? `${value}` : value;
|
||||
};
|
||||
|
||||
const windowProxies = new Map<number, BrowserWindowProxy>()
|
||||
const windowProxies = new Map<number, BrowserWindowProxy>();
|
||||
|
||||
const getOrCreateProxy = (guestId: number) => {
|
||||
let proxy = windowProxies.get(guestId)
|
||||
let proxy = windowProxies.get(guestId);
|
||||
if (proxy == null) {
|
||||
proxy = new BrowserWindowProxy(guestId)
|
||||
windowProxies.set(guestId, proxy)
|
||||
proxy = new BrowserWindowProxy(guestId);
|
||||
windowProxies.set(guestId, proxy);
|
||||
}
|
||||
return proxy
|
||||
}
|
||||
return proxy;
|
||||
};
|
||||
|
||||
const removeProxy = (guestId: number) => {
|
||||
windowProxies.delete(guestId)
|
||||
}
|
||||
windowProxies.delete(guestId);
|
||||
};
|
||||
|
||||
type LocationProperties = 'hash' | 'href' | 'host' | 'hostname' | 'origin' | 'pathname' | 'port' | 'protocol' | 'search'
|
||||
|
||||
|
@ -65,52 +65,52 @@ class LocationProxy {
|
|||
private static ProxyProperty<T> (target: LocationProxy, propertyKey: LocationProperties) {
|
||||
Object.defineProperty(target, propertyKey, {
|
||||
get: function (this: LocationProxy): T | string {
|
||||
const guestURL = this.getGuestURL()
|
||||
const value = guestURL ? guestURL[propertyKey] : ''
|
||||
return value === undefined ? '' : value
|
||||
const guestURL = this.getGuestURL();
|
||||
const value = guestURL ? guestURL[propertyKey] : '';
|
||||
return value === undefined ? '' : value;
|
||||
},
|
||||
set: function (this: LocationProxy, newVal: T) {
|
||||
const guestURL = this.getGuestURL()
|
||||
const guestURL = this.getGuestURL();
|
||||
if (guestURL) {
|
||||
// TypeScript doesn't want us to assign to read-only variables.
|
||||
// It's right, that's bad, but we're doing it anway.
|
||||
(guestURL as any)[propertyKey] = newVal
|
||||
(guestURL as any)[propertyKey] = newVal;
|
||||
|
||||
return this._invokeWebContentsMethod('loadURL', guestURL.toString())
|
||||
return this._invokeWebContentsMethod('loadURL', guestURL.toString());
|
||||
}
|
||||
}
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
constructor (guestId: number) {
|
||||
// eslint will consider the constructor "useless"
|
||||
// unless we assign them in the body. It's fine, that's what
|
||||
// TS would do anyway.
|
||||
this.guestId = guestId
|
||||
this.getGuestURL = this.getGuestURL.bind(this)
|
||||
this.guestId = guestId;
|
||||
this.getGuestURL = this.getGuestURL.bind(this);
|
||||
}
|
||||
|
||||
public toString (): string {
|
||||
return this.href
|
||||
return this.href;
|
||||
}
|
||||
|
||||
private getGuestURL (): URL | null {
|
||||
const urlString = this._invokeWebContentsMethodSync('getURL') as string
|
||||
const urlString = this._invokeWebContentsMethodSync('getURL') as string;
|
||||
try {
|
||||
return new URL(urlString)
|
||||
return new URL(urlString);
|
||||
} catch (e) {
|
||||
console.error('LocationProxy: failed to parse string', urlString, e)
|
||||
console.error('LocationProxy: failed to parse string', urlString, e);
|
||||
}
|
||||
|
||||
return null
|
||||
return null;
|
||||
}
|
||||
|
||||
private _invokeWebContentsMethod (method: string, ...args: any[]) {
|
||||
return ipcRendererInternal.invoke('ELECTRON_GUEST_WINDOW_MANAGER_WEB_CONTENTS_METHOD', this.guestId, method, ...args)
|
||||
return ipcRendererInternal.invoke('ELECTRON_GUEST_WINDOW_MANAGER_WEB_CONTENTS_METHOD', this.guestId, method, ...args);
|
||||
}
|
||||
|
||||
private _invokeWebContentsMethodSync (method: string, ...args: any[]) {
|
||||
return ipcRendererUtils.invokeSync('ELECTRON_GUEST_WINDOW_MANAGER_WEB_CONTENTS_METHOD', this.guestId, method, ...args)
|
||||
return ipcRendererUtils.invokeSync('ELECTRON_GUEST_WINDOW_MANAGER_WEB_CONTENTS_METHOD', this.guestId, method, ...args);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -124,54 +124,54 @@ class BrowserWindowProxy {
|
|||
// so for now, we'll have to make do with an "any" in the mix.
|
||||
// https://github.com/Microsoft/TypeScript/issues/2521
|
||||
public get location (): LocationProxy | any {
|
||||
return this._location
|
||||
return this._location;
|
||||
}
|
||||
|
||||
public set location (url: string | any) {
|
||||
url = resolveURL(url, this.location.href)
|
||||
this._invokeWebContentsMethod('loadURL', url)
|
||||
url = resolveURL(url, this.location.href);
|
||||
this._invokeWebContentsMethod('loadURL', url);
|
||||
}
|
||||
|
||||
constructor (guestId: number) {
|
||||
this.guestId = guestId
|
||||
this._location = new LocationProxy(guestId)
|
||||
this.guestId = guestId;
|
||||
this._location = new LocationProxy(guestId);
|
||||
|
||||
ipcRendererInternal.once(`ELECTRON_GUEST_WINDOW_MANAGER_WINDOW_CLOSED_${guestId}`, () => {
|
||||
removeProxy(guestId)
|
||||
this.closed = true
|
||||
})
|
||||
removeProxy(guestId);
|
||||
this.closed = true;
|
||||
});
|
||||
}
|
||||
|
||||
public close () {
|
||||
this._invokeWindowMethod('destroy')
|
||||
this._invokeWindowMethod('destroy');
|
||||
}
|
||||
|
||||
public focus () {
|
||||
this._invokeWindowMethod('focus')
|
||||
this._invokeWindowMethod('focus');
|
||||
}
|
||||
|
||||
public blur () {
|
||||
this._invokeWindowMethod('blur')
|
||||
this._invokeWindowMethod('blur');
|
||||
}
|
||||
|
||||
public print () {
|
||||
this._invokeWebContentsMethod('print')
|
||||
this._invokeWebContentsMethod('print');
|
||||
}
|
||||
|
||||
public postMessage (message: any, targetOrigin: string) {
|
||||
ipcRendererInternal.invoke('ELECTRON_GUEST_WINDOW_MANAGER_WINDOW_POSTMESSAGE', this.guestId, message, toString(targetOrigin), window.location.origin)
|
||||
ipcRendererInternal.invoke('ELECTRON_GUEST_WINDOW_MANAGER_WINDOW_POSTMESSAGE', this.guestId, message, toString(targetOrigin), window.location.origin);
|
||||
}
|
||||
|
||||
public eval (code: string) {
|
||||
this._invokeWebContentsMethod('executeJavaScript', code)
|
||||
this._invokeWebContentsMethod('executeJavaScript', code);
|
||||
}
|
||||
|
||||
private _invokeWindowMethod (method: string, ...args: any[]) {
|
||||
return ipcRendererInternal.invoke('ELECTRON_GUEST_WINDOW_MANAGER_WINDOW_METHOD', this.guestId, method, ...args)
|
||||
return ipcRendererInternal.invoke('ELECTRON_GUEST_WINDOW_MANAGER_WINDOW_METHOD', this.guestId, method, ...args);
|
||||
}
|
||||
|
||||
private _invokeWebContentsMethod (method: string, ...args: any[]) {
|
||||
return ipcRendererInternal.invoke('ELECTRON_GUEST_WINDOW_MANAGER_WEB_CONTENTS_METHOD', this.guestId, method, ...args)
|
||||
return ipcRendererInternal.invoke('ELECTRON_GUEST_WINDOW_MANAGER_WEB_CONTENTS_METHOD', this.guestId, method, ...args);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -181,33 +181,33 @@ export const windowSetup = (
|
|||
if (!process.sandboxed && guestInstanceId == null) {
|
||||
// Override default window.close.
|
||||
window.close = function () {
|
||||
ipcRendererInternal.send('ELECTRON_BROWSER_WINDOW_CLOSE')
|
||||
}
|
||||
ipcRendererInternal.send('ELECTRON_BROWSER_WINDOW_CLOSE');
|
||||
};
|
||||
}
|
||||
|
||||
if (!usesNativeWindowOpen) {
|
||||
// Make the browser window or guest view emit "new-window" event.
|
||||
(window as any).open = function (url?: string, frameName?: string, features?: string) {
|
||||
if (url != null && url !== '') {
|
||||
url = resolveURL(url, location.href)
|
||||
url = resolveURL(url, location.href);
|
||||
}
|
||||
const guestId = ipcRendererInternal.sendSync('ELECTRON_GUEST_WINDOW_MANAGER_WINDOW_OPEN', url, toString(frameName), toString(features))
|
||||
const guestId = ipcRendererInternal.sendSync('ELECTRON_GUEST_WINDOW_MANAGER_WINDOW_OPEN', url, toString(frameName), toString(features));
|
||||
if (guestId != null) {
|
||||
return getOrCreateProxy(guestId)
|
||||
return getOrCreateProxy(guestId);
|
||||
} else {
|
||||
return null
|
||||
return null;
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
if (openerId != null) {
|
||||
window.opener = getOrCreateProxy(openerId)
|
||||
window.opener = getOrCreateProxy(openerId);
|
||||
}
|
||||
|
||||
// But we do not support prompt().
|
||||
window.prompt = function () {
|
||||
throw new Error('prompt() is and will not be supported.')
|
||||
}
|
||||
throw new Error('prompt() is and will not be supported.');
|
||||
};
|
||||
|
||||
if (!usesNativeWindowOpen || openerId != null) {
|
||||
ipcRendererInternal.on('ELECTRON_GUEST_WINDOW_POSTMESSAGE', function (
|
||||
|
@ -219,36 +219,36 @@ export const windowSetup = (
|
|||
// Why any? We can't construct a MessageEvent and we can't
|
||||
// use `as MessageEvent` because you're not supposed to override
|
||||
// data, origin, and source
|
||||
const event: any = document.createEvent('Event')
|
||||
event.initEvent('message', false, false)
|
||||
const event: any = document.createEvent('Event');
|
||||
event.initEvent('message', false, false);
|
||||
|
||||
event.data = message
|
||||
event.origin = sourceOrigin
|
||||
event.source = getOrCreateProxy(sourceId)
|
||||
event.data = message;
|
||||
event.origin = sourceOrigin;
|
||||
event.source = getOrCreateProxy(sourceId);
|
||||
|
||||
window.dispatchEvent(event as MessageEvent)
|
||||
})
|
||||
window.dispatchEvent(event as MessageEvent);
|
||||
});
|
||||
}
|
||||
|
||||
if (!process.sandboxed && !rendererProcessReuseEnabled) {
|
||||
window.history.back = function () {
|
||||
ipcRendererInternal.send('ELECTRON_NAVIGATION_CONTROLLER_GO_BACK')
|
||||
}
|
||||
ipcRendererInternal.send('ELECTRON_NAVIGATION_CONTROLLER_GO_BACK');
|
||||
};
|
||||
|
||||
window.history.forward = function () {
|
||||
ipcRendererInternal.send('ELECTRON_NAVIGATION_CONTROLLER_GO_FORWARD')
|
||||
}
|
||||
ipcRendererInternal.send('ELECTRON_NAVIGATION_CONTROLLER_GO_FORWARD');
|
||||
};
|
||||
|
||||
window.history.go = function (offset: number) {
|
||||
ipcRendererInternal.send('ELECTRON_NAVIGATION_CONTROLLER_GO_TO_OFFSET', +offset)
|
||||
}
|
||||
ipcRendererInternal.send('ELECTRON_NAVIGATION_CONTROLLER_GO_TO_OFFSET', +offset);
|
||||
};
|
||||
|
||||
Object.defineProperty(window.history, 'length', {
|
||||
get: function () {
|
||||
return ipcRendererInternal.sendSync('ELECTRON_NAVIGATION_CONTROLLER_LENGTH')
|
||||
return ipcRendererInternal.sendSync('ELECTRON_NAVIGATION_CONTROLLER_LENGTH');
|
||||
},
|
||||
set () {}
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
if (guestInstanceId != null) {
|
||||
|
@ -259,27 +259,27 @@ export const windowSetup = (
|
|||
// Note that this results in duplicate visibilitychange events (since
|
||||
// Chromium also fires them) and potentially incorrect visibility change.
|
||||
// We should reconsider this decision for Electron 2.0.
|
||||
let cachedVisibilityState = isHiddenPage ? 'hidden' : 'visible'
|
||||
let cachedVisibilityState = isHiddenPage ? 'hidden' : 'visible';
|
||||
|
||||
// Subscribe to visibilityState changes.
|
||||
ipcRendererInternal.on('ELECTRON_GUEST_INSTANCE_VISIBILITY_CHANGE', function (_event, visibilityState: VisibilityState) {
|
||||
if (cachedVisibilityState !== visibilityState) {
|
||||
cachedVisibilityState = visibilityState
|
||||
document.dispatchEvent(new Event('visibilitychange'))
|
||||
cachedVisibilityState = visibilityState;
|
||||
document.dispatchEvent(new Event('visibilitychange'));
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
// Make document.hidden and document.visibilityState return the correct value.
|
||||
Object.defineProperty(document, 'hidden', {
|
||||
get: function () {
|
||||
return cachedVisibilityState !== 'visible'
|
||||
return cachedVisibilityState !== 'visible';
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
Object.defineProperty(document, 'visibilityState', {
|
||||
get: function () {
|
||||
return cachedVisibilityState
|
||||
return cachedVisibilityState;
|
||||
}
|
||||
})
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue