Bug 1280978 - don't show the modal highlighting ui on very large pages, because it degrades performance too much. r?jaws
MozReview-Commit-ID: IQOsxKtBW09
--- a/toolkit/content/widgets/findbar.xml
+++ b/toolkit/content/widgets/findbar.xml
@@ -1311,16 +1311,23 @@
<method name="onHighlightFinished">
<parameter name="result"/>
<body><![CDATA[
// Noop.
]]></body>
</method>
+ <method name="onModalHighlightShowing">
+ <parameter name="data"/>
+ <body><![CDATA[
+ this._prefsvc.setBoolPref("findbar.modalHighlight.showing", data.showing);
+ ]]></body>
+ </method>
+
<method name="onCurrentSelection">
<parameter name="aSelectionString" />
<parameter name="aIsInitialSelection" />
<body><![CDATA[
// Ignore the prefill if the user has already typed in the findbar,
// it would have been overwritten anyway. See bug 1198465.
if (aIsInitialSelection && !this._startFindDeferred)
return;
--- a/toolkit/modules/FinderHighlighter.jsm
+++ b/toolkit/modules/FinderHighlighter.jsm
@@ -179,29 +179,62 @@ FinderHighlighter.prototype = {
modalHighlightRectsMap: new Map(),
previousRangeRectsAndTexts: { rectList: [], textList: [] }
});
}
return gWindows.get(window);
},
/**
- * Notify all registered listeners that the 'Highlight All' operation finished.
+ * Notify all registered listeners of a specific event.
*
- * @param {Boolean} highlight Whether highlighting was turned on
+ * @param {String} messageName Name of the message handler
+ * @param {Object} [message] Arguments for the message handler. Optional.
*/
- notifyFinished(highlight) {
+ _notify(messageName, message) {
for (let l of this.finder._listeners) {
try {
- l.onHighlightFinished(highlight);
+ l[messageName](message);
} catch (ex) {}
}
},
/**
+ * Check if we should show the modal highlighting UI. Currently, this depends
+ * on two factors:
+ * 1. Yes, when the 'findbar.modalHighlight' pref is set to TRUE.
+ * 2. Yes, when the page is not too large to be efficiently highlighted by the
+ * modal UI.
+ * Since drawing semi-transparent overlays on top of very large document doesn't
+ * scale, we have to turn the feature off based on the `kPageIsTooBigPx`
+ * threshold as hardcoded above.
+ *
+ * @param {Object} dict Dictionary of properties belonging to the
+ * currently active window
+ * @param {nsIDOMWindow} [window] The dimmed background is overlaying this window.
+ * Optional.
+ * @return {Boolean}
+ */
+ shouldShowModalUI(dict, window = null) {
+ if (!dict || !this._modal)
+ return this._modal;
+
+ if (this._isPageTooBig(dict)) {
+ if (window && dict.modalHighlightAllMask) {
+ this._removeRangeOutline(window);
+ this._removeHighlightAllMask(window);
+ this._removeModalHighlightListeners(window);
+ }
+ return false;
+ }
+
+ return true;
+ },
+
+ /**
* Toggle highlighting all occurrences of a word in a page. This method will
* be called recursively for each (i)frame inside a page.
*
* @param {Booolean} highlight Whether highlighting should be turned on
* @param {String} word Needle to search for and highlight when found
* @param {Boolean} linksOnly Only consider nodes that are links for the search
* @yield {Promise} that resolves once the operation has finished
*/
@@ -224,32 +257,33 @@ FinderHighlighter.prototype = {
caseSensitive: this.finder._fastFind.caseSensitive,
entireWord: this.finder._fastFind.entireWord,
linksOnly, word,
finder: this.finder,
listener: this,
useCache: true,
window
};
- if (this._modal && this.iterator._areParamsEqual(params, dict.lastIteratorParams))
+ if (this.shouldShowModalUI(dict) && this.iterator._areParamsEqual(params, dict.lastIteratorParams))
return this._found;
- if (!this._modal)
+ if (!this.shouldShowModalUI(dict, window))
dict.visible = true;
yield this.iterator.start(params);
if (this._found)
this.finder._outlineLink(true);
} else {
this.hide(window);
// Removing the highlighting always succeeds, so return true.
this._found = true;
}
- this.notifyFinished({ highlight, found: this._found });
+ // Notify all registered listeners that the 'Highlight All' operation finished.
+ this._notify("onHighlightFinished", { highlight, found: this._found });
return this._found;
}),
// FinderIterator listener implementation
onIteratorRangeFound(range) {
this.highlightRange(range);
@@ -262,17 +296,17 @@ FinderHighlighter.prototype = {
this.clear(this.finder._getWindow());
},
onIteratorStart(params) {
let window = this.finder._getWindow();
let dict = this.getForWindow(window);
// Save a clean params set for use later in the `update()` method.
dict.lastIteratorParams = params;
- if (!this._modal)
+ if (!this.shouldShowModalUI(dict))
this.hide(window, this.finder._fastFind.getFoundRange());
this.clear(window);
},
/**
* Add a range to the find selection, i.e. highlight it, and if it's inside an
* editable node, track it.
*
@@ -281,25 +315,25 @@ FinderHighlighter.prototype = {
highlightRange(range) {
let node = range.startContainer;
let editableNode = this._getEditableNode(node);
let window = node.ownerDocument.defaultView;
let controller = this.finder._getSelectionController(window);
if (editableNode) {
controller = editableNode.editor.selectionController;
}
+ let dict = this.getForWindow(window.top);
- if (this._modal) {
+ if (this.shouldShowModalUI(dict, window)) {
this._modalHighlight(range, controller, window);
} else {
let findSelection = controller.getSelection(Ci.nsISelectionController.SELECTION_FIND);
findSelection.addRange(range);
// Check if the range is inside an iframe.
if (window != window.top) {
- let dict = this.getForWindow(window.top);
if (!dict.frames.has(window))
dict.frames.set(window, null);
}
}
if (editableNode) {
// Highlighting added, so cache this editor, and hook up listeners
// to ensure we deal properly with edits within the highlighting
@@ -312,20 +346,21 @@ FinderHighlighter.prototype = {
* the page.
*
* @param {nsIDOMWindow} window The dimmed background will overlay this window.
* Optional, defaults to the finder window.
*/
show(window = null) {
window = (window || this.finder._getWindow()).top;
let dict = this.getForWindow(window);
- if (!this._modal || dict.visible)
+ if (!this.shouldShowModalUI(dict, window) || dict.visible)
return;
dict.visible = true;
+ this._notify("onModalHighlightShowing", { showing: true });
this._maybeCreateModalHighlightNodes(window);
this._addModalHighlightListeners(window);
},
/**
* Clear all highlighted matches. If modal highlighting is enabled and
* the outline + dimmed background is currently visible, both will be hidden.
@@ -378,16 +413,17 @@ FinderHighlighter.prototype = {
}
dict.lastWindowDimensions = { width: 0, height: 0 };
this._removeRangeOutline(window);
this._removeHighlightAllMask(window);
this._removeModalHighlightListeners(window);
dict.visible = false;
+ this._notify("onModalHighlightShowing", { showing: 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
* CSS styles that influence the text are copied and applied to the outline.
*
@@ -411,17 +447,17 @@ FinderHighlighter.prototype = {
let dict = this.getForWindow(window);
let foundRange = this.finder._fastFind.getFoundRange();
if (data.result == Ci.nsITypeAheadFind.FIND_NOTFOUND || !data.searchString || !foundRange) {
this.hide();
return;
}
- if (!this._modal) {
+ if (!this.shouldShowModalUI(dict, window)) {
if (this._highlightAll) {
dict.previousFoundRange = dict.currentFoundRange;
dict.currentFoundRange = foundRange;
let params = this.iterator.params;
if (dict.visible && this.iterator._areParamsEqual(params, dict.lastIteratorParams))
return;
if (!dict.visible && !params)
params = {word: data.searchString, linksOnly: data.linksOnly};
@@ -437,17 +473,17 @@ FinderHighlighter.prototype = {
if (!dict.visible)
this.show(window);
else
this._maybeCreateModalHighlightNodes(window);
}
let outlineNode = dict.modalHighlightOutline;
- if (outlineNode && !this._isPageTooBig(dict)) {
+ if (outlineNode) {
let animation;
if (dict.animations) {
for (animation of dict.animations)
animation.finish();
}
dict.animations = [];
for (let i = dict.previousRangeRectsAndTexts.rectList.length - 1; i >= 0; --i) {
animation = outlineNode.setAnimationForElement(kModalOutlineId + i,
@@ -523,17 +559,18 @@ FinderHighlighter.prototype = {
* and when it's turned off, the found occurrences will be removed from the mask.
*
* @param {Boolean} highlightAll
*/
onHighlightAllChange(highlightAll) {
this._highlightAll = highlightAll;
if (!highlightAll) {
let window = this.finder._getWindow();
- if (!this._modal)
+ let dict = this.getForWindow(window.top);
+ if (!this.shouldShowModalUI(dict))
this.hide(window);
this.clear(window);
this._scheduleRepaintOfMask(window);
}
},
/**
* Utility; removes all ranges from the find selection that belongs to a
@@ -1195,21 +1232,21 @@ FinderHighlighter.prototype = {
* {Boolean} scrollOnly TRUE when the page has scrolled in the meantime,
* which means that the dynamically 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, updateAllRanges } =
{ contentChanged: false, scrollOnly: false, updateAllRanges: false }) {
- if (!this._modal)
+ window = window.top;
+ let dict = this.getForWindow(window);
+ if (!this.shouldShowModalUI(dict, window))
return;
- window = window.top;
- let dict = this.getForWindow(window);
let hasDynamicRanges = !!dict.dynamicRangesSet.size;
let pageIsTooBig = this._isPageTooBig(dict);
let repaintDynamicRanges = ((scrollOnly || contentChanged) && hasDynamicRanges
&& !pageIsTooBig);
// When we request to repaint unconditionally, we mean to call
// `_repaintHighlightAllMask()` right after the timeout.
if (!dict.unconditionalRepaintRequested)
--- a/toolkit/modules/RemoteFinder.jsm
+++ b/toolkit/modules/RemoteFinder.jsm
@@ -79,16 +79,20 @@ RemoteFinder.prototype = {
case "Finder:CurrentSelectionResult":
callback = "onCurrentSelection";
params = [ aMessage.data.selection, aMessage.data.initial ];
break;
case "Finder:HighlightFinished":
callback = "onHighlightFinished";
params = [ aMessage.data ];
break;
+ case "Finder:ModalHighlightShowing":
+ callback = "onModalHighlightShowing";
+ params = [ aMessage.data ];
+ break;
}
for (let l of this._listeners) {
// Don't let one callback throwing stop us calling the rest
try {
l[callback].apply(l, params);
} catch (e) {
if (!l[callback]) {
@@ -246,16 +250,20 @@ RemoteFinderListener.prototype = {
onMatchesCountResult(aData) {
this._global.sendAsyncMessage("Finder:MatchesResult", aData);
},
onHighlightFinished(aData) {
this._global.sendAsyncMessage("Finder:HighlightFinished", aData);
},
+ onModalHighlightShowing(aData) {
+ this._global.sendAsyncMessage("Finder:ModalHighlightShowing", aData);
+ },
+
receiveMessage(aMessage) {
let data = aMessage.data;
switch (aMessage.name) {
case "Finder:CaseSensitive":
this._finder.caseSensitive = data.caseSensitive;
break;
--- a/widget/nsXPLookAndFeel.cpp
+++ b/widget/nsXPLookAndFeel.cpp
@@ -462,17 +462,17 @@ nsXPLookAndFeel::Init()
Preferences::AddBoolVarCache(&sUseNativeColors,
"ui.use_native_colors",
sUseNativeColors);
Preferences::AddBoolVarCache(&sUseStandinsForNativeColors,
"ui.use_standins_for_native_colors",
sUseStandinsForNativeColors);
Preferences::AddBoolVarCache(&sFindbarModalHighlight,
- "findbar.modalHighlight",
+ "findbar.modalHighlight.showing",
sFindbarModalHighlight);
if (XRE_IsContentProcess()) {
mozilla::dom::ContentChild* cc =
mozilla::dom::ContentChild::GetSingleton();
LookAndFeel::SetIntCache(cc->LookAndFeelCache());
// This is only ever used once during initialization, and can be cleared now.