diff options
Diffstat (limited to 'ext/fg/js')
| -rw-r--r-- | ext/fg/js/float.js | 38 | ||||
| -rw-r--r-- | ext/fg/js/frontend-api-sender.js | 10 | ||||
| -rw-r--r-- | ext/fg/js/frontend-initialize.js | 11 | ||||
| -rw-r--r-- | ext/fg/js/frontend.js | 2 | ||||
| -rw-r--r-- | ext/fg/js/popup-proxy-host.js | 64 | ||||
| -rw-r--r-- | ext/fg/js/popup-proxy.js | 6 | ||||
| -rw-r--r-- | ext/fg/js/popup.js | 109 | ||||
| -rw-r--r-- | ext/fg/js/source.js | 14 | 
8 files changed, 160 insertions, 94 deletions
| diff --git a/ext/fg/js/float.js b/ext/fg/js/float.js index d31b8336..440a9731 100644 --- a/ext/fg/js/float.js +++ b/ext/fg/js/float.js @@ -16,7 +16,7 @@   * along with this program.  If not, see <https://www.gnu.org/licenses/>.   */ -/*global popupNestedInitialize, Display*/ +/*global popupNestedInitialize, apiForward, Display*/  class DisplayFloat extends Display {      constructor() { @@ -29,11 +29,31 @@ class DisplayFloat extends Display {          };          this._orphaned = false; +        this._prepareInvoked = false;          yomichan.on('orphaned', () => this.onOrphaned());          window.addEventListener('message', (e) => this.onMessage(e), false);      } +    async prepare(options, popupInfo, url, childrenSupported, scale, uniqueId) { +        if (this._prepareInvoked) { return; } +        this._prepareInvoked = true; + +        await super.prepare(options); + +        const {id, depth, parentFrameId} = popupInfo; +        this.optionsContext.depth = depth; +        this.optionsContext.url = url; + +        if (childrenSupported) { +            popupNestedInitialize(id, depth, parentFrameId, url); +        } + +        this.setContentScale(scale); + +        apiForward('popupPrepareCompleted', {uniqueId}); +    } +      onError(error) {          if (this._orphaned) {              this.setContent('orphaned'); @@ -93,20 +113,6 @@ class DisplayFloat extends Display {      setContentScale(scale) {          document.body.style.fontSize = `${scale}em`;      } - -    async initialize(options, popupInfo, url, childrenSupported, scale) { -        await super.initialize(options); - -        const {id, depth, parentFrameId} = popupInfo; -        this.optionsContext.depth = depth; -        this.optionsContext.url = url; - -        if (childrenSupported) { -            popupNestedInitialize(id, depth, parentFrameId, url); -        } - -        this.setContentScale(scale); -    }  }  DisplayFloat._onKeyDownHandlers = new Map([ @@ -123,7 +129,7 @@ DisplayFloat._messageHandlers = new Map([      ['setContent', (self, {type, details}) => self.setContent(type, details)],      ['clearAutoPlayTimer', (self) => self.clearAutoPlayTimer()],      ['setCustomCss', (self, {css}) => self.setCustomCss(css)], -    ['initialize', (self, {options, popupInfo, url, childrenSupported, scale}) => self.initialize(options, popupInfo, url, childrenSupported, scale)], +    ['prepare', (self, {options, popupInfo, url, childrenSupported, scale, uniqueId}) => self.prepare(options, popupInfo, url, childrenSupported, scale, uniqueId)],      ['setContentScale', (self, {scale}) => self.setContentScale(scale)]  ]); diff --git a/ext/fg/js/frontend-api-sender.js b/ext/fg/js/frontend-api-sender.js index 93c2e593..8dc6aaf3 100644 --- a/ext/fg/js/frontend-api-sender.js +++ b/ext/fg/js/frontend-api-sender.js @@ -19,7 +19,7 @@  class FrontendApiSender {      constructor() { -        this.senderId = FrontendApiSender.generateId(16); +        this.senderId = yomichan.generateId(16);          this.ackTimeout = 3000; // 3 seconds          this.responseTimeout = 10000; // 10 seconds          this.callbacks = new Map(); @@ -123,12 +123,4 @@ class FrontendApiSender {          info.timer = null;          info.reject(new Error(reason));      } - -    static generateId(length) { -        let id = ''; -        for (let i = 0; i < length; ++i) { -            id += Math.floor(Math.random() * 256).toString(16).padStart(2, '0'); -        } -        return id; -    }  } diff --git a/ext/fg/js/frontend-initialize.js b/ext/fg/js/frontend-initialize.js index c32e97d4..54b874f2 100644 --- a/ext/fg/js/frontend-initialize.js +++ b/ext/fg/js/frontend-initialize.js @@ -22,13 +22,16 @@ async function main() {      const data = window.frontendInitializationData || {};      const {id, depth=0, parentFrameId, ignoreNodes, url, proxy=false} = data; -    let popupHost = null; -    if (!proxy) { -        popupHost = new PopupProxyHost(); +    let popup; +    if (proxy) { +        popup = new PopupProxy(null, depth + 1, id, parentFrameId, url); +    } else { +        const popupHost = new PopupProxyHost();          await popupHost.prepare(); + +        popup = popupHost.getOrCreatePopup();      } -    const popup = proxy ? new PopupProxy(depth + 1, id, parentFrameId, url) : popupHost.createPopup(null, depth);      const frontend = new Frontend(popup, ignoreNodes);      await frontend.prepare();  } diff --git a/ext/fg/js/frontend.js b/ext/fg/js/frontend.js index 3611d44e..67045241 100644 --- a/ext/fg/js/frontend.js +++ b/ext/fg/js/frontend.js @@ -56,7 +56,7 @@ class Frontend extends TextScanner {              }              yomichan.on('orphaned', () => this.onOrphaned()); -            yomichan.on('optionsUpdate', () => this.updateOptions()); +            yomichan.on('optionsUpdated', () => this.updateOptions());              yomichan.on('zoomChanged', (e) => this.onZoomChanged(e));              chrome.runtime.onMessage.addListener(this.onRuntimeMessage.bind(this)); diff --git a/ext/fg/js/popup-proxy-host.js b/ext/fg/js/popup-proxy-host.js index 98729796..e55801ff 100644 --- a/ext/fg/js/popup-proxy-host.js +++ b/ext/fg/js/popup-proxy-host.js @@ -34,7 +34,7 @@ class PopupProxyHost {          if (typeof frameId !== 'number') { return; }          this._apiReceiver = new FrontendApiReceiver(`popup-proxy-host#${frameId}`, new Map([ -            ['createNestedPopup', ({parentId}) => this._onApiCreateNestedPopup(parentId)], +            ['getOrCreatePopup', ({id, parentId}) => this._onApiGetOrCreatePopup(id, parentId)],              ['setOptions', ({id, options}) => this._onApiSetOptions(id, options)],              ['hide', ({id, changeFocus}) => this._onApiHide(id, changeFocus)],              ['isVisible', ({id}) => this._onApiIsVisibleAsync(id)], @@ -47,14 +47,51 @@ class PopupProxyHost {          ]));      } -    createPopup(parentId, depth) { -        return this._createPopupInternal(parentId, depth).popup; +    getOrCreatePopup(id=null, parentId=null) { +        // Find by existing id +        if (id !== null) { +            const popup = this._popups.get(id); +            if (typeof popup !== 'undefined') { +                return popup; +            } +        } + +        // Find by existing parent id +        let parent = null; +        if (parentId !== null) { +            parent = this._popups.get(parentId); +            if (typeof parent !== 'undefined') { +                const popup = parent.child; +                if (popup !== null) { +                    return popup; +                } +            } else { +                parent = null; +            } +        } + +        // New unique id +        if (id === null) { +            id = this._nextId++; +        } + +        // Create new popup +        const depth = (parent !== null ? parent.depth + 1 : 0); +        const popup = new Popup(id, depth, this._frameIdPromise); +        if (parent !== null) { +            popup.setParent(parent); +        } +        this._popups.set(id, popup); +        return popup;      }      // Message handlers -    async _onApiCreateNestedPopup(parentId) { -        return this._createPopupInternal(parentId, 0).id; +    async _onApiGetOrCreatePopup(id, parentId) { +        const popup = this.getOrCreatePopup(id, parentId); +        return { +            id: popup.id +        };      }      async _onApiSetOptions(id, options) { @@ -106,25 +143,10 @@ class PopupProxyHost {      // Private functions -    _createPopupInternal(parentId, depth) { -        const parent = (typeof parentId === 'string' && this._popups.has(parentId) ? this._popups.get(parentId) : null); -        const id = `${this._nextId}`; -        if (parent !== null) { -            depth = parent.depth + 1; -        } -        ++this._nextId; -        const popup = new Popup(id, depth, this._frameIdPromise); -        if (parent !== null) { -            popup.setParent(parent); -        } -        this._popups.set(id, popup); -        return {popup, id}; -    } -      _getPopup(id) {          const popup = this._popups.get(id);          if (typeof popup === 'undefined') { -            throw new Error('Invalid popup ID'); +            throw new Error(`Invalid popup ID ${id}`);          }          return popup;      } diff --git a/ext/fg/js/popup-proxy.js b/ext/fg/js/popup-proxy.js index db6dffb1..093cdd2e 100644 --- a/ext/fg/js/popup-proxy.js +++ b/ext/fg/js/popup-proxy.js @@ -19,10 +19,10 @@  /*global FrontendApiSender*/  class PopupProxy { -    constructor(depth, parentId, parentFrameId, url) { +    constructor(id, depth, parentId, parentFrameId, url) {          this._parentId = parentId;          this._parentFrameId = parentFrameId; -        this._id = null; +        this._id = id;          this._idPromise = null;          this._depth = depth;          this._url = url; @@ -113,7 +113,7 @@ class PopupProxy {      }      async _getPopupIdAsync() { -        const id = await this._invokeHostApi('createNestedPopup', {parentId: this._parentId}); +        const {id} = await this._invokeHostApi('getOrCreatePopup', {id: this._id, parentId: this._parentId});          this._id = id;          return id;      } diff --git a/ext/fg/js/popup.js b/ext/fg/js/popup.js index 0b142dda..de05f9f5 100644 --- a/ext/fg/js/popup.js +++ b/ext/fg/js/popup.js @@ -28,8 +28,6 @@ class Popup {          this._child = null;          this._childrenSupported = true;          this._injectPromise = null; -        this._isInjected = false; -        this._isInjectedAndLoaded = false;          this._visible = false;          this._visibleOverride = null;          this._options = null; @@ -41,19 +39,28 @@ class Popup {          this._container.className = 'yomichan-float';          this._container.addEventListener('mousedown', (e) => e.stopPropagation());          this._container.addEventListener('scroll', (e) => e.stopPropagation()); -        this._container.setAttribute('src', chrome.runtime.getURL('/fg/float.html'));          this._container.style.width = '0px';          this._container.style.height = '0px'; +        this._fullscreenEventListeners = new EventListenerCollection(); +          this._updateVisibility();      }      // Public properties +    get id() { +        return this._id; +    } +      get parent() {          return this._parent;      } +    get child() { +        return this._child; +    } +      get depth() {          return this._depth;      } @@ -118,16 +125,12 @@ class Popup {      }      clearAutoPlayTimer() { -        if (this._isInjectedAndLoaded) { -            this._invokeApi('clearAutoPlayTimer'); -        } +        this._invokeApi('clearAutoPlayTimer');      }      setContentScale(scale) {          this._contentScale = scale; -        if (this._isInjectedAndLoaded) { -            this._invokeApi('setContentScale', {scale}); -        } +        this._invokeApi('setContentScale', {scale});      }      // Popup-only public functions @@ -147,7 +150,7 @@ class Popup {      }      isVisibleSync() { -        return this._isInjected && (this._visibleOverride !== null ? this._visibleOverride : this._visible); +        return (this._visibleOverride !== null ? this._visibleOverride : this._visible);      }      updateTheme() { @@ -226,8 +229,10 @@ class Popup {          return new Promise((resolve) => {              const parentFrameId = (typeof this._frameId === 'number' ? this._frameId : null);              this._container.addEventListener('load', () => { -                this._isInjectedAndLoaded = true; -                this._invokeApi('initialize', { +                const uniqueId = yomichan.generateId(32); +                Popup._listenForDisplayPrepareCompleted(uniqueId, resolve); + +                this._invokeApi('prepare', {                      options: this._options,                      popupInfo: {                          id: this._id, @@ -236,17 +241,47 @@ class Popup {                      },                      url: this.url,                      childrenSupported: this._childrenSupported, -                    scale: this._contentScale +                    scale: this._contentScale, +                    uniqueId                  }); -                resolve();              }); -            this._observeFullscreen(); +            this._observeFullscreen(true);              this._onFullscreenChanged();              this.setCustomOuterCss(this._options.general.customPopupOuterCss, false); -            this._isInjected = true; +            this._container.setAttribute('src', chrome.runtime.getURL('/fg/float.html'));          });      } +    _observeFullscreen(observe) { +        if (!observe) { +            this._fullscreenEventListeners.removeAllEventListeners(); +            return; +        } + +        if (this._fullscreenEventListeners.size > 0) { +            // Already observing +            return; +        } + +        const fullscreenEvents = [ +            'fullscreenchange', +            'MSFullscreenChange', +            'mozfullscreenchange', +            'webkitfullscreenchange' +        ]; +        const onFullscreenChanged = () => this._onFullscreenChanged(); +        for (const eventName of fullscreenEvents) { +            this._fullscreenEventListeners.addEventListener(document, eventName, onFullscreenChanged, false); +        } +    } + +    _onFullscreenChanged() { +        const parent = (Popup._getFullscreenElement() || document.body || null); +        if (parent !== null && this._container.parentNode !== parent) { +            parent.appendChild(this._container); +        } +    } +      async _show(elementRect, writingMode) {          await this._inject(); @@ -328,38 +363,36 @@ class Popup {      }      _invokeApi(action, params={}) { -        if (!this._isInjectedAndLoaded) { -            throw new Error('Frame not loaded'); -        } -        this._container.contentWindow.postMessage({action, params}, '*'); -    } - -    _observeFullscreen() { -        const fullscreenEvents = [ -            'fullscreenchange', -            'MSFullscreenChange', -            'mozfullscreenchange', -            'webkitfullscreenchange' -        ]; -        for (const eventName of fullscreenEvents) { -            document.addEventListener(eventName, () => this._onFullscreenChanged(), false); +        if (this._container.contentWindow) { +            this._container.contentWindow.postMessage({action, params}, '*');          }      } -    _getFullscreenElement() { +    static _getFullscreenElement() {          return (              document.fullscreenElement ||              document.msFullscreenElement ||              document.mozFullScreenElement || -            document.webkitFullscreenElement +            document.webkitFullscreenElement || +            null          );      } -    _onFullscreenChanged() { -        const parent = (this._getFullscreenElement() || document.body || null); -        if (parent !== null && this._container.parentNode !== parent) { -            parent.appendChild(this._container); -        } +    static _listenForDisplayPrepareCompleted(uniqueId, resolve) { +        const runtimeMessageCallback = ({action, params}, sender, callback) => { +            if ( +                action === 'popupPrepareCompleted' && +                typeof params === 'object' && +                params !== null && +                params.uniqueId === uniqueId +            ) { +                chrome.runtime.onMessage.removeListener(runtimeMessageCallback); +                callback(); +                resolve(); +                return false; +            } +        }; +        chrome.runtime.onMessage.addListener(runtimeMessageCallback);      }      static _getPositionForHorizontalText(elementRect, width, height, viewport, offsetScale, optionsGeneral) { diff --git a/ext/fg/js/source.js b/ext/fg/js/source.js index 11d3ff0e..fa785ec4 100644 --- a/ext/fg/js/source.js +++ b/ext/fg/js/source.js @@ -82,7 +82,11 @@ class TextSourceRange {      }      equals(other) { -        if (other === null) { +        if (!( +            typeof other === 'object' && +            other !== null && +            other instanceof TextSourceRange +        )) {              return false;          }          if (this.imposterSourceElement !== null) { @@ -409,6 +413,12 @@ class TextSourceElement {      }      equals(other) { -        return other && other.element === this.element && other.content === this.content; +        return ( +            typeof other === 'object' && +            other !== null && +            other instanceof TextSourceElement && +            other.element === this.element && +            other.content === this.content +        );      }  } |