diff options
Diffstat (limited to 'ext/js/dom/document-util.js')
| -rw-r--r-- | ext/js/dom/document-util.js | 87 | 
1 files changed, 65 insertions, 22 deletions
| diff --git a/ext/js/dom/document-util.js b/ext/js/dom/document-util.js index b974387e..41f44afe 100644 --- a/ext/js/dom/document-util.js +++ b/ext/js/dom/document-util.js @@ -24,10 +24,9 @@  class DocumentUtil {      constructor() {          this._transparentColorPattern = /rgba\s*\([^)]*,\s*0(?:\.0+)?\s*\)/; -        this._cssZoomSupported = (typeof document.createElement('div').style.zoom === 'string');      } -    getRangeFromPoint(x, y, deepContentScan) { +    getRangeFromPoint(x, y, {deepContentScan, normalizeCssZoom}) {          const elements = this._getElementsFromPoint(x, y, deepContentScan);          let imposter = null;          let imposterContainer = null; @@ -52,7 +51,7 @@ class DocumentUtil {              }          } -        const range = this._caretRangeFromPointExt(x, y, deepContentScan ? elements : []); +        const range = this._caretRangeFromPointExt(x, y, deepContentScan ? elements : [], normalizeCssZoom);          if (range !== null) {              if (imposter !== null) {                  this._setImposterStyle(imposterContainer.style, 'z-index', '-2147483646'); @@ -175,6 +174,60 @@ class DocumentUtil {          };      } +    /** +     * Computes the scaling adjustment that is necessary for client space coordinates based on the +     * CSS zoom level. +     * @param {Node} node A node in the document. +     * @returns {number} The scaling factor. +     */ +    static computeZoomScale(node) { +        if (this._cssZoomSupported === null) { +            this._cssZoomSupported = (typeof document.createElement('div').style.zoom === 'string'); +        } +        if (!this._cssZoomSupported) { return 1; } +        // documentElement must be excluded because the computer style of its zoom property is inconsistent. +        // * If CSS `:root{zoom:X;}` is specified, the computed zoom will always report `X`. +        // * If CSS `:root{zoom:X;}` is not specified, the computed zoom report the browser's zoom level. +        // Therefor, if CSS root zoom is specified as a value other than 1, the adjusted {x, y} values +        // would be incorrect, which is not new behaviour. +        let scale = 1; +        const {ELEMENT_NODE, DOCUMENT_FRAGMENT_NODE} = Node; +        const {documentElement} = document; +        for (; node !== null && node !== documentElement; node = node.parentNode) { +            const {nodeType} = node; +            if (nodeType === DOCUMENT_FRAGMENT_NODE) { +                const {host} = node; +                if (typeof host !== 'undefined') { +                    node = host; +                } +                continue; +            } else if (nodeType !== ELEMENT_NODE) { +                continue; +            } +            let {zoom} = getComputedStyle(node); +            if (typeof zoom !== 'string') { continue; } +            zoom = Number.parseFloat(zoom); +            if (!Number.isFinite(zoom) || zoom === 0) { continue; } +            scale *= zoom; +        } +        return scale; +    } + +    static convertRectZoomCoordinates(rect, node) { +        const scale = this.computeZoomScale(node); +        return (scale === 1 ? rect : new DOMRect(rect.left * scale, rect.top * scale, rect.width * scale, rect.height * scale)); +    } + +    static convertMultipleRectZoomCoordinates(rects, node) { +        const scale = this.computeZoomScale(node); +        if (scale === 1) { return rects; } +        const results = []; +        for (const rect of rects) { +            results.push(new DOMRect(rect.left * scale, rect.top * scale, rect.width * scale, rect.height * scale)); +        } +        return results; +    } +      static isPointInRect(x, y, rect) {          return (              x >= rect.left && x < rect.right && @@ -435,7 +488,7 @@ class DocumentUtil {          return e !== null ? [e] : [];      } -    _isPointInRange(x, y, range) { +    _isPointInRange(x, y, range, normalizeCssZoom) {          // Require a text node to start          const {startContainer} = range;          if (startContainer.nodeType !== Node.TEXT_NODE) { @@ -443,8 +496,10 @@ class DocumentUtil {          }          // Convert CSS zoom coordinates -        if (this._cssZoomSupported) { -            ({x, y} = this._convertCssZoomCoordinates(x, y, startContainer)); +        if (normalizeCssZoom) { +            const scale = DocumentUtil.computeZoomScale(startContainer); +            x /= scale; +            y /= scale;          }          // Scan forward @@ -583,7 +638,7 @@ class DocumentUtil {          }      } -    _caretRangeFromPointExt(x, y, elements) { +    _caretRangeFromPointExt(x, y, elements, normalizeCssZoom) {          let previousStyles = null;          try {              let i = 0; @@ -596,7 +651,7 @@ class DocumentUtil {                  const startContainer = range.startContainer;                  if (startContinerPre !== startContainer) { -                    if (this._isPointInRange(x, y, range)) { +                    if (this._isPointInRange(x, y, range, normalizeCssZoom)) {                          return range;                      }                      startContinerPre = startContainer; @@ -668,18 +723,6 @@ class DocumentUtil {      _isElementUserSelectAll(element) {          return getComputedStyle(element).userSelect === 'all';      } - -    _convertCssZoomCoordinates(x, y, node) { -        const ELEMENT_NODE = Node.ELEMENT_NODE; -        for (; node !== null; node = node.parentNode) { -            if (node.nodeType !== ELEMENT_NODE) { continue; } -            let {zoom} = getComputedStyle(node); -            if (typeof zoom !== 'string') { continue; } -            zoom = Number.parseFloat(zoom); -            if (!Number.isFinite(zoom) || zoom === 0) { continue; } -            x /= zoom; -            y /= zoom; -        } -        return {x, y}; -    }  } +// eslint-disable-next-line no-underscore-dangle +DocumentUtil._cssZoomSupported = null; |