chore: tsify web-contents (#24325)
This commit is contained in:
parent
82af855579
commit
1f23807271
7 changed files with 168 additions and 140 deletions
|
@ -222,7 +222,7 @@ auto_filenames = {
|
||||||
"lib/browser/api/view.ts",
|
"lib/browser/api/view.ts",
|
||||||
"lib/browser/api/views/image-view.ts",
|
"lib/browser/api/views/image-view.ts",
|
||||||
"lib/browser/api/web-contents-view.ts",
|
"lib/browser/api/web-contents-view.ts",
|
||||||
"lib/browser/api/web-contents.js",
|
"lib/browser/api/web-contents.ts",
|
||||||
"lib/browser/chrome-extension-shim.ts",
|
"lib/browser/chrome-extension-shim.ts",
|
||||||
"lib/browser/default-menu.ts",
|
"lib/browser/default-menu.ts",
|
||||||
"lib/browser/desktop-capturer.ts",
|
"lib/browser/desktop-capturer.ts",
|
||||||
|
@ -234,7 +234,7 @@ auto_filenames = {
|
||||||
"lib/browser/ipc-main-internal-utils.ts",
|
"lib/browser/ipc-main-internal-utils.ts",
|
||||||
"lib/browser/ipc-main-internal.ts",
|
"lib/browser/ipc-main-internal.ts",
|
||||||
"lib/browser/message-port-main.ts",
|
"lib/browser/message-port-main.ts",
|
||||||
"lib/browser/navigation-controller.js",
|
"lib/browser/navigation-controller.ts",
|
||||||
"lib/browser/remote/objects-registry.ts",
|
"lib/browser/remote/objects-registry.ts",
|
||||||
"lib/browser/remote/server.ts",
|
"lib/browser/remote/server.ts",
|
||||||
"lib/browser/rpc-server.ts",
|
"lib/browser/rpc-server.ts",
|
||||||
|
|
|
@ -1,17 +1,15 @@
|
||||||
'use strict';
|
import { app, ipcMain, session, deprecate } from 'electron';
|
||||||
|
import type { MenuItem, MenuItemConstructorOptions, WebContentsInternal } from 'electron';
|
||||||
|
|
||||||
const { EventEmitter } = require('events');
|
import * as url from 'url';
|
||||||
const electron = require('electron');
|
import * as path from 'path';
|
||||||
const path = require('path');
|
import { internalWindowOpen } from '../guest-window-manager';
|
||||||
const url = require('url');
|
import { NavigationController } from '../navigation-controller';
|
||||||
const { app, ipcMain, session } = electron;
|
import { ipcMainInternal } from '../ipc-main-internal';
|
||||||
|
import * as ipcMainUtils from '../ipc-main-internal-utils';
|
||||||
const { internalWindowOpen } = require('@electron/internal/browser/guest-window-manager');
|
import { parseFeatures } from '../../common/parse-features-string';
|
||||||
const NavigationController = require('@electron/internal/browser/navigation-controller');
|
import { MessagePortMain } from '../message-port-main';
|
||||||
const { ipcMainInternal } = require('@electron/internal/browser/ipc-main-internal');
|
import { EventEmitter } from 'events';
|
||||||
const ipcMainUtils = require('@electron/internal/browser/ipc-main-internal-utils');
|
|
||||||
const { parseFeatures } = require('@electron/internal/common/parse-features-string');
|
|
||||||
const { MessagePortMain } = require('@electron/internal/browser/message-port-main');
|
|
||||||
|
|
||||||
// session is not used here, the purpose is to make sure session is initalized
|
// session is not used here, the purpose is to make sure session is initalized
|
||||||
// before the webContents module.
|
// before the webContents module.
|
||||||
|
@ -23,8 +21,18 @@ const getNextId = function () {
|
||||||
return ++nextId;
|
return ++nextId;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/* eslint-disable camelcase */
|
||||||
|
type MediaSize = {
|
||||||
|
name: string,
|
||||||
|
custom_display_name: string,
|
||||||
|
height_microns: number,
|
||||||
|
width_microns: number,
|
||||||
|
is_default?: 'true',
|
||||||
|
}
|
||||||
|
/* eslint-enable camelcase */
|
||||||
|
|
||||||
// Stock page sizes
|
// Stock page sizes
|
||||||
const PDFPageSizes = {
|
const PDFPageSizes: Record<string, MediaSize> = {
|
||||||
A5: {
|
A5: {
|
||||||
custom_display_name: 'A5',
|
custom_display_name: 'A5',
|
||||||
height_microns: 210000,
|
height_microns: 210000,
|
||||||
|
@ -67,8 +75,8 @@ const PDFPageSizes = {
|
||||||
// Default printing setting
|
// Default printing setting
|
||||||
const defaultPrintingSetting = {
|
const defaultPrintingSetting = {
|
||||||
// Customizable.
|
// Customizable.
|
||||||
pageRange: [],
|
pageRange: [] as {from: number, to: number}[],
|
||||||
mediaSize: {},
|
mediaSize: {} as MediaSize,
|
||||||
landscape: false,
|
landscape: false,
|
||||||
headerFooterEnabled: false,
|
headerFooterEnabled: false,
|
||||||
marginsType: 0,
|
marginsType: 0,
|
||||||
|
@ -93,18 +101,18 @@ const defaultPrintingSetting = {
|
||||||
copies: 1,
|
copies: 1,
|
||||||
// 2 = color - see ColorModel in //printing/print_job_constants.h
|
// 2 = color - see ColorModel in //printing/print_job_constants.h
|
||||||
color: 2,
|
color: 2,
|
||||||
collate: true
|
collate: true,
|
||||||
|
printerType: 2,
|
||||||
|
title: undefined as string | undefined,
|
||||||
|
url: undefined as string | undefined
|
||||||
};
|
};
|
||||||
|
|
||||||
// JavaScript implementations of WebContents.
|
// JavaScript implementations of WebContents.
|
||||||
const binding = process._linkedBinding('electron_browser_web_contents');
|
const binding = process._linkedBinding('electron_browser_web_contents');
|
||||||
const { WebContents } = binding;
|
const { WebContents } = binding as { WebContents: { prototype: WebContentsInternal } };
|
||||||
|
|
||||||
Object.setPrototypeOf(NavigationController.prototype, EventEmitter.prototype);
|
Object.setPrototypeOf(WebContents.prototype, EventEmitter.prototype);
|
||||||
Object.setPrototypeOf(WebContents.prototype, NavigationController.prototype);
|
|
||||||
|
|
||||||
// WebContents::send(channel, args..)
|
|
||||||
// WebContents::sendToAll(channel, args..)
|
|
||||||
WebContents.prototype.send = function (channel, ...args) {
|
WebContents.prototype.send = function (channel, ...args) {
|
||||||
if (typeof channel !== 'string') {
|
if (typeof channel !== 'string') {
|
||||||
throw new Error('Missing required channel argument');
|
throw new Error('Missing required channel argument');
|
||||||
|
@ -123,17 +131,6 @@ WebContents.prototype.postMessage = function (...args) {
|
||||||
this._postMessage(...args);
|
this._postMessage(...args);
|
||||||
};
|
};
|
||||||
|
|
||||||
WebContents.prototype.sendToAll = function (channel, ...args) {
|
|
||||||
if (typeof channel !== 'string') {
|
|
||||||
throw new Error('Missing required channel argument');
|
|
||||||
}
|
|
||||||
|
|
||||||
const internal = false;
|
|
||||||
const sendToAll = true;
|
|
||||||
|
|
||||||
return this._send(internal, sendToAll, channel, args);
|
|
||||||
};
|
|
||||||
|
|
||||||
WebContents.prototype._sendInternal = function (channel, ...args) {
|
WebContents.prototype._sendInternal = function (channel, ...args) {
|
||||||
if (typeof channel !== 'string') {
|
if (typeof channel !== 'string') {
|
||||||
throw new Error('Missing required channel argument');
|
throw new Error('Missing required channel argument');
|
||||||
|
@ -185,15 +182,15 @@ const webFrameMethods = [
|
||||||
'insertText',
|
'insertText',
|
||||||
'removeInsertedCSS',
|
'removeInsertedCSS',
|
||||||
'setVisualZoomLevelLimits'
|
'setVisualZoomLevelLimits'
|
||||||
];
|
] as ('insertCSS' | 'insertText' | 'removeInsertedCSS' | 'setVisualZoomLevelLimits')[];
|
||||||
|
|
||||||
for (const method of webFrameMethods) {
|
for (const method of webFrameMethods) {
|
||||||
WebContents.prototype[method] = function (...args) {
|
WebContents.prototype[method] = function (...args: any[]): Promise<any> {
|
||||||
return ipcMainUtils.invokeInWebContents(this, false, 'ELECTRON_INTERNAL_RENDERER_WEB_FRAME_METHOD', method, ...args);
|
return ipcMainUtils.invokeInWebContents(this, false, 'ELECTRON_INTERNAL_RENDERER_WEB_FRAME_METHOD', method, ...args);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const waitTillCanExecuteJavaScript = async (webContents) => {
|
const waitTillCanExecuteJavaScript = async (webContents: WebContentsInternal) => {
|
||||||
if (webContents.getURL() && !webContents.isLoadingMainFrame()) return;
|
if (webContents.getURL() && !webContents.isLoadingMainFrame()) return;
|
||||||
|
|
||||||
return new Promise((resolve) => {
|
return new Promise((resolve) => {
|
||||||
|
@ -326,7 +323,7 @@ WebContents.prototype.printToPDF = function (options) {
|
||||||
height_microns: Math.ceil(pageSize.height),
|
height_microns: Math.ceil(pageSize.height),
|
||||||
width_microns: Math.ceil(pageSize.width)
|
width_microns: Math.ceil(pageSize.width)
|
||||||
};
|
};
|
||||||
} else if (PDFPageSizes[pageSize]) {
|
} else if (Object.prototype.hasOwnProperty.call(PDFPageSizes, pageSize)) {
|
||||||
printSettings.mediaSize = PDFPageSizes[pageSize];
|
printSettings.mediaSize = PDFPageSizes[pageSize];
|
||||||
} else {
|
} else {
|
||||||
const error = new Error(`Unsupported pageSize: ${pageSize}`);
|
const error = new Error(`Unsupported pageSize: ${pageSize}`);
|
||||||
|
@ -360,14 +357,14 @@ WebContents.prototype.print = function (options = {}, callback) {
|
||||||
throw new Error('height and width properties are required for pageSize');
|
throw new Error('height and width properties are required for pageSize');
|
||||||
}
|
}
|
||||||
// Dimensions in Microns - 1 meter = 10^6 microns
|
// Dimensions in Microns - 1 meter = 10^6 microns
|
||||||
options.mediaSize = {
|
(options as any).mediaSize = {
|
||||||
name: 'CUSTOM',
|
name: 'CUSTOM',
|
||||||
custom_display_name: 'Custom',
|
custom_display_name: 'Custom',
|
||||||
height_microns: Math.ceil(pageSize.height),
|
height_microns: Math.ceil(pageSize.height),
|
||||||
width_microns: Math.ceil(pageSize.width)
|
width_microns: Math.ceil(pageSize.width)
|
||||||
};
|
};
|
||||||
} else if (PDFPageSizes[pageSize]) {
|
} else if (PDFPageSizes[pageSize]) {
|
||||||
options.mediaSize = PDFPageSizes[pageSize];
|
(options as any).mediaSize = PDFPageSizes[pageSize];
|
||||||
} else {
|
} else {
|
||||||
throw new Error(`Unsupported pageSize: ${pageSize}`);
|
throw new Error(`Unsupported pageSize: ${pageSize}`);
|
||||||
}
|
}
|
||||||
|
@ -410,23 +407,23 @@ WebContents.prototype.loadFile = function (filePath, options = {}) {
|
||||||
}));
|
}));
|
||||||
};
|
};
|
||||||
|
|
||||||
const addReplyToEvent = (event) => {
|
const addReplyToEvent = (event: any) => {
|
||||||
event.reply = (...args) => {
|
event.reply = (...args: any[]) => {
|
||||||
event.sender.sendToFrame(event.frameId, ...args);
|
event.sender.sendToFrame(event.frameId, ...args);
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
const addReplyInternalToEvent = (event) => {
|
const addReplyInternalToEvent = (event: any) => {
|
||||||
Object.defineProperty(event, '_replyInternal', {
|
Object.defineProperty(event, '_replyInternal', {
|
||||||
configurable: false,
|
configurable: false,
|
||||||
enumerable: false,
|
enumerable: false,
|
||||||
value: (...args) => {
|
value: (...args: any[]) => {
|
||||||
event.sender._sendToFrameInternal(event.frameId, ...args);
|
event.sender._sendToFrameInternal(event.frameId, ...args);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const addReturnValueToEvent = (event) => {
|
const addReturnValueToEvent = (event: any) => {
|
||||||
Object.defineProperty(event, 'returnValue', {
|
Object.defineProperty(event, 'returnValue', {
|
||||||
set: (value) => event.sendReply([value]),
|
set: (value) => event.sendReply([value]),
|
||||||
get: () => {}
|
get: () => {}
|
||||||
|
@ -436,14 +433,30 @@ const addReturnValueToEvent = (event) => {
|
||||||
// Add JavaScript wrappers for WebContents class.
|
// Add JavaScript wrappers for WebContents class.
|
||||||
WebContents.prototype._init = function () {
|
WebContents.prototype._init = function () {
|
||||||
// The navigation controller.
|
// The navigation controller.
|
||||||
NavigationController.call(this, this);
|
const navigationController = new NavigationController(this);
|
||||||
|
this.loadURL = navigationController.loadURL.bind(navigationController);
|
||||||
|
this.getURL = navigationController.getURL.bind(navigationController);
|
||||||
|
this.stop = navigationController.stop.bind(navigationController);
|
||||||
|
this.reload = navigationController.reload.bind(navigationController);
|
||||||
|
this.reloadIgnoringCache = navigationController.reloadIgnoringCache.bind(navigationController);
|
||||||
|
this.canGoBack = navigationController.canGoBack.bind(navigationController);
|
||||||
|
this.canGoForward = navigationController.canGoForward.bind(navigationController);
|
||||||
|
this.canGoToIndex = navigationController.canGoToIndex.bind(navigationController);
|
||||||
|
this.canGoToOffset = navigationController.canGoToOffset.bind(navigationController);
|
||||||
|
this.clearHistory = navigationController.clearHistory.bind(navigationController);
|
||||||
|
this.goBack = navigationController.goBack.bind(navigationController);
|
||||||
|
this.goForward = navigationController.goForward.bind(navigationController);
|
||||||
|
this.goToIndex = navigationController.goToIndex.bind(navigationController);
|
||||||
|
this.goToOffset = navigationController.goToOffset.bind(navigationController);
|
||||||
|
this.getActiveIndex = navigationController.getActiveIndex.bind(navigationController);
|
||||||
|
this.length = navigationController.length.bind(navigationController);
|
||||||
|
|
||||||
// Every remote callback from renderer process would add a listener to the
|
// Every remote callback from renderer process would add a listener to the
|
||||||
// render-view-deleted event, so ignore the listeners warning.
|
// render-view-deleted event, so ignore the listeners warning.
|
||||||
this.setMaxListeners(0);
|
this.setMaxListeners(0);
|
||||||
|
|
||||||
// Dispatch IPC messages to the ipc module.
|
// Dispatch IPC messages to the ipc module.
|
||||||
this.on('-ipc-message', function (event, internal, channel, args) {
|
this.on('-ipc-message' as any, function (this: WebContentsInternal, event: any, internal: boolean, channel: string, args: any[]) {
|
||||||
if (internal) {
|
if (internal) {
|
||||||
addReplyInternalToEvent(event);
|
addReplyInternalToEvent(event);
|
||||||
ipcMainInternal.emit(channel, event, ...args);
|
ipcMainInternal.emit(channel, event, ...args);
|
||||||
|
@ -454,21 +467,21 @@ WebContents.prototype._init = function () {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
this.on('-ipc-invoke', function (event, internal, channel, args) {
|
this.on('-ipc-invoke' as any, function (event: any, internal: boolean, channel: string, args: any[]) {
|
||||||
event._reply = (result) => event.sendReply({ result });
|
event._reply = (result: any) => event.sendReply({ result });
|
||||||
event._throw = (error) => {
|
event._throw = (error: Error) => {
|
||||||
console.error(`Error occurred in handler for '${channel}':`, error);
|
console.error(`Error occurred in handler for '${channel}':`, error);
|
||||||
event.sendReply({ error: error.toString() });
|
event.sendReply({ error: error.toString() });
|
||||||
};
|
};
|
||||||
const target = internal ? ipcMainInternal : ipcMain;
|
const target = internal ? ipcMainInternal : ipcMain;
|
||||||
if (target._invokeHandlers.has(channel)) {
|
if ((target as any)._invokeHandlers.has(channel)) {
|
||||||
target._invokeHandlers.get(channel)(event, ...args);
|
(target as any)._invokeHandlers.get(channel)(event, ...args);
|
||||||
} else {
|
} else {
|
||||||
event._throw(`No handler registered for '${channel}'`);
|
event._throw(`No handler registered for '${channel}'`);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
this.on('-ipc-message-sync', function (event, internal, channel, args) {
|
this.on('-ipc-message-sync' as any, function (this: WebContentsInternal, event: any, internal: boolean, channel: string, args: any[]) {
|
||||||
addReturnValueToEvent(event);
|
addReturnValueToEvent(event);
|
||||||
if (internal) {
|
if (internal) {
|
||||||
addReplyInternalToEvent(event);
|
addReplyInternalToEvent(event);
|
||||||
|
@ -480,15 +493,15 @@ WebContents.prototype._init = function () {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
this.on('-ipc-ports', function (event, internal, channel, message, ports) {
|
this.on('-ipc-ports' as any, function (event: any, internal: boolean, channel: string, message: any, ports: any[]) {
|
||||||
event.ports = ports.map(p => new MessagePortMain(p));
|
event.ports = ports.map(p => new MessagePortMain(p));
|
||||||
ipcMain.emit(channel, event, message);
|
ipcMain.emit(channel, event, message);
|
||||||
});
|
});
|
||||||
|
|
||||||
// Handle context menu action request from pepper plugin.
|
// Handle context menu action request from pepper plugin.
|
||||||
this.on('pepper-context-menu', function (event, params, callback) {
|
this.on('pepper-context-menu' as any, function (event: any, params: {x: number, y: number, menu: Array<(MenuItemConstructorOptions) | (MenuItem)>}, callback: () => void) {
|
||||||
// Access Menu via electron.Menu to prevent circular require.
|
// Access Menu via electron.Menu to prevent circular require.
|
||||||
const menu = electron.Menu.buildFromTemplate(params.menu);
|
const menu = require('electron').Menu.buildFromTemplate(params.menu);
|
||||||
menu.popup({
|
menu.popup({
|
||||||
window: event.sender.getOwnerBrowserWindow(),
|
window: event.sender.getOwnerBrowserWindow(),
|
||||||
x: params.x,
|
x: params.x,
|
||||||
|
@ -506,14 +519,14 @@ WebContents.prototype._init = function () {
|
||||||
});
|
});
|
||||||
|
|
||||||
// The devtools requests the webContents to reload.
|
// The devtools requests the webContents to reload.
|
||||||
this.on('devtools-reload-page', function () {
|
this.on('devtools-reload-page', function (this: WebContentsInternal) {
|
||||||
this.reload();
|
this.reload();
|
||||||
});
|
});
|
||||||
|
|
||||||
if (this.getType() !== 'remote') {
|
if (this.getType() !== 'remote') {
|
||||||
// Make new windows requested by links behave like "window.open".
|
// Make new windows requested by links behave like "window.open".
|
||||||
this.on('-new-window', (event, url, frameName, disposition,
|
this.on('-new-window' as any, (event: any, url: string, frameName: string, disposition: string,
|
||||||
rawFeatures, referrer, postData) => {
|
rawFeatures: string, referrer: string, postData: string) => {
|
||||||
const { options, webPreferences, additionalFeatures } = parseFeatures(rawFeatures);
|
const { options, webPreferences, additionalFeatures } = parseFeatures(rawFeatures);
|
||||||
const mergedOptions = {
|
const mergedOptions = {
|
||||||
show: true,
|
show: true,
|
||||||
|
@ -529,9 +542,9 @@ WebContents.prototype._init = function () {
|
||||||
|
|
||||||
// Create a new browser window for the native implementation of
|
// Create a new browser window for the native implementation of
|
||||||
// "window.open", used in sandbox and nativeWindowOpen mode.
|
// "window.open", used in sandbox and nativeWindowOpen mode.
|
||||||
this.on('-add-new-contents', (event, webContents, disposition,
|
this.on('-add-new-contents' as any, (event: any, webContents: WebContentsInternal, disposition: string,
|
||||||
userGesture, left, top, width, height, url, frameName,
|
userGesture: boolean, left: number, top: number, width: number, height: number, url: string, frameName: string,
|
||||||
referrer, rawFeatures, postData) => {
|
referrer: string, rawFeatures: string, postData: string) => {
|
||||||
if ((disposition !== 'foreground-tab' && disposition !== 'new-window' &&
|
if ((disposition !== 'foreground-tab' && disposition !== 'new-window' &&
|
||||||
disposition !== 'background-tab')) {
|
disposition !== 'background-tab')) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
|
@ -554,7 +567,7 @@ WebContents.prototype._init = function () {
|
||||||
|
|
||||||
const prefs = this.getWebPreferences() || {};
|
const prefs = this.getWebPreferences() || {};
|
||||||
if (prefs.webviewTag && prefs.contextIsolation) {
|
if (prefs.webviewTag && prefs.contextIsolation) {
|
||||||
electron.deprecate.log('Security Warning: A WebContents was just created with both webviewTag and contextIsolation enabled. This combination is fundamentally less secure and effectively bypasses the protections of contextIsolation. We strongly recommend you move away from webviews to OOPIF or BrowserView in order for your app to be more secure');
|
deprecate.log('Security Warning: A WebContents was just created with both webviewTag and contextIsolation enabled. This combination is fundamentally less secure and effectively bypasses the protections of contextIsolation. We strongly recommend you move away from webviews to OOPIF or BrowserView in order for your app to be more secure');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -599,16 +612,15 @@ WebContents.prototype._init = function () {
|
||||||
};
|
};
|
||||||
|
|
||||||
// Public APIs.
|
// Public APIs.
|
||||||
module.exports = {
|
export function create (options = {}) {
|
||||||
create (options = {}) {
|
|
||||||
return binding.create(options);
|
return binding.create(options);
|
||||||
},
|
}
|
||||||
|
|
||||||
fromId (id) {
|
export function fromId (id: string) {
|
||||||
return binding.fromId(id);
|
return binding.fromId(id);
|
||||||
},
|
}
|
||||||
|
|
||||||
getFocusedWebContents () {
|
export function getFocusedWebContents () {
|
||||||
let focused = null;
|
let focused = null;
|
||||||
for (const contents of binding.getAllWebContents()) {
|
for (const contents of binding.getAllWebContents()) {
|
||||||
if (!contents.isFocused()) continue;
|
if (!contents.isFocused()) continue;
|
||||||
|
@ -618,9 +630,7 @@ module.exports = {
|
||||||
if (contents.getType() === 'webview') return contents;
|
if (contents.getType() === 'webview') return contents;
|
||||||
}
|
}
|
||||||
return focused;
|
return focused;
|
||||||
},
|
}
|
||||||
|
export function getAllWebContents () {
|
||||||
getAllWebContents () {
|
|
||||||
return binding.getAllWebContents();
|
return binding.getAllWebContents();
|
||||||
}
|
}
|
||||||
};
|
|
|
@ -56,7 +56,7 @@ const mergeBrowserWindowOptions = function (embedder, options) {
|
||||||
let parentOptions = embedder.browserWindowOptions;
|
let parentOptions = embedder.browserWindowOptions;
|
||||||
|
|
||||||
// if parent's visibility is available, that overrides 'show' flag (#12125)
|
// if parent's visibility is available, that overrides 'show' flag (#12125)
|
||||||
const win = BrowserWindow.fromWebContents(embedder.webContents);
|
const win = BrowserWindow.fromWebContents(embedder);
|
||||||
if (win != null) {
|
if (win != null) {
|
||||||
parentOptions = {
|
parentOptions = {
|
||||||
...win.getBounds(),
|
...win.getBounds(),
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
'use strict';
|
import { ipcMainInternal } from './ipc-main-internal';
|
||||||
|
import type { WebContents, LoadURLOptions } from 'electron/main';
|
||||||
const { ipcMainInternal } = require('@electron/internal/browser/ipc-main-internal');
|
import { EventEmitter } from 'events';
|
||||||
|
|
||||||
// The history operation in renderer is redirected to browser.
|
// The history operation in renderer is redirected to browser.
|
||||||
ipcMainInternal.on('ELECTRON_NAVIGATION_CONTROLLER_GO_BACK', function (event) {
|
ipcMainInternal.on('ELECTRON_NAVIGATION_CONTROLLER_GO_BACK', function (event) {
|
||||||
|
@ -16,7 +16,7 @@ ipcMainInternal.on('ELECTRON_NAVIGATION_CONTROLLER_GO_TO_OFFSET', function (even
|
||||||
});
|
});
|
||||||
|
|
||||||
ipcMainInternal.on('ELECTRON_NAVIGATION_CONTROLLER_LENGTH', function (event) {
|
ipcMainInternal.on('ELECTRON_NAVIGATION_CONTROLLER_LENGTH', function (event) {
|
||||||
event.returnValue = event.sender.length();
|
event.returnValue = (event.sender as any).length();
|
||||||
});
|
});
|
||||||
|
|
||||||
// JavaScript implementation of Chromium's NavigationController.
|
// JavaScript implementation of Chromium's NavigationController.
|
||||||
|
@ -24,9 +24,14 @@ ipcMainInternal.on('ELECTRON_NAVIGATION_CONTROLLER_LENGTH', function (event) {
|
||||||
// control on user land, and only rely on WebContents.loadURL for navigation.
|
// control on user land, and only rely on WebContents.loadURL for navigation.
|
||||||
// This helps us avoid Chromium's various optimizations so we can ensure renderer
|
// This helps us avoid Chromium's various optimizations so we can ensure renderer
|
||||||
// process is restarted everytime.
|
// process is restarted everytime.
|
||||||
const NavigationController = (function () {
|
export class NavigationController extends EventEmitter {
|
||||||
function NavigationController (webContents) {
|
currentIndex: number = -1;
|
||||||
this.webContents = webContents;
|
inPageIndex: number = -1;
|
||||||
|
pendingIndex: number = -1;
|
||||||
|
history: string[] = [];
|
||||||
|
|
||||||
|
constructor (private webContents: WebContents) {
|
||||||
|
super();
|
||||||
this.clearHistory();
|
this.clearHistory();
|
||||||
|
|
||||||
// webContents may have already navigated to a page.
|
// webContents may have already navigated to a page.
|
||||||
|
@ -34,7 +39,7 @@ const NavigationController = (function () {
|
||||||
this.currentIndex++;
|
this.currentIndex++;
|
||||||
this.history.push(this.webContents._getURL());
|
this.history.push(this.webContents._getURL());
|
||||||
}
|
}
|
||||||
this.webContents.on('navigation-entry-committed', (event, url, inPage, replaceEntry) => {
|
this.webContents.on('navigation-entry-committed' as any, (event: any, url: string, inPage: boolean, replaceEntry: boolean) => {
|
||||||
if (this.inPageIndex > -1 && !inPage) {
|
if (this.inPageIndex > -1 && !inPage) {
|
||||||
// Navigated to a new page, clear in-page mark.
|
// Navigated to a new page, clear in-page mark.
|
||||||
this.inPageIndex = -1;
|
this.inPageIndex = -1;
|
||||||
|
@ -59,16 +64,16 @@ const NavigationController = (function () {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
NavigationController.prototype.loadURL = function (url, options) {
|
loadURL (url: string, options?: LoadURLOptions): Promise<void> {
|
||||||
if (options == null) {
|
if (options == null) {
|
||||||
options = {};
|
options = {};
|
||||||
}
|
}
|
||||||
const p = new Promise((resolve, reject) => {
|
const p = new Promise<void>((resolve, reject) => {
|
||||||
const resolveAndCleanup = () => {
|
const resolveAndCleanup = () => {
|
||||||
removeListeners();
|
removeListeners();
|
||||||
resolve();
|
resolve();
|
||||||
};
|
};
|
||||||
const rejectAndCleanup = (errorCode, errorDescription, url) => {
|
const rejectAndCleanup = (errorCode: number, errorDescription: string, url: string) => {
|
||||||
const err = new Error(`${errorDescription} (${errorCode}) loading '${typeof url === 'string' ? url.substr(0, 2048) : url}'`);
|
const err = new Error(`${errorDescription} (${errorCode}) loading '${typeof url === 'string' ? url.substr(0, 2048) : url}'`);
|
||||||
Object.assign(err, { errno: errorCode, code: errorDescription, url });
|
Object.assign(err, { errno: errorCode, code: errorDescription, url });
|
||||||
removeListeners();
|
removeListeners();
|
||||||
|
@ -77,14 +82,14 @@ const NavigationController = (function () {
|
||||||
const finishListener = () => {
|
const finishListener = () => {
|
||||||
resolveAndCleanup();
|
resolveAndCleanup();
|
||||||
};
|
};
|
||||||
const failListener = (event, errorCode, errorDescription, validatedURL, isMainFrame, frameProcessId, frameRoutingId) => {
|
const failListener = (event: any, errorCode: number, errorDescription: string, validatedURL: string, isMainFrame: boolean) => {
|
||||||
if (isMainFrame) {
|
if (isMainFrame) {
|
||||||
rejectAndCleanup(errorCode, errorDescription, validatedURL);
|
rejectAndCleanup(errorCode, errorDescription, validatedURL);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let navigationStarted = false;
|
let navigationStarted = false;
|
||||||
const navigationListener = (event, url, isSameDocument, isMainFrame, frameProcessId, frameRoutingId, navigationId) => {
|
const navigationListener = (event: any, url: string, isSameDocument: boolean, isMainFrame: boolean) => {
|
||||||
if (isMainFrame) {
|
if (isMainFrame) {
|
||||||
if (navigationStarted && !isSameDocument) {
|
if (navigationStarted && !isSameDocument) {
|
||||||
// the webcontents has started another unrelated navigation in the
|
// the webcontents has started another unrelated navigation in the
|
||||||
|
@ -129,58 +134,58 @@ const NavigationController = (function () {
|
||||||
this.webContents._loadURL(url, options);
|
this.webContents._loadURL(url, options);
|
||||||
this.webContents.emit('load-url', url, options);
|
this.webContents.emit('load-url', url, options);
|
||||||
return p;
|
return p;
|
||||||
};
|
}
|
||||||
|
|
||||||
NavigationController.prototype.getURL = function () {
|
getURL () {
|
||||||
if (this.currentIndex === -1) {
|
if (this.currentIndex === -1) {
|
||||||
return '';
|
return '';
|
||||||
} else {
|
} else {
|
||||||
return this.history[this.currentIndex];
|
return this.history[this.currentIndex];
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
NavigationController.prototype.stop = function () {
|
stop () {
|
||||||
this.pendingIndex = -1;
|
this.pendingIndex = -1;
|
||||||
return this.webContents._stop();
|
return this.webContents._stop();
|
||||||
};
|
}
|
||||||
|
|
||||||
NavigationController.prototype.reload = function () {
|
reload () {
|
||||||
this.pendingIndex = this.currentIndex;
|
this.pendingIndex = this.currentIndex;
|
||||||
return this.webContents._loadURL(this.getURL(), {});
|
return this.webContents._loadURL(this.getURL(), {});
|
||||||
};
|
}
|
||||||
|
|
||||||
NavigationController.prototype.reloadIgnoringCache = function () {
|
reloadIgnoringCache () {
|
||||||
this.pendingIndex = this.currentIndex;
|
this.pendingIndex = this.currentIndex;
|
||||||
return this.webContents._loadURL(this.getURL(), {
|
return this.webContents._loadURL(this.getURL(), {
|
||||||
extraHeaders: 'pragma: no-cache\n',
|
extraHeaders: 'pragma: no-cache\n',
|
||||||
reloadIgnoringCache: true
|
reloadIgnoringCache: true
|
||||||
});
|
} as any);
|
||||||
};
|
}
|
||||||
|
|
||||||
NavigationController.prototype.canGoBack = function () {
|
canGoBack () {
|
||||||
return this.getActiveIndex() > 0;
|
return this.getActiveIndex() > 0;
|
||||||
};
|
}
|
||||||
|
|
||||||
NavigationController.prototype.canGoForward = function () {
|
canGoForward () {
|
||||||
return this.getActiveIndex() < this.history.length - 1;
|
return this.getActiveIndex() < this.history.length - 1;
|
||||||
};
|
}
|
||||||
|
|
||||||
NavigationController.prototype.canGoToIndex = function (index) {
|
canGoToIndex (index: number) {
|
||||||
return index >= 0 && index < this.history.length;
|
return index >= 0 && index < this.history.length;
|
||||||
};
|
}
|
||||||
|
|
||||||
NavigationController.prototype.canGoToOffset = function (offset) {
|
canGoToOffset (offset: number) {
|
||||||
return this.canGoToIndex(this.currentIndex + offset);
|
return this.canGoToIndex(this.currentIndex + offset);
|
||||||
};
|
}
|
||||||
|
|
||||||
NavigationController.prototype.clearHistory = function () {
|
clearHistory () {
|
||||||
this.history = [];
|
this.history = [];
|
||||||
this.currentIndex = -1;
|
this.currentIndex = -1;
|
||||||
this.pendingIndex = -1;
|
this.pendingIndex = -1;
|
||||||
this.inPageIndex = -1;
|
this.inPageIndex = -1;
|
||||||
};
|
}
|
||||||
|
|
||||||
NavigationController.prototype.goBack = function () {
|
goBack () {
|
||||||
if (!this.canGoBack()) {
|
if (!this.canGoBack()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -190,9 +195,9 @@ const NavigationController = (function () {
|
||||||
} else {
|
} else {
|
||||||
return this.webContents._loadURL(this.history[this.pendingIndex], {});
|
return this.webContents._loadURL(this.history[this.pendingIndex], {});
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
NavigationController.prototype.goForward = function () {
|
goForward () {
|
||||||
if (!this.canGoForward()) {
|
if (!this.canGoForward()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -202,17 +207,17 @@ const NavigationController = (function () {
|
||||||
} else {
|
} else {
|
||||||
return this.webContents._loadURL(this.history[this.pendingIndex], {});
|
return this.webContents._loadURL(this.history[this.pendingIndex], {});
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
NavigationController.prototype.goToIndex = function (index) {
|
goToIndex (index: number) {
|
||||||
if (!this.canGoToIndex(index)) {
|
if (!this.canGoToIndex(index)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.pendingIndex = index;
|
this.pendingIndex = index;
|
||||||
return this.webContents._loadURL(this.history[this.pendingIndex], {});
|
return this.webContents._loadURL(this.history[this.pendingIndex], {});
|
||||||
};
|
}
|
||||||
|
|
||||||
NavigationController.prototype.goToOffset = function (offset) {
|
goToOffset (offset: number) {
|
||||||
if (!this.canGoToOffset(offset)) {
|
if (!this.canGoToOffset(offset)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -223,21 +228,17 @@ const NavigationController = (function () {
|
||||||
} else {
|
} else {
|
||||||
return this.goToIndex(pendingIndex);
|
return this.goToIndex(pendingIndex);
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
NavigationController.prototype.getActiveIndex = function () {
|
getActiveIndex () {
|
||||||
if (this.pendingIndex === -1) {
|
if (this.pendingIndex === -1) {
|
||||||
return this.currentIndex;
|
return this.currentIndex;
|
||||||
} else {
|
} else {
|
||||||
return this.pendingIndex;
|
return this.pendingIndex;
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
NavigationController.prototype.length = function () {
|
length () {
|
||||||
return this.history.length;
|
return this.history.length;
|
||||||
};
|
}
|
||||||
|
}
|
||||||
return NavigationController;
|
|
||||||
})();
|
|
||||||
|
|
||||||
module.exports = NavigationController;
|
|
|
@ -84,7 +84,7 @@ describe('renderer nodeIntegrationInSubFrames', () => {
|
||||||
w.loadFile(path.resolve(__dirname, `fixtures/sub-frames/frame-container${fixtureSuffix}.html`));
|
w.loadFile(path.resolve(__dirname, `fixtures/sub-frames/frame-container${fixtureSuffix}.html`));
|
||||||
const details = await detailsPromise;
|
const details = await detailsPromise;
|
||||||
const senders = details.map(event => event[0].sender);
|
const senders = details.map(event => event[0].sender);
|
||||||
const isolatedGlobals = await Promise.all(senders.map(sender => sender.webContents.executeJavaScript('window.isolatedGlobal')));
|
const isolatedGlobals = await Promise.all(senders.map(sender => sender.executeJavaScript('window.isolatedGlobal')));
|
||||||
for (const result of isolatedGlobals) {
|
for (const result of isolatedGlobals) {
|
||||||
if (webPreferences.contextIsolation) {
|
if (webPreferences.contextIsolation) {
|
||||||
expect(result).to.be.undefined();
|
expect(result).to.be.undefined();
|
||||||
|
|
|
@ -12,7 +12,7 @@ app.whenReady().then(function () {
|
||||||
callback('Hello World!');
|
callback('Hello World!');
|
||||||
});
|
});
|
||||||
|
|
||||||
web.webContents.loadURL('test://abc/hello.txt');
|
web.loadURL('test://abc/hello.txt');
|
||||||
|
|
||||||
web.webContents.on('did-finish-load', () => app.quit());
|
web.on('did-finish-load', () => app.quit());
|
||||||
});
|
});
|
||||||
|
|
17
typings/internal-electron.d.ts
vendored
17
typings/internal-electron.d.ts
vendored
|
@ -31,7 +31,13 @@ declare namespace Electron {
|
||||||
|
|
||||||
interface WebContents {
|
interface WebContents {
|
||||||
_getURL(): string;
|
_getURL(): string;
|
||||||
|
_loadURL(url: string, options: Electron.LoadURLOptions): void;
|
||||||
|
_stop(): void;
|
||||||
|
_goBack(): void;
|
||||||
|
_goForward(): void;
|
||||||
|
_goToOffset(offset: number): void;
|
||||||
getOwnerBrowserWindow(): Electron.BrowserWindow;
|
getOwnerBrowserWindow(): Electron.BrowserWindow;
|
||||||
|
getWebPreferences(): Electron.WebPreferences;
|
||||||
getLastWebPreferences(): Electron.WebPreferences;
|
getLastWebPreferences(): Electron.WebPreferences;
|
||||||
_getPreloadPaths(): string[];
|
_getPreloadPaths(): string[];
|
||||||
equal(other: WebContents): boolean;
|
equal(other: WebContents): boolean;
|
||||||
|
@ -86,8 +92,19 @@ declare namespace Electron {
|
||||||
}
|
}
|
||||||
|
|
||||||
interface WebContentsInternal extends Electron.WebContents {
|
interface WebContentsInternal extends Electron.WebContents {
|
||||||
|
_send(internal: boolean, sendToAll: boolean, channel: string, args: any): boolean;
|
||||||
|
_sendToFrame(internal: boolean, sendToAll: boolean, frameId: number, channel: string, args: any): boolean;
|
||||||
|
_sendToFrameInternal(frameId: number, channel: string, args: any): boolean;
|
||||||
|
_postMessage(channel: string, message: any, transfer?: any[]): void;
|
||||||
_sendInternal(channel: string, ...args: any[]): void;
|
_sendInternal(channel: string, ...args: any[]): void;
|
||||||
_sendInternalToAll(channel: string, ...args: any[]): void;
|
_sendInternalToAll(channel: string, ...args: any[]): void;
|
||||||
|
_printToPDF(options: any): Promise<Buffer>;
|
||||||
|
_print(options: any, callback?: (success: boolean, failureReason: string) => void): void;
|
||||||
|
_getPrinters(): Electron.PrinterInfo[];
|
||||||
|
_init(): void;
|
||||||
|
canGoToIndex(index: number): boolean;
|
||||||
|
getActiveIndex(): number;
|
||||||
|
length(): number;
|
||||||
}
|
}
|
||||||
|
|
||||||
const deprecate: ElectronInternal.DeprecationUtil;
|
const deprecate: ElectronInternal.DeprecationUtil;
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue