Bug 1055508 - Make the findbar respect the search clipboard as well as prefillwithselection in e10s. r=mikedeboer

This commit is contained in:
Blake Kaplan 2015-01-16 15:11:00 +01:00
parent 0715fc3d04
commit b6d9283771
7 changed files with 230 additions and 130 deletions

View File

@ -219,7 +219,7 @@ skip-if = e10s
[browser_bug565667.js]
run-if = toolkit == "cocoa"
[browser_bug567306.js]
skip-if = e10s
skip-if = e10s # Bug XXX - Needs some massaging to run in e10s
[browser_bug575561.js]
[browser_bug575830.js]
skip-if = e10s # Bug 1056146 - zoom tests use FullZoomHelper and break in e10s

View File

@ -13,8 +13,8 @@ function test() {
whenNewWindowLoaded(undefined, function (win) {
whenDelayedStartupFinished(win, function () {
let selectedBrowser = win.gBrowser.selectedBrowser;
selectedBrowser.addEventListener("pageshow", function() {
selectedBrowser.removeEventListener("pageshow", arguments.callee, true);
selectedBrowser.addEventListener("pageshow", function pageshowListener() {
selectedBrowser.removeEventListener("pageshow", pageshowListener, true);
ok(true, "pageshow listener called: " + win.content.location);
waitForFocus(function () {
onFocus(win);
@ -39,7 +39,11 @@ function onFocus(win) {
ok(!win.gFindBarInitialized, "find bar is not yet initialized");
let findBar = win.gFindBar;
selectText(win.content);
findBar.onFindCommand();
findBar.onFindCommand().then(onInitialized.bind(null, findBar, win));
}
function onInitialized(findBar, win) {
// When the OS supports the Find Clipboard (OSX), the find field value is
// persisted across Fx sessions, thus not useful to test.
if (!HasFindClipboard)

View File

@ -2,13 +2,6 @@
* http://creativecommons.org/publicdomain/zero/1.0/
*/
///////////////////
//
// Whitelisting this test.
// As part of bug 1077403, the leaking uncaught rejection should be fixed.
//
thisTestLeaksUncaughtRejectionsAndShouldBeFixed("");
var gTab = null;
function load(url, cb) {

View File

@ -93,9 +93,11 @@
testNormalFind();
gFindBar.close();
ok(gFindBar.hidden, "Failed to close findbar after testNormalFind");
yield openFindbar();
testNormalFindWithComposition();
gFindBar.close();
ok(gFindBar.hidden, "findbar should be hidden after testNormalFindWithComposition");
yield openFindbar();
testAutoCaseSensitivityUI();
testQuickFindText();
gFindBar.close();
@ -103,7 +105,8 @@
testFindWithHighlight();
gFindBar.close();
ok(gFindBar.hidden, "Failed to close findbar after testFindWithHighlight");
testFindbarSelection();
yield Task.spawn(testFindbarSelection);
ok(gFindBar.hidden, "Failed to close findbar after testFindbarSelection");
testDrop();
testQuickFindLink();
if (gHasFindClipboard) {
@ -112,17 +115,18 @@
yield testFindCountUI();
gFindBar.close();
ok(gFindBar.hidden, "Failed to close findbar after testFindCountUI");
yield openFindbar();
yield testFindAfterCaseChanged();
gFindBar.close();
yield openFindbar();
yield testFailedStringReset();
gFindBar.close();
yield testQuickFindClose();
finish();
});
function testFindbarSelection() {
function* testFindbarSelection() {
function checkFindbarState(aTestName, aExpSelection) {
document.getElementById("cmd_find").doCommand();
ok(!gFindBar.hidden, "testFindbarSelection: failed to open findbar: " + aTestName);
ok(document.commandDispatcher.focusedElement == gFindBar._findField.inputField,
"testFindbarSelection: find field is not focused: " + aTestName);
@ -145,16 +149,19 @@
cRange.setEnd(cH2, 1);
cSelection.removeAllRanges();
cSelection.addRange(cRange);
yield openFindbar();
checkFindbarState("plain text", SEARCH_TEXT);
// test nsIDOMNSEditableElement with selection
var textInput = gBrowser.contentDocument.getElementById("text");
textInput.focus();
textInput.select();
yield openFindbar();
checkFindbarState("text input", SAMPLE_TEXT);
// test non-editable nsIDOMNSEditableElement (button)
gBrowser.contentDocument.getElementById("button").focus();
yield openFindbar();
checkFindbarState("button", "");
}
@ -226,9 +233,12 @@
}
}
function testNormalFindWithComposition() {
function openFindbar() {
document.getElementById("cmd_find").doCommand();
return gFindBar._startFindDeferred.promise;
}
function testNormalFindWithComposition() {
ok(!gFindBar.hidden, "testNormalFindWithComposition: findbar should be open");
ok(document.commandDispatcher.focusedElement == gFindBar._findField.inputField,
"testNormalFindWithComposition: find field should be focused");
@ -261,7 +271,7 @@
synthesizeComposition({ type: "compositioncommitasis" });
ok(gBrowser.contentWindow.getSelection().toString().toLowerCase() == searchStr,
is(gBrowser.contentWindow.getSelection().toString().toLowerCase(), searchStr,
"testNormalFindWithComposition: text should be found after committing composition");
testClipboardSearchString(gBrowser.contentWindow.getSelection().toString());
@ -273,7 +283,6 @@
function testAutoCaseSensitivityUI() {
var matchCaseCheckbox = gFindBar.getElement("find-case-sensitive");
var matchCaseLabel = gFindBar.getElement("match-case-status");
document.getElementById("cmd_find").doCommand();
ok(!matchCaseCheckbox.hidden, "match case box is hidden in manual mode");
ok(matchCaseLabel.hidden, "match case label is visible in manual mode");
@ -331,10 +340,21 @@
//clearFocus();
gFindBar._findField.value = "";
// For this test, we want to closely control the selection. The easiest
// way to do so is to replace the implementation of
// Finder.getInitialSelection with a no-op and call the findbar's callback
// (onCurrentSelection(..., true)) ourselves with our hand-picked
// selection.
let oldGetInitialSelection = gFindBar.browser.finder.getInitialSelection;
let searchStr;
gFindBar.browser.finder.getInitialSelection = function(){};
let findCommand = document.getElementById("cmd_find");
findCommand.doCommand();
let searchStr = "e";
gFindBar.onCurrentSelection("", true);
searchStr = "e";
enterStringIntoFindField(searchStr);
let a = gFindBar._findField.value;
@ -342,11 +362,11 @@
let c = gFindBar._browser.finder.searchString;
ok(a == b && b == c, "testFindWithHighlight 1: " + a + ", " + b + ", " + c + ".");
let oldGetInitialSelection = gFindBar._getInitialSelection;
searchStr = "t";
gFindBar._getInitialSelection = () => searchStr;
findCommand.doCommand();
gFindBar._getInitialSelection = oldGetInitialSelection;
gFindBar.onCurrentSelection(searchStr, true);
gFindBar.browser.finder.getInitialSelection = oldGetInitialSelection;
a = gFindBar._findField.value;
b = gFindBar._browser.finder._fastFind.searchString;
@ -472,7 +492,6 @@
// See bug 1051187.
function testFindAfterCaseChanged() {
let deferred = Promise.defer();
document.getElementById("cmd_find").doCommand();
// Search to set focus on "Text Test" so that searching for "t" selects first
// (upper case!) "T".
@ -499,7 +518,6 @@
// 2. Uncheck case sensitivity button to match the string.
function testFailedStringReset(aCallback) {
let deferred = Promise.defer();
document.getElementById("cmd_find").doCommand();
var prefsvc = Cc["@mozilla.org/preferences-service;1"].
getService(Components.interfaces.nsIPrefBranch);

View File

@ -215,10 +215,15 @@
<field name="_flashFindBar">0</field>
<field name="_initialFlashFindBarCount">6</field>
<!--
- For tests that need to know when the find bar is finished
- initializing, we store a promise to notify on.
-->
<field name="_startFindDeferred">null</field>
<property name="prefillWithSelection"
onget="return this.getAttribute('prefillwithselection') != 'false'"
onset="this.setAttribute('prefillwithselection', val); return val;"/>
<field name="_selectionMaxLen">150</field>
<method name="getElement">
<parameter name="aAnonymousID"/>
@ -1026,43 +1031,6 @@
]]></body>
</method>
<method name="_getInitialSelection">
<body><![CDATA[
let focusedElement = document.commandDispatcher.focusedElement;
let selText;
if (focusedElement instanceof Components.interfaces.nsIDOMNSEditableElement &&
focusedElement.editor &&
focusedElement.ownerDocument.defaultView.top == this._browser.contentWindow)
{
// The user may have a selection in an input or textarea
selText = focusedElement.editor.selectionController
.getSelection(Components.interfaces.nsISelectionController.SELECTION_NORMAL)
.toString();
}
else {
// Look for any selected text on the actual page
let focusedWindow = document.commandDispatcher.focusedWindow;
if (focusedWindow.top == this._browser.contentWindow)
selText = focusedWindow.getSelection().toString();
}
if (!selText)
return "";
// Process our text to get rid of unwanted characters
if (selText.length > this._selectionMaxLen) {
let pattern = new RegExp("^(?:\\s*.){0," + this._selectionMaxLen + "}");
pattern.test(selText);
selText = RegExp.lastMatch;
}
return selText.replace(/^\s+/, "")
.replace(/\s+$/, "")
.replace(/\s+/g, " ")
.substr(0, this._selectionMaxLen);
]]></body>
</method>
<method name="_dispatchFindEvent">
<parameter name="aType"/>
<parameter name="aFindPrevious"/>
@ -1102,28 +1070,29 @@
--this._flashFindBar);
}
let {PromiseUtils} =
Components.utils.import("resource://gre/modules/PromiseUtils.jsm", {});
this._startFindDeferred = PromiseUtils.defer();
let startFindPromise = this._startFindDeferred.promise;
if (this.prefillWithSelection)
userWantsPrefill =
prefsvc.getBoolPref("accessibility.typeaheadfind.prefillwithselection");
let initialString = null;
if (this.prefillWithSelection && userWantsPrefill)
initialString = this._getInitialSelection();
#ifdef XP_MACOSX
if (!initialString) {
let clipboardSearchString = this.browser.finder.clipboardSearchString;
if (clipboardSearchString)
initialString = clipboardSearchString;
if (this.prefillWithSelection && userWantsPrefill) {
// NB: We have to focus this._findField here so tests that send
// key events can open and close the find bar synchronously.
this._findField.focus();
this.browser.finder.getInitialSelection();
return startFindPromise;
}
#endif
if (initialString)
this._findField.value = initialString;
this._enableFindButtons(!!this._findField.value);
this._findField.select();
this._findField.focus();
// If userWantsPrefill is false but prefillWithSelection is true,
// then we might need to check the selection clipboard. Call
// onCurrentSelection to do so.
// Note: this.onCurrentSelection clears this._startFindDeferred.
this.onCurrentSelection("", true);
return startFindPromise;
]]></body>
</method>
@ -1135,7 +1104,7 @@
-->
<method name="onFindCommand">
<body><![CDATA[
this.startFind(this.FIND_NORMAL);
return this.startFind(this.FIND_NORMAL);
]]></body>
</method>
@ -1148,10 +1117,8 @@
<parameter name="aFindPrevious"/>
<body><![CDATA[
let findString = this._browser.finder.searchString || this._findField.value;
if (!findString) {
this.startFind();
return;
}
if (!findString)
return this.startFind();
// We dispatch the findAgain event here instead of in _findAgain since
// if there is a find event handler that prevents the default then
@ -1265,6 +1232,33 @@
]]></body>
</method>
<method name="onCurrentSelection">
<parameter name="aSelectionString" />
<parameter name="aIsInitialSelection" />
<body><![CDATA[
#ifdef XP_MACOSX
if (aIsInitialSelection && !aSelectionString) {
let clipboardSearchString = this.browser.finder.clipboardSearchString;
if (clipboardSearchString)
aSelectionString = clipboardSearchString;
}
#endif
if (aSelectionString)
this._findField.value = aSelectionString;
if (aIsInitialSelection) {
this._enableFindButtons(!!this._findField.value);
this._findField.select();
this._findField.focus();
if (this._startFindDeferred) {
this._startFindDeferred.resolve();
this._startFindDeferred = null;
}
}
]]></body>
</method>
<!--
- This handler may cancel a request to focus content by returning |false|
- explicitly.

View File

@ -1,8 +1,9 @@
// vim: set ts=2 sw=2 sts=2 tw=80:
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
this.EXPORTED_SYMBOLS = ["Finder"];
this.EXPORTED_SYMBOLS = ["Finder","GetClipboardSearchString"];
const Ci = Components.interfaces;
const Cc = Components.classes;
@ -24,6 +25,7 @@ XPCOMUtils.defineLazyServiceGetter(this, "ClipboardHelper",
"nsIClipboardHelper");
const kHighlightIterationSizeMax = 100;
const kSelectionMaxLen = 150;
function Finder(docShell) {
this._fastFind = Cc["@mozilla.org/typeaheadfind;1"].createInstance(Ci.nsITypeAheadFind);
@ -90,31 +92,10 @@ Finder.prototype = {
},
get clipboardSearchString() {
let searchString = "";
if (!Clipboard.supportsFindClipboard())
return searchString;
try {
let trans = Cc["@mozilla.org/widget/transferable;1"]
.createInstance(Ci.nsITransferable);
trans.init(this._getWindow()
.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIWebNavigation)
.QueryInterface(Ci.nsILoadContext));
trans.addDataFlavor("text/unicode");
Clipboard.getData(trans, Ci.nsIClipboard.kFindClipboard);
let data = {};
let dataLen = {};
trans.getTransferData("text/unicode", data, dataLen);
if (data.value) {
data = data.value.QueryInterface(Ci.nsISupportsString);
searchString = data.toString();
}
} catch (ex) {}
return searchString;
return GetClipboardSearchString(this._getWindow()
.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIWebNavigation)
.QueryInterface(Ci.nsILoadContext));
},
set clipboardSearchString(aSearchString) {
@ -165,12 +146,8 @@ Finder.prototype = {
* selected text in the window, on supported platforms (i.e. OSX).
*/
setSearchStringToSelection: function() {
// Find the selected text.
let selection = this._getWindow().getSelection();
// Don't go for empty selections.
if (!selection.rangeCount)
return null;
let searchString = (selection.toString() || "").trim();
let searchString = this.getActiveSelectionText();
// Empty strings are rather useless to search for.
if (!searchString.length)
return null;
@ -201,6 +178,44 @@ Finder.prototype = {
}
}),
getInitialSelection: function() {
this._getWindow().setTimeout(() => {
let initialSelection = this.getActiveSelectionText();
for (let l of this._listeners) {
try {
l.onCurrentSelection(initialSelection, true);
} catch (ex) {}
}
}, 0);
},
getActiveSelectionText: function() {
let focusedWindow = {};
let focusedElement =
Services.focus.getFocusedElementForWindow(this._getWindow(), true,
focusedWindow);
focusedWindow = focusedWindow.value;
let selText;
if (focusedElement instanceof Ci.nsIDOMNSEditableElement &&
focusedElement.editor) {
// The user may have a selection in an input or textarea.
selText = focusedElement.editor.selectionController
.getSelection(Ci.nsISelectionController.SELECTION_NORMAL)
.toString();
} else {
// Look for any selected text on the actual page.
selText = focusedWindow.getSelection().toString();
}
if (!selText)
return "";
// Process our text to get rid of unwanted characters.
return selText.trim().replace(/\s+/g, " ").substr(0, kSelectionMaxLen);
},
enableSelection: function() {
this._fastFind.setSelectionModeAndRepaint(Ci.nsISelectionController.SELECTION_ON);
this._restoreOriginalOutline();
@ -647,8 +662,14 @@ Finder.prototype = {
_getSelectionController: function(aWindow) {
// display: none iframes don't have a selection controller, see bug 493658
if (!aWindow.innerWidth || !aWindow.innerHeight)
try {
if (!aWindow.innerWidth || !aWindow.innerHeight)
return null;
} catch (e) {
// If getting innerWidth or innerHeight throws, we can't get a selection
// controller.
return null;
}
// Yuck. See bug 138068.
let docShell = aWindow.QueryInterface(Ci.nsIInterfaceRequestor)
@ -1002,3 +1023,28 @@ Finder.prototype = {
QueryInterface: XPCOMUtils.generateQI([Ci.nsIWebProgressListener,
Ci.nsISupportsWeakReference])
};
function GetClipboardSearchString(aLoadContext) {
let searchString = "";
if (!Clipboard.supportsFindClipboard())
return searchString;
try {
let trans = Cc["@mozilla.org/widget/transferable;1"]
.createInstance(Ci.nsITransferable);
trans.init(aLoadContext);
trans.addDataFlavor("text/unicode");
Clipboard.getData(trans, Ci.nsIClipboard.kFindClipboard);
let data = {};
let dataLen = {};
trans.getTransferData("text/unicode", data, dataLen);
if (data.value) {
data = data.value.QueryInterface(Ci.nsISupportsString);
searchString = data.toString();
}
} catch (ex) {}
return searchString;
}

View File

@ -1,4 +1,5 @@
// -*- indent-tabs-mode: nil; js-indent-level: 2 -*-
// vim: set ts=2 sw=2 sts=2 et tw=80: */
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
@ -11,39 +12,53 @@ const Cu = Components.utils;
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
XPCOMUtils.defineLazyGetter(this, "GetClipboardSearchString",
() => Cu.import("resource://gre/modules/Finder.jsm", {}).GetClipboardSearchString
);
function RemoteFinder(browser) {
this._browser = browser;
this._listeners = [];
this._listeners = new Set();
this._searchString = null;
this._browser.messageManager.addMessageListener("Finder:Result", this);
this._browser.messageManager.addMessageListener("Finder:MatchesResult", this);
this._browser.messageManager.sendAsyncMessage("Finder:Initialize");
let mm = this._browser.messageManager;
mm.addMessageListener("Finder:Result", this);
mm.addMessageListener("Finder:MatchesResult", this);
mm.addMessageListener("Finder:CurrentSelectionResult",this);
mm.sendAsyncMessage("Finder:Initialize");
}
RemoteFinder.prototype = {
addResultListener: function (aListener) {
if (this._listeners.indexOf(aListener) === -1)
this._listeners.push(aListener);
this._listeners.add(aListener);
},
removeResultListener: function (aListener) {
this._listeners = this._listeners.filter(l => l != aListener);
this._listeners.delete(aListener);
},
receiveMessage: function (aMessage) {
// Only Finder:Result messages have the searchString field.
if (aMessage.name == "Finder:Result") {
this._searchString = aMessage.data.searchString;
let callback;
let params;
switch (aMessage.name) {
case "Finder:Result":
this._searchString = aMessage.data.searchString;
callback = "onFindResult";
params = [ aMessage.data ];
break;
case "Finder:MatchesResult":
callback = "onMatchesCountResult";
params = [ aMessage.data ];
break;
case "Finder:CurrentSelectionResult":
callback = "onCurrentSelection";
params = [ aMessage.data.selection, aMessage.data.initial ];
break;
}
// The parent can receive either one of the two types of message.
for (let l of this._listeners) {
if (aMessage.name == "Finder:Result") {
l.onFindResult(aMessage.data);
} else if (aMessage.name == "Finder:MatchesResult") {
l.onMatchesCountResult(aMessage.data);
}
l[callback].apply(l, params);
}
},
@ -51,11 +66,23 @@ RemoteFinder.prototype = {
return this._searchString;
},
get clipboardSearchString() {
return GetClipboardSearchString(this._browser.loadContext);
},
setSearchStringToSelection() {
this._browser.messageManager.sendAsyncMessage("Finder:SetSearchStringToSelection", {});
},
set caseSensitive(aSensitive) {
this._browser.messageManager.sendAsyncMessage("Finder:CaseSensitive",
{ caseSensitive: aSensitive });
},
getInitialSelection: function() {
this._browser.messageManager.sendAsyncMessage("Finder:GetInitialSelection", {});
},
fastFind: function (aSearchString, aLinksOnly) {
this._browser.messageManager.sendAsyncMessage("Finder:FastFind",
{ searchString: aSearchString,
@ -127,6 +154,8 @@ RemoteFinderListener.prototype = {
"Finder:CaseSensitive",
"Finder:FastFind",
"Finder:FindAgain",
"Finder:SetSearchStringToSelection",
"Finder:GetInitialSelection",
"Finder:Highlight",
"Finder:EnableSelection",
"Finder:RemoveSelection",
@ -153,6 +182,22 @@ RemoteFinderListener.prototype = {
this._finder.caseSensitive = data.caseSensitive;
break;
case "Finder:SetSearchStringToSelection": {
let selection = this._finder.setSearchStringToSelection();
this._global.sendAsyncMessage("Finder:CurrentSelectionResult",
{ selection: selection,
initial: false });
break;
}
case "Finder:GetInitialSelection": {
let selection = this._finder.getActiveSelectionText();
this._global.sendAsyncMessage("Finder:CurrentSelectionResult",
{ selection: selection,
initial: true });
break;
}
case "Finder:FastFind":
this._finder.fastFind(data.searchString, data.linksOnly);
break;