Backed out changeset af4cc6c04573 (bug 1282070) for failures in test_bug263683.xul CLOSED TREE

This commit is contained in:
Wes Kocher 2016-08-16 11:58:33 -07:00
Родитель f4c829a76c
Коммит e9556c03b7
4 изменённых файлов: 110 добавлений и 225 удалений

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

@ -31,7 +31,6 @@ function Finder(docShell) {
this._fastFind = Cc["@mozilla.org/typeaheadfind;1"].createInstance(Ci.nsITypeAheadFind);
this._fastFind.init(docShell);
this._currentFoundRange = null;
this._docShell = docShell;
this._listeners = [];
this._previousLink = null;
@ -63,8 +62,7 @@ Finder.prototype = {
.getInterface(Ci.nsIWebProgress)
.removeProgressListener(this, Ci.nsIWebProgress.NOTIFY_LOCATION);
this._listeners = [];
this._currentFoundRange = this._fastFind = this._docShell = this._previousLink =
this._highlighter = null;
this._fastFind = this._docShell = this._previousLink = this._highlighter = null;
},
addResultListener: function (aListener) {
@ -141,15 +139,11 @@ Finder.prototype = {
},
set caseSensitive(aSensitive) {
if (this._fastFind.caseSensitive === aSensitive)
return;
this._fastFind.caseSensitive = aSensitive;
this.iterator.reset();
},
set entireWord(aEntireWord) {
if (this._fastFind.entireWord === aEntireWord)
return;
this._fastFind.entireWord = aEntireWord;
this.iterator.reset();
},
@ -385,21 +379,12 @@ Finder.prototype = {
}
},
_notifyMatchesCount: function(result = this._currentMatchesCountResult) {
if (!result)
return;
// The `_currentFound` property is only used for internal bookkeeping.
delete result._currentFound;
if (result.total == this._currentMatchLimit)
result.total = -1;
_notifyMatchesCount: function(result) {
for (let l of this._listeners) {
try {
l.onMatchesCountResult(result);
} catch (ex) {}
}
this._currentMatchesCountResult = null;
},
requestMatchesCount: function(aWord, aMatchLimit, aLinksOnly) {
@ -413,14 +398,12 @@ Finder.prototype = {
}
let window = this._getWindow();
this._currentFoundRange = this._fastFind.getFoundRange();
this._currentMatchLimit = aMatchLimit;
this._currentMatchesCountResult = {
let result = {
total: 0,
current: 0,
_currentFound: false
};
let foundRange = this._fastFind.getFoundRange();
this.iterator.start({
caseSensitive: this._fastFind.caseSensitive,
@ -428,38 +411,30 @@ Finder.prototype = {
finder: this,
limit: aMatchLimit,
linksOnly: aLinksOnly,
listener: this,
onRange: range => {
++result.total;
if (!result._currentFound) {
++result.current;
result._currentFound = (foundRange &&
range.startContainer == foundRange.startContainer &&
range.startOffset == foundRange.startOffset &&
range.endContainer == foundRange.endContainer &&
range.endOffset == foundRange.endOffset);
}
},
useCache: true,
word: aWord
}).then(this._notifyMatchesCount.bind(this));
}).then(() => {
// The `_currentFound` property is only used for internal bookkeeping.
delete result._currentFound;
if (result.total == aMatchLimit)
result.total = -1;
this._notifyMatchesCount(result);
});
},
// FinderIterator listener implementation
onIteratorBeforeRestart() {},
onIteratorRangeFound(range) {
let result = this._currentMatchesCountResult;
if (!result)
return;
++result.total;
if (!result._currentFound) {
++result.current;
result._currentFound = (this._currentFoundRange &&
range.startContainer == this._currentFoundRange.startContainer &&
range.startOffset == this._currentFoundRange.startOffset &&
range.endContainer == this._currentFoundRange.endContainer &&
range.endOffset == this._currentFoundRange.endOffset);
}
},
onIteratorReset() {},
onIteratorRestart({ word, linksOnly }) {
this.requestMatchesCount(word, this._currentMatchLimit, linksOnly);
},
_getWindow: function () {
return this._docShell.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindow);
},

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

@ -18,7 +18,6 @@ XPCOMUtils.defineLazyGetter(this, "kDebug", () => {
return Services.prefs.getPrefType(kDebugPref) && Services.prefs.getBoolPref(kDebugPref);
});
const kContentChangeThresholdPx = 5;
const kModalHighlightRepaintFreqMs = 10;
const kHighlightAllPref = "findbar.highlightAll";
const kModalHighlightPref = "findbar.modalHighlight";
@ -208,12 +207,14 @@ FinderHighlighter.prototype = {
let window = this.finder._getWindow();
let controller = this.finder._getSelectionController(window);
let doc = window.document;
this._found = false;
let found = false;
this.clear();
if (!controller || !doc || !doc.documentElement) {
// Without the selection controller,
// we are unable to (un)highlight any matches
return this._found;
return found;
}
if (highlight) {
@ -222,49 +223,40 @@ FinderHighlighter.prototype = {
entireWord: this.finder._fastFind.entireWord,
linksOnly, word,
finder: this.finder,
listener: this,
onRange: range => {
this.highlightRange(range, controller, window);
found = true;
},
useCache: true
});
if (this._found)
if (found)
this.finder._outlineLink(true);
} else {
this.hide(window);
this.clear();
this.iterator.reset();
// Removing the highlighting always succeeds, so return true.
this._found = true;
found = true;
}
return this._found;
return found;
}),
// FinderIterator listener implementation
onIteratorBeforeRestart() {
this.clear();
},
onIteratorRangeFound(range) {
this.highlightRange(range);
this._found = true;
},
onIteratorReset() {
this.clear();
},
onIteratorRestart() {},
/**
* Add a range to the find selection, i.e. highlight it, and if it's inside an
* editable node, track it.
*
* @param {nsIDOMRange} range Range object to be highlighted
* @param {nsIDOMRange} range Range object to be highlighted
* @param {nsISelectionController} controller Selection controller of the
* document that the range belongs
* to
* @param {nsIDOMWindow} window Window object, whose DOM tree
* is being traversed
*/
highlightRange(range) {
highlightRange(range, controller, window) {
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;
}
@ -459,8 +451,6 @@ FinderHighlighter.prototype = {
* everything when the user starts to find in page again.
*/
onLocationChange() {
this.clear();
if (!this._modalHighlightOutline)
return;
@ -750,14 +740,13 @@ 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 };
maskNode.setAttribute("id", kMaskId);
maskNode.setAttribute("class", kMaskId + (kDebug ? ` ${kModalIdPrefix}-findbar-debug` : ""));
maskNode.setAttribute("style", `width: ${width}px; height: ${height}px;`);
if (this._brightText)
maskNode.setAttribute("brighttext", "true");
if (paintContent || this._modalHighlightAllMask) {
if (paintContent) {
// Create a DOM node for each rectangle representing the ranges we found.
let maskContent = [];
const kRectClassName = kModalIdPrefix + "-findbar-modalHighlight-rect";
@ -805,37 +794,12 @@ 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.
*/
_scheduleRepaintOfMask(window, contentChanged = false) {
_scheduleRepaintOfMask(window) {
if (this._modalRepaintScheduler)
window.clearTimeout(this._modalRepaintScheduler);
// When we request to repaint unconditionally, we mean to call
// `_repaintHighlightAllMask()` right after the timeout.
if (!this._unconditionalRepaintRequested)
this._unconditionalRepaintRequested = !contentChanged;
this._modalRepaintScheduler = window.setTimeout(() => {
if (this._unconditionalRepaintRequested) {
this._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)) {
return;
}
this.iterator.restart(this.finder);
this._lastWindowDimensions = { width, height };
this._repaintHighlightAllMask(window);
}, kModalHighlightRepaintFreqMs);
this._modalRepaintScheduler = window.setTimeout(
this._repaintHighlightAllMask.bind(this, window), kModalHighlightRepaintFreqMs);
},
/**
@ -874,12 +838,12 @@ FinderHighlighter.prototype = {
return;
this._highlightListeners = [
this._scheduleRepaintOfMask.bind(this, window, true),
this._scheduleRepaintOfMask.bind(this, window),
this.hide.bind(this, window, null)
];
let target = this.iterator._getDocShell(window).chromeEventHandler;
target.addEventListener("MozAfterPaint", this._highlightListeners[0]);
window.addEventListener("DOMContentLoaded", this._highlightListeners[0]);
window.addEventListener("click", this._highlightListeners[1]);
window.addEventListener("resize", this._highlightListeners[1]);
},
/**
@ -891,9 +855,9 @@ FinderHighlighter.prototype = {
if (!this._highlightListeners)
return;
let target = this.iterator._getDocShell(window).chromeEventHandler;
target.removeEventListener("MozAfterPaint", this._highlightListeners[0]);
window.removeEventListener("DOMContentLoaded", this._highlightListeners[0]);
window.removeEventListener("click", this._highlightListeners[1]);
window.removeEventListener("resize", this._highlightListeners[1]);
this._highlightListeners = null;
},

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

@ -42,9 +42,9 @@ this.FinderIterator = {
* yield `undefined`, instead of a range.
* Upon re-entrance after a break, we check if `stop()` was called during the
* break and if so, we stop iterating.
* Results are also passed to the `listener.onIteratorRangeFound` callback
* method, along with a flag that specifies if the result comes from the cache
* or is fresh. The callback also adheres to the `limit` flag.
* Results are also passed to the `onRange` callback method, along with a flag
* that specifies if the result comes from the cache or is fresh. The callback
* also adheres to the `limit` flag.
* The returned promise is resolved when 1) the limit is reached, 2) when all
* the ranges have been found or 3) when `stop()` is called whilst iterating.
*
@ -58,19 +58,14 @@ this.FinderIterator = {
* @param {Boolean} [options.linksOnly] Only yield ranges that are inside a
* hyperlink (used by QuickFind).
* Optional, defaults to `false`.
* @param {Object} options.listener Listener object that implements the
* following callback functions:
* - onIteratorBeforeRestart({Object} iterParams);
* - onIteratorRangeFound({nsIDOMRange} range);
* - onIteratorReset();
* - onIteratorRestart({Object} iterParams);
* @param {Function} options.onRange Callback invoked when a range is found
* @param {Boolean} [options.useCache] Whether to allow results already
* present in the cache or demand fresh.
* Optional, defaults to `false`.
* @param {String} options.word Word to search for
* @return {Promise}
*/
start({ caseSensitive, entireWord, finder, limit, linksOnly, listener, useCache, word }) {
start({ caseSensitive, entireWord, finder, limit, linksOnly, onRange, useCache, word }) {
// Take care of default values for non-required options.
if (typeof limit != "number")
limit = -1;
@ -88,27 +83,23 @@ this.FinderIterator = {
throw new Error("Missing required option 'finder'");
if (!word)
throw new Error("Missing required option 'word'");
if (typeof listener != "object" || !listener.onIteratorRangeFound)
throw new TypeError("Missing valid, required option 'listener'");
if (typeof onRange != "function")
throw new TypeError("Missing valid, required option 'onRange'");
// If the listener was added before, make sure the promise is resolved before
// we replace it with another.
if (this._listeners.has(listener)) {
let { onEnd } = this._listeners.get(listener);
if (onEnd)
onEnd();
}
// Don't add the same listener twice.
if (this._listeners.has(onRange))
throw new Error("Already listening to iterator results");
let window = finder._getWindow();
let resolver;
let promise = new Promise(resolve => resolver = resolve);
let iterParams = { caseSensitive, entireWord, linksOnly, useCache, word };
this._listeners.set(listener, { limit, onEnd: resolver });
this._listeners.set(onRange, { limit, onEnd: resolver });
// If we're not running anymore and we're requesting the previous result, use it.
if (!this.running && this._previousResultAvailable(iterParams)) {
this._yieldPreviousResult(listener, window);
this._yieldPreviousResult(onRange, window);
return promise;
}
@ -119,7 +110,7 @@ this.FinderIterator = {
throw new Error(`We're currently iterating over '${this._currentParams.word}', not '${word}'`);
// if we're still running, yield the set we have built up this far.
this._yieldIntermediateResult(listener, window);
this._yieldIntermediateResult(onRange, window);
return promise;
}
@ -158,29 +149,7 @@ this.FinderIterator = {
for (let [, { onEnd }] of this._listeners)
onEnd();
},
/**
* Stops the iteration that currently running, if it is, and start a new one
* with the exact same params as before.
*
* @param {Finder} finder Currently active Finder instance
*/
restart(finder) {
// Capture current iterator params before we stop the show.
let iterParams = this.params;
// If the params don't contain a 'word' param, the iterator has never run before.
if (!iterParams.word)
return;
this.stop();
// Restart manually.
this.running = true;
this._currentParams = iterParams;
this._notifyListeners("beforeRestart", iterParams);
this._findAllRanges(finder, finder._getWindow(), ++this._spawnId);
this._notifyListeners("restart", iterParams);
this._listeners.clear();
},
/**
@ -196,7 +165,6 @@ this.FinderIterator = {
this.ranges = [];
this.running = false;
this._notifyListeners("reset");
for (let [, { onEnd }] of this._listeners)
onEnd();
this._listeners.clear();
@ -223,24 +191,6 @@ this.FinderIterator = {
this._currentParams.word == word);
},
/**
* Safely notify all registered listeners that an event has occurred.
*
* @param {String} callback Name of the callback to invoke
* @param {...[mixed]} params Arbitrary amount of arguments that will be
* passed to the callback in-order.
*/
_notifyListeners(callback, ...params) {
callback = "onIterator" + callback.charAt(0).toUpperCase() + callback.substr(1);
for (let [listener] of this._listeners) {
try {
listener[callback](...params);
} catch (ex) {
Cu.reportError("FinderIterator Error: " + ex);
}
}
},
/**
* Internal; check if an iteration request is available in the previous result
* that we cached.
@ -283,17 +233,17 @@ this.FinderIterator = {
* make sure we don't block the host process too long. In the case of a break
* like this, we yield `undefined`, instead of a range.
*
* @param {Object} listener Listener object
* @param {Function} onRange Callback invoked when a range is found
* @param {Array} rangeSource Set of ranges to iterate over
* @param {nsIDOMWindow} window The window object is only really used
* for access to `setTimeout`
* @yield {nsIDOMRange}
*/
_yieldResult: function* (listener, rangeSource, window) {
_yieldResult: function* (onRange, rangeSource, window) {
// We keep track of the number of iterations to allow a short pause between
// every `kIterationSizeMax` number of iterations.
let iterCount = 0;
let { limit, onEnd } = this._listeners.get(listener);
let { limit, onEnd } = this._listeners.get(onRange);
let ranges = rangeSource.slice(0, limit > -1 ? limit : undefined);
for (let range of ranges) {
try {
@ -306,13 +256,13 @@ this.FinderIterator = {
// Pass a flag that is `true` when we're returning the result from a
// cached previous iteration.
listener.onIteratorRangeFound(range, !this.running);
onRange(range, !this.running);
yield range;
if (++iterCount >= kIterationSizeMax) {
iterCount = 0;
// Make sure to save the current limit for later.
this._listeners.set(listener, { limit, onEnd });
this._listeners.set(onRange, { limit, onEnd });
// Sleep for the rest of this cycle.
yield new Promise(resolve => window.setTimeout(resolve, 0));
// After a sleep, the set of ranges may have updated.
@ -321,14 +271,14 @@ this.FinderIterator = {
if (limit !== -1 && --limit === 0) {
// We've reached our limit; no need to do more work.
this._listeners.delete(listener);
this._listeners.delete(onRange);
onEnd();
return;
}
}
// Save the updated limit globally.
this._listeners.set(listener, { limit, onEnd });
this._listeners.set(onRange, { limit, onEnd });
},
/**
@ -336,18 +286,20 @@ this.FinderIterator = {
* mark the listener as 'catching up', meaning it will not receive fresh
* results from a running iterator.
*
* @param {Object} listener Listener object
* @param {nsIDOMWindow} window The window object is only really used
* for access to `setTimeout`
* @param {Function} onRange Callback invoked when a range is found
* @param {nsIDOMWindow} window The window object is only really used
* for access to `setTimeout`
* @yield {nsIDOMRange}
*/
_yieldPreviousResult: Task.async(function* (listener, window) {
this._catchingUp.add(listener);
yield* this._yieldResult(listener, this._previousRanges, window);
this._catchingUp.delete(listener);
let { onEnd } = this._listeners.get(listener);
if (onEnd)
_yieldPreviousResult: Task.async(function* (onRange, window) {
this._catchingUp.add(onRange);
yield* this._yieldResult(onRange, this._previousRanges, window);
this._catchingUp.delete(onRange);
let { onEnd } = this._listeners.get(onRange);
if (onEnd) {
onEnd();
this._listeners.delete(onRange);
}
}),
/**
@ -355,15 +307,15 @@ this.FinderIterator = {
* mark the listener as 'catching up', meaning it will not receive fresh
* results from the running iterator.
*
* @param {Object} listener Listener object
* @param {nsIDOMWindow} window The window object is only really used
* for access to `setTimeout`
* @param {Function} onRange Callback invoked when a range is found
* @param {nsIDOMWindow} window The window object is only really used
* for access to `setTimeout`
* @yield {nsIDOMRange}
*/
_yieldIntermediateResult: Task.async(function* (listener, window) {
this._catchingUp.add(listener);
yield* this._yieldResult(listener, this.ranges, window);
this._catchingUp.delete(listener);
_yieldIntermediateResult: Task.async(function* (onRange, window) {
this._catchingUp.add(onRange);
yield* this._yieldResult(onRange, this.ranges, window);
this._catchingUp.delete(onRange);
}),
/**
@ -396,21 +348,21 @@ this.FinderIterator = {
this.ranges.push(range);
// Call each listener with the range we just found.
for (let [listener, { limit, onEnd }] of this._listeners) {
if (this._catchingUp.has(listener))
for (let [onRange, { limit, onEnd }] of this._listeners) {
if (this._catchingUp.has(onRange))
continue;
listener.onIteratorRangeFound(range);
onRange(range);
if (limit !== -1 && --limit === 0) {
// We've reached our limit; no need to do more work for this listener.
this._listeners.delete(listener);
this._listeners.delete(onRange);
onEnd();
continue;
}
// Save the updated limit globally.
this._listeners.set(listener, { limit, onEnd });
this._listeners.set(onRange, { limit, onEnd });
}
yield range;

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

@ -45,11 +45,9 @@ add_task(function* test_start() {
caseSensitive: false,
entireWord: false,
finder: gMockFinder,
listener: {
onIteratorRangeFound(range) {
++count;
Assert.equal(range.toString(), findText, "Text content should match");
}
onRange: range => {
++count;
Assert.equal(range.toString(), findText, "Text content should match");
},
word: findText
});
@ -57,8 +55,6 @@ add_task(function* test_start() {
Assert.equal(rangeCount, count, "Amount of ranges yielded should match!");
Assert.ok(!FinderIterator.running, "Running state should match");
Assert.equal(FinderIterator._previousRanges.length, rangeCount, "Ranges cache should match");
FinderIterator.reset();
});
add_task(function* test_valid_arguments() {
@ -72,7 +68,7 @@ add_task(function* test_valid_arguments() {
caseSensitive: false,
entireWord: false,
finder: gMockFinder,
listener: { onIteratorRangeFound(range) { ++count; } },
onRange: range => ++count,
word: findText
});
@ -84,14 +80,14 @@ add_task(function* test_valid_arguments() {
count = 0;
Assert.throws(() => FinderIterator.start({
entireWord: false,
listener: { onIteratorRangeFound(range) { ++count; } },
onRange: range => ++count,
word: findText
}), /Missing required option 'caseSensitive'/, "Should throw when missing an argument");
FinderIterator.reset();
Assert.throws(() => FinderIterator.start({
caseSensitive: false,
listener: { onIteratorRangeFound(range) { ++count; } },
onRange: range => ++count,
word: findText
}), /Missing required option 'entireWord'/, "Should throw when missing an argument");
FinderIterator.reset();
@ -99,7 +95,7 @@ add_task(function* test_valid_arguments() {
Assert.throws(() => FinderIterator.start({
caseSensitive: false,
entireWord: false,
listener: { onIteratorRangeFound(range) { ++count; } },
onRange: range => ++count,
word: findText
}), /Missing required option 'finder'/, "Should throw when missing an argument");
FinderIterator.reset();
@ -109,14 +105,14 @@ add_task(function* test_valid_arguments() {
entireWord: false,
finder: gMockFinder,
word: findText
}), /Missing valid, required option 'listener'/, "Should throw when missing an argument");
}), /Missing valid, required option 'onRange'/, "Should throw when missing an argument");
FinderIterator.reset();
Assert.throws(() => FinderIterator.start({
caseSensitive: false,
entireWord: true,
finder: gMockFinder,
listener: { onIteratorRangeFound(range) { ++count; } },
onRange: range => ++count
}), /Missing required option 'word'/, "Should throw when missing an argument");
FinderIterator.reset();
@ -133,7 +129,7 @@ add_task(function* test_stop() {
caseSensitive: false,
entireWord: false,
finder: gMockFinder,
listener: { onIteratorRangeFound(range) { ++count; } },
onRange: range => ++count,
word: findText
});
@ -142,8 +138,6 @@ add_task(function* test_stop() {
yield whenDone;
Assert.equal(count, 100, "Number of ranges should match `kIterationSizeMax`");
FinderIterator.reset();
});
add_task(function* test_reset() {
@ -156,7 +150,7 @@ add_task(function* test_reset() {
caseSensitive: false,
entireWord: false,
finder: gMockFinder,
listener: { onIteratorRangeFound(range) { ++count; } },
onRange: range => ++count,
word: findText
});
@ -187,7 +181,7 @@ add_task(function* test_parallel_starts() {
caseSensitive: false,
entireWord: false,
finder: gMockFinder,
listener: { onIteratorRangeFound(range) { ++count; } },
onRange: range => ++count,
word: findText
});
@ -200,7 +194,7 @@ add_task(function* test_parallel_starts() {
caseSensitive: false,
entireWord: false,
finder: gMockFinder,
listener: { onIteratorRangeFound(range) { ++count2; } },
onRange: range => ++count2,
word: findText
});