Bug 1163961 - Browser API: Page search. r=kchen, r=ehsan

This commit is contained in:
Paul Rouget 2015-06-09 20:04:00 -04:00
Родитель fce6dd199a
Коммит 53184296fe
12 изменённых файлов: 363 добавлений и 2 удалений

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

@ -229,7 +229,10 @@ BrowserElementChild.prototype = {
"activate-next-paint-listener": this._activateNextPaintListener.bind(this),
"set-input-method-active": this._recvSetInputMethodActive.bind(this),
"deactivate-next-paint-listener": this._deactivateNextPaintListener.bind(this),
"do-command": this._recvDoCommand
"do-command": this._recvDoCommand,
"find-all": this._recvFindAll.bind(this),
"find-next": this._recvFindNext.bind(this),
"clear-match": this._recvClearMatch.bind(this),
}
addMessageListener("browser-element-api:call", function(aMessage) {
@ -1240,6 +1243,57 @@ BrowserElementChild.prototype = {
}
},
_initFinder: function() {
if (!this._finder) {
try {
this._findLimit = Services.prefs.getIntPref("accessibility.typeaheadfind.matchesCountLimit");
} catch (e) {
// Pref not available, assume 0, no match counting.
this._findLimit = 0;
}
let {Finder} = Components.utils.import("resource://gre/modules/Finder.jsm", {});
this._finder = new Finder(docShell);
this._finder.addResultListener({
onMatchesCountResult: (data) => {
sendAsyncMsg('findchange', {
active: true,
searchString: this._finder.searchString,
searchLimit: this._findLimit,
activeMatchOrdinal: data.current,
numberOfMatches: data.total
});
}
});
}
},
_recvFindAll: function(data) {
this._initFinder();
let searchString = data.json.searchString;
this._finder.caseSensitive = data.json.caseSensitive;
this._finder.fastFind(searchString, false, false);
this._finder.requestMatchesCount(searchString, this._findLimit, false);
},
_recvFindNext: function(data) {
if (!this._finder) {
debug("findNext() called before findAll()");
return;
}
this._finder.findAgain(data.json.backward, false, false);
this._finder.requestMatchesCount(this._finder.searchString, this._findLimit, false);
},
_recvClearMatch: function(data) {
if (!this._finder) {
debug("clearMach() called before findAll()");
return;
}
this._finder.removeSelection();
sendAsyncMsg('findchange', {active: false});
},
_recvSetInputMethodActive: function(data) {
let msgData = { id: data.json.id };
if (!this._isContentWindowCreated) {

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

@ -206,6 +206,7 @@ BrowserElementParent.prototype = {
"selectionstatechanged": this._handleSelectionStateChanged,
"scrollviewchange": this._handleScrollViewChange,
"caretstatechanged": this._handleCaretStateChanged,
"findchange": this._handleFindChange
};
let mmSecuritySensitiveCalls = {
@ -475,6 +476,12 @@ BrowserElementParent.prototype = {
this._frameElement.dispatchEvent(evt);
},
_handleFindChange: function(data) {
let evt = this._createEvent("findchange", data.json,
/* cancelable = */ false);
this._frameElement.dispatchEvent(evt);
},
_createEvent: function(evtName, detail, cancelable) {
// This will have to change if we ever want to send a CustomEvent with null
// detail. For now, it's OK.
@ -646,6 +653,23 @@ BrowserElementParent.prototype = {
getCanGoForward: defineDOMRequestMethod('get-can-go-forward'),
getContentDimensions: defineDOMRequestMethod('get-contentdimensions'),
findAll: defineNoReturnMethod(function(searchString, caseSensitivity) {
return this._sendAsyncMsg('find-all', {
searchString,
caseSensitive: caseSensitivity == Ci.nsIBrowserElementAPI.FIND_CASE_SENSITIVE
});
}),
findNext: defineNoReturnMethod(function(direction) {
return this._sendAsyncMsg('find-next', {
backward: direction == Ci.nsIBrowserElementAPI.FIND_BACKWARD
});
}),
clearMatch: defineNoReturnMethod(function() {
return this._sendAsyncMsg('clear-match');
}),
goBack: defineNoReturnMethod(function() {
this._sendAsyncMsg('go-back');
}),

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

@ -0,0 +1,146 @@
/* Any copyright is dedicated to the public domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
// Bug 1163961 - Test search API
"use strict";
SimpleTest.waitForExplicitFinish();
browserElementTestHelpers.setEnabledPref(true);
browserElementTestHelpers.addPermission();
function runTest() {
let iframe = document.createElement('iframe');
iframe.setAttribute('mozbrowser', 'true');
iframe.src = 'data:text/html,foo bar foo XXX Foo BAR foobar foobar';
const once = (eventName) => {
return new Promise((resolve) => {
iframe.addEventListener(eventName, function onEvent(...args) {
iframe.removeEventListener(eventName, onEvent);
resolve(...args);
});
});
}
// Test if all key=>value pairs in o1 are present in o2.
const c = (o1, o2) => Object.keys(o1).every(k => o1[k] == o2[k]);
let testCount = 0;
once('mozbrowserloadend').then(() => {
iframe.findAll('foo', 'case-insensitive');
return once('mozbrowserfindchange');
}).then(({detail}) => {
ok(c(detail, {
msg_name: "findchange",
active: true,
searchString: 'foo',
searchLimit: 100,
activeMatchOrdinal: 1,
numberOfMatches: 5,
}), `test ${testCount++}`);
iframe.findNext('forward');
return once('mozbrowserfindchange');
}).then(({detail}) => {
ok(c(detail, {
msg_name: "findchange",
active: true,
searchString: 'foo',
searchLimit: 100,
activeMatchOrdinal: 2,
numberOfMatches: 5,
}), `test ${testCount++}`);
iframe.findNext('backward');
return once('mozbrowserfindchange');
}).then(({detail}) => {
ok(c(detail, {
msg_name: "findchange",
active: true,
searchString: 'foo',
searchLimit: 100,
activeMatchOrdinal: 1,
numberOfMatches: 5,
}), `test ${testCount++}`);
iframe.findAll('xxx', 'case-sensitive');
return once('mozbrowserfindchange');
}).then(({detail}) => {
ok(c(detail, {
msg_name: "findchange",
active: true,
searchString: 'xxx',
searchLimit: 100,
activeMatchOrdinal: 0,
numberOfMatches: 0,
}), `test ${testCount++}`);
iframe.findAll('bar', 'case-insensitive');
return once('mozbrowserfindchange');
}).then(({detail}) => {
ok(c(detail, {
msg_name: "findchange",
active: true,
searchString: 'bar',
searchLimit: 100,
activeMatchOrdinal: 1,
numberOfMatches: 4,
}), `test ${testCount++}`);
iframe.findNext('forward');
return once('mozbrowserfindchange');
}).then(({detail}) => {
ok(c(detail, {
msg_name: "findchange",
active: true,
searchString: 'bar',
searchLimit: 100,
activeMatchOrdinal: 2,
numberOfMatches: 4,
}), `test ${testCount++}`);
iframe.findNext('forward');
return once('mozbrowserfindchange');
}).then(({detail}) => {
ok(c(detail, {
msg_name: "findchange",
active: true,
searchString: 'bar',
searchLimit: 100,
activeMatchOrdinal: 3,
numberOfMatches: 4,
}), `test ${testCount++}`);
iframe.findNext('forward');
return once('mozbrowserfindchange');
}).then(({detail}) => {
ok(c(detail, {
msg_name: "findchange",
active: true,
searchString: 'bar',
searchLimit: 100,
activeMatchOrdinal: 4,
numberOfMatches: 4,
}), `test ${testCount++}`);
iframe.findNext('forward');
return once('mozbrowserfindchange');
}).then(({detail}) => {
ok(c(detail, {
msg_name: "findchange",
active: true,
searchString: 'bar',
searchLimit: 100,
activeMatchOrdinal: 1,
numberOfMatches: 4,
}), `test ${testCount++}`);
iframe.clearMatch();
return once('mozbrowserfindchange');
}).then(({detail}) => {
ok(c(detail, {
msg_name: "findchange",
active: false
}), `test ${testCount++}`);
SimpleTest.finish();
});
document.body.appendChild(iframe);
}
addEventListener('testready', runTest);

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

@ -7,6 +7,7 @@ skip-if = os == "android" || (toolkit == "cocoa" && debug) || buildapp == 'mulet
support-files =
browserElement_OpenMixedProcess.js
file_browserElement_OpenMixedProcess.html
browserElement_Find.js
browserElement_OpenTab.js
[test_browserElement_oop_Viewmode.html]
@ -41,6 +42,7 @@ skip-if = (toolkit == 'gonk' && !debug)
disabled = bug 1022281
[test_browserElement_oop_ErrorSecurity.html]
skip-if = (toolkit == 'gonk' && !debug)
[test_browserElement_oop_Find.html]
[test_browserElement_oop_FirstPaint.html]
[test_browserElement_oop_ForwardName.html]
[test_browserElement_oop_FrameWrongURI.html]

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

@ -29,6 +29,7 @@ support-files =
browserElement_Download.js
browserElement_ErrorSecurity.js
browserElement_ExposableURI.js
browserElement_Find.js
browserElement_FirstPaint.js
browserElement_ForwardName.js
browserElement_FrameWrongURI.js
@ -159,6 +160,7 @@ skip-if = os == "android" || toolkit == 'gonk' # embed-apps doesn't work in the
[test_browserElement_inproc_Download.html]
disabled = bug 1022281
[test_browserElement_inproc_ExposableURI.html]
[test_browserElement_inproc_Find.html]
[test_browserElement_inproc_FirstPaint.html]
[test_browserElement_inproc_ForwardName.html]
[test_browserElement_inproc_FrameWrongURI.html]

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

@ -0,0 +1,19 @@
<!DOCTYPE HTML>
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=1163961
-->
<head>
<title>Test for Bug 1163961</title>
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<script type="application/javascript" src="browserElementTestHelpers.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
</head>
<body>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1163961">Mozilla Bug 1163961</a>
<script type="application/javascript;version=1.7" src="browserElement_Find.js">
</script>
</body>
</html>

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

@ -0,0 +1,19 @@
<!DOCTYPE HTML>
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=1163961
-->
<head>
<title>Test for Bug 1163961</title>
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<script type="application/javascript" src="browserElementTestHelpers.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
</head>
<body>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1163961">Mozilla Bug 1163961</a>
<script type="application/javascript;version=1.7" src="browserElement_Find.js">
</script>
</body>
</html>

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

@ -26,9 +26,15 @@ interface nsIBrowserElementNextPaintListener : nsISupports
* Interface to the BrowserElementParent implementation. All methods
* but setFrameLoader throw when the remote process is dead.
*/
[scriptable, uuid(3811446f-90bb-42c1-b2b6-aae3603b61e1)]
[scriptable, uuid(8ecb598c-f886-11e4-9915-778f934fbf93)]
interface nsIBrowserElementAPI : nsISupports
{
const long FIND_CASE_SENSITIVE = 0;
const long FIND_CASE_INSENSITIVE = 1;
const long FIND_FORWARD = 0;
const long FIND_BACKWARD = 1;
void setFrameLoader(in nsIFrameLoader frameLoader);
void setVisible(in boolean visible);
@ -67,6 +73,10 @@ interface nsIBrowserElementAPI : nsISupports
nsIDOMDOMRequest getCanGoForward();
nsIDOMDOMRequest getContentDimensions();
void findAll(in DOMString searchString, in long caseSensitivity);
void findNext(in long direction);
void clearMatch();
void addNextPaintListener(in nsIBrowserElementNextPaintListener listener);
void removeNextPaintListener(in nsIBrowserElementNextPaintListener listener);

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

@ -376,6 +376,62 @@ nsBrowserElement::GetContentDimensions(ErrorResult& aRv)
return req.forget().downcast<DOMRequest>();
}
void
nsBrowserElement::FindAll(const nsAString& aSearchString,
BrowserFindCaseSensitivity aCaseSensitivity,
ErrorResult& aRv)
{
NS_ENSURE_TRUE_VOID(IsBrowserElementOrThrow(aRv));
NS_ENSURE_TRUE_VOID(IsNotWidgetOrThrow(aRv));
uint32_t caseSensitivity;
if (aCaseSensitivity == BrowserFindCaseSensitivity::Case_insensitive) {
caseSensitivity = nsIBrowserElementAPI::FIND_CASE_INSENSITIVE;
} else {
caseSensitivity = nsIBrowserElementAPI::FIND_CASE_SENSITIVE;
}
nsresult rv = mBrowserElementAPI->FindAll(aSearchString, caseSensitivity);
if (NS_FAILED(rv)) {
aRv.Throw(rv);
}
}
void
nsBrowserElement::FindNext(BrowserFindDirection aDirection,
ErrorResult& aRv)
{
NS_ENSURE_TRUE_VOID(IsBrowserElementOrThrow(aRv));
NS_ENSURE_TRUE_VOID(IsNotWidgetOrThrow(aRv));
uint32_t direction;
if (aDirection == BrowserFindDirection::Backward) {
direction = nsIBrowserElementAPI::FIND_BACKWARD;
} else {
direction = nsIBrowserElementAPI::FIND_FORWARD;
}
nsresult rv = mBrowserElementAPI->FindNext(direction);
if (NS_FAILED(rv)) {
aRv.Throw(rv);
}
}
void
nsBrowserElement::ClearMatch(ErrorResult& aRv)
{
NS_ENSURE_TRUE_VOID(IsBrowserElementOrThrow(aRv));
NS_ENSURE_TRUE_VOID(IsNotWidgetOrThrow(aRv));
nsresult rv = mBrowserElementAPI->ClearMatch();
if (NS_FAILED(rv)) {
aRv.Throw(rv);
}
}
void
nsBrowserElement::AddNextPaintListener(BrowserElementNextPaintEventCallback& aListener,
ErrorResult& aRv)

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

@ -20,6 +20,8 @@ namespace dom {
struct BrowserElementDownloadOptions;
class BrowserElementNextPaintEventCallback;
class DOMRequest;
enum class BrowserFindCaseSensitivity: uint32_t;
enum class BrowserFindDirection: uint32_t;
} // namespace dom
class ErrorResult;
@ -80,6 +82,11 @@ public:
already_AddRefed<dom::DOMRequest> GetCanGoForward(ErrorResult& aRv);
already_AddRefed<dom::DOMRequest> GetContentDimensions(ErrorResult& aRv);
void FindAll(const nsAString& aSearchString, dom::BrowserFindCaseSensitivity aCaseSensitivity,
ErrorResult& aRv);
void FindNext(dom::BrowserFindDirection aDirection, ErrorResult& aRv);
void ClearMatch(ErrorResult& aRv);
void AddNextPaintListener(dom::BrowserElementNextPaintEventCallback& listener,
ErrorResult& aRv);
void RemoveNextPaintListener(dom::BrowserElementNextPaintEventCallback& listener,

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

@ -6,6 +6,9 @@
callback BrowserElementNextPaintEventCallback = void ();
enum BrowserFindCaseSensitivity { "case-sensitive", "case-insensitive" };
enum BrowserFindDirection { "forward", "backward" };
dictionary BrowserElementDownloadOptions {
DOMString? filename;
DOMString? referrer;
@ -146,4 +149,20 @@ interface BrowserElementPrivileged {
Pref="dom.mozBrowserFramesEnabled",
CheckPermissions="browser"]
void setNFCFocus(boolean isFocus);
[Throws,
Pref="dom.mozBrowserFramesEnabled",
CheckPermissions="browser"]
void findAll(DOMString searchString, BrowserFindCaseSensitivity caseSensitivity);
[Throws,
Pref="dom.mozBrowserFramesEnabled",
CheckPermissions="browser"]
void findNext(BrowserFindDirection direction);
[Throws,
Pref="dom.mozBrowserFramesEnabled",
CheckPermissions="browser"]
void clearMatch();
};

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

@ -1048,3 +1048,6 @@ function GetClipboardSearchString(aLoadContext) {
return searchString;
}
this.Finder = Finder;
this.GetClipboardSearchString = GetClipboardSearchString;