Bug 1290842 - reduce the amount calls to the highlighter upon the first find action and improve the behavior when modal highlighting is not enabled now that we have a FinderHighlighter class we can use. r=jaws

MozReview-Commit-ID: K01dpqp8LSf
This commit is contained in:
Mike de Boer 2016-08-02 10:40:21 +02:00
parent 22b8bf8d31
commit ebc9f1764d
4 changed files with 140 additions and 87 deletions

View File

@ -587,6 +587,23 @@
]]></body>
</method>
<method name="_setHighlightTimeout">
<body><![CDATA[
if (this._highlightTimeout)
clearTimeout(this._highlightTimeout);
let word = this._findField.value;
// Bug 429723. Don't attempt to highlight ""
if (!this._highlightAll || !word)
return;
this._highlightTimeout = setTimeout(() => {
this.browser.finder.highlight(true, word,
this._findMode == this.FIND_LINKS);
}, 500);
]]></body>
</method>
<!--
- Updates the case-sensitivity mode of the findbar and its UI.
- @param [optional] aString
@ -681,8 +698,7 @@
// Just set the pref; our observer will change the find bar behavior.
prefsvc.setBoolPref("findbar.entireword", aEntireWord);
if (this.getElement("highlight").checked)
this._setHighlightTimeout();
this._setHighlightTimeout();
]]></body>
</method>
@ -1026,8 +1042,7 @@
}
this._enableFindButtons(val);
if (this.getElement("highlight").checked)
this._setHighlightTimeout();
this._setHighlightTimeout();
this._updateCaseSensitivity(val);
this._updateEntireWord();
@ -1070,18 +1085,6 @@
]]></body>
</method>
<method name="_setHighlightTimeout">
<body><![CDATA[
if (this._highlightTimeout)
clearTimeout(this._highlightTimeout);
this._highlightTimeout =
setTimeout(function(aSelf) {
aSelf.toggleHighlight(false);
aSelf.toggleHighlight(true);
}, 500, this);
]]></body>
</method>
<method name="_findAgain">
<parameter name="aFindPrevious"/>
<body><![CDATA[

View File

@ -134,10 +134,12 @@ Finder.prototype = {
set caseSensitive(aSensitive) {
this._fastFind.caseSensitive = aSensitive;
this.iterator.reset();
},
set entireWord(aEntireWord) {
this._fastFind.entireWord = aEntireWord;
this.iterator.reset();
},
get highlighter() {

View File

@ -19,6 +19,7 @@ XPCOMUtils.defineLazyGetter(this, "kDebug", () => {
});
const kModalHighlightRepaintFreqMs = 10;
const kHighlightAllPref = "findbar.highlightAll";
const kModalHighlightPref = "findbar.modalHighlight";
const kFontPropsCSS = ["color", "font-family", "font-kerning", "font-size",
"font-size-adjust", "font-stretch", "font-variant", "font-weight", "letter-spacing",
@ -100,7 +101,6 @@ const kModalStyle = `
.findbar-modalHighlight-outlineMask[brighttext] > .findbar-modalHighlight-rect {
background: #000;
}`;
const kXULNS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
function mockAnonymousContentNode(domNode) {
return {
@ -137,8 +137,11 @@ function mockAnonymousContentNode(domNode) {
* @param {Finder} finder Finder.jsm instance
*/
function FinderHighlighter(finder) {
this.finder = finder;
this._currentFoundRange = null;
this._modal = Services.prefs.getBoolPref(kModalHighlightPref);
this._highlightAll = Services.prefs.getBoolPref(kHighlightAllPref);
this.finder = finder;
this.visible = false;
}
FinderHighlighter.prototype = {
@ -259,50 +262,47 @@ FinderHighlighter.prototype = {
* If modal highlighting is enabled, show the dimmed background that will overlay
* the page.
*
* @param {nsIDOMWindow} window The dimmed background will overlay this window.
* Optional, defaults to the finder window.
* @return {AnonymousContent} Reference to the node inserted into the
* CanvasFrame. It'll also be stored in the
* `_modalHighlightOutline` member variable.
* @param {nsIDOMWindow} window The dimmed background will overlay this window.
* Optional, defaults to the finder window.
*/
show(window = null) {
if (!this._modal)
return null;
if (!this._modal || this.visible)
return;
this.visible = true;
window = window || this.finder._getWindow();
let anonNode = this._maybeCreateModalHighlightNodes(window);
this._maybeCreateModalHighlightNodes(window);
this._addModalHighlightListeners(window);
return anonNode;
},
/**
* Clear all highlighted matches. If modal highlighting is enabled and
* the outline + dimmed background is currently visible, both will be hidden.
*
* @param {nsIDOMWindow} window The dimmed background will overlay this window.
* Optional, defaults to the finder window.
* @param {nsIDOMRange} skipRange A range that should not be removed from the
* find selection.
*/
hide(window = null) {
hide(window = null, skipRange = null) {
window = window || this.finder._getWindow();
let doc = window.document;
let controller = this.finder._getSelectionController(window);
let sel = controller.getSelection(Ci.nsISelectionController.SELECTION_FIND);
sel.removeAllRanges();
this._clearSelection(this.finder._getSelectionController(window), skipRange);
// Next, check our editor cache, for editors belonging to this
// document
if (this._editors) {
for (let x = this._editors.length - 1; x >= 0; --x) {
if (this._editors[x].document == doc) {
sel = this._editors[x].selectionController
.getSelection(Ci.nsISelectionController.SELECTION_FIND);
sel.removeAllRanges();
this._clearSelection(this._editors[x].selectionController, skipRange);
// We don't need to listen to this editor any more
this._unhookListenersAtIndex(x);
}
}
}
if (!this._modal)
if (!this._modal || !this.visible)
return;
if (this._modalHighlightOutline)
@ -311,6 +311,8 @@ FinderHighlighter.prototype = {
this._removeHighlightAllMask(window);
this._removeModalHighlightListeners(window);
delete this._brightText;
this.visible = false;
},
/**
@ -335,52 +337,69 @@ FinderHighlighter.prototype = {
* by the consumer of the Finder.
*/
update(data) {
if (!this._modal)
let window = this.finder._getWindow();
let foundRange = this.finder._fastFind.getFoundRange();
if (!this._modal) {
if (this._highlightAll) {
this.hide(window, foundRange);
let params = this.iterator.params;
if (params.word)
this.highlight(true, params.word, params.linksOnly);
}
return;
}
// Place the match placeholder on top of the current found range.
let foundRange = this.finder._fastFind.getFoundRange();
if (data.result == Ci.nsITypeAheadFind.FIND_NOTFOUND || !foundRange) {
this.hide();
return;
}
let window = this.finder._getWindow();
let textContent = this._getRangeContentArray(foundRange);
if (!textContent.length) {
this.hide(window);
return;
let outlineNode;
if (foundRange !== this._currentFoundRange || data.findAgain) {
this._currentFoundRange = foundRange;
let textContent = this._getRangeContentArray(foundRange);
if (!textContent.length) {
this.hide(window);
return;
}
let rect = foundRange.getBoundingClientRect();
let fontStyle = this._getRangeFontStyle(foundRange);
if (typeof this._brightText == "undefined") {
this._brightText = this._isColorBright(fontStyle.color);
}
// Text color in the outline is determined by our stylesheet.
delete fontStyle.color;
if (!this.visible)
this.show(window);
else
this._maybeCreateModalHighlightNodes(window);
outlineNode = this._modalHighlightOutline;
outlineNode.setTextContentForElement(kModalOutlineId + "-text", textContent.join(" "));
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`);
}
let rect = foundRange.getBoundingClientRect();
let fontStyle = this._getRangeFontStyle(foundRange);
if (typeof this._brightText == "undefined") {
this._brightText = this._isColorBright(fontStyle.color);
}
// Text color in the outline is determined by our stylesheet.
delete fontStyle.color;
let anonNode = this.show(window);
anonNode.setTextContentForElement(kModalOutlineId + "-text", textContent.join(" "));
anonNode.setAttributeForElement(kModalOutlineId + "-text", "style",
this._getHTMLFontStyle(fontStyle));
if (typeof anonNode.getAttributeForElement(kModalOutlineId, "hidden") == "string")
anonNode.removeAttributeForElement(kModalOutlineId, "hidden");
let { scrollX, scrollY } = this._getScrollPosition(window);
anonNode.setAttributeForElement(kModalOutlineId, "style",
`top: ${scrollY + rect.top}px; left: ${scrollX + rect.left}px`);
if (typeof anonNode.getAttributeForElement(kModalOutlineId, "grow") == "string")
outlineNode = this._modalHighlightOutline;
if (typeof outlineNode.getAttributeForElement(kModalOutlineId, "grow") == "string")
return;
window.requestAnimationFrame(() => {
anonNode.setAttributeForElement(kModalOutlineId, "grow", true);
outlineNode.setAttributeForElement(kModalOutlineId, "grow", true);
this._listenForOutlineEvent(kModalOutlineId, "transitionend", () => {
try {
anonNode.removeAttributeForElement(kModalOutlineId, "grow");
outlineNode.removeAttributeForElement(kModalOutlineId, "grow");
} catch (ex) {}
});
});
@ -441,12 +460,33 @@ FinderHighlighter.prototype = {
* @param {Boolean} highlightAll
*/
onHighlightAllChange(highlightAll) {
this._highlightAll = highlightAll;
if (this._modal && !highlightAll) {
this.clear();
this._scheduleRepaintOfMask(this.finder._getWindow());
}
},
/**
* Utility; removes all ranges from the find selection that belongs to a
* controller. Optionally skips a specific range.
*
* @param {nsISelectionController} controller
* @param {nsIDOMRange} skipRange
*/
_clearSelection(controller, skipRange = null) {
let sel = controller.getSelection(Ci.nsISelectionController.SELECTION_FIND);
if (!skipRange) {
sel.removeAllRanges();
} else {
for (let i = sel.rangeCount - 1; i >= 0; --i) {
let range = sel.getRangeAt(i);
if (range !== skipRange)
sel.removeRange(range);
}
}
},
/**
* Utility; get the nsIDOMWindowUtils for a window.
*
@ -605,14 +645,16 @@ FinderHighlighter.prototype = {
* Lazily insert the nodes we need as anonymous content into the CanvasFrame
* of a window.
*
* @param {nsIDOMWindow} window Window to draw in.
* @return {AnonymousContent} The reference to the outline node, NOT the mask.
* @param {nsIDOMWindow} window Window to draw in.
*/
_maybeCreateModalHighlightNodes(window) {
if (this._modalHighlightOutline) {
if (!this._modalHighlightAllMask)
this._repaintHighlightAllMask(window);
return this._modalHighlightOutline;
if (!this._modalHighlightAllMask) {
// Make sure to at least show the dimmed background.
this._repaintHighlightAllMask(window, false);
this._scheduleRepaintOfMask(window);
}
return;
}
let document = window.document;
@ -623,7 +665,7 @@ FinderHighlighter.prototype = {
this._maybeCreateModalHighlightNodes(window);
};
document.addEventListener("visibilitychange", onVisibilityChange);
return null;
return;
}
this._maybeInstallStyleSheet(window);
@ -641,13 +683,12 @@ FinderHighlighter.prototype = {
outlineBox.appendChild(outlineBoxText);
container.appendChild(outlineBox);
this._repaintHighlightAllMask(window);
this._modalHighlightOutline = kDebug ?
mockAnonymousContentNode(document.body.appendChild(container.firstChild)) :
document.insertAnonymousContent(container);
return this._modalHighlightOutline;
// Make sure to at least show the dimmed background.
this._repaintHighlightAllMask(window, false);
},
/**
@ -656,8 +697,9 @@ FinderHighlighter.prototype = {
* the ranges that were found.
*
* @param {nsIDOMWindow} window Window to draw in.
* @param {Boolean} [paintContent]
*/
_repaintHighlightAllMask(window) {
_repaintHighlightAllMask(window, paintContent = true) {
let document = window.document;
const kMaskId = kModalIdPrefix + "-findbar-modalHighlight-outlineMask";
@ -671,18 +713,20 @@ FinderHighlighter.prototype = {
if (this._brightText)
maskNode.setAttribute("brighttext", "true");
// 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 rects of this._modalHighlightRectsMap.values()) {
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>`);
if (paintContent) {
// 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 rects of this._modalHighlightRectsMap.values()) {
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("");
}
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.

View File

@ -29,6 +29,10 @@ this.FinderIterator = {
// Expose `kIterationSizeMax` to the outside world for unit tests to use.
get kIterationSizeMax() { return kIterationSizeMax },
get params() {
return Object.assign({}, this._currentParams || this._previousParams);
},
/**
* Start iterating the active Finder docShell, using the options below. When
* it already started at the request of another consumer, we first yield the