diff --git a/filenames.auto.gni b/filenames.auto.gni index d8baa35249d6e..b31eb37f01fbd 100644 --- a/filenames.auto.gni +++ b/filenames.auto.gni @@ -222,7 +222,7 @@ auto_filenames = { "lib/browser/api/view.ts", "lib/browser/api/views/image-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/default-menu.ts", "lib/browser/desktop-capturer.ts", @@ -234,7 +234,7 @@ auto_filenames = { "lib/browser/ipc-main-internal-utils.ts", "lib/browser/ipc-main-internal.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/server.ts", "lib/browser/rpc-server.ts", diff --git a/lib/browser/api/web-contents.js b/lib/browser/api/web-contents.ts similarity index 73% rename from lib/browser/api/web-contents.js rename to lib/browser/api/web-contents.ts index 553bd10ed74e2..eac2537925e03 100644 --- a/lib/browser/api/web-contents.js +++ b/lib/browser/api/web-contents.ts @@ -1,17 +1,15 @@ -'use strict'; - -const { EventEmitter } = require('events'); -const electron = require('electron'); -const path = require('path'); -const url = require('url'); -const { app, ipcMain, session } = electron; - -const { internalWindowOpen } = require('@electron/internal/browser/guest-window-manager'); -const NavigationController = require('@electron/internal/browser/navigation-controller'); -const { ipcMainInternal } = require('@electron/internal/browser/ipc-main-internal'); -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'); +import { app, ipcMain, session, deprecate } from 'electron'; +import type { MenuItem, MenuItemConstructorOptions, WebContentsInternal } from 'electron'; + +import * as url from 'url'; +import * as path from 'path'; +import { internalWindowOpen } from '../guest-window-manager'; +import { NavigationController } from '../navigation-controller'; +import { ipcMainInternal } from '../ipc-main-internal'; +import * as ipcMainUtils from '../ipc-main-internal-utils'; +import { parseFeatures } from '../../common/parse-features-string'; +import { MessagePortMain } from '../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. @@ -23,8 +21,18 @@ 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 = { +const PDFPageSizes: Record = { A5: { custom_display_name: 'A5', height_microns: 210000, @@ -67,8 +75,8 @@ const PDFPageSizes = { // Default printing setting const defaultPrintingSetting = { // Customizable. - pageRange: [], - mediaSize: {}, + pageRange: [] as {from: number, to: number}[], + mediaSize: {} as MediaSize, landscape: false, headerFooterEnabled: false, marginsType: 0, @@ -93,18 +101,18 @@ const defaultPrintingSetting = { copies: 1, // 2 = color - see ColorModel in //printing/print_job_constants.h color: 2, - collate: true + 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; +const { WebContents } = binding as { WebContents: { prototype: WebContentsInternal } }; -Object.setPrototypeOf(NavigationController.prototype, EventEmitter.prototype); -Object.setPrototypeOf(WebContents.prototype, NavigationController.prototype); +Object.setPrototypeOf(WebContents.prototype, EventEmitter.prototype); -// WebContents::send(channel, args..) -// WebContents::sendToAll(channel, args..) WebContents.prototype.send = function (channel, ...args) { if (typeof channel !== 'string') { throw new Error('Missing required channel argument'); @@ -123,17 +131,6 @@ WebContents.prototype.postMessage = function (...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) { if (typeof channel !== 'string') { throw new Error('Missing required channel argument'); @@ -185,15 +182,15 @@ const webFrameMethods = [ 'insertText', 'removeInsertedCSS', 'setVisualZoomLevelLimits' -]; +] as ('insertCSS' | 'insertText' | 'removeInsertedCSS' | 'setVisualZoomLevelLimits')[]; for (const method of webFrameMethods) { - WebContents.prototype[method] = function (...args) { + WebContents.prototype[method] = function (...args: any[]): Promise { 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; return new Promise((resolve) => { @@ -326,7 +323,7 @@ WebContents.prototype.printToPDF = function (options) { height_microns: Math.ceil(pageSize.height), width_microns: Math.ceil(pageSize.width) }; - } else if (PDFPageSizes[pageSize]) { + } else if (Object.prototype.hasOwnProperty.call(PDFPageSizes, pageSize)) { printSettings.mediaSize = PDFPageSizes[pageSize]; } else { 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'); } // Dimensions in Microns - 1 meter = 10^6 microns - options.mediaSize = { + (options as any).mediaSize = { name: 'CUSTOM', custom_display_name: 'Custom', height_microns: Math.ceil(pageSize.height), width_microns: Math.ceil(pageSize.width) }; } else if (PDFPageSizes[pageSize]) { - options.mediaSize = PDFPageSizes[pageSize]; + (options as any).mediaSize = PDFPageSizes[pageSize]; } else { throw new Error(`Unsupported pageSize: ${pageSize}`); } @@ -410,23 +407,23 @@ WebContents.prototype.loadFile = function (filePath, options = {}) { })); }; -const addReplyToEvent = (event) => { - event.reply = (...args) => { +const addReplyToEvent = (event: any) => { + event.reply = (...args: any[]) => { event.sender.sendToFrame(event.frameId, ...args); }; }; -const addReplyInternalToEvent = (event) => { +const addReplyInternalToEvent = (event: any) => { Object.defineProperty(event, '_replyInternal', { configurable: false, enumerable: false, - value: (...args) => { + value: (...args: any[]) => { event.sender._sendToFrameInternal(event.frameId, ...args); } }); }; -const addReturnValueToEvent = (event) => { +const addReturnValueToEvent = (event: any) => { Object.defineProperty(event, 'returnValue', { set: (value) => event.sendReply([value]), get: () => {} @@ -436,14 +433,30 @@ const addReturnValueToEvent = (event) => { // Add JavaScript wrappers for WebContents class. WebContents.prototype._init = function () { // 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 // render-view-deleted event, so ignore the listeners warning. this.setMaxListeners(0); // 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) { addReplyInternalToEvent(event); ipcMainInternal.emit(channel, event, ...args); @@ -454,21 +467,21 @@ WebContents.prototype._init = function () { } }); - this.on('-ipc-invoke', function (event, internal, channel, args) { - event._reply = (result) => event.sendReply({ result }); - event._throw = (error) => { + 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._invokeHandlers.has(channel)) { - target._invokeHandlers.get(channel)(event, ...args); + 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', 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); if (internal) { 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)); ipcMain.emit(channel, event, message); }); // 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. - const menu = electron.Menu.buildFromTemplate(params.menu); + const menu = require('electron').Menu.buildFromTemplate(params.menu); menu.popup({ window: event.sender.getOwnerBrowserWindow(), x: params.x, @@ -506,14 +519,14 @@ WebContents.prototype._init = function () { }); // The devtools requests the webContents to reload. - this.on('devtools-reload-page', function () { + this.on('devtools-reload-page', function (this: WebContentsInternal) { this.reload(); }); if (this.getType() !== 'remote') { // Make new windows requested by links behave like "window.open". - this.on('-new-window', (event, url, frameName, disposition, - rawFeatures, referrer, postData) => { + 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, @@ -529,9 +542,9 @@ WebContents.prototype._init = function () { // Create a new browser window for the native implementation of // "window.open", used in sandbox and nativeWindowOpen mode. - this.on('-add-new-contents', (event, webContents, disposition, - userGesture, left, top, width, height, url, frameName, - referrer, rawFeatures, postData) => { + this.on('-add-new-contents' as any, (event: any, webContents: 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(); @@ -554,7 +567,7 @@ WebContents.prototype._init = function () { const prefs = this.getWebPreferences() || {}; 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,28 +612,25 @@ WebContents.prototype._init = function () { }; // Public APIs. -module.exports = { - create (options = {}) { - return binding.create(options); - }, - - fromId (id) { - return binding.fromId(id); - }, +export function create (options = {}) { + return binding.create(options); +} - 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 fromId (id: string) { + return binding.fromId(id); +} - getAllWebContents () { - return binding.getAllWebContents(); +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(); +} diff --git a/lib/browser/guest-window-manager.js b/lib/browser/guest-window-manager.js index 9d14fbe637156..008714917507c 100644 --- a/lib/browser/guest-window-manager.js +++ b/lib/browser/guest-window-manager.js @@ -56,7 +56,7 @@ const mergeBrowserWindowOptions = function (embedder, options) { let parentOptions = embedder.browserWindowOptions; // 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) { parentOptions = { ...win.getBounds(), diff --git a/lib/browser/navigation-controller.js b/lib/browser/navigation-controller.ts similarity index 78% rename from lib/browser/navigation-controller.js rename to lib/browser/navigation-controller.ts index b80e8cf656936..6632ec9855cd6 100644 --- a/lib/browser/navigation-controller.js +++ b/lib/browser/navigation-controller.ts @@ -1,6 +1,6 @@ -'use strict'; - -const { ipcMainInternal } = require('@electron/internal/browser/ipc-main-internal'); +import { ipcMainInternal } from './ipc-main-internal'; +import type { WebContents, LoadURLOptions } from 'electron/main'; +import { EventEmitter } from 'events'; // The history operation in renderer is redirected to browser. 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) { - event.returnValue = event.sender.length(); + event.returnValue = (event.sender as any).length(); }); // 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. // This helps us avoid Chromium's various optimizations so we can ensure renderer // process is restarted everytime. -const NavigationController = (function () { - function NavigationController (webContents) { - this.webContents = webContents; +export class NavigationController extends EventEmitter { + currentIndex: number = -1; + inPageIndex: number = -1; + pendingIndex: number = -1; + history: string[] = []; + + constructor (private webContents: WebContents) { + super(); this.clearHistory(); // webContents may have already navigated to a page. @@ -34,7 +39,7 @@ const NavigationController = (function () { this.currentIndex++; 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) { // Navigated to a new page, clear in-page mark. this.inPageIndex = -1; @@ -59,16 +64,16 @@ const NavigationController = (function () { }); } - NavigationController.prototype.loadURL = function (url, options) { + loadURL (url: string, options?: LoadURLOptions): Promise { if (options == null) { options = {}; } - const p = new Promise((resolve, reject) => { + const p = new Promise((resolve, reject) => { const resolveAndCleanup = () => { removeListeners(); 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}'`); Object.assign(err, { errno: errorCode, code: errorDescription, url }); removeListeners(); @@ -77,14 +82,14 @@ const NavigationController = (function () { const finishListener = () => { resolveAndCleanup(); }; - const failListener = (event, errorCode, errorDescription, validatedURL, isMainFrame, frameProcessId, frameRoutingId) => { + const failListener = (event: any, errorCode: number, errorDescription: string, validatedURL: string, isMainFrame: boolean) => { if (isMainFrame) { rejectAndCleanup(errorCode, errorDescription, validatedURL); } }; 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 (navigationStarted && !isSameDocument) { // the webcontents has started another unrelated navigation in the @@ -129,58 +134,58 @@ const NavigationController = (function () { this.webContents._loadURL(url, options); this.webContents.emit('load-url', url, options); return p; - }; + } - NavigationController.prototype.getURL = function () { + getURL () { if (this.currentIndex === -1) { return ''; } else { return this.history[this.currentIndex]; } - }; + } - NavigationController.prototype.stop = function () { + stop () { this.pendingIndex = -1; return this.webContents._stop(); - }; + } - NavigationController.prototype.reload = function () { + reload () { this.pendingIndex = this.currentIndex; return this.webContents._loadURL(this.getURL(), {}); - }; + } - NavigationController.prototype.reloadIgnoringCache = function () { + reloadIgnoringCache () { this.pendingIndex = this.currentIndex; return this.webContents._loadURL(this.getURL(), { extraHeaders: 'pragma: no-cache\n', reloadIgnoringCache: true - }); - }; + } as any); + } - NavigationController.prototype.canGoBack = function () { + canGoBack () { return this.getActiveIndex() > 0; - }; + } - NavigationController.prototype.canGoForward = function () { + canGoForward () { return this.getActiveIndex() < this.history.length - 1; - }; + } - NavigationController.prototype.canGoToIndex = function (index) { + canGoToIndex (index: number) { return index >= 0 && index < this.history.length; - }; + } - NavigationController.prototype.canGoToOffset = function (offset) { + canGoToOffset (offset: number) { return this.canGoToIndex(this.currentIndex + offset); - }; + } - NavigationController.prototype.clearHistory = function () { + clearHistory () { this.history = []; this.currentIndex = -1; this.pendingIndex = -1; this.inPageIndex = -1; - }; + } - NavigationController.prototype.goBack = function () { + goBack () { if (!this.canGoBack()) { return; } @@ -190,9 +195,9 @@ const NavigationController = (function () { } else { return this.webContents._loadURL(this.history[this.pendingIndex], {}); } - }; + } - NavigationController.prototype.goForward = function () { + goForward () { if (!this.canGoForward()) { return; } @@ -202,17 +207,17 @@ const NavigationController = (function () { } else { return this.webContents._loadURL(this.history[this.pendingIndex], {}); } - }; + } - NavigationController.prototype.goToIndex = function (index) { + goToIndex (index: number) { if (!this.canGoToIndex(index)) { return; } this.pendingIndex = index; return this.webContents._loadURL(this.history[this.pendingIndex], {}); - }; + } - NavigationController.prototype.goToOffset = function (offset) { + goToOffset (offset: number) { if (!this.canGoToOffset(offset)) { return; } @@ -223,21 +228,17 @@ const NavigationController = (function () { } else { return this.goToIndex(pendingIndex); } - }; + } - NavigationController.prototype.getActiveIndex = function () { + getActiveIndex () { if (this.pendingIndex === -1) { return this.currentIndex; } else { return this.pendingIndex; } - }; + } - NavigationController.prototype.length = function () { + length () { return this.history.length; - }; - - return NavigationController; -})(); - -module.exports = NavigationController; + } +} diff --git a/spec-main/api-subframe-spec.ts b/spec-main/api-subframe-spec.ts index e5623d79a40b5..eb3c8b5b0a66a 100644 --- a/spec-main/api-subframe-spec.ts +++ b/spec-main/api-subframe-spec.ts @@ -84,7 +84,7 @@ describe('renderer nodeIntegrationInSubFrames', () => { w.loadFile(path.resolve(__dirname, `fixtures/sub-frames/frame-container${fixtureSuffix}.html`)); const details = await detailsPromise; 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) { if (webPreferences.contextIsolation) { expect(result).to.be.undefined(); diff --git a/spec-main/fixtures/api/custom-protocol-shutdown.js b/spec-main/fixtures/api/custom-protocol-shutdown.js index 1cf6ddbda5bc7..b59a936a61232 100644 --- a/spec-main/fixtures/api/custom-protocol-shutdown.js +++ b/spec-main/fixtures/api/custom-protocol-shutdown.js @@ -12,7 +12,7 @@ app.whenReady().then(function () { 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()); }); diff --git a/typings/internal-electron.d.ts b/typings/internal-electron.d.ts index 2f6384e7b6814..00be2ab3d0518 100644 --- a/typings/internal-electron.d.ts +++ b/typings/internal-electron.d.ts @@ -31,7 +31,13 @@ declare namespace Electron { interface WebContents { _getURL(): string; + _loadURL(url: string, options: Electron.LoadURLOptions): void; + _stop(): void; + _goBack(): void; + _goForward(): void; + _goToOffset(offset: number): void; getOwnerBrowserWindow(): Electron.BrowserWindow; + getWebPreferences(): Electron.WebPreferences; getLastWebPreferences(): Electron.WebPreferences; _getPreloadPaths(): string[]; equal(other: WebContents): boolean; @@ -86,8 +92,19 @@ declare namespace Electron { } 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; _sendInternalToAll(channel: string, ...args: any[]): void; + _printToPDF(options: any): Promise; + _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;