"use strict"; var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; return c > 3 && r && Object.defineProperty(target, key, r), r; }; Object.defineProperty(exports, "__esModule", { value: true }); const ipc_renderer_internal_1 = require("@electron/internal/renderer/ipc-renderer-internal"); // This file implements the following APIs: // - window.history.back() // - window.history.forward() // - window.history.go() // - window.history.length // - window.open() // - window.opener.blur() // - window.opener.close() // - window.opener.eval() // - window.opener.focus() // - window.opener.location // - window.opener.print() // - window.opener.postMessage() // - window.prompt() // - document.hidden // - document.visibilityState const { defineProperty } = Object; // Helper function to resolve relative url. const a = window.document.createElement('a'); const resolveURL = function (url) { a.href = url; return a.href; }; // Use this method to ensure values expected as strings in the main process // are convertible to strings in the renderer process. This ensures exceptions // converting values to strings are thrown in this process. const toString = (value) => { return value != null ? `${value}` : value; }; const windowProxies = {}; const getOrCreateProxy = (guestId) => { let proxy = windowProxies[guestId]; if (proxy == null) { proxy = new BrowserWindowProxy(guestId); windowProxies[guestId] = proxy; } return proxy; }; const removeProxy = (guestId) => { delete windowProxies[guestId]; }; class LocationProxy { constructor(guestId) { // eslint will consider the constructor "useless" // unless we assign them in the body. It's fine, that's what // TS would do anyway. this.guestId = guestId; this.getGuestURL = this.getGuestURL.bind(this); } /** * Beware: This decorator will have the _prototype_ as the `target`. It defines properties * commonly found in URL on the LocationProxy. */ static ProxyProperty(target, propertyKey) { Object.defineProperty(target, propertyKey, { get: function () { const guestURL = this.getGuestURL(); const value = guestURL ? guestURL[propertyKey] : ''; return value === undefined ? '' : value; }, set: function (newVal) { const guestURL = this.getGuestURL(); if (guestURL) { // TypeScript doesn't want us to assign to read-only variables. // It's right, that's bad, but we're doing it anway. guestURL[propertyKey] = newVal; return ipc_renderer_internal_1.ipcRendererInternal.sendSync('ELECTRON_GUEST_WINDOW_MANAGER_WEB_CONTENTS_METHOD_SYNC', this.guestId, 'loadURL', guestURL.toString()); } } }); } toString() { return this.href; } getGuestURL() { const urlString = ipc_renderer_internal_1.ipcRendererInternal.sendSync('ELECTRON_GUEST_WINDOW_MANAGER_WEB_CONTENTS_METHOD_SYNC', this.guestId, 'getURL'); try { return new URL(urlString); } catch (e) { console.error('LocationProxy: failed to parse string', urlString, e); } return null; } } __decorate([ LocationProxy.ProxyProperty ], LocationProxy.prototype, "hash", void 0); __decorate([ LocationProxy.ProxyProperty ], LocationProxy.prototype, "href", void 0); __decorate([ LocationProxy.ProxyProperty ], LocationProxy.prototype, "host", void 0); __decorate([ LocationProxy.ProxyProperty ], LocationProxy.prototype, "hostname", void 0); __decorate([ LocationProxy.ProxyProperty ], LocationProxy.prototype, "origin", void 0); __decorate([ LocationProxy.ProxyProperty ], LocationProxy.prototype, "pathname", void 0); __decorate([ LocationProxy.ProxyProperty ], LocationProxy.prototype, "port", void 0); __decorate([ LocationProxy.ProxyProperty ], LocationProxy.prototype, "protocol", void 0); __decorate([ LocationProxy.ProxyProperty ], LocationProxy.prototype, "search", void 0); class BrowserWindowProxy { constructor(guestId) { this.closed = false; this.guestId = guestId; this._location = new LocationProxy(guestId); ipc_renderer_internal_1.ipcRendererInternal.once(`ELECTRON_GUEST_WINDOW_MANAGER_WINDOW_CLOSED_${guestId}`, () => { removeProxy(guestId); this.closed = true; }); } // TypeScript doesn't allow getters/accessors with different types, // so for now, we'll have to make do with an "any" in the mix. // https://github.com/Microsoft/TypeScript/issues/2521 get location() { return this._location; } set location(url) { url = resolveURL(url); ipc_renderer_internal_1.ipcRendererInternal.sendSync('ELECTRON_GUEST_WINDOW_MANAGER_WEB_CONTENTS_METHOD_SYNC', this.guestId, 'loadURL', url); } close() { ipc_renderer_internal_1.ipcRendererInternal.send('ELECTRON_GUEST_WINDOW_MANAGER_WINDOW_CLOSE', this.guestId); } focus() { ipc_renderer_internal_1.ipcRendererInternal.send('ELECTRON_GUEST_WINDOW_MANAGER_WINDOW_METHOD', this.guestId, 'focus'); } blur() { ipc_renderer_internal_1.ipcRendererInternal.send('ELECTRON_GUEST_WINDOW_MANAGER_WINDOW_METHOD', this.guestId, 'blur'); } print() { ipc_renderer_internal_1.ipcRendererInternal.send('ELECTRON_GUEST_WINDOW_MANAGER_WEB_CONTENTS_METHOD', this.guestId, 'print'); } postMessage(message, targetOrigin) { ipc_renderer_internal_1.ipcRendererInternal.send('ELECTRON_GUEST_WINDOW_MANAGER_WINDOW_POSTMESSAGE', this.guestId, message, toString(targetOrigin), window.location.origin); } eval(...args) { ipc_renderer_internal_1.ipcRendererInternal.send('ELECTRON_GUEST_WINDOW_MANAGER_WEB_CONTENTS_METHOD', this.guestId, 'executeJavaScript', ...args); } } exports.windowSetup = (guestInstanceId, openerId, isHiddenPage, usesNativeWindowOpen) => { if (guestInstanceId == null) { // Override default window.close. window.close = function () { ipc_renderer_internal_1.ipcRendererInternal.sendSync('ELECTRON_BROWSER_WINDOW_CLOSE'); }; } if (!usesNativeWindowOpen) { // Make the browser window or guest view emit "new-window" event. window.open = function (url, frameName, features) { if (url != null && url !== '') { url = resolveURL(url); } const guestId = ipc_renderer_internal_1.ipcRendererInternal.sendSync('ELECTRON_GUEST_WINDOW_MANAGER_WINDOW_OPEN', url, toString(frameName), toString(features)); if (guestId != null) { return getOrCreateProxy(guestId); } else { return null; } }; if (openerId != null) { window.opener = getOrCreateProxy(openerId); } } // But we do not support prompt(). window.prompt = function () { throw new Error('prompt() is and will not be supported.'); }; ipc_renderer_internal_1.ipcRendererInternal.on('ELECTRON_GUEST_WINDOW_POSTMESSAGE', function (_event, sourceId, message, sourceOrigin) { // Manually dispatch event instead of using postMessage because we also need to // set event.source. // // Why any? We can't construct a MessageEvent and we can't // use `as MessageEvent` because you're not supposed to override // data, origin, and source const event = document.createEvent('Event'); event.initEvent('message', false, false); event.data = message; event.origin = sourceOrigin; event.source = getOrCreateProxy(sourceId); window.dispatchEvent(event); }); window.history.back = function () { ipc_renderer_internal_1.ipcRendererInternal.send('ELECTRON_NAVIGATION_CONTROLLER_GO_BACK'); }; window.history.forward = function () { ipc_renderer_internal_1.ipcRendererInternal.send('ELECTRON_NAVIGATION_CONTROLLER_GO_FORWARD'); }; window.history.go = function (offset) { ipc_renderer_internal_1.ipcRendererInternal.send('ELECTRON_NAVIGATION_CONTROLLER_GO_TO_OFFSET', +offset); }; defineProperty(window.history, 'length', { get: function () { return ipc_renderer_internal_1.ipcRendererInternal.sendSync('ELECTRON_NAVIGATION_CONTROLLER_LENGTH'); } }); if (guestInstanceId != null) { // Webview `document.visibilityState` tracks window visibility (and ignores // the actual element visibility) for backwards compatibility. // See discussion in #9178. // // Note that this results in duplicate visibilitychange events (since // Chromium also fires them) and potentially incorrect visibility change. // We should reconsider this decision for Electron 2.0. let cachedVisibilityState = isHiddenPage ? 'hidden' : 'visible'; // Subscribe to visibilityState changes. ipc_renderer_internal_1.ipcRendererInternal.on('ELECTRON_GUEST_INSTANCE_VISIBILITY_CHANGE', function (_event, visibilityState) { if (cachedVisibilityState !== visibilityState) { cachedVisibilityState = visibilityState; document.dispatchEvent(new Event('visibilitychange')); } }); // Make document.hidden and document.visibilityState return the correct value. defineProperty(document, 'hidden', { get: function () { return cachedVisibilityState !== 'visible'; } }); defineProperty(document, 'visibilityState', { get: function () { return cachedVisibilityState; } }); } };