Bug 1279695 - update the position of ranges that are inside fixed, sticky positioned container nodes or frameset or iframe at each repaint or scroll. r=jaws

MozReview-Commit-ID: J8YK1s2kArq
This commit is contained in:
Mike de Boer 2016-08-25 20:11:46 +02:00
Родитель 56661a22a9
Коммит 915c0ea08b
3 изменённых файлов: 333 добавлений и 141 удалений

Просмотреть файл

@ -484,7 +484,7 @@ skip-if = (os == "win" && !debug)
[browser_windowopen_reflows.js]
skip-if = buildapp == 'mulet'
[browser_zbug569342.js]
skip-if = e10s # Bug 1094240 - has findbar-related failures
skip-if = e10s || debug # Bug 1094240 - has findbar-related failures
[browser_registerProtocolHandler_notification.js]
[browser_no_mcb_on_http_site.js]
tags = mcb

Просмотреть файл

@ -13,13 +13,14 @@ Cu.import("resource://gre/modules/Task.jsm");
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "Color", "resource://gre/modules/Color.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "Rect", "resource://gre/modules/Geometry.jsm");
XPCOMUtils.defineLazyGetter(this, "kDebug", () => {
const kDebugPref = "findbar.modalHighlight.debug";
return Services.prefs.getPrefType(kDebugPref) && Services.prefs.getBoolPref(kDebugPref);
});
const kContentChangeThresholdPx = 5;
const kModalHighlightRepaintFreqMs = 100;
const kModalHighlightRepaintFreqMs = 200;
const kHighlightAllPref = "findbar.highlightAll";
const kModalHighlightPref = "findbar.modalHighlight";
const kFontPropsCSS = ["color", "font-family", "font-kerning", "font-size",
@ -144,6 +145,8 @@ function mockAnonymousContentNode(domNode) {
};
}
let gWindows = new Map();
/**
* FinderHighlighter class that is used by Finder.jsm to take care of the
* 'Highlight All' feature, which can highlight all find occurrences in a page.
@ -151,12 +154,9 @@ function mockAnonymousContentNode(domNode) {
* @param {Finder} finder Finder.jsm instance
*/
function FinderHighlighter(finder) {
this._currentFoundRange = null;
this._modal = Services.prefs.getBoolPref(kModalHighlightPref);
this._highlightAll = Services.prefs.getBoolPref(kHighlightAllPref);
this._lastIteratorParams = null;
this._modal = Services.prefs.getBoolPref(kModalHighlightPref);
this.finder = finder;
this.visible = false;
}
FinderHighlighter.prototype = {
@ -183,6 +183,34 @@ FinderHighlighter.prototype = {
return this._modalStyleSheetURI;
},
/**
* Each window is unique, globally, and the relation between an active
* highlighting session and a window is 1:1.
* For each window we track a number of properties which _at least_ consist of
* - {Set} dynamicRangesSet Set of ranges that may move around, depending
* on page layout changes and user input
* - {Map} frames Collection of frames that were encountered
* when inspecting the found ranges
* - {Boolean} installedSheet Whether the modal stylesheet was loaded
* already
* - {Map} modalHighlightRectsMap Collection of ranges and their corresponding
* Rects
*
* @param {nsIDOMWindow} window
* @return {Object}
*/
getForWindow(window, propName = null) {
if (!gWindows.has(window)) {
gWindows.set(window, {
dynamicRangesSet: new Set(),
frames: new Map(),
installedSheet: false,
modalHighlightRectsMap: new Map()
});
}
return gWindows.get(window);
},
/**
* Notify all registered listeners that the 'Highlight All' operation finished.
*
@ -207,6 +235,7 @@ FinderHighlighter.prototype = {
*/
highlight: Task.async(function* (highlight, word, linksOnly) {
let window = this.finder._getWindow();
let dict = this.getForWindow(window);
let controller = this.finder._getSelectionController(window);
let doc = window.document;
this._found = false;
@ -227,7 +256,7 @@ FinderHighlighter.prototype = {
listener: this,
useCache: true
};
if (this.iterator._areParamsEqual(params, this._lastIteratorParams))
if (this.iterator._areParamsEqual(params, dict.lastIteratorParams))
return this._found;
if (params) {
yield this.iterator.start(params);
@ -252,17 +281,19 @@ FinderHighlighter.prototype = {
},
onIteratorReset() {
this.clear();
this.clear(this.finder._getWindow());
},
onIteratorRestart() {},
onIteratorStart(params) {
let window = this.finder._getWindow();
let dict = this.getForWindow(window);
// Save a clean params set for use later in the `update()` method.
this._lastIteratorParams = params;
this.clear();
dict.lastIteratorParams = params;
this.clear(window);
if (!this._modal)
this.hide(this.finder._getWindow(), this.finder._fastFind.getFoundRange());
this.hide(window, this.finder._fastFind.getFoundRange());
},
/**
@ -302,11 +333,13 @@ FinderHighlighter.prototype = {
* Optional, defaults to the finder window.
*/
show(window = null) {
if (!this._modal || this.visible)
window = (window || this.finder._getWindow()).top;
let dict = this.getForWindow(window);
if (!this._modal || dict.visible)
return;
this.visible = true;
window = window || this.finder._getWindow();
dict.visible = true;
this._maybeCreateModalHighlightNodes(window);
this._addModalHighlightListeners(window);
},
@ -327,14 +360,17 @@ FinderHighlighter.prototype = {
if (event && event.type == "click" && event.button !== 0)
return;
window = window || this.finder._getWindow();
window = (window || this.finder._getWindow()).top;
let dict = this.getForWindow(window);
let doc = window.document;
this._clearSelection(this.finder._getSelectionController(window), skipRange);
for (let frame of dict.frames)
this._clearSelection(this.finder._getSelectionController(frame), skipRange);
// Next, check our editor cache, for editors belonging to this
// document
if (this._editors) {
let doc = window.document;
for (let x = this._editors.length - 1; x >= 0; --x) {
if (this._editors[x].document == doc) {
this._clearSelection(this._editors[x].selectionController, skipRange);
@ -344,20 +380,20 @@ FinderHighlighter.prototype = {
}
}
if (this._modalRepaintScheduler) {
window.clearTimeout(this._modalRepaintScheduler);
this._modalRepaintScheduler = null;
if (dict.modalRepaintScheduler) {
window.clearTimeout(dict.modalRepaintScheduler);
dict.modalRepaintScheduler = null;
}
this._lastWindowDimensions = null;
dict.lastWindowDimensions = null;
if (this._modalHighlightOutline)
this._modalHighlightOutline.setAttributeForElement(kModalOutlineId, "hidden", "true");
if (dict.modalHighlightOutline)
dict.modalHighlightOutline.setAttributeForElement(kModalOutlineId, "hidden", "true");
this._removeHighlightAllMask(window);
this._removeModalHighlightListeners(window);
delete this._brightText;
delete dict.brightText;
this.visible = false;
dict.visible = false;
},
/**
@ -383,12 +419,13 @@ FinderHighlighter.prototype = {
*/
update(data) {
let window = this.finder._getWindow();
let dict = this.getForWindow(window);
let foundRange = this.finder._fastFind.getFoundRange();
if (!this._modal) {
if (this._highlightAll) {
this._currentFoundRange = foundRange;
dict.currentFoundRange = foundRange;
let params = this.iterator.params;
if (this.iterator._areParamsEqual(params, this._lastIteratorParams))
if (this.iterator._areParamsEqual(params, dict.lastIteratorParams))
return;
if (params)
this.highlight(true, params.word, params.linksOnly);
@ -403,8 +440,8 @@ FinderHighlighter.prototype = {
}
let outlineNode;
if (foundRange !== this._currentFoundRange || data.findAgain) {
this._currentFoundRange = foundRange;
if (foundRange !== dict.currentFoundRange || data.findAgain) {
dict.currentFoundRange = foundRange;
let textContent = this._getRangeContentArray(foundRange);
if (!textContent.length) {
@ -412,36 +449,20 @@ FinderHighlighter.prototype = {
return;
}
let rect = foundRange.getClientRects()[0];
let fontStyle = this._getRangeFontStyle(foundRange);
if (typeof this._brightText == "undefined") {
this._brightText = this._isColorBright(fontStyle.color);
if (typeof dict.brightText == "undefined") {
dict.brightText = this._isColorBright(fontStyle.color);
}
// Text color in the outline is determined by our stylesheet.
delete fontStyle.color;
if (!this.visible)
if (!dict.visible)
this.show(window);
else
this._maybeCreateModalHighlightNodes(window);
outlineNode = this._modalHighlightOutline;
outlineNode.setTextContentForElement(kModalOutlineId + "-text", textContent.join(" "));
// Correct the line-height to align the text in the middle of the box.
fontStyle.lineHeight = rect.height + "px";
outlineNode.setAttributeForElement(kModalOutlineId + "-text", "style",
this._getHTMLFontStyle(fontStyle));
if (typeof outlineNode.getAttributeForElement(kModalOutlineId, "hidden") == "string")
outlineNode.removeAttributeForElement(kModalOutlineId, "hidden");
let { scrollX, scrollY } = this._getScrollPosition(window);
outlineNode.setAttributeForElement(kModalOutlineId, "style",
`top: ${scrollY + rect.top}px; left: ${scrollX + rect.left}px;
height: ${rect.height}px; width: ${rect.width}px;`);
this._updateRangeOutline(dict, textContent, fontStyle);
}
outlineNode = this._modalHighlightOutline;
outlineNode = dict.modalHighlightOutline;
try {
outlineNode.removeAttributeForElement(kModalOutlineId, "grow");
} catch (ex) {}
@ -454,12 +475,18 @@ FinderHighlighter.prototype = {
* Invalidates the list by clearing the map of highglighted ranges that we
* keep to build the mask for.
*/
clear() {
this._currentFoundRange = null;
clear(window = null) {
if (!window) {
// Reset the Map, because no range references a node anymore.
gWindows.clear();
return;
}
// Reset the Map, because no range references a node anymore.
if (this._modalHighlightRectsMap)
this._modalHighlightRectsMap.clear();
let dict = this.getForWindow(window.top);
dict.currentFoundRange = null;
dict.dynamicRangesSet.clear();
dict.frames.clear();
dict.modalHighlightRectsMap.clear();
},
/**
@ -469,21 +496,22 @@ FinderHighlighter.prototype = {
* everything when the user starts to find in page again.
*/
onLocationChange() {
this.clear();
let window = this.finder._getWindow();
let dict = this.getForWindow(window);
this.clear(window);
if (!this._modalHighlightOutline)
if (!dict.modalHighlightOutline)
return;
if (kDebug) {
this._modalHighlightOutline.remove();
dict.modalHighlightOutline.remove();
} else {
try {
this.finder._getWindow().document
.removeAnonymousContent(this._modalHighlightOutline);
window.document.removeAnonymousContent(dict.modalHighlightOutline);
} catch (ex) {}
}
this._modalHighlightOutline = null;
dict.modalHighlightOutline = null;
},
/**
@ -523,6 +551,8 @@ FinderHighlighter.prototype = {
* @param {nsIDOMRange} restoreRange
*/
_clearSelection(controller, restoreRange = null) {
if (!controller)
return;
let sel = controller.getSelection(Ci.nsISelectionController.SELECTION_FIND);
sel.removeAllRanges();
if (restoreRange) {
@ -546,19 +576,41 @@ FinderHighlighter.prototype = {
},
/**
* Utility; wrapper around nsIDOMWindowUtils#getScrollXY.
* Utility; returns the bounds of the page relative to the viewport.
* If the pages is part of a frameset or inside an iframe of any kind, its
* offset is accounted for.
* Geometry.jsm takes care of the DOMRect calculations.
*
* @param {nsDOMWindow} window Optional, defaults to the finder window.
* @return {Object} The current scroll position.
* @param {nsIDOMWindow} window
* @return {Rect}
*/
_getScrollPosition(window = null) {
_getRootBounds(window) {
let dwu = this._getDWU(window);
let cssPageRect = Rect.fromRect(dwu.getRootBounds());
let scrollX = {};
let scrollY = {};
this._getDWU(window).getScrollXY(false, scrollX, scrollY);
return {
scrollX: scrollX.value,
scrollY: scrollY.value
};
dwu.getScrollXY(false, scrollX, scrollY);
cssPageRect.translate(scrollX.value, scrollY.value);
// If we're in a frame, update the position of the rect (top/ left).
let currWin = window;
while (currWin != window.top) {
// Since the frame is an element inside a parent window, we'd like to
// learn its position relative to it.
let el = this._getDWU(currWin).containerElement;
currWin = window.parent;
dwu = this._getDWU(currWin);
let parentRect = Rect.fromRect(dwu.getBoundsWithoutFlushing(el));
// Always take the scroll position into account.
dwu.getScrollXY(false, scrollX, scrollY);
parentRect.translate(scrollX.value, scrollY.value);
cssPageRect.translate(parentRect.left, parentRect.top);
}
return cssPageRect;
},
/**
@ -572,7 +624,7 @@ FinderHighlighter.prototype = {
_getWindowDimensions(window) {
// First we'll try without flushing layout, because it's way faster.
let dwu = this._getDWU(window);
let {width, height} = dwu.getBoundsWithoutFlushing(window.document.body);
let { width, height } = dwu.getRootBounds();
if (!width || !height) {
// We need a flush after all :'(
@ -658,6 +710,141 @@ FinderHighlighter.prototype = {
return new Color(...cssColor).isBright;
},
/**
* Checks if a range is inside a DOM node that's positioned in a way that it
* doesn't scroll along when the document is scrolled and/ or zoomed. This
* is the case for 'fixed' and 'sticky' positioned elements and elements inside
* (i)frames.
*
* @param {nsIDOMRange} range Range that be enclosed in a fixed container
* @return {Boolean}
*/
_isInDynamicContainer(range) {
const kFixed = new Set(["fixed", "sticky"]);
let node = range.startContainer;
while (node.nodeType != 1)
node = node.parentNode;
let document = node.ownerDocument;
let window = document.defaultView;
let dict = this.getForWindow(window.top);
// Check if we're in a frameset (including iframes).
if (window != window.top) {
if (!dict.frames.has(window))
dict.frames.set(window, null);
return true;
}
do {
if (kFixed.has(window.getComputedStyle(node, null).position))
return true;
node = node.parentNode;
} while (node && node != document.documentElement)
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
*/
_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) {
bounds = this._getRootBounds(window);
dict.frames.set(window, bounds);
}
} else
bounds = this._getRootBounds(window);
let rects = new Set();
// A range may consist of multiple rectangles, we can also do these kind of
// precise cut-outs. range.getBoundingClientRect() returns the fully
// encompassing rectangle, which is too much for our purpose here.
for (let dims of range.getClientRects()) {
rects.add({
height: dims.bottom - dims.top,
width: dims.right - dims.left,
y: dims.top + bounds.top,
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);
},
/**
* 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
*/
_updateFixedRangesRects(dict) {
for (let range of dict.dynamicRangesSet)
this._updateRangeRects(range, false, dict);
// Reset the frame bounds cache.
for (let frame of dict.frames.keys())
dict.frames.set(frame, null);
},
/**
* Update the content, position and style of the yellow current found range
* outline the floats atop the mask with the dimmed background.
*
* @param {Object} dict Dictionary of properties belonging to the
* 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;
if (!outlineNode || !range)
return;
let rect = range.getClientRects()[0];
if (!rect)
return;
if (!fontStyle)
fontStyle = this._getRangeFontStyle(range);
// Text color in the outline is determined by our stylesheet.
delete fontStyle.color;
if (textContent.length)
outlineNode.setTextContentForElement(kModalOutlineId + "-text", textContent.join(" "));
// Correct the line-height to align the text in the middle of the box.
fontStyle.lineHeight = rect.height + "px";
outlineNode.setAttributeForElement(kModalOutlineId + "-text", "style",
this._getHTMLFontStyle(fontStyle));
if (typeof outlineNode.getAttributeForElement(kModalOutlineId, "hidden") == "string")
outlineNode.removeAttributeForElement(kModalOutlineId, "hidden");
let window = range.startContainer.ownerDocument.defaultView;
let { left, top } = this._getRootBounds(window);
outlineNode.setAttributeForElement(kModalOutlineId, "style",
`top: ${top + rect.top}px; left: ${left + rect.left}px;
height: ${rect.height}px; width: ${rect.width}px;`);
},
/**
* Add a range to the list of ranges to highlight on, or cut out of, the dimmed
* background.
@ -669,24 +856,7 @@ FinderHighlighter.prototype = {
if (!this._getRangeContentArray(range).length)
return;
let rects = new Set();
// Absolute positions should include the viewport scroll offset.
let { scrollX, scrollY } = this._getScrollPosition(window);
// A range may consist of multiple rectangles, we can also do these kind of
// precise cut-outs. range.getBoundingClientRect() returns the fully
// encompassing rectangle, which is too much for our purpose here.
for (let dims of range.getClientRects()) {
rects.add({
height: dims.bottom - dims.top,
width: dims.right - dims.left,
y: dims.top + scrollY,
x: dims.left + scrollX
});
}
if (!this._modalHighlightRectsMap)
this._modalHighlightRectsMap = new Map();
this._modalHighlightRectsMap.set(range, rects);
this._updateRangeRects(range);
this.show(window);
// We don't repaint the mask right away, but pass it off to a render loop of
@ -701,8 +871,10 @@ FinderHighlighter.prototype = {
* @param {nsIDOMWindow} window Window to draw in.
*/
_maybeCreateModalHighlightNodes(window) {
if (this._modalHighlightOutline) {
if (!this._modalHighlightAllMask) {
window = window.top;
let dict = this.getForWindow(window);
if (dict.modalHighlightOutline) {
if (!dict.modalHighlightAllMask) {
// Make sure to at least show the dimmed background.
this._repaintHighlightAllMask(window, false);
this._scheduleRepaintOfMask(window);
@ -738,7 +910,7 @@ FinderHighlighter.prototype = {
outlineBox.appendChild(outlineBoxText);
container.appendChild(outlineBox);
this._modalHighlightOutline = kDebug ?
dict.modalHighlightOutline = kDebug ?
mockAnonymousContentNode(document.body.appendChild(container.firstChild)) :
document.insertAnonymousContent(container);
@ -755,6 +927,8 @@ FinderHighlighter.prototype = {
* @param {Boolean} [paintContent]
*/
_repaintHighlightAllMask(window, paintContent = true) {
window = window.top;
let dict = this.getForWindow(window);
let document = window.document;
const kMaskId = kModalIdPrefix + "-findbar-modalHighlight-outlineMask";
@ -762,23 +936,23 @@ FinderHighlighter.prototype = {
// Make sure the dimmed mask node takes the full width and height that's available.
let {width, height} = this._getWindowDimensions(window);
this._lastWindowDimensions = { width, height };
dict.lastWindowDimensions = { width, height };
maskNode.setAttribute("id", kMaskId);
maskNode.setAttribute("class", kMaskId + (kDebug ? ` ${kModalIdPrefix}-findbar-debug` : ""));
maskNode.setAttribute("style", `width: ${width}px; height: ${height}px;`);
if (this._brightText)
if (dict.brightText)
maskNode.setAttribute("brighttext", "true");
if (paintContent || this._modalHighlightAllMask) {
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";
if (this._modalHighlightRectsMap) {
for (let [range, rects] of this._modalHighlightRectsMap) {
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>`);
}
for (let [range, rects] of dict.modalHighlightRectsMap) {
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>`);
}
}
maskNode.innerHTML = maskContent.join("");
@ -788,7 +962,7 @@ FinderHighlighter.prototype = {
// free to alter DOM nodes inside the CanvasFrame.
this._removeHighlightAllMask(window);
this._modalHighlightAllMask = kDebug ?
dict.modalHighlightAllMask = kDebug ?
mockAnonymousContentNode(document.body.appendChild(maskNode)) :
document.insertAnonymousContent(maskNode);
},
@ -799,19 +973,21 @@ FinderHighlighter.prototype = {
* @param {nsIDOMWindow} window
*/
_removeHighlightAllMask(window) {
if (!this._modalHighlightAllMask)
window = window.top;
let dict = this.getForWindow(window);
if (!dict.modalHighlightAllMask)
return;
// If the current window isn't the one the content was inserted into, this
// will fail, but that's fine.
if (kDebug) {
this._modalHighlightAllMask.remove();
dict.modalHighlightAllMask.remove();
} else {
try {
window.document.removeAnonymousContent(this._modalHighlightAllMask);
window.document.removeAnonymousContent(dict.modalHighlightAllMask);
} catch (ex) {}
}
this._modalHighlightAllMask = null;
dict.modalHighlightAllMask = null;
},
/**
@ -820,37 +996,46 @@ FinderHighlighter.prototype = {
* `kModalHighlightRepaintFreqMs` milliseconds.
*
* @param {nsIDOMWindow} window
* @param {Boolean} contentChanged Whether the documents' content changed
* in the meantime. This happens when the
* DOM is updated whilst the page is loaded.
* @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.
*/
_scheduleRepaintOfMask(window, contentChanged = false) {
if (this._modalRepaintScheduler) {
window.clearTimeout(this._modalRepaintScheduler);
this._modalRepaintScheduler = null;
}
_scheduleRepaintOfMask(window, { contentChanged, scrollOnly } = { contentChanged: false, scrollOnly: false }) {
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 (!this._unconditionalRepaintRequested)
this._unconditionalRepaintRequested = !contentChanged;
if (!dict.unconditionalRepaintRequested)
dict.unconditionalRepaintRequested = !contentChanged || repaintFixedNodes;
this._modalRepaintScheduler = window.setTimeout(() => {
if (this._unconditionalRepaintRequested) {
this._unconditionalRepaintRequested = false;
if (dict.modalRepaintScheduler)
return;
dict.modalRepaintScheduler = window.setTimeout(() => {
dict.modalRepaintScheduler = null;
if (dict.unconditionalRepaintRequested) {
dict.unconditionalRepaintRequested = false;
this._repaintHighlightAllMask(window);
return;
}
let { width, height } = this._getWindowDimensions(window);
if (!this._modalHighlightRectsMap ||
(Math.abs(this._lastWindowDimensions.width - width) < kContentChangeThresholdPx &&
Math.abs(this._lastWindowDimensions.height - height) < kContentChangeThresholdPx)) {
if (!dict.modalHighlightRectsMap.size ||
(Math.abs(dict.lastWindowDimensions.width - width) < kContentChangeThresholdPx &&
Math.abs(dict.lastWindowDimensions.height - height) < kContentChangeThresholdPx)) {
return;
}
this.iterator.restart(this.finder);
this._lastWindowDimensions = { width, height };
dict.lastWindowDimensions = { width, height };
this._repaintHighlightAllMask(window);
}, kModalHighlightRepaintFreqMs);
},
@ -864,12 +1049,9 @@ FinderHighlighter.prototype = {
* @param {nsIDOMWindow} window
*/
_maybeInstallStyleSheet(window) {
let document = window.document;
// The WeakMap is a cheap method to make sure we don't needlessly insert the
// same sheet twice.
if (!this._modalInstalledSheets)
this._modalInstalledSheets = new WeakMap();
if (this._modalInstalledSheets.has(document))
window = window.top;
let dict = this.getForWindow(window);
if (dict.installedSheet)
return;
let dwu = this._getDWU(window);
@ -877,7 +1059,7 @@ FinderHighlighter.prototype = {
try {
dwu.loadSheetUsingURIString(uri, dwu.AGENT_SHEET);
} catch (e) {}
this._modalInstalledSheets.set(document, uri);
dict.installedSheet = true;
},
/**
@ -887,16 +1069,22 @@ FinderHighlighter.prototype = {
* @param {nsIDOMWindow} window
*/
_addModalHighlightListeners(window) {
if (this._highlightListeners)
window = window.top;
let dict = this.getForWindow(window);
if (dict.highlightListeners)
return;
this._highlightListeners = [
this._scheduleRepaintOfMask.bind(this, window, true),
window = window.top;
dict.highlightListeners = [
this._scheduleRepaintOfMask.bind(this, window, { contentChanged: true }),
this._scheduleRepaintOfMask.bind(this, window, { scrollOnly: true }),
this.hide.bind(this, window, null)
];
let target = this.iterator._getDocShell(window).chromeEventHandler;
target.addEventListener("MozAfterPaint", this._highlightListeners[0]);
window.addEventListener("click", this._highlightListeners[1]);
target.addEventListener("MozAfterPaint", dict.highlightListeners[0]);
target.addEventListener("DOMMouseScroll", dict.highlightListeners[1]);
target.addEventListener("mousewheel", dict.highlightListeners[1]);
target.addEventListener("click", dict.highlightListeners[2]);
},
/**
@ -905,14 +1093,18 @@ FinderHighlighter.prototype = {
* @param {nsIDOMWindow} window
*/
_removeModalHighlightListeners(window) {
if (!this._highlightListeners)
window = window.top;
let dict = this.getForWindow(window);
if (!dict.highlightListeners)
return;
let target = this.iterator._getDocShell(window).chromeEventHandler;
target.removeEventListener("MozAfterPaint", this._highlightListeners[0]);
window.removeEventListener("click", this._highlightListeners[1]);
target.removeEventListener("MozAfterPaint", dict.highlightListeners[0]);
target.removeEventListener("DOMMouseScroll", dict.highlightListeners[1]);
target.removeEventListener("mousewheel", dict.highlightListeners[1]);
target.removeEventListener("click", dict.highlightListeners[2]);
this._highlightListeners = null;
dict.highlightListeners = null;
},
/**

Просмотреть файл

@ -155,8 +155,8 @@ add_task(function* testModalResults() {
}],
["o", {
rectCount: 492,
insertCalls: [1, 3],
removeCalls: [1, 2]
insertCalls: [1, 4],
removeCalls: [1, 3]
}]
]);
let url = kFixtureBaseURL + "file_FinderSample.html";
@ -294,7 +294,7 @@ add_task(function* testHighlightAllToggle() {
expectedResult = {
rectCount: 0,
insertCalls: [0, 1],
removeCalls: [1, 1]
removeCalls: [0, 1]
};
promise = promiseTestHighlighterOutput(browser, word, expectedResult);
yield SpecialPowers.pushPrefEnv({ "set": [[ kHighlightAllPref, false ]] });