diff --git a/src/ext/lib/settings/SiteSettings.ts b/src/ext/lib/settings/SiteSettings.ts index 349bf95..c17e909 100644 --- a/src/ext/lib/settings/SiteSettings.ts +++ b/src/ext/lib/settings/SiteSettings.ts @@ -176,7 +176,7 @@ export class SiteSettings { * Is extension allowed to run in current environment * @param isTheater * @param isFullscreen - * @returns + * @returns ExtensionMode */ isEnabledForEnvironment(isTheater: boolean, isFullscreen: boolean) { const env = this._getEnvironment(isTheater, isFullscreen); diff --git a/src/ext/lib/uwui/UI.js b/src/ext/lib/uwui/UI.js index 37666ae..60d6912 100644 --- a/src/ext/lib/uwui/UI.js +++ b/src/ext/lib/uwui/UI.js @@ -66,32 +66,48 @@ class UI { */ - iframe.onload = function() { - document.addEventListener('mousemove', (event) => { - const coords = { - x: event.pageX - iframe.offsetLeft, - y: event.pageY - iframe.offsetTop - }; + // set uiIframe for handleMessage + this.uiIframe = iframe; - // ask the iframe to check whether there's a clickable element - iframe.contentWindow.postMessage( - { - action: 'uwui-probe', - coords, - ts: +new Date() // this should be accurate enough for our purposes - }, - uiURI - ); - }, true); + const fn = (event) => { + // remove self on fucky wuckies + if (!iframe?.contentWindow ) { + document.removeEventListener('mousemove', fn, true); + return; + } + + const coords = { + x: event.pageX - this.uiIframe.offsetLeft, + y: event.pageY - this.uiIframe.offsetTop + }; + + // ask the iframe to check whether there's a clickable element + this.uiIframe.contentWindow.postMessage( + { + action: 'uwui-probe', + coords, + ts: +new Date() // this should be accurate enough for our purposes + }, + uiURI + ); + } + + iframe.onload = function() { + document.addEventListener('mousemove', fn, true); } rootDiv.appendChild(iframe); - // subscribe to events coming back to us - window.addEventListener('message', (event) => this.handleMessage(event)); + // subscribe to events coming back to us. Unsubscribe if iframe vanishes. + const messageHandlerFn = (message) => { + if (!iframe?.contentWindow) { + window.removeEventListener('message', messageHandlerFn); + return; + } + this.handleMessage(message); + } + window.addEventListener('message', messageHandlerFn); - // set uiIframe for handleMessage - this.uiIframe = iframe; /* set up event bus tunnel from content script to UI — necessary if we want to receive * like current zoom levels & current aspect ratio & stuff. Some of these things are @@ -102,18 +118,35 @@ class UI { 'uw-config-broadcast', { function: (config) => { - iframe.contentWindow.postMessage( - { - action: 'uw-bus-tunnel', - payload: {action: 'uw-config-broadcast', config} - }, - uiURI - ) + // because existence of UI is not guaranteed — UI is not shown when extension is inactive. + // If extension is inactive due to "player element isn't big enough to justify it", however, + // we can still receive eventBus messages. + if (this.element && this.uiIframe) { + this.uiIframe.contentWindow.postMessage( + { + action: 'uw-bus-tunnel', + payload: {action: 'uw-config-broadcast', config} + }, + uiURI + ) + } } } ); } + async enable() { + // if root element is not present, we need to init the UI. + if (!this.element) { + await this.init(); + } + // otherwise, we don't have to do anything + } + disable() { + if (this.element) { + this.destroy(); + } + } /** * Handles events received from the iframe. @@ -139,14 +172,21 @@ class UI { * @param {*} newUiConfig */ replace(newUiConfig) { - this.element?.remove(); this.uiConfig = newUiConfig; - this.init(); + + if (this.element) { + this.element?.remove(); + this.init(); + } } destroy() { // this.comms?.destroy(); + this.uiIframe?.remove(); this.element?.remove(); + + this.uiIframe = undefined; + this.element = undefined; } } diff --git a/src/ext/lib/video-data/PlayerData.ts b/src/ext/lib/video-data/PlayerData.ts index 78339da..725a001 100644 --- a/src/ext/lib/video-data/PlayerData.ts +++ b/src/ext/lib/video-data/PlayerData.ts @@ -125,7 +125,6 @@ class PlayerData { // this.notificationService = new PlayerNotificationUi(this.element, this.settings, this.eventBus); this.ui = new UI('ultrawidifyUi', {parentElement: this.element, eventBus: this.eventBus}); - // this.ui.init(); this.dimensions = undefined; this.overlayNode = undefined; @@ -178,6 +177,7 @@ class PlayerData { this.element.classList.add(this.playerCssClass); this.startChangeDetection(); this.videoData.enable({fromPlayer: true}); + this.ui.enable(); } /** @@ -192,6 +192,7 @@ class PlayerData { this.enabled = false; this.element.classList.remove(this.playerCssClass); this.videoData.disable({fromPlayer: true}); + this.ui.disable(); } /** @@ -251,7 +252,6 @@ class PlayerData { // in every other case, we need to check if the player is still // big enough to warrant our extension running. - this.handleSizeConstraints(currentPlayerDimensions); this.handleDimensionChanges(currentPlayerDimensions, this.dimensions); @@ -265,49 +265,26 @@ class PlayerData { * @param currentPlayerDimensions */ private handleSizeConstraints(currentPlayerDimensions: PlayerDimensions) { - // Check if extension is allowed to run in current combination of theater + full screen - const canEnable = this.siteSettings.isEnabledForEnvironment(this.isFullscreen, this.isTheaterMode); + const canEnable = this.siteSettings.isEnabledForEnvironment(this.isFullscreen, this.isTheaterMode) === ExtensionMode.Enabled; // Enable/disable - if (!this.enabled && canEnable) { - this.enable(); - } else if (this.enabled && !canEnable) { - this.disable(); + if (canEnable) { + if (!this.enabled) { + // we should really check other constraints first before enabling + this.enable(); + this.handleDimensionChanges(currentPlayerDimensions, this.dimensions); + return; + } + } else { + // if canEnable comes out negative, there's no amount of constraints that will + // cause PlayerData to be enabled. + if (this.enabled) { + this.handleDimensionChanges(currentPlayerDimensions, this.dimensions); + this.disable(); + } return; } - - // Check if autodetection is allowed to run in current combination of theater + full screen - // if (this.siteSettings.isAardEnabledForEnvironment(this.isFullscreen, this.isTheaterMode)) { - // this.eventBus.send('disable-aard'); - // } - - // never disable ultrawidify in full screen - // if (this.isFullscreen) { - // this.enable(); - // return; - // } - - // if 'disable on small players' option is not enabled, the extension will run in any case - // if (!restrictions?.disableOnSmallPlayers) { - // this.enable(); - // return; - // } - - // If we only allow ultrawidify in full screen, we disable it when not in full screen - // if (restrictions.onlyAllowInFullscreen && !currentPlayerDimensions.fullscreen) { - // this.disable(); - // return; - // } - - // if current width or height are smaller than the minimum, the extension will not run - // if (restrictions.minAllowedHeight > currentPlayerDimensions?.height || restrictions.minAllowedWidth > currentPlayerDimensions?.width) { - // this.disable(); - // return; - // } - - // in this case, the player is big enough to warrant enabling Ultrawidify - this.enable(); } @@ -343,31 +320,18 @@ class PlayerData { } try { - if (BrowserDetect.firefox) { - this.observer = new ResizeObserver( - _.debounce( // don't do this too much: - () => this.onPlayerDimensionsChanged, - 250, // do it once per this many ms - { - leading: true, // do it when we call this fallback first - trailing: true // do it after the timeout if we call this callback few more times - } - ) - ); - } else { - // Chrome for some reason insists that this.onPlayerDimensionsChanged is not a function - // when it's not wrapped into an anonymous function - this.observer = new ResizeObserver( - _.debounce( // don't do this too much: - (m,o) => this.onPlayerDimensionsChanged(m,o), - 250, // do it once per this many ms - { - leading: true, // do it when we call this fallback first - trailing: true // do it after the timeout if we call this callback few more times - } - ) - ); - } + this.observer = new ResizeObserver( + _.debounce( // don't do this too much: + (m,o) => { + this.onPlayerDimensionsChanged(m,o) + }, + 250, // do it once per this many ms + { + leading: true, // do it when we call this fallback first + trailing: true // do it after the timeout if we call this callback few more times + } + ) + ); const observerConf = { attributes: true, diff --git a/src/ext/lib/video-data/VideoData.ts b/src/ext/lib/video-data/VideoData.ts index 79e93cb..a3a9893 100644 --- a/src/ext/lib/video-data/VideoData.ts +++ b/src/ext/lib/video-data/VideoData.ts @@ -178,7 +178,6 @@ class VideoData { console.error('Failed to inject base css!', e); } } - unsetBaseClass() { this.mutationObserver.disconnect(); this.mutationObserver = undefined;