8bbdc224ac
* chore: bump chromium in DEPS to 7fb9778894d73378bff51087ce869ea5aa6e5d5d * chore: bump chromium in DEPS to 83da426e53d423f0530fc23433b6d2c4d0548442 * update patches * remove chromeos-only TtsControllerDelegate Refs: https://chromium-review.googlesource.com/c/chromium/src/+/2255314 * SharedUserScriptMaster -> SharedUserScriptManager Refs: https://chromium-review.googlesource.com/c/chromium/src/+/2258357 * avoid deprecated DISALLOW_COPY_AND_ASSIGN https://groups.google.com/a/chromium.org/forum/#!msg/cxx/qwH2hxaEjac/TUKq6eqfCwAJ * chore: bump chromium in DEPS to b2eaf9ff4e6b03267bf279583ea20ceb2b25e9d0 * update patches * rename GetHighContrastColorScheme -> GetPlatformHighContrastColorScheme Refs: https://chromium-review.googlesource.com/c/chromium/src/+/2250224 * remove vulkan info collection Refs: https://chromium-review.googlesource.com/c/chromium/src/+/2252818 * add max_xcode_version build var Refs: https://chromium-review.googlesource.com/c/chromium/src/+/2264867 * add missing headers * chore: bump chromium in DEPS to cded18ca1138f7e8efc904f077ddcca34f0135cf * update patches * add empty floc blocklist to BrowserProcessImpl Refs: https://chromium-review.googlesource.com/c/chromium/src/+/2240873 * chore: bump chromium in DEPS to f06602226cd80bf677b2ce013a94a2fb7f6ac58d * chore: bump chromium in DEPS to 747aa4bfc74fc6cf7f08ee72624cd69ae41ae28d * chore: bump chromium in DEPS to 31c0105e50fcc4e94de33e5c8602c755ace4a32b * chore: update patches * Reland "[base] Stop including check.h, notreached.h, etc. in logging.h" https://chromium-review.googlesource.com/c/chromium/src/+/2264297 * X11 and Ozone: make sure gfx::AcceleratedWidget to be uint32_t https://chromium-review.googlesource.com/c/chromium/src/+/2260554 * Move zygote from //services/service_manager back to //content https://chromium-review.googlesource.com/c/chromium/src/+/2252466 * chore: update v8 patches * [XProto] Remove usage of Shape extension https://chromium-review.googlesource.com/c/chromium/src/+/2262113 * fixup! add empty floc blocklist to BrowserProcessImpl * Require macOS 10.15.1 sdk https://chromium-review.googlesource.com/c/chromium/src/+/2238504 * Use newer Xcode version 11.5.0 * update src cache * chore: bump chromium in DEPS to 60a9883e35db3f6f91916f0878e88e1849c17b11 * chore: update patches * Reland "Reland "New toolchain for Windows 10 19041 SDK"" https://chromium-review.googlesource.com/c/chromium/src/+/2255527 * update patches * Convert raw NonClientFrameViews to unique_ptrs https://chromium-review.googlesource.com/c/chromium/src/+/2240417 * [printing] Move PrintHostMsg_DidPreviewDocument_Params to print.mojom https://chromium-review.googlesource.com/c/chromium/src/+/2257035 * chore: bump chromium in DEPS to 12c233c2a85bfa28fb279f390121ba681e52a71b * chore: update patches * Removing oppressive language for the directory chrome/browser/apps https://chromium-review.googlesource.com/c/chromium/src/+/2269822 * Inclusion: rename SpellcheckLanguageBlacklistPolicyHandler https://chromium-review.googlesource.com/c/chromium/src/+/2267646 * Clean up duplicate WebContents "is fullscreen" functions https://chromium-review.googlesource.com/c/chromium/src/+/2275148 * Adds icon loading service with sandbox for Windows. https://chromium-review.googlesource.com/c/chromium/src/+/1987273 * No more Vulkan info collection for UMA on Windows https://chromium-review.googlesource.com/c/chromium/src/+/2252818 * fix lint * chore: update buildflag conditions * chore: bump chromium in DEPS to a837d4c4230ace4f10b2768728f4044b7995dfa5 * update hunspell files * chore: update patches * Make content::FileSelectListener a RefCounted https://chromium-review.googlesource.com/c/chromium/src/+/2275338 * fix build failures on MAS * update patches * fixup! Reland "[base] Stop including check.h, notreached.h, etc. in logging.h" * fix build on windows * Check for GDI exhaustion if window creation fails https://chromium-review.googlesource.com/c/chromium/src/+/2244124 * chore: bump chromium in DEPS to 2c9b2a73be4ef9ec22d8b6da8e174cb80753f125 * chore: update patches * Network Service: Move DeleteCookiePredicate into public folder https://chromium-review.googlesource.com/c/chromium/src/+/2264186 * chore: bump chromium in DEPS to fa2606299bcc02c362528d26b5dcf8c8a0db0735 * chore: bump chromium in DEPS to d9c235d1227204dbae3708daae851573a3566b94 * chore: bump chromium in DEPS to 2f82c284243c035f49a747fd1ead6c44b4b31093 * chore: update patches * Move creating the LayerTreeSettings into blink. https://chromium-review.googlesource.com/c/chromium/src/+/2267720 * chore: bump chromium in DEPS to 914112f1d9af9e4974059dc403da62699a55550f * update patches * chore: bump chromium in DEPS to e0bc1ffae6393fc543a2da94c88167df75859b36 * refactor: match upstream print preview handling (#24452) * update patches * chore: bump chromium in DEPS to 0881423156abe084164b51ab58ce93a8bd380524 * update patches * update patches * give a type to pendingPromise * chore: bump chromium in DEPS to 11a8c1534b16d130075d378a64187360ba4e7820 * update patches * 2272609: Move //services/service_manager/sandbox to //sandbox/policy. https://chromium-review.googlesource.com/c/chromium/src/+/2272609 * update patches * fixup! 2272609: Move //services/service_manager/sandbox to //sandbox/policy. * fixup! 2272609: Move //services/service_manager/sandbox to //sandbox/policy. * 2264511: Cookies: Update SetCanonicalCookie to return CookieAccessResult https://chromium-review.googlesource.com/c/chromium/src/+/2264511 * chore: fix setAlwaysOnTop test The window must be visible for state to be updated properly. * Revert "Migrate modules/desktop_capture and modules/video_capture to webrtc::Mutex." https://webrtc-review.googlesource.com/c/src/+/179080 * update patches Co-authored-by: Andy Locascio <andy@slack-corp.com> Co-authored-by: deepak1556 <hop2deep@gmail.com> Co-authored-by: Samuel Attard <samuel.r.attard@gmail.com> Co-authored-by: John Kleinschmidt <jkleinsc@github.com> Co-authored-by: Electron Bot <anonymous@electronjs.org> Co-authored-by: Shelley Vohr <shelley.vohr@gmail.com> Co-authored-by: Jeremy Rose <nornagon@nornagon.net> Co-authored-by: Samuel Attard <marshallofsound@electronjs.org>
670 lines
22 KiB
TypeScript
670 lines
22 KiB
TypeScript
import { app, ipcMain, session, deprecate } from 'electron/main';
|
|
import type { MenuItem, MenuItemConstructorOptions } from 'electron/main';
|
|
|
|
import * as url from 'url';
|
|
import * as path from 'path';
|
|
import { internalWindowOpen } from '@electron/internal/browser/guest-window-manager';
|
|
import { NavigationController } from '@electron/internal/browser/navigation-controller';
|
|
import { ipcMainInternal } from '@electron/internal/browser/ipc-main-internal';
|
|
import * as ipcMainUtils from '@electron/internal/browser/ipc-main-internal-utils';
|
|
import { parseFeatures } from '@electron/internal/common/parse-features-string';
|
|
import { MessagePortMain } from '@electron/internal/browser/message-port-main';
|
|
import { EventEmitter } from 'events';
|
|
|
|
// session is not used here, the purpose is to make sure session is initalized
|
|
// before the webContents module.
|
|
// eslint-disable-next-line
|
|
session
|
|
|
|
let nextId = 0;
|
|
const getNextId = function () {
|
|
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
|
|
const PDFPageSizes: Record<string, MediaSize> = {
|
|
A5: {
|
|
custom_display_name: 'A5',
|
|
height_microns: 210000,
|
|
name: 'ISO_A5',
|
|
width_microns: 148000
|
|
},
|
|
A4: {
|
|
custom_display_name: 'A4',
|
|
height_microns: 297000,
|
|
name: 'ISO_A4',
|
|
is_default: 'true',
|
|
width_microns: 210000
|
|
},
|
|
A3: {
|
|
custom_display_name: 'A3',
|
|
height_microns: 420000,
|
|
name: 'ISO_A3',
|
|
width_microns: 297000
|
|
},
|
|
Legal: {
|
|
custom_display_name: 'Legal',
|
|
height_microns: 355600,
|
|
name: 'NA_LEGAL',
|
|
width_microns: 215900
|
|
},
|
|
Letter: {
|
|
custom_display_name: 'Letter',
|
|
height_microns: 279400,
|
|
name: 'NA_LETTER',
|
|
width_microns: 215900
|
|
},
|
|
Tabloid: {
|
|
height_microns: 431800,
|
|
name: 'NA_LEDGER',
|
|
width_microns: 279400,
|
|
custom_display_name: 'Tabloid'
|
|
}
|
|
};
|
|
|
|
// The minimum micron size Chromium accepts is that where:
|
|
// Per printing/units.h:
|
|
// * kMicronsPerInch - Length of an inch in 0.001mm unit.
|
|
// * kPointsPerInch - Length of an inch in CSS's 1pt unit.
|
|
//
|
|
// Formula: (kPointsPerInch / kMicronsPerInch) * size >= 1
|
|
//
|
|
// Practically, this means microns need to be > 352 microns.
|
|
// We therefore need to verify this or it will silently fail.
|
|
const isValidCustomPageSize = (width: number, height: number) => {
|
|
return [width, height].every(x => x > 352);
|
|
};
|
|
|
|
// Default printing setting
|
|
const defaultPrintingSetting = {
|
|
// Customizable.
|
|
pageRange: [] as {from: number, to: number}[],
|
|
mediaSize: {} as MediaSize,
|
|
landscape: false,
|
|
headerFooterEnabled: false,
|
|
marginsType: 0,
|
|
scaleFactor: 100,
|
|
shouldPrintBackgrounds: false,
|
|
shouldPrintSelectionOnly: false,
|
|
// Non-customizable.
|
|
printWithCloudPrint: false,
|
|
printWithPrivet: false,
|
|
printWithExtension: false,
|
|
pagesPerSheet: 1,
|
|
isFirstRequest: false,
|
|
previewUIID: 0,
|
|
previewModifiable: true,
|
|
printToPDF: true,
|
|
deviceName: 'Save as PDF',
|
|
generateDraftData: true,
|
|
dpiHorizontal: 72,
|
|
dpiVertical: 72,
|
|
rasterizePDF: false,
|
|
duplex: 0,
|
|
copies: 1,
|
|
// 2 = color - see ColorModel in //printing/print_job_constants.h
|
|
color: 2,
|
|
collate: true,
|
|
printerType: 2,
|
|
title: undefined as string | undefined,
|
|
url: undefined as string | undefined
|
|
};
|
|
|
|
// JavaScript implementations of WebContents.
|
|
const binding = process._linkedBinding('electron_browser_web_contents');
|
|
const { WebContents } = binding as { WebContents: { prototype: Electron.WebContentsInternal } };
|
|
|
|
Object.setPrototypeOf(WebContents.prototype, EventEmitter.prototype);
|
|
|
|
WebContents.prototype.send = function (channel, ...args) {
|
|
if (typeof channel !== 'string') {
|
|
throw new Error('Missing required channel argument');
|
|
}
|
|
|
|
const internal = false;
|
|
const sendToAll = false;
|
|
|
|
return this._send(internal, sendToAll, channel, args);
|
|
};
|
|
|
|
WebContents.prototype.postMessage = function (...args) {
|
|
if (Array.isArray(args[2])) {
|
|
args[2] = args[2].map(o => o instanceof MessagePortMain ? o._internalPort : o);
|
|
}
|
|
this._postMessage(...args);
|
|
};
|
|
|
|
WebContents.prototype._sendInternal = function (channel, ...args) {
|
|
if (typeof channel !== 'string') {
|
|
throw new Error('Missing required channel argument');
|
|
}
|
|
|
|
const internal = true;
|
|
const sendToAll = false;
|
|
|
|
return this._send(internal, sendToAll, channel, args);
|
|
};
|
|
WebContents.prototype._sendInternalToAll = function (channel, ...args) {
|
|
if (typeof channel !== 'string') {
|
|
throw new Error('Missing required channel argument');
|
|
}
|
|
|
|
const internal = true;
|
|
const sendToAll = true;
|
|
|
|
return this._send(internal, sendToAll, channel, args);
|
|
};
|
|
WebContents.prototype.sendToFrame = function (frameId, channel, ...args) {
|
|
if (typeof channel !== 'string') {
|
|
throw new Error('Missing required channel argument');
|
|
} else if (typeof frameId !== 'number') {
|
|
throw new Error('Missing required frameId argument');
|
|
}
|
|
|
|
const internal = false;
|
|
const sendToAll = false;
|
|
|
|
return this._sendToFrame(internal, sendToAll, frameId, channel, args);
|
|
};
|
|
WebContents.prototype._sendToFrameInternal = function (frameId, channel, ...args) {
|
|
if (typeof channel !== 'string') {
|
|
throw new Error('Missing required channel argument');
|
|
} else if (typeof frameId !== 'number') {
|
|
throw new Error('Missing required frameId argument');
|
|
}
|
|
|
|
const internal = true;
|
|
const sendToAll = false;
|
|
|
|
return this._sendToFrame(internal, sendToAll, frameId, channel, args);
|
|
};
|
|
|
|
// Following methods are mapped to webFrame.
|
|
const webFrameMethods = [
|
|
'insertCSS',
|
|
'insertText',
|
|
'removeInsertedCSS',
|
|
'setVisualZoomLevelLimits'
|
|
] as ('insertCSS' | 'insertText' | 'removeInsertedCSS' | 'setVisualZoomLevelLimits')[];
|
|
|
|
for (const method of webFrameMethods) {
|
|
WebContents.prototype[method] = function (...args: any[]): Promise<any> {
|
|
return ipcMainUtils.invokeInWebContents(this, false, 'ELECTRON_INTERNAL_RENDERER_WEB_FRAME_METHOD', method, ...args);
|
|
};
|
|
}
|
|
|
|
const waitTillCanExecuteJavaScript = async (webContents: Electron.WebContentsInternal) => {
|
|
if (webContents.getURL() && !webContents.isLoadingMainFrame()) return;
|
|
|
|
return new Promise((resolve) => {
|
|
webContents.once('did-stop-loading', () => {
|
|
resolve();
|
|
});
|
|
});
|
|
};
|
|
|
|
// Make sure WebContents::executeJavaScript would run the code only when the
|
|
// WebContents has been loaded.
|
|
WebContents.prototype.executeJavaScript = async function (code, hasUserGesture) {
|
|
await waitTillCanExecuteJavaScript(this);
|
|
return ipcMainUtils.invokeInWebContents(this, false, 'ELECTRON_INTERNAL_RENDERER_WEB_FRAME_METHOD', 'executeJavaScript', String(code), !!hasUserGesture);
|
|
};
|
|
WebContents.prototype.executeJavaScriptInIsolatedWorld = async function (worldId, code, hasUserGesture) {
|
|
await waitTillCanExecuteJavaScript(this);
|
|
return ipcMainUtils.invokeInWebContents(this, false, 'ELECTRON_INTERNAL_RENDERER_WEB_FRAME_METHOD', 'executeJavaScriptInIsolatedWorld', worldId, code, !!hasUserGesture);
|
|
};
|
|
|
|
// Translate the options of printToPDF.
|
|
|
|
let pendingPromise: Promise<any> | undefined;
|
|
WebContents.prototype.printToPDF = async function (options) {
|
|
const printSettings = {
|
|
...defaultPrintingSetting,
|
|
requestID: getNextId()
|
|
};
|
|
|
|
if (options.landscape !== undefined) {
|
|
if (typeof options.landscape !== 'boolean') {
|
|
const error = new Error('landscape must be a Boolean');
|
|
return Promise.reject(error);
|
|
}
|
|
printSettings.landscape = options.landscape;
|
|
}
|
|
|
|
if (options.scaleFactor !== undefined) {
|
|
if (typeof options.scaleFactor !== 'number') {
|
|
const error = new Error('scaleFactor must be a Number');
|
|
return Promise.reject(error);
|
|
}
|
|
printSettings.scaleFactor = options.scaleFactor;
|
|
}
|
|
|
|
if (options.marginsType !== undefined) {
|
|
if (typeof options.marginsType !== 'number') {
|
|
const error = new Error('marginsType must be a Number');
|
|
return Promise.reject(error);
|
|
}
|
|
printSettings.marginsType = options.marginsType;
|
|
}
|
|
|
|
if (options.printSelectionOnly !== undefined) {
|
|
if (typeof options.printSelectionOnly !== 'boolean') {
|
|
const error = new Error('printSelectionOnly must be a Boolean');
|
|
return Promise.reject(error);
|
|
}
|
|
printSettings.shouldPrintSelectionOnly = options.printSelectionOnly;
|
|
}
|
|
|
|
if (options.printBackground !== undefined) {
|
|
if (typeof options.printBackground !== 'boolean') {
|
|
const error = new Error('printBackground must be a Boolean');
|
|
return Promise.reject(error);
|
|
}
|
|
printSettings.shouldPrintBackgrounds = options.printBackground;
|
|
}
|
|
|
|
if (options.pageRanges !== undefined) {
|
|
const pageRanges = options.pageRanges;
|
|
if (!Object.prototype.hasOwnProperty.call(pageRanges, 'from') || !Object.prototype.hasOwnProperty.call(pageRanges, 'to')) {
|
|
const error = new Error('pageRanges must be an Object with \'from\' and \'to\' properties');
|
|
return Promise.reject(error);
|
|
}
|
|
|
|
if (typeof pageRanges.from !== 'number') {
|
|
const error = new Error('pageRanges.from must be a Number');
|
|
return Promise.reject(error);
|
|
}
|
|
|
|
if (typeof pageRanges.to !== 'number') {
|
|
const error = new Error('pageRanges.to must be a Number');
|
|
return Promise.reject(error);
|
|
}
|
|
|
|
// Chromium uses 1-based page ranges, so increment each by 1.
|
|
printSettings.pageRange = [{
|
|
from: pageRanges.from + 1,
|
|
to: pageRanges.to + 1
|
|
}];
|
|
}
|
|
|
|
if (options.headerFooter !== undefined) {
|
|
const headerFooter = options.headerFooter;
|
|
printSettings.headerFooterEnabled = true;
|
|
if (typeof headerFooter === 'object') {
|
|
if (!headerFooter.url || !headerFooter.title) {
|
|
const error = new Error('url and title properties are required for headerFooter');
|
|
return Promise.reject(error);
|
|
}
|
|
if (typeof headerFooter.title !== 'string') {
|
|
const error = new Error('headerFooter.title must be a String');
|
|
return Promise.reject(error);
|
|
}
|
|
printSettings.title = headerFooter.title;
|
|
|
|
if (typeof headerFooter.url !== 'string') {
|
|
const error = new Error('headerFooter.url must be a String');
|
|
return Promise.reject(error);
|
|
}
|
|
printSettings.url = headerFooter.url;
|
|
} else {
|
|
const error = new Error('headerFooter must be an Object');
|
|
return Promise.reject(error);
|
|
}
|
|
}
|
|
|
|
// Optionally set size for PDF.
|
|
if (options.pageSize !== undefined) {
|
|
const pageSize = options.pageSize;
|
|
if (typeof pageSize === 'object') {
|
|
if (!pageSize.height || !pageSize.width) {
|
|
const error = new Error('height and width properties are required for pageSize');
|
|
return Promise.reject(error);
|
|
}
|
|
|
|
// Dimensions in Microns - 1 meter = 10^6 microns
|
|
const height = Math.ceil(pageSize.height);
|
|
const width = Math.ceil(pageSize.width);
|
|
if (!isValidCustomPageSize(width, height)) {
|
|
const error = new Error('height and width properties must be minimum 352 microns.');
|
|
return Promise.reject(error);
|
|
}
|
|
|
|
printSettings.mediaSize = {
|
|
name: 'CUSTOM',
|
|
custom_display_name: 'Custom',
|
|
height_microns: height,
|
|
width_microns: width
|
|
};
|
|
} else if (Object.prototype.hasOwnProperty.call(PDFPageSizes, pageSize)) {
|
|
printSettings.mediaSize = PDFPageSizes[pageSize];
|
|
} else {
|
|
const error = new Error(`Unsupported pageSize: ${pageSize}`);
|
|
return Promise.reject(error);
|
|
}
|
|
} else {
|
|
printSettings.mediaSize = PDFPageSizes.A4;
|
|
}
|
|
|
|
// Chromium expects this in a 0-100 range number, not as float
|
|
printSettings.scaleFactor = Math.ceil(printSettings.scaleFactor) % 100;
|
|
// PrinterType enum from //printing/print_job_constants.h
|
|
printSettings.printerType = 2;
|
|
if (this._printToPDF) {
|
|
if (pendingPromise) {
|
|
pendingPromise = pendingPromise.then(() => this._printToPDF(printSettings));
|
|
} else {
|
|
pendingPromise = this._printToPDF(printSettings);
|
|
}
|
|
return pendingPromise;
|
|
} else {
|
|
const error = new Error('Printing feature is disabled');
|
|
return Promise.reject(error);
|
|
}
|
|
};
|
|
|
|
WebContents.prototype.print = function (options = {}, callback) {
|
|
// TODO(codebytere): deduplicate argument sanitization by moving rest of
|
|
// print param logic into new file shared between printToPDF and print
|
|
if (typeof options === 'object') {
|
|
// Optionally set size for PDF.
|
|
if (options.pageSize !== undefined) {
|
|
const pageSize = options.pageSize;
|
|
if (typeof pageSize === 'object') {
|
|
if (!pageSize.height || !pageSize.width) {
|
|
throw new Error('height and width properties are required for pageSize');
|
|
}
|
|
|
|
// Dimensions in Microns - 1 meter = 10^6 microns
|
|
const height = Math.ceil(pageSize.height);
|
|
const width = Math.ceil(pageSize.width);
|
|
if (!isValidCustomPageSize(width, height)) {
|
|
throw new Error('height and width properties must be minimum 352 microns.');
|
|
}
|
|
|
|
(options as any).mediaSize = {
|
|
name: 'CUSTOM',
|
|
custom_display_name: 'Custom',
|
|
height_microns: height,
|
|
width_microns: width
|
|
};
|
|
} else if (PDFPageSizes[pageSize]) {
|
|
(options as any).mediaSize = PDFPageSizes[pageSize];
|
|
} else {
|
|
throw new Error(`Unsupported pageSize: ${pageSize}`);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (this._print) {
|
|
if (callback) {
|
|
this._print(options, callback);
|
|
} else {
|
|
this._print(options);
|
|
}
|
|
} else {
|
|
console.error('Error: Printing feature is disabled.');
|
|
}
|
|
};
|
|
|
|
WebContents.prototype.getPrinters = function () {
|
|
if (this._getPrinters) {
|
|
return this._getPrinters();
|
|
} else {
|
|
console.error('Error: Printing feature is disabled.');
|
|
return [];
|
|
}
|
|
};
|
|
|
|
WebContents.prototype.loadFile = function (filePath, options = {}) {
|
|
if (typeof filePath !== 'string') {
|
|
throw new Error('Must pass filePath as a string');
|
|
}
|
|
const { query, search, hash } = options;
|
|
|
|
return this.loadURL(url.format({
|
|
protocol: 'file',
|
|
slashes: true,
|
|
pathname: path.resolve(app.getAppPath(), filePath),
|
|
query,
|
|
search,
|
|
hash
|
|
}));
|
|
};
|
|
|
|
const addReplyToEvent = (event: any) => {
|
|
event.reply = (...args: any[]) => {
|
|
event.sender.sendToFrame(event.frameId, ...args);
|
|
};
|
|
};
|
|
|
|
const addReplyInternalToEvent = (event: any) => {
|
|
Object.defineProperty(event, '_replyInternal', {
|
|
configurable: false,
|
|
enumerable: false,
|
|
value: (...args: any[]) => {
|
|
event.sender._sendToFrameInternal(event.frameId, ...args);
|
|
}
|
|
});
|
|
};
|
|
|
|
const addReturnValueToEvent = (event: any) => {
|
|
Object.defineProperty(event, 'returnValue', {
|
|
set: (value) => event.sendReply([value]),
|
|
get: () => {}
|
|
});
|
|
};
|
|
|
|
// Add JavaScript wrappers for WebContents class.
|
|
WebContents.prototype._init = function () {
|
|
// The navigation controller.
|
|
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
|
|
// render-view-deleted event, so ignore the listeners warning.
|
|
this.setMaxListeners(0);
|
|
|
|
// Dispatch IPC messages to the ipc module.
|
|
this.on('-ipc-message' as any, function (this: Electron.WebContentsInternal, event: any, internal: boolean, channel: string, args: any[]) {
|
|
if (internal) {
|
|
addReplyInternalToEvent(event);
|
|
ipcMainInternal.emit(channel, event, ...args);
|
|
} else {
|
|
addReplyToEvent(event);
|
|
this.emit('ipc-message', event, channel, ...args);
|
|
ipcMain.emit(channel, event, ...args);
|
|
}
|
|
});
|
|
|
|
this.on('-ipc-invoke' as any, function (event: any, internal: boolean, channel: string, args: any[]) {
|
|
event._reply = (result: any) => event.sendReply({ result });
|
|
event._throw = (error: Error) => {
|
|
console.error(`Error occurred in handler for '${channel}':`, error);
|
|
event.sendReply({ error: error.toString() });
|
|
};
|
|
const target = internal ? ipcMainInternal : ipcMain;
|
|
if ((target as any)._invokeHandlers.has(channel)) {
|
|
(target as any)._invokeHandlers.get(channel)(event, ...args);
|
|
} else {
|
|
event._throw(`No handler registered for '${channel}'`);
|
|
}
|
|
});
|
|
|
|
this.on('-ipc-message-sync' as any, function (this: Electron.WebContentsInternal, event: any, internal: boolean, channel: string, args: any[]) {
|
|
addReturnValueToEvent(event);
|
|
if (internal) {
|
|
addReplyInternalToEvent(event);
|
|
ipcMainInternal.emit(channel, event, ...args);
|
|
} else {
|
|
addReplyToEvent(event);
|
|
this.emit('ipc-message-sync', event, channel, ...args);
|
|
ipcMain.emit(channel, event, ...args);
|
|
}
|
|
});
|
|
|
|
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));
|
|
ipcMain.emit(channel, event, message);
|
|
});
|
|
|
|
// Handle context menu action request from pepper plugin.
|
|
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.
|
|
const menu = require('electron').Menu.buildFromTemplate(params.menu);
|
|
menu.popup({
|
|
window: event.sender.getOwnerBrowserWindow(),
|
|
x: params.x,
|
|
y: params.y,
|
|
callback
|
|
});
|
|
});
|
|
|
|
this.on('crashed', (event, ...args) => {
|
|
app.emit('renderer-process-crashed', event, this, ...args);
|
|
});
|
|
|
|
this.on('render-process-gone', (event, ...args) => {
|
|
app.emit('render-process-gone', event, this, ...args);
|
|
});
|
|
|
|
// The devtools requests the webContents to reload.
|
|
this.on('devtools-reload-page', function (this: Electron.WebContentsInternal) {
|
|
this.reload();
|
|
});
|
|
|
|
if (this.getType() !== 'remote') {
|
|
// Make new windows requested by links behave like "window.open".
|
|
this.on('-new-window' as any, (event: any, url: string, frameName: string, disposition: string,
|
|
rawFeatures: string, referrer: string, postData: string) => {
|
|
const { options, webPreferences, additionalFeatures } = parseFeatures(rawFeatures);
|
|
const mergedOptions = {
|
|
show: true,
|
|
width: 800,
|
|
height: 600,
|
|
title: frameName,
|
|
webPreferences,
|
|
...options
|
|
};
|
|
|
|
internalWindowOpen(event, url, referrer, frameName, disposition, mergedOptions, additionalFeatures, postData);
|
|
});
|
|
|
|
// Create a new browser window for the native implementation of
|
|
// "window.open", used in sandbox and nativeWindowOpen mode.
|
|
this.on('-add-new-contents' as any, (event: any, webContents: Electron.WebContentsInternal, disposition: string,
|
|
userGesture: boolean, left: number, top: number, width: number, height: number, url: string, frameName: string,
|
|
referrer: string, rawFeatures: string, postData: string) => {
|
|
if ((disposition !== 'foreground-tab' && disposition !== 'new-window' &&
|
|
disposition !== 'background-tab')) {
|
|
event.preventDefault();
|
|
return;
|
|
}
|
|
|
|
const { options, webPreferences, additionalFeatures } = parseFeatures(rawFeatures);
|
|
const mergedOptions = {
|
|
show: true,
|
|
width: 800,
|
|
height: 600,
|
|
webContents,
|
|
title: frameName,
|
|
webPreferences,
|
|
...options
|
|
};
|
|
|
|
internalWindowOpen(event, url, referrer, frameName, disposition, mergedOptions, additionalFeatures, postData);
|
|
});
|
|
|
|
const prefs = this.getWebPreferences() || {};
|
|
if (prefs.webviewTag && prefs.contextIsolation) {
|
|
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');
|
|
}
|
|
}
|
|
|
|
this.on('login', (event, ...args) => {
|
|
app.emit('login', event, this, ...args);
|
|
});
|
|
|
|
const event = process._linkedBinding('electron_browser_event').createEmpty();
|
|
app.emit('web-contents-created', event, this);
|
|
|
|
// Properties
|
|
|
|
Object.defineProperty(this, 'audioMuted', {
|
|
get: () => this.isAudioMuted(),
|
|
set: (muted) => this.setAudioMuted(muted)
|
|
});
|
|
|
|
Object.defineProperty(this, 'userAgent', {
|
|
get: () => this.getUserAgent(),
|
|
set: (agent) => this.setUserAgent(agent)
|
|
});
|
|
|
|
Object.defineProperty(this, 'zoomLevel', {
|
|
get: () => this.getZoomLevel(),
|
|
set: (level) => this.setZoomLevel(level)
|
|
});
|
|
|
|
Object.defineProperty(this, 'zoomFactor', {
|
|
get: () => this.getZoomFactor(),
|
|
set: (factor) => this.setZoomFactor(factor)
|
|
});
|
|
|
|
Object.defineProperty(this, 'frameRate', {
|
|
get: () => this.getFrameRate(),
|
|
set: (rate) => this.setFrameRate(rate)
|
|
});
|
|
|
|
Object.defineProperty(this, 'backgroundThrottling', {
|
|
get: () => this.getBackgroundThrottling(),
|
|
set: (allowed) => this.setBackgroundThrottling(allowed)
|
|
});
|
|
};
|
|
|
|
// Public APIs.
|
|
export function create (options = {}) {
|
|
return binding.create(options);
|
|
}
|
|
|
|
export function fromId (id: string) {
|
|
return binding.fromId(id);
|
|
}
|
|
|
|
export function getFocusedWebContents () {
|
|
let focused = null;
|
|
for (const contents of binding.getAllWebContents()) {
|
|
if (!contents.isFocused()) continue;
|
|
if (focused == null) focused = contents;
|
|
// Return webview web contents which may be embedded inside another
|
|
// web contents that is also reporting as focused
|
|
if (contents.getType() === 'webview') return contents;
|
|
}
|
|
return focused;
|
|
}
|
|
export function getAllWebContents () {
|
|
return binding.getAllWebContents();
|
|
}
|