diff options
Diffstat (limited to 'ext')
| -rw-r--r-- | ext/fg/js/frontend.js | 2 | ||||
| -rw-r--r-- | ext/fg/js/popup-factory.js | 3 | ||||
| -rw-r--r-- | ext/fg/js/popup.js | 191 | 
3 files changed, 102 insertions, 94 deletions
| diff --git a/ext/fg/js/frontend.js b/ext/fg/js/frontend.js index 78440991..1326f33f 100644 --- a/ext/fg/js/frontend.js +++ b/ext/fg/js/frontend.js @@ -41,7 +41,7 @@ class Frontend {          this._optionsUpdatePending = false;          this._textScanner = new TextScanner({              node: window, -            ignoreElements: () => this._popup.isProxy() ? [] : [this._popup.getContainer()], +            ignoreElements: () => this._popup.isProxy() ? [] : [this._popup.getFrame()],              ignorePoint: (x, y) => this._popup.containsPoint(x, y),              search: this._search.bind(this)          }); diff --git a/ext/fg/js/popup-factory.js b/ext/fg/js/popup-factory.js index 21e64dd0..b10acbaf 100644 --- a/ext/fg/js/popup-factory.js +++ b/ext/fg/js/popup-factory.js @@ -87,6 +87,7 @@ class PopupFactory {              popup.setParent(parent);          }          this._popups.set(id, popup); +        popup.prepare();          return popup;      } @@ -168,7 +169,7 @@ class PopupFactory {      _convertPopupPointToRootPagePoint(popup, x, y) {          if (popup.parent !== null) { -            const popupRect = popup.parent.getContainerRect(); +            const popupRect = popup.parent.getFrameRect();              x += popupRect.x;              y += popupRect.y;          } diff --git a/ext/fg/js/popup.js b/ext/fg/js/popup.js index 79b37251..c024bb64 100644 --- a/ext/fg/js/popup.js +++ b/ext/fg/js/popup.js @@ -36,23 +36,18 @@ class Popup {          this._options = null;          this._optionsContext = null;          this._contentScale = 1.0; -        this._containerSizeContentScale = null;          this._targetOrigin = chrome.runtime.getURL('/').replace(/\/$/, '');          this._previousOptionsContextSource = null; -        this._containerSecret = null; -        this._containerToken = null; -        this._container = document.createElement('iframe'); -        this._container.className = 'yomichan-float'; -        this._container.addEventListener('mousedown', (e) => e.stopPropagation()); -        this._container.addEventListener('scroll', (e) => e.stopPropagation()); -        this._container.style.width = '0px'; -        this._container.style.height = '0px'; -        this._container.addEventListener('load', this._onFrameLoad.bind(this)); +        this._frameSizeContentScale = null; +        this._frameSecret = null; +        this._frameToken = null; +        this._frame = document.createElement('iframe'); +        this._frame.className = 'yomichan-float'; +        this._frame.style.width = '0'; +        this._frame.style.height = '0';          this._fullscreenEventListeners = new EventListenerCollection(); - -        this._updateVisibility();      }      // Public properties @@ -79,6 +74,13 @@ class Popup {      // Public functions +    prepare() { +        this._updateVisibility(); +        this._frame.addEventListener('mousedown', (e) => e.stopPropagation()); +        this._frame.addEventListener('scroll', (e) => e.stopPropagation()); +        this._frame.addEventListener('load', this._onFrameLoad.bind(this)); +    } +      isProxy() {          return false;      } @@ -118,7 +120,7 @@ class Popup {      async containsPoint(x, y) {          for (let popup = this; popup !== null && popup.isVisibleSync(); popup = popup._child) { -            const rect = popup._container.getBoundingClientRect(); +            const rect = popup._frame.getBoundingClientRect();              if (x >= rect.left && y >= rect.top && x < rect.right && y < rect.bottom) {                  return true;              } @@ -173,12 +175,12 @@ class Popup {      }      updateTheme() { -        this._container.dataset.yomichanTheme = this._options.general.popupOuterTheme; -        this._container.dataset.yomichanSiteColor = this._getSiteColor(); +        this._frame.dataset.yomichanTheme = this._options.general.popupOuterTheme; +        this._frame.dataset.yomichanSiteColor = this._getSiteColor();      }      async setCustomOuterCss(css, useWebExtensionApi) { -        return await Popup._injectStylesheet( +        return await this._injectStylesheet(              'yomichan-popup-outer-user-stylesheet',              'code',              css, @@ -190,12 +192,12 @@ class Popup {          this._childrenSupported = value;      } -    getContainer() { -        return this._container; +    getFrame() { +        return this._frame;      } -    getContainerRect() { -        return this._container.getBoundingClientRect(); +    getFrameRect() { +        return this._frame.getBoundingClientRect();      }      // Private functions @@ -220,11 +222,11 @@ class Popup {          return new Promise((resolve, reject) => {              const tokenMap = new Map();              let timer = null; -            let containerLoadedResolve = null; -            let containerLoadedReject = null; -            const containerLoaded = new Promise((resolve2, reject2) => { -                containerLoadedResolve = resolve2; -                containerLoadedReject = reject2; +            let frameLoadedResolve = null; +            let frameLoadedReject = null; +            const frameLoaded = new Promise((resolve2, reject2) => { +                frameLoadedResolve = resolve2; +                frameLoadedReject = reject2;              });              const postMessage = (action, params) => { @@ -252,7 +254,7 @@ class Popup {                      if (!isObject(message)) { return; }                      const {action, params} = message;                      if (!isObject(params)) { return; } -                    await containerLoaded; +                    await frameLoaded;                      if (timer === null) { return; } // Done                      switch (action) { @@ -282,7 +284,7 @@ class Popup {              };              const onLoad = () => { -                if (containerLoadedResolve === null) { +                if (frameLoadedResolve === null) {                      cleanup();                      reject(new Error('Unexpected load event'));                      return; @@ -292,9 +294,9 @@ class Popup {                      return;                  } -                containerLoadedResolve(); -                containerLoadedResolve = null; -                containerLoadedReject = null; +                frameLoadedResolve(); +                frameLoadedResolve = null; +                frameLoadedReject = null;              };              const cleanup = () => { @@ -302,10 +304,10 @@ class Popup {                  clearTimeout(timer);                  timer = null; -                containerLoadedResolve = null; -                if (containerLoadedReject !== null) { -                    containerLoadedReject(new Error('Terminated')); -                    containerLoadedReject = null; +                frameLoadedResolve = null; +                if (frameLoadedReject !== null) { +                    frameLoadedReject(new Error('Terminated')); +                    frameLoadedReject = null;                  }                  chrome.runtime.onMessage.removeListener(onMessage); @@ -322,7 +324,7 @@ class Popup {              frame.addEventListener('load', onLoad);              // Prevent unhandled rejections -            containerLoaded.catch(() => {}); // NOP +            frameLoaded.catch(() => {}); // NOP              setupFrame(frame);          }); @@ -331,15 +333,15 @@ class Popup {      async _createInjectPromise() {          this._injectStyles(); -        const {secret, token} = await this._initializeFrame(this._container, this._targetOrigin, this._frameId, (frame) => { +        const {secret, token} = await this._initializeFrame(this._frame, this._targetOrigin, this._frameId, (frame) => {              frame.removeAttribute('src');              frame.removeAttribute('srcdoc');              frame.setAttribute('src', chrome.runtime.getURL('/fg/float.html'));              this._observeFullscreen(true);              this._onFullscreenChanged();          }); -        this._containerSecret = secret; -        this._containerToken = token; +        this._frameSecret = secret; +        this._frameToken = token;          // Configure          const messageId = yomichan.generateId(16); @@ -374,22 +376,22 @@ class Popup {      }      _resetFrame() { -        const parent = this._container.parentNode; +        const parent = this._frame.parentNode;          if (parent !== null) { -            parent.removeChild(this._container); +            parent.removeChild(this._frame);          } -        this._container.removeAttribute('src'); -        this._container.removeAttribute('srcdoc'); +        this._frame.removeAttribute('src'); +        this._frame.removeAttribute('srcdoc'); -        this._containerSecret = null; -        this._containerToken = null; +        this._frameSecret = null; +        this._frameToken = null;          this._injectPromise = null;          this._injectPromiseComplete = false;      }      async _injectStyles() {          try { -            await Popup._injectStylesheet('yomichan-popup-outer-stylesheet', 'file', '/fg/css/client.css', true); +            await this._injectStylesheet('yomichan-popup-outer-stylesheet', 'file', '/fg/css/client.css', true);          } catch (e) {              // NOP          } @@ -426,8 +428,8 @@ class Popup {      _onFullscreenChanged() {          const parent = this._getFrameParentElement(); -        if (parent !== null && this._container.parentNode !== parent) { -            parent.appendChild(this._container); +        if (parent !== null && this._frame.parentNode !== parent) { +            parent.appendChild(this._frame);          }      } @@ -435,31 +437,31 @@ class Popup {          await this._inject();          const optionsGeneral = this._options.general; -        const container = this._container; -        const containerRect = container.getBoundingClientRect(); -        const getPosition = ( -            writingMode === 'horizontal-tb' || optionsGeneral.popupVerticalTextPosition === 'default' ? -            Popup._getPositionForHorizontalText : -            Popup._getPositionForVerticalText -        ); +        const frame = this._frame; +        const frameRect = frame.getBoundingClientRect(); -        const viewport = Popup._getViewport(optionsGeneral.popupScaleRelativeToVisualViewport); +        const viewport = this._getViewport(optionsGeneral.popupScaleRelativeToVisualViewport);          const scale = this._contentScale; -        const scaleRatio = this._containerSizeContentScale === null ? 1.0 : scale / this._containerSizeContentScale; -        this._containerSizeContentScale = scale; -        let [x, y, width, height, below] = getPosition( +        const scaleRatio = this._frameSizeContentScale === null ? 1.0 : scale / this._frameSizeContentScale; +        this._frameSizeContentScale = scale; +        const getPositionArgs = [              elementRect, -            Math.max(containerRect.width * scaleRatio, optionsGeneral.popupWidth * scale), -            Math.max(containerRect.height * scaleRatio, optionsGeneral.popupHeight * scale), +            Math.max(frameRect.width * scaleRatio, optionsGeneral.popupWidth * scale), +            Math.max(frameRect.height * scaleRatio, optionsGeneral.popupHeight * scale),              viewport,              scale,              optionsGeneral,              writingMode +        ]; +        let [x, y, width, height, below] = ( +            writingMode === 'horizontal-tb' || optionsGeneral.popupVerticalTextPosition === 'default' ? +            this._getPositionForHorizontalText(...getPositionArgs) : +            this._getPositionForVerticalText(...getPositionArgs)          );          const fullWidth = (optionsGeneral.popupDisplayMode === 'full-width'); -        container.classList.toggle('yomichan-float-full-width', fullWidth); -        container.classList.toggle('yomichan-float-above', !below); +        frame.classList.toggle('yomichan-float-full-width', fullWidth); +        frame.classList.toggle('yomichan-float-above', !below);          if (optionsGeneral.popupDisplayMode === 'full-width') {              x = viewport.left; @@ -467,10 +469,10 @@ class Popup {              width = viewport.right - viewport.left;          } -        container.style.left = `${x}px`; -        container.style.top = `${y}px`; -        container.style.width = `${width}px`; -        container.style.height = `${height}px`; +        frame.style.left = `${x}px`; +        frame.style.top = `${y}px`; +        frame.style.width = `${width}px`; +        frame.style.height = `${height}px`;          this._setVisible(true);          if (this._child !== null) { @@ -484,20 +486,20 @@ class Popup {      }      _updateVisibility() { -        this._container.style.setProperty('visibility', this.isVisibleSync() ? 'visible' : 'hidden', 'important'); +        this._frame.style.setProperty('visibility', this.isVisibleSync() ? 'visible' : 'hidden', 'important');      }      _focusParent() {          if (this._parent !== null) {              // Chrome doesn't like focusing iframe without contentWindow. -            const contentWindow = this._parent._container.contentWindow; +            const contentWindow = this._parent.getFrame().contentWindow;              if (contentWindow !== null) {                  contentWindow.focus();              }          } else {              // Firefox doesn't like focusing window without first blurring the iframe. -            // this.container.contentWindow.blur() doesn't work on Firefox for some reason. -            this._container.blur(); +            // this._frame.contentWindow.blur() doesn't work on Firefox for some reason. +            this._frame.blur();              // This is needed for Chrome.              window.focus();          } @@ -507,19 +509,19 @@ class Popup {          const color = [255, 255, 255];          const {documentElement, body} = document;          if (documentElement !== null) { -            Popup._addColor(color, Popup._getColorInfo(window.getComputedStyle(documentElement).backgroundColor)); +            this._addColor(color, window.getComputedStyle(documentElement).backgroundColor);          }          if (body !== null) { -            Popup._addColor(color, Popup._getColorInfo(window.getComputedStyle(body).backgroundColor)); +            this._addColor(color, window.getComputedStyle(body).backgroundColor);          }          const dark = (color[0] < 128 && color[1] < 128 && color[2] < 128);          return dark ? 'dark' : 'light';      }      _invokeApi(action, params={}) { -        const secret = this._containerSecret; -        const token = this._containerToken; -        const contentWindow = this._container.contentWindow; +        const secret = this._frameSecret; +        const token = this._frameToken; +        const contentWindow = this._frame.contentWindow;          if (secret === null || token === null || contentWindow === null) { return; }          contentWindow.postMessage({action, params, secret, token}, this._targetOrigin); @@ -541,12 +543,12 @@ class Popup {          return fullscreenElement;      } -    static _getPositionForHorizontalText(elementRect, width, height, viewport, offsetScale, optionsGeneral) { +    _getPositionForHorizontalText(elementRect, width, height, viewport, offsetScale, optionsGeneral) {          const preferBelow = (optionsGeneral.popupHorizontalTextPosition === 'below');          const horizontalOffset = optionsGeneral.popupHorizontalOffset * offsetScale;          const verticalOffset = optionsGeneral.popupVerticalOffset * offsetScale; -        const [x, w] = Popup._getConstrainedPosition( +        const [x, w] = this._getConstrainedPosition(              elementRect.right - horizontalOffset,              elementRect.left + horizontalOffset,              width, @@ -554,7 +556,7 @@ class Popup {              viewport.right,              true          ); -        const [y, h, below] = Popup._getConstrainedPositionBinary( +        const [y, h, below] = this._getConstrainedPositionBinary(              elementRect.top - verticalOffset,              elementRect.bottom + verticalOffset,              height, @@ -565,12 +567,12 @@ class Popup {          return [x, y, w, h, below];      } -    static _getPositionForVerticalText(elementRect, width, height, viewport, offsetScale, optionsGeneral, writingMode) { -        const preferRight = Popup._isVerticalTextPopupOnRight(optionsGeneral.popupVerticalTextPosition, writingMode); +    _getPositionForVerticalText(elementRect, width, height, viewport, offsetScale, optionsGeneral, writingMode) { +        const preferRight = this._isVerticalTextPopupOnRight(optionsGeneral.popupVerticalTextPosition, writingMode);          const horizontalOffset = optionsGeneral.popupHorizontalOffset2 * offsetScale;          const verticalOffset = optionsGeneral.popupVerticalOffset2 * offsetScale; -        const [x, w] = Popup._getConstrainedPositionBinary( +        const [x, w] = this._getConstrainedPositionBinary(              elementRect.left - horizontalOffset,              elementRect.right + horizontalOffset,              width, @@ -578,7 +580,7 @@ class Popup {              viewport.right,              preferRight          ); -        const [y, h, below] = Popup._getConstrainedPosition( +        const [y, h, below] = this._getConstrainedPosition(              elementRect.bottom - verticalOffset,              elementRect.top + verticalOffset,              height, @@ -589,20 +591,22 @@ class Popup {          return [x, y, w, h, below];      } -    static _isVerticalTextPopupOnRight(positionPreference, writingMode) { +    _isVerticalTextPopupOnRight(positionPreference, writingMode) {          switch (positionPreference) {              case 'before': -                return !Popup._isWritingModeLeftToRight(writingMode); +                return !this._isWritingModeLeftToRight(writingMode);              case 'after': -                return Popup._isWritingModeLeftToRight(writingMode); +                return this._isWritingModeLeftToRight(writingMode);              case 'left':                  return false;              case 'right':                  return true; +            default: +                return false;          }      } -    static _isWritingModeLeftToRight(writingMode) { +    _isWritingModeLeftToRight(writingMode) {          switch (writingMode) {              case 'vertical-lr':              case 'sideways-lr': @@ -612,7 +616,7 @@ class Popup {          }      } -    static _getConstrainedPosition(positionBefore, positionAfter, size, minLimit, maxLimit, after) { +    _getConstrainedPosition(positionBefore, positionAfter, size, minLimit, maxLimit, after) {          size = Math.min(size, maxLimit - minLimit);          let position; @@ -627,7 +631,7 @@ class Popup {          return [position, size, after];      } -    static _getConstrainedPositionBinary(positionBefore, positionAfter, size, minLimit, maxLimit, after) { +    _getConstrainedPositionBinary(positionBefore, positionAfter, size, minLimit, maxLimit, after) {          const overflowBefore = minLimit - (positionBefore - size);          const overflowAfter = (positionAfter + size) - maxLimit; @@ -647,7 +651,10 @@ class Popup {          return [position, size, after];      } -    static _addColor(target, color) { +    _addColor(target, cssColor) { +        if (typeof cssColor !== 'string') { return; } + +        const color = this._getColorInfo(cssColor);          if (color === null) { return; }          const a = color[3]; @@ -659,7 +666,7 @@ class Popup {          }      } -    static _getColorInfo(cssColor) { +    _getColorInfo(cssColor) {          const m = /^\s*rgba?\s*\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*(?:,\s*([\d.]+)\s*)?\)\s*$/.exec(cssColor);          if (m === null) { return null; } @@ -672,7 +679,7 @@ class Popup {          ];      } -    static _getViewport(useVisualViewport) { +    _getViewport(useVisualViewport) {          const visualViewport = window.visualViewport;          if (visualViewport !== null && typeof visualViewport === 'object') {              const left = visualViewport.offsetLeft; @@ -706,7 +713,7 @@ class Popup {          };      } -    static async _injectStylesheet(id, type, value, useWebExtensionApi) { +    async _injectStylesheet(id, type, value, useWebExtensionApi) {          const injectedStylesheets = Popup._injectedStylesheets;          if (yomichan.isExtensionUrl(window.location.href)) { |