asarfuckery/electronasar/canary/renderer/window-setup.js

247 lines
10 KiB
JavaScript

"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 <webview> 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;
}
});
}
};