зеркало из https://github.com/electron/electron.git
refactor: Port window-setup to TS (#16894)
* refactor: Port window-setup to TS * refactor: Make the linter happy * refactor: Sneaky little TS error * refactor: Correctly import window-setup * refactor: Implement feedback <3 * refactor: Allow decorators in TS * refactor: Use named windowSetup in isolatedRenderer * refactor: Help TS understand * refactor: Welp, use createEvent again * refactor: Use the correct target in the decorator
This commit is contained in:
Родитель
2492f0bcac
Коммит
6cd75744ef
|
@ -72,7 +72,7 @@ filenames = {
|
|||
"lib/renderer/remote.js",
|
||||
"lib/renderer/security-warnings.js",
|
||||
"lib/renderer/web-frame-init.js",
|
||||
"lib/renderer/window-setup.js",
|
||||
"lib/renderer/window-setup.ts",
|
||||
"lib/renderer/web-view/guest-view-internal.js",
|
||||
"lib/renderer/web-view/web-view-attributes.js",
|
||||
"lib/renderer/web-view/web-view-constants.js",
|
||||
|
|
|
@ -18,5 +18,6 @@ const isolatedWorldArgs = v8Util.getHiddenValue(isolatedWorld, 'isolated-world-a
|
|||
|
||||
if (isolatedWorldArgs) {
|
||||
const { ipcRenderer, guestInstanceId, isHiddenPage, openerId, usesNativeWindowOpen } = isolatedWorldArgs
|
||||
require('@electron/internal/renderer/window-setup')(ipcRenderer, guestInstanceId, openerId, isHiddenPage, usesNativeWindowOpen)
|
||||
const { windowSetup } = require('@electron/internal/renderer/window-setup')
|
||||
windowSetup(ipcRenderer, guestInstanceId, openerId, isHiddenPage, usesNativeWindowOpen)
|
||||
}
|
||||
|
|
|
@ -73,7 +73,8 @@ switch (window.location.protocol) {
|
|||
break
|
||||
default: {
|
||||
// Override default web functions.
|
||||
require('@electron/internal/renderer/window-setup')(ipcRenderer, guestInstanceId, openerId, isHiddenPage, usesNativeWindowOpen)
|
||||
const { windowSetup } = require('@electron/internal/renderer/window-setup')
|
||||
windowSetup(ipcRenderer, guestInstanceId, openerId, isHiddenPage, usesNativeWindowOpen)
|
||||
|
||||
// Inject content scripts.
|
||||
if (process.isMainFrame) {
|
||||
|
|
|
@ -1,239 +0,0 @@
|
|||
'use strict'
|
||||
|
||||
// This file should have no requires since it is used by the isolated context
|
||||
// preload bundle. Instead arguments should be passed in for everything it
|
||||
// needs.
|
||||
|
||||
// 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, defineProperties } = 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 = (ipcRenderer, guestId) => {
|
||||
let proxy = windowProxies[guestId]
|
||||
if (proxy == null) {
|
||||
proxy = new BrowserWindowProxy(ipcRenderer, guestId)
|
||||
windowProxies[guestId] = proxy
|
||||
}
|
||||
return proxy
|
||||
}
|
||||
|
||||
const removeProxy = (guestId) => {
|
||||
delete windowProxies[guestId]
|
||||
}
|
||||
|
||||
function LocationProxy (ipcRenderer, guestId) {
|
||||
const getGuestURL = function () {
|
||||
const urlString = ipcRenderer.sendSync('ELECTRON_GUEST_WINDOW_MANAGER_WEB_CONTENTS_METHOD_SYNC', guestId, 'getURL')
|
||||
try {
|
||||
return new URL(urlString)
|
||||
} catch (e) {
|
||||
console.error('LocationProxy: failed to parse string', urlString, e)
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
const propertyProxyFor = function (property) {
|
||||
return {
|
||||
get: function () {
|
||||
const guestURL = getGuestURL()
|
||||
const value = guestURL ? guestURL[property] : ''
|
||||
return value === undefined ? '' : value
|
||||
},
|
||||
set: function (newVal) {
|
||||
const guestURL = getGuestURL()
|
||||
if (guestURL) {
|
||||
guestURL[property] = newVal
|
||||
return ipcRenderer.sendSync(
|
||||
'ELECTRON_GUEST_WINDOW_MANAGER_WEB_CONTENTS_METHOD_SYNC',
|
||||
guestId, 'loadURL', guestURL.toString())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
defineProperties(this, {
|
||||
hash: propertyProxyFor('hash'),
|
||||
href: propertyProxyFor('href'),
|
||||
host: propertyProxyFor('host'),
|
||||
hostname: propertyProxyFor('hostname'),
|
||||
origin: propertyProxyFor('origin'),
|
||||
pathname: propertyProxyFor('pathname'),
|
||||
port: propertyProxyFor('port'),
|
||||
protocol: propertyProxyFor('protocol'),
|
||||
search: propertyProxyFor('search')
|
||||
})
|
||||
|
||||
this.toString = function () {
|
||||
return this.href
|
||||
}
|
||||
}
|
||||
|
||||
function BrowserWindowProxy (ipcRenderer, guestId) {
|
||||
this.closed = false
|
||||
|
||||
const location = new LocationProxy(ipcRenderer, guestId)
|
||||
defineProperty(this, 'location', {
|
||||
get: function () {
|
||||
return location
|
||||
},
|
||||
set: function (url) {
|
||||
url = resolveURL(url)
|
||||
return ipcRenderer.sendSync('ELECTRON_GUEST_WINDOW_MANAGER_WEB_CONTENTS_METHOD_SYNC', guestId, 'loadURL', url)
|
||||
}
|
||||
})
|
||||
|
||||
ipcRenderer.once(`ELECTRON_GUEST_WINDOW_MANAGER_WINDOW_CLOSED_${guestId}`, () => {
|
||||
removeProxy(guestId)
|
||||
this.closed = true
|
||||
})
|
||||
|
||||
this.close = () => {
|
||||
ipcRenderer.send('ELECTRON_GUEST_WINDOW_MANAGER_WINDOW_CLOSE', guestId)
|
||||
}
|
||||
|
||||
this.focus = () => {
|
||||
ipcRenderer.send('ELECTRON_GUEST_WINDOW_MANAGER_WINDOW_METHOD', guestId, 'focus')
|
||||
}
|
||||
|
||||
this.blur = () => {
|
||||
ipcRenderer.send('ELECTRON_GUEST_WINDOW_MANAGER_WINDOW_METHOD', guestId, 'blur')
|
||||
}
|
||||
|
||||
this.print = () => {
|
||||
ipcRenderer.send('ELECTRON_GUEST_WINDOW_MANAGER_WEB_CONTENTS_METHOD', guestId, 'print')
|
||||
}
|
||||
|
||||
this.postMessage = (message, targetOrigin) => {
|
||||
ipcRenderer.send('ELECTRON_GUEST_WINDOW_MANAGER_WINDOW_POSTMESSAGE', guestId, message, toString(targetOrigin), window.location.origin)
|
||||
}
|
||||
|
||||
this.eval = (...args) => {
|
||||
ipcRenderer.send('ELECTRON_GUEST_WINDOW_MANAGER_WEB_CONTENTS_METHOD', guestId, 'executeJavaScript', ...args)
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = (ipcRenderer, guestInstanceId, openerId, hiddenPage, usesNativeWindowOpen) => {
|
||||
if (guestInstanceId == null) {
|
||||
// Override default window.close.
|
||||
window.close = function () {
|
||||
ipcRenderer.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 = ipcRenderer.sendSync('ELECTRON_GUEST_WINDOW_MANAGER_WINDOW_OPEN', url, toString(frameName), toString(features))
|
||||
if (guestId != null) {
|
||||
return getOrCreateProxy(ipcRenderer, guestId)
|
||||
} else {
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
if (openerId != null) {
|
||||
window.opener = getOrCreateProxy(ipcRenderer, openerId)
|
||||
}
|
||||
}
|
||||
|
||||
// But we do not support prompt().
|
||||
window.prompt = function () {
|
||||
throw new Error('prompt() is and will not be supported.')
|
||||
}
|
||||
|
||||
ipcRenderer.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.
|
||||
event = document.createEvent('Event')
|
||||
event.initEvent('message', false, false)
|
||||
event.data = message
|
||||
event.origin = sourceOrigin
|
||||
event.source = getOrCreateProxy(ipcRenderer, sourceId)
|
||||
window.dispatchEvent(event)
|
||||
})
|
||||
|
||||
window.history.back = function () {
|
||||
ipcRenderer.send('ELECTRON_NAVIGATION_CONTROLLER_GO_BACK')
|
||||
}
|
||||
|
||||
window.history.forward = function () {
|
||||
ipcRenderer.send('ELECTRON_NAVIGATION_CONTROLLER_GO_FORWARD')
|
||||
}
|
||||
|
||||
window.history.go = function (offset) {
|
||||
ipcRenderer.send('ELECTRON_NAVIGATION_CONTROLLER_GO_TO_OFFSET', +offset)
|
||||
}
|
||||
|
||||
defineProperty(window.history, 'length', {
|
||||
get: function () {
|
||||
return ipcRenderer.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 = hiddenPage ? 'hidden' : 'visible'
|
||||
|
||||
// Subscribe to visibilityState changes.
|
||||
ipcRenderer.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
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
|
@ -0,0 +1,276 @@
|
|||
// This file should have no requires since it is used by the isolated context
|
||||
// preload bundle. Instead arguments should be passed in for everything it
|
||||
// needs.
|
||||
|
||||
// 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, defineProperties } = Object
|
||||
|
||||
// Helper function to resolve relative url.
|
||||
const a = window.document.createElement('a')
|
||||
const resolveURL = function (url: string) {
|
||||
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: any) => {
|
||||
return value != null ? `${value}` : value
|
||||
}
|
||||
|
||||
const windowProxies: Record<number, BrowserWindowProxy> = {}
|
||||
|
||||
const getOrCreateProxy = (ipcRenderer: Electron.IpcRenderer, guestId: number) => {
|
||||
let proxy = windowProxies[guestId]
|
||||
if (proxy == null) {
|
||||
proxy = new BrowserWindowProxy(ipcRenderer, guestId)
|
||||
windowProxies[guestId] = proxy
|
||||
}
|
||||
return proxy
|
||||
}
|
||||
|
||||
const removeProxy = (guestId: number) => {
|
||||
delete windowProxies[guestId]
|
||||
}
|
||||
|
||||
type LocationProperties = 'hash' | 'href' | 'host' | 'hostname' | 'origin' | 'pathname' | 'port' | 'protocol' | 'search'
|
||||
|
||||
class LocationProxy {
|
||||
@LocationProxy.ProxyProperty public hash!: string;
|
||||
@LocationProxy.ProxyProperty public href!: string;
|
||||
@LocationProxy.ProxyProperty public host!: string;
|
||||
@LocationProxy.ProxyProperty public hostname!: string;
|
||||
@LocationProxy.ProxyProperty public origin!: string;
|
||||
@LocationProxy.ProxyProperty public pathname!: string;
|
||||
@LocationProxy.ProxyProperty public port!: string;
|
||||
@LocationProxy.ProxyProperty public protocol!: string;
|
||||
@LocationProxy.ProxyProperty public search!: URLSearchParams;
|
||||
|
||||
private ipcRenderer: Electron.IpcRenderer;
|
||||
private guestId: number;
|
||||
|
||||
/**
|
||||
* Beware: This decorator will have the _prototype_ as the `target`. It defines properties
|
||||
* commonly found in URL on the LocationProxy.
|
||||
*/
|
||||
private static ProxyProperty<T> (target: LocationProxy, propertyKey: LocationProperties) {
|
||||
Object.defineProperty(target, propertyKey, {
|
||||
get: function (): T | string {
|
||||
const guestURL = this.getGuestURL()
|
||||
const value = guestURL ? guestURL[propertyKey] : ''
|
||||
return value === undefined ? '' : value
|
||||
},
|
||||
set: function (newVal: T) {
|
||||
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 as any)[propertyKey] = newVal
|
||||
|
||||
return this.ipcRenderer.sendSync(
|
||||
'ELECTRON_GUEST_WINDOW_MANAGER_WEB_CONTENTS_METHOD_SYNC',
|
||||
this.guestId, 'loadURL', guestURL.toString())
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
constructor (ipcRenderer: Electron.IpcRenderer, guestId: number) {
|
||||
// eslint will consider the constructor "useless"
|
||||
// unless we assign them in the body. It's fine, that's what
|
||||
// TS would do anyway.
|
||||
this.ipcRenderer = ipcRenderer
|
||||
this.guestId = guestId
|
||||
this.getGuestURL = this.getGuestURL.bind(this)
|
||||
}
|
||||
|
||||
public toString (): string {
|
||||
return this.href
|
||||
}
|
||||
|
||||
private getGuestURL (): URL | null {
|
||||
const urlString = this.ipcRenderer.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
|
||||
}
|
||||
}
|
||||
|
||||
class BrowserWindowProxy {
|
||||
public closed: boolean = false
|
||||
|
||||
private _location: LocationProxy
|
||||
private guestId: number
|
||||
private ipcRenderer: Electron.IpcRenderer
|
||||
|
||||
// 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
|
||||
public get location (): LocationProxy | any {
|
||||
return this._location
|
||||
}
|
||||
public set location (url: string | any) {
|
||||
url = resolveURL(url)
|
||||
this.ipcRenderer.sendSync('ELECTRON_GUEST_WINDOW_MANAGER_WEB_CONTENTS_METHOD_SYNC', this.guestId, 'loadURL', url)
|
||||
}
|
||||
|
||||
constructor (ipcRenderer: Electron.IpcRenderer, guestId: number) {
|
||||
this.guestId = guestId
|
||||
this.ipcRenderer = ipcRenderer
|
||||
this._location = new LocationProxy(ipcRenderer, guestId)
|
||||
|
||||
ipcRenderer.once(`ELECTRON_GUEST_WINDOW_MANAGER_WINDOW_CLOSED_${guestId}`, () => {
|
||||
removeProxy(guestId)
|
||||
this.closed = true
|
||||
})
|
||||
}
|
||||
|
||||
public close () {
|
||||
this.ipcRenderer.send('ELECTRON_GUEST_WINDOW_MANAGER_WINDOW_CLOSE', this.guestId)
|
||||
}
|
||||
|
||||
public focus () {
|
||||
this.ipcRenderer.send('ELECTRON_GUEST_WINDOW_MANAGER_WINDOW_METHOD', this.guestId, 'focus')
|
||||
}
|
||||
|
||||
public blur () {
|
||||
this.ipcRenderer.send('ELECTRON_GUEST_WINDOW_MANAGER_WINDOW_METHOD', this.guestId, 'blur')
|
||||
}
|
||||
|
||||
public print () {
|
||||
this.ipcRenderer.send('ELECTRON_GUEST_WINDOW_MANAGER_WEB_CONTENTS_METHOD', this.guestId, 'print')
|
||||
}
|
||||
|
||||
public postMessage (message: any, targetOrigin: any) {
|
||||
this.ipcRenderer.send('ELECTRON_GUEST_WINDOW_MANAGER_WINDOW_POSTMESSAGE', this.guestId, message, toString(targetOrigin), window.location.origin)
|
||||
}
|
||||
|
||||
public eval (...args: any[]) {
|
||||
this.ipcRenderer.send('ELECTRON_GUEST_WINDOW_MANAGER_WEB_CONTENTS_METHOD', this.guestId, 'executeJavaScript', ...args)
|
||||
}
|
||||
}
|
||||
|
||||
export const windowSetup = (
|
||||
ipcRenderer: Electron.IpcRenderer, guestInstanceId: number, openerId: number, isHiddenPage: boolean, usesNativeWindowOpen: boolean
|
||||
) => {
|
||||
if (guestInstanceId == null) {
|
||||
// Override default window.close.
|
||||
window.close = function () {
|
||||
ipcRenderer.sendSync('ELECTRON_BROWSER_WINDOW_CLOSE')
|
||||
}
|
||||
}
|
||||
|
||||
if (!usesNativeWindowOpen) {
|
||||
// Make the browser window or guest view emit "new-window" event.
|
||||
(window as any).open = function (url?: string, frameName?: string, features?: string) {
|
||||
if (url != null && url !== '') {
|
||||
url = resolveURL(url)
|
||||
}
|
||||
const guestId = ipcRenderer.sendSync('ELECTRON_GUEST_WINDOW_MANAGER_WINDOW_OPEN', url, toString(frameName), toString(features))
|
||||
if (guestId != null) {
|
||||
return getOrCreateProxy(ipcRenderer, guestId)
|
||||
} else {
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
if (openerId != null) {
|
||||
window.opener = getOrCreateProxy(ipcRenderer, openerId)
|
||||
}
|
||||
}
|
||||
|
||||
// But we do not support prompt().
|
||||
window.prompt = function () {
|
||||
throw new Error('prompt() is and will not be supported.')
|
||||
}
|
||||
|
||||
ipcRenderer.on('ELECTRON_GUEST_WINDOW_POSTMESSAGE', function (
|
||||
_event: Electron.Event, sourceId: number, message: any, sourceOrigin: string
|
||||
) {
|
||||
// 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: any = document.createEvent('Event')
|
||||
event.initEvent('message', false, false)
|
||||
|
||||
event.data = message
|
||||
event.origin = sourceOrigin
|
||||
event.source = getOrCreateProxy(ipcRenderer, sourceId)
|
||||
|
||||
window.dispatchEvent(event as MessageEvent)
|
||||
})
|
||||
|
||||
window.history.back = function () {
|
||||
ipcRenderer.send('ELECTRON_NAVIGATION_CONTROLLER_GO_BACK')
|
||||
}
|
||||
|
||||
window.history.forward = function () {
|
||||
ipcRenderer.send('ELECTRON_NAVIGATION_CONTROLLER_GO_FORWARD')
|
||||
}
|
||||
|
||||
window.history.go = function (offset: number) {
|
||||
ipcRenderer.send('ELECTRON_NAVIGATION_CONTROLLER_GO_TO_OFFSET', +offset)
|
||||
}
|
||||
|
||||
defineProperty(window.history, 'length', {
|
||||
get: function () {
|
||||
return ipcRenderer.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.
|
||||
ipcRenderer.on('ELECTRON_GUEST_INSTANCE_VISIBILITY_CHANGE', function (_event: Electron.Event, visibilityState: 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
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
|
@ -8,7 +8,7 @@
|
|||
"dom.iterable"
|
||||
],
|
||||
"sourceMap": true,
|
||||
"experimentalDecorators": false,
|
||||
"experimentalDecorators": true,
|
||||
"strict": true,
|
||||
"baseUrl": ".",
|
||||
"allowJs": true,
|
||||
|
|
Загрузка…
Ссылка в новой задаче