--- a/toolkit/modules/FinderHighlighter.jsm
+++ b/toolkit/modules/FinderHighlighter.jsm
@@ -15,16 +15,17 @@ Cu.import("resource://gre/modules/XPCOMU
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 kBrightTextSampleSize = 5;
const kModalHighlightRepaintFreqMs = 100;
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", "line-height",
"letter-spacing", "text-emphasis", "text-orientation", "text-transform", "word-spacing"];
const kFontPropsCamelCase = kFontPropsCSS.map(prop => {
let parts = prop.split("-");
@@ -352,17 +353,16 @@ FinderHighlighter.prototype = {
if (dict.modalHighlightOutline) {
dict.modalHighlightOutline.setAttributeForElement(kModalOutlineId, "style",
dict.modalHighlightOutline.getAttributeForElement(kModalOutlineId, "style") +
"; opacity: 0");
}
this._removeHighlightAllMask(window);
this._removeModalHighlightListeners(window);
- delete dict.brightText;
dict.visible = false;
},
/**
* Called by the Finder after a find result comes in; update the position and
* content of the outline to the newly found occurrence.
* To make sure that the outline covers the found range completely, all the
@@ -408,30 +408,25 @@ FinderHighlighter.prototype = {
dict.currentFoundRange = foundRange;
let textContent = this._getRangeContentArray(foundRange);
if (!textContent.length) {
this.hide(window);
return;
}
- let fontStyle = this._getRangeFontStyle(foundRange);
- if (typeof dict.brightText == "undefined") {
- dict.brightText = this._isColorBright(fontStyle.color);
- }
-
if (data.findAgain)
dict.updateAllRanges = true;
if (!dict.visible)
this.show(window);
else
this._maybeCreateModalHighlightNodes(window);
- this._updateRangeOutline(dict, textContent, fontStyle);
+ this._updateRangeOutline(dict, textContent);
}
let outlineNode = dict.modalHighlightOutline;
if (outlineNode) {
if (dict.animation)
dict.animation.finish();
dict.animation = outlineNode.setAnimationForElement(kModalOutlineId,
Cu.cloneInto(kModalOutlineAnim.keyframes, window), kModalOutlineAnim.duration);
@@ -457,16 +452,17 @@ FinderHighlighter.prototype = {
}
let dict = this.getForWindow(window.top);
if (dict.animation)
dict.animation.finish();
dict.dynamicRangesSet.clear();
dict.frames.clear();
dict.modalHighlightRectsMap.clear();
+ dict.brightText = null;
},
/**
* When the current page is refreshed or navigated away from, the CanvasFrame
* contents is not valid anymore, i.e. all anonymous content is destroyed.
* We need to clear the references we keep, which'll make sure we redraw
* everything when the user starts to find in page again.
*/
@@ -707,16 +703,58 @@ FinderHighlighter.prototype = {
cssColor = cssColor.match(kRGBRE);
if (!cssColor || !cssColor.length)
return false;
cssColor.shift();
return new Color(...cssColor).isBright;
},
/**
+ * Detects if the overall text color in the page can be described as bright.
+ * This is done according to the following algorithm:
+ * 1. With the entire set of ranges that we have found thusfar;
+ * 2. Get an odd-numbered `sampleSize`, with a maximum of `kBrightTextSampleSize`
+ * ranges,
+ * 3. Slice the set of ranges into `sampleSize` number of equal parts,
+ * 4. Grab the first range for each slice and inspect the brightness of the
+ * color of its text content.
+ * 5. When the majority of ranges are counted as contain bright colored text,
+ * the page is considered to contain bright text overall.
+ *
+ * @param {Object} dict Dictionary of properties belonging to the
+ * currently active window. The page text color property
+ * will be recorded in `dict.brightText` as `true` or `false`.
+ */
+ _detectBrightText(dict) {
+ let sampleSize = Math.min(dict.modalHighlightRectsMap.size, kBrightTextSampleSize);
+ let ranges = [...dict.modalHighlightRectsMap.keys()];
+ let rangesCount = ranges.length;
+ // Make sure the sample size is an odd number.
+ if (sampleSize % 2 == 0) {
+ // Make the currently found range weigh heavier.
+ if (dict.currentFoundRange) {
+ ranges.push(dict.currentFoundRange);
+ ++sampleSize;
+ ++rangesCount;
+ } else {
+ --sampleSize;
+ }
+ }
+ let brightCount = 0;
+ for (let i = 0; i < sampleSize; ++i) {
+ let range = ranges[Math.floor((rangesCount / sampleSize) * i)];
+ let fontStyle = this._getRangeFontStyle(range);
+ if (this._isColorBright(fontStyle.color))
+ ++brightCount;
+ }
+
+ dict.brightText = (brightCount >= Math.ceil(sampleSize / 2));
+ },
+
+ /**
* 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 dynamic container
* @return {Boolean}
*/
@@ -1007,16 +1045,18 @@ FinderHighlighter.prototype = {
maskNode.setAttribute("id", kMaskId);
dict.modalHighlightAllMask = kDebug ?
mockAnonymousContentNode((document.body || document.documentElement).appendChild(maskNode)) :
document.insertAnonymousContent(maskNode);
}
// Make sure the dimmed mask node takes the full width and height that's available.
let {width, height} = dict.lastWindowDimensions = this._getWindowDimensions(window);
+ if (typeof dict.brightText != "boolean" || dict.updateAllRanges)
+ this._detectBrightText(dict);
let maskStyle = this._getStyleString(kModalStyles.maskNode,
[ ["width", width + "px"], ["height", height + "px"] ],
dict.brightText ? kModalStyles.maskNodeBrightText : [],
kDebug ? kModalStyles.maskNodeDebug : []);
dict.modalHighlightAllMask.setAttributeForElement(kMaskId, "style", maskStyle);
if (dict.brightText)
dict.modalHighlightAllMask.setAttributeForElement(kMaskId, "brighttext", "true");