Bug 1298435 - upon browser window resize, update all the rects we track for each range we found and correctly reposition all oulines atop the mask. r?jaws draft
authorMike de Boer <mdeboer@mozilla.com>
Tue, 30 Aug 2016 13:25:58 +0200
changeset 407375 a91e70d797b5c3622d66f98b5ccba1f315662379
parent 407374 c485d31f19948bef2009e2d141a24ebe5bef2316
child 407500 a6cff2c689366a485e90b1cbb769a5c5d44e6db2
push id27954
push usermdeboer@mozilla.com
push dateTue, 30 Aug 2016 11:26:34 +0000
reviewersjaws
bugs1298435
milestone51.0a1
Bug 1298435 - upon browser window resize, update all the rects we track for each range we found and correctly reposition all oulines atop the mask. r?jaws MozReview-Commit-ID: 46q0hVxfczW
toolkit/modules/FinderHighlighter.jsm
--- a/toolkit/modules/FinderHighlighter.jsm
+++ b/toolkit/modules/FinderHighlighter.jsm
@@ -757,23 +757,24 @@ FinderHighlighter.prototype = {
 
     return false;
   },
 
   /**
    * Read and store the rectangles that encompass the entire region of a range
    * for use by the drawing function of the highlighter.
    *
-   * @param {nsIDOMRange} range            Range to fetch the rectangles from
-   * @param {Boolean}     [checkIfDynamic] Whether we should check if the range
-   *                                       is dynamic as per the rules in
-   *                                       `_isInDynamicContainer()`. Optional,
-   *                                       defaults to `true`
-   * @param {Object}     [dict]            Dictionary of properties belonging to
-   *                                       the currently active window
+   * @param  {nsIDOMRange} range            Range to fetch the rectangles from
+   * @param  {Boolean}     [checkIfDynamic] Whether we should check if the range
+   *                                        is dynamic as per the rules in
+   *                                        `_isInDynamicContainer()`. Optional,
+   *                                        defaults to `true`
+   * @param  {Object}      [dict]           Dictionary of properties belonging to
+   *                                        the currently active window
+   * @return {Set}         Set of rects that were found for the range
    */
   _updateRangeRects(range, checkIfDynamic = true, dict = null) {
     let window = range.startContainer.ownerDocument.defaultView;
     let bounds;
     // If the window is part of a frameset, try to cache the bounds query.
     if (dict && dict.frames.has(window)) {
       bounds = dict.frames.get(window);
       if (!bounds) {
@@ -795,16 +796,17 @@ FinderHighlighter.prototype = {
         x: dims.left + bounds.left
       });
     }
 
     dict = dict || this.getForWindow(window.top);
     dict.modalHighlightRectsMap.set(range, rects);
     if (checkIfDynamic && this._isInDynamicContainer(range))
       dict.dynamicRangesSet.add(range);
+    return rects;
   },
 
   /**
    * Re-read the rectangles of the ranges that we keep track of separately,
    * because they're enclosed by a position: fixed container DOM node.
    *
    * @param {Object} dict Dictionary of properties belonging to the currently
    *                      active window
@@ -825,17 +827,17 @@ FinderHighlighter.prototype = {
    *                               currently active window
    * @param {Array}  [textContent] Array of text that's inside the range. Optional,
    *                               defaults to an empty array
    * @param {Object} [fontStyle]   Dictionary of CSS styles in camelCase as
    *                               returned by `_getRangeFontStyle()`. Optional
    */
   _updateRangeOutline(dict, textContent = [], fontStyle = null) {
     let outlineNode = dict.modalHighlightOutline;
-    let range = dict.currentFoundRange;
+    let range = this.finder._fastFind.getFoundRange();
     if (!outlineNode || !range)
       return;
     let rect = range.getClientRects()[0];
     if (!rect)
       return;
 
     if (!fontStyle)
       fontStyle = this._getRangeFontStyle(range);
@@ -959,21 +961,24 @@ FinderHighlighter.prototype = {
 
     if (paintContent || dict.modalHighlightAllMask) {
       this._updateRangeOutline(dict);
       this._updateFixedRangesRects(dict);
       // Create a DOM node for each rectangle representing the ranges we found.
       let maskContent = [];
       const kRectClassName = kModalIdPrefix + "-findbar-modalHighlight-rect";
       for (let [range, rects] of dict.modalHighlightRectsMap) {
+        if (dict.updateAllRanges)
+          rects = this._updateRangeRects(range);
         for (let rect of rects) {
           maskContent.push(`<div class="${kRectClassName}" style="top: ${rect.y}px;
             left: ${rect.x}px; height: ${rect.height}px; width: ${rect.width}px;"></div>`);
         }
       }
+      dict.updateAllRanges = false;
       maskNode.innerHTML = maskContent.join("");
     }
 
     // Always remove the current mask and insert it a-fresh, because we're not
     // free to alter DOM nodes inside the CanvasFrame.
     this._removeHighlightAllMask(window);
 
     dict.modalHighlightAllMask = kDebug ?
@@ -1007,35 +1012,41 @@ FinderHighlighter.prototype = {
   /**
    * Doing a full repaint each time a range is delivered by the highlight iterator
    * is way too costly, thus we pipe the frequency down to every
    * `kModalHighlightRepaintFreqMs` milliseconds.
    *
    * @param {nsIDOMWindow} window
    * @param {Object}       options Dictionary of painter hints that contains the
    *                               following properties:
-   *   {Boolean} contentChanged Whether the documents' content changed in the
-   *                            meantime. This happens when the DOM is updated
-   *                            whilst the page is loaded.
-   *   {Boolean} scrollOnly     TRUE when the page has scrolled in the meantime,
-   *                            which means that the fixed positioned elements
-   *                            need to be repainted.
+   *   {Boolean} contentChanged  Whether the documents' content changed in the
+   *                             meantime. This happens when the DOM is updated
+   *                             whilst the page is loaded.
+   *   {Boolean} scrollOnly      TRUE when the page has scrolled in the meantime,
+   *                             which means that the fixed positioned elements
+   *                             need to be repainted.
+   *   {Boolean} updateAllRanges Whether to recalculate the rects of all ranges
+   *                             that were found up until now.
    */
-  _scheduleRepaintOfMask(window, { contentChanged, scrollOnly } = { contentChanged: false, scrollOnly: false }) {
+  _scheduleRepaintOfMask(window, { contentChanged, scrollOnly, updateAllRanges } =
+                                 { contentChanged: false, scrollOnly: false, updateAllRanges: false }) {
     if (!this._modal)
       return;
 
     window = window.top;
     let dict = this.getForWindow(window);
     let repaintFixedNodes = (scrollOnly && !!dict.dynamicRangesSet.size);
 
     // When we request to repaint unconditionally, we mean to call
     // `_repaintHighlightAllMask()` right after the timeout.
     if (!dict.unconditionalRepaintRequested)
       dict.unconditionalRepaintRequested = !contentChanged || repaintFixedNodes;
+    // Some events, like a resize, call for recalculation of all the rects of all ranges.
+    if (!dict.updateAllRanges)
+      dict.updateAllRanges = updateAllRanges;
 
     if (dict.modalRepaintScheduler)
       return;
 
     dict.modalRepaintScheduler = window.setTimeout(() => {
       dict.modalRepaintScheduler = null;
 
       if (dict.unconditionalRepaintRequested) {
@@ -1089,42 +1100,45 @@ FinderHighlighter.prototype = {
     window = window.top;
     let dict = this.getForWindow(window);
     if (dict.highlightListeners)
       return;
 
     window = window.top;
     dict.highlightListeners = [
       this._scheduleRepaintOfMask.bind(this, window, { contentChanged: true }),
+      this._scheduleRepaintOfMask.bind(this, window, { updateAllRanges: true }),
       this._scheduleRepaintOfMask.bind(this, window, { scrollOnly: true }),
       this.hide.bind(this, window, null)
     ];
     let target = this.iterator._getDocShell(window).chromeEventHandler;
     target.addEventListener("MozAfterPaint", dict.highlightListeners[0]);
-    target.addEventListener("DOMMouseScroll", dict.highlightListeners[1]);
-    target.addEventListener("mousewheel", dict.highlightListeners[1]);
-    target.addEventListener("click", dict.highlightListeners[2]);
+    target.addEventListener("resize", dict.highlightListeners[1]);
+    target.addEventListener("DOMMouseScroll", dict.highlightListeners[2]);
+    target.addEventListener("mousewheel", dict.highlightListeners[2]);
+    target.addEventListener("click", dict.highlightListeners[3]);
   },
 
   /**
    * Remove event listeners from content.
    *
    * @param {nsIDOMWindow} window
    */
   _removeModalHighlightListeners(window) {
     window = window.top;
     let dict = this.getForWindow(window);
     if (!dict.highlightListeners)
       return;
 
     let target = this.iterator._getDocShell(window).chromeEventHandler;
     target.removeEventListener("MozAfterPaint", dict.highlightListeners[0]);
-    target.removeEventListener("DOMMouseScroll", dict.highlightListeners[1]);
-    target.removeEventListener("mousewheel", dict.highlightListeners[1]);
-    target.removeEventListener("click", dict.highlightListeners[2]);
+    target.removeEventListener("resize", dict.highlightListeners[1]);
+    target.removeEventListener("DOMMouseScroll", dict.highlightListeners[2]);
+    target.removeEventListener("mousewheel", dict.highlightListeners[2]);
+    target.removeEventListener("click", dict.highlightListeners[3]);
 
     dict.highlightListeners = null;
   },
 
   /**
    * For a given node returns its editable parent or null if there is none.
    * It's enough to check if node is a text node and its parent's parent is
    * instance of nsIDOMNSEditableElement.