mirror of
https://github.com/mozilla/gecko-dev.git
synced 2025-02-03 12:35:58 +00:00
Bug 469443 - Form Manager Storage should be a JavaScript-based component. r=gavin
This commit is contained in:
parent
f7550df414
commit
55da1cfdc5
@ -197,7 +197,6 @@ pref("browser.startup.homepage", "resource:/browserconfig.properties"
|
||||
pref("browser.enable_automatic_image_resizing", true);
|
||||
pref("browser.chrome.site_icons", true);
|
||||
pref("browser.chrome.favicons", true);
|
||||
pref("browser.formfill.enable", true);
|
||||
pref("browser.warnOnQuit", true);
|
||||
pref("browser.warnOnRestart", true);
|
||||
pref("browser.fullscreen.autohide", true);
|
||||
|
@ -295,7 +295,7 @@ function runTest(testNum) {
|
||||
*/
|
||||
|
||||
default:
|
||||
ok(false, "Unexpected invocataion of test #" + testNum);
|
||||
ok(false, "Unexpected invocation of test #" + testNum);
|
||||
subwindow.close();
|
||||
SimpleTest.finish();
|
||||
return;
|
||||
|
@ -248,6 +248,7 @@ bin/components/aboutRights.js
|
||||
bin/components/aboutRobots.js
|
||||
bin/components/aboutCertError.js
|
||||
bin/components/nsBadCertHandler.js
|
||||
bin/components/nsFormAutoComplete.js
|
||||
|
||||
; Modules
|
||||
bin/modules/*
|
||||
|
@ -254,6 +254,7 @@ bin\components\aboutRights.js
|
||||
bin\components\aboutRobots.js
|
||||
bin\components\aboutCertError.js
|
||||
bin\components\nsBadCertHandler.js
|
||||
bin\components\nsFormAutoComplete.js
|
||||
|
||||
; Modules
|
||||
bin\modules\*
|
||||
|
@ -2712,6 +2712,11 @@ pref("signon.SignonFileName3", "signons3.txt"); // obsolete
|
||||
pref("signon.autofillForms", true);
|
||||
pref("signon.debug", false); // logs to Error Console
|
||||
|
||||
// Satchel (Form Manager) prefs
|
||||
pref("browser.formfill.enable", true);
|
||||
pref("browser.formfill.debug", false);
|
||||
|
||||
|
||||
// Zoom prefs
|
||||
pref("browser.zoom.full", false);
|
||||
pref("zoom.minPercent", 30);
|
||||
|
@ -617,7 +617,7 @@ function runTest(testNum) {
|
||||
return;
|
||||
|
||||
default:
|
||||
ok(false, "Unexpected invocataion of test #" + testNum);
|
||||
ok(false, "Unexpected invocation of test #" + testNum);
|
||||
SimpleTest.finish();
|
||||
return;
|
||||
}
|
||||
|
@ -47,6 +47,7 @@ MODULE = satchel
|
||||
XPIDL_MODULE = satchel
|
||||
|
||||
XPIDLSRCS = nsIFormFillController.idl \
|
||||
nsIFormAutoComplete.idl \
|
||||
nsIFormHistory.idl \
|
||||
$(NULL)
|
||||
|
||||
|
52
toolkit/components/satchel/public/nsIFormAutoComplete.idl
Normal file
52
toolkit/components/satchel/public/nsIFormAutoComplete.idl
Normal file
@ -0,0 +1,52 @@
|
||||
/* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla Public License Version
|
||||
* 1.1 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
* http://www.mozilla.org/MPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
* for the specific language governing rights and limitations under the
|
||||
* License.
|
||||
*
|
||||
* The Original Code is mozilla.org code.
|
||||
*
|
||||
* The Initial Developer of the Original Code is Mozilla Corporation.
|
||||
* Portions created by the Initial Developer are Copyright (C) 2009
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Justin Dolske <dolske@mozilla.com> (original author)
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
* of those above. If you wish to allow use of your version of this file only
|
||||
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
* use your version of this file under the terms of the MPL, indicate your
|
||||
* decision by deleting the provisions above and replace them with the notice
|
||||
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
* the provisions above, a recipient may use your version of this file under
|
||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
|
||||
#include "nsISupports.idl"
|
||||
|
||||
interface nsIAutoCompleteResult;
|
||||
|
||||
[scriptable, uuid(2f5bb765-428e-4e33-b2d8-441eb7ddf730)]
|
||||
|
||||
interface nsIFormAutoComplete: nsISupports {
|
||||
/**
|
||||
* Generate results for a form input autocomplete menu.
|
||||
*/
|
||||
nsIAutoCompleteResult autoCompleteSearch(
|
||||
in AString aInputName,
|
||||
in AString aSearchString,
|
||||
in nsIAutoCompleteResult aPreviousResult);
|
||||
};
|
@ -50,6 +50,10 @@ IS_COMPONENT = 1
|
||||
LIBXUL_LIBRARY = 1
|
||||
EXPORT_LIBRARY = 1
|
||||
|
||||
EXTRA_COMPONENTS = \
|
||||
nsFormAutoComplete.js \
|
||||
$(NULL)
|
||||
|
||||
REQUIRES = \
|
||||
xpcom \
|
||||
string \
|
||||
@ -92,4 +96,3 @@ EXTRA_DSO_LDOPTS += \
|
||||
ifdef MOZ_MORKREADER
|
||||
EXTRA_DSO_LDOPTS += $(DEPTH)/db/morkreader/$(LIB_PREFIX)morkreader_s.$(LIB_SUFFIX)
|
||||
endif
|
||||
|
||||
|
311
toolkit/components/satchel/src/nsFormAutoComplete.js
Normal file
311
toolkit/components/satchel/src/nsFormAutoComplete.js
Normal file
@ -0,0 +1,311 @@
|
||||
/* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla Public License Version
|
||||
* 1.1 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
* http://www.mozilla.org/MPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
* for the specific language governing rights and limitations under the
|
||||
* License.
|
||||
*
|
||||
* The Original Code is mozilla.org code.
|
||||
*
|
||||
* The Initial Developer of the Original Code is Mozilla Corporation.
|
||||
* Portions created by the Initial Developer are Copyright (C) 2009
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Justin Dolske <dolske@mozilla.com> (original author)
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
* of those above. If you wish to allow use of your version of this file only
|
||||
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
* use your version of this file under the terms of the MPL, indicate your
|
||||
* decision by deleting the provisions above and replace them with the notice
|
||||
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
* the provisions above, a recipient may use your version of this file under
|
||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
|
||||
const Cc = Components.classes;
|
||||
const Ci = Components.interfaces;
|
||||
const Cr = Components.results;
|
||||
|
||||
Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
|
||||
function FormAutoComplete() {
|
||||
this.init();
|
||||
}
|
||||
|
||||
FormAutoComplete.prototype = {
|
||||
classDescription: "FormAutoComplete",
|
||||
contractID: "@mozilla.org/satchel/form-autocomplete;1",
|
||||
classID: Components.ID("{c11c21b2-71c9-4f87-a0f8-5e13f50495fd}"),
|
||||
QueryInterface : XPCOMUtils.generateQI([Ci.nsIFormAutoComplete, Ci.nsISupportsWeakReference]),
|
||||
|
||||
__logService : null, // Console logging service, used for debugging.
|
||||
get _logService() {
|
||||
if (!this.__logService)
|
||||
this.__logService = Cc["@mozilla.org/consoleservice;1"].
|
||||
getService(Ci.nsIConsoleService);
|
||||
return this.__logService;
|
||||
},
|
||||
|
||||
__formHistory : null,
|
||||
get _formHistory() {
|
||||
if (!this.__formHistory)
|
||||
this.__formHistory = Cc["@mozilla.org/satchel/form-history;1"].
|
||||
getService(Ci.nsIFormHistory2);
|
||||
return this.__formHistory;
|
||||
},
|
||||
|
||||
__observerService : null, // Observer Service, for notifications
|
||||
get _observerService() {
|
||||
if (!this.__observerService)
|
||||
this.__observerService = Cc["@mozilla.org/observer-service;1"].
|
||||
getService(Ci.nsIObserverService);
|
||||
return this.__observerService;
|
||||
},
|
||||
|
||||
_prefBranch : null,
|
||||
_enabled : true, // mirrors browser.formfill.enable preference
|
||||
_debug : false, // mirrors browser.formfill.debug
|
||||
|
||||
init : function() {
|
||||
// Preferences. Add observer so we get notified of changes.
|
||||
this._prefBranch = Cc["@mozilla.org/preferences-service;1"].
|
||||
getService(Ci.nsIPrefService).getBranch("browser.formfill.");
|
||||
this._prefBranch.QueryInterface(Ci.nsIPrefBranch2);
|
||||
this._prefBranch.addObserver("", this.observer, false);
|
||||
this.observer._self = this;
|
||||
|
||||
this._debug = this._prefBranch.getBoolPref("debug");
|
||||
this._enabled = this._prefBranch.getBoolPref("enable");
|
||||
|
||||
this._dbStmts = [];
|
||||
|
||||
this._observerService.addObserver(this.observer, "xpcom-shutdown", false);
|
||||
},
|
||||
|
||||
observer : {
|
||||
_self : null,
|
||||
|
||||
QueryInterface : XPCOMUtils.generateQI([Ci.nsIObserver,
|
||||
Ci.nsISupportsWeakReference]),
|
||||
|
||||
observe : function (subject, topic, data) {
|
||||
let self = this._self;
|
||||
if (topic == "nsPref:changed") {
|
||||
let prefName = data;
|
||||
self.log("got change to " + prefName + " preference");
|
||||
|
||||
if (prefName == "debug") {
|
||||
self._debug = self._prefBranch.getBoolPref("debug");
|
||||
} else if (prefName == "enable") {
|
||||
self._enabled = self._prefBranch.getBoolPref("enable");
|
||||
} else {
|
||||
self.log("Oops! Pref not handled, change ignored.");
|
||||
}
|
||||
} else if (topic == "xpcom-shutdown") {
|
||||
self._dbStmts = null;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
/*
|
||||
* log
|
||||
*
|
||||
* Internal function for logging debug messages to the Error Console
|
||||
* window
|
||||
*/
|
||||
log : function (message) {
|
||||
if (!this._debug)
|
||||
return;
|
||||
dump("FormAutoComplete: " + message + "\n");
|
||||
this._logService.logStringMessage("FormAutoComplete: " + message);
|
||||
},
|
||||
|
||||
|
||||
/*
|
||||
* autoCompleteSearch
|
||||
*
|
||||
* aInputName -- |name| attribute from the form input being autocompleted.
|
||||
* aSearchString -- current value of the input
|
||||
* aPreviousResult -- previous search result, if any.
|
||||
*
|
||||
* Returns: an nsIAutoCompleteResult
|
||||
*/
|
||||
autoCompleteSearch : function (aInputName, aSearchString, aPreviousResult) {
|
||||
if (!this._enabled)
|
||||
return null;
|
||||
|
||||
this.log("AutoCompleteSearch invoked. Search is: " + aSearchString);
|
||||
|
||||
let result = null;
|
||||
|
||||
if (aPreviousResult) {
|
||||
this.log("Using previous autocomplete result");
|
||||
result = aPreviousResult;
|
||||
|
||||
// We have a list of results for a shorter search string, so just
|
||||
// filter them further based on the new search string.
|
||||
// Count backwards, because result.matchCount is decremented
|
||||
// when we remove an entry.
|
||||
for (let i = result.matchCount - 1; i >= 0; i--) {
|
||||
let match = result.getValueAt(i);
|
||||
|
||||
// Remove results that are too short, or have different prefix.
|
||||
// XXX bug 394604 -- .toLowerCase can be wrong for some intl chars
|
||||
if (aSearchString.length > match.length ||
|
||||
aSearchString.toLowerCase() !=
|
||||
match.substr(0, aSearchString.length).toLowerCase())
|
||||
{
|
||||
this.log("Removing autocomplete entry '" + match + "'");
|
||||
result.removeValueAt(i, false);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
this.log("Creating new autocomplete search result.");
|
||||
let entries = this.getAutoCompleteValues(aInputName, aSearchString);
|
||||
result = new FormAutoCompleteResult(this._formHistory, entries, aInputName, aSearchString);
|
||||
}
|
||||
|
||||
return result;
|
||||
},
|
||||
|
||||
getAutoCompleteValues : function (fieldName, searchString) {
|
||||
let values = [];
|
||||
|
||||
let query = "SELECT value FROM moz_formhistory " +
|
||||
"WHERE fieldname=:fieldname AND value LIKE :valuePrefix ESCAPE '/' " +
|
||||
"ORDER BY UPPER(value) ASC";
|
||||
let params = {
|
||||
fieldname: fieldName,
|
||||
valuePrefix: null // set below...
|
||||
}
|
||||
|
||||
let stmt;
|
||||
try {
|
||||
stmt = this._dbCreateStatement(query, params);
|
||||
|
||||
// Chicken and egg problem: Need the statement to escape the params we
|
||||
// pass to the function that gives us the statement. So, fix it up now.
|
||||
stmt.params.valuePrefix = stmt.escapeStringForLIKE(searchString, "/") + "%";
|
||||
|
||||
while (stmt.step())
|
||||
values.push(stmt.row.value);
|
||||
} catch (e) {
|
||||
this.log("getValues failed: " + e.name + " : " + e.message);
|
||||
throw "DB failed getting form autocomplete falues";
|
||||
} finally {
|
||||
stmt.reset();
|
||||
}
|
||||
|
||||
return values;
|
||||
},
|
||||
|
||||
|
||||
_dbStmts : null,
|
||||
|
||||
_dbCreateStatement : function (query, params) {
|
||||
let stmt = this._dbStmts[query];
|
||||
// Memoize the statements
|
||||
if (!stmt) {
|
||||
this.log("Creating new statement for query: " + query);
|
||||
stmt = this._formHistory.DBConnection.createStatement(query);
|
||||
this._dbStmts[query] = stmt;
|
||||
}
|
||||
// Replace parameters, must be done 1 at a time
|
||||
if (params)
|
||||
for (let i in params)
|
||||
stmt.params[i] = params[i];
|
||||
return stmt;
|
||||
}
|
||||
|
||||
}; // end of FormAutoComplete implementation
|
||||
|
||||
|
||||
|
||||
|
||||
// nsIAutoCompleteResult implementation
|
||||
function FormAutoCompleteResult (formHistory, entries, fieldName, searchString) {
|
||||
this.formHistory = formHistory;
|
||||
this.entries = entries;
|
||||
this.fieldName = fieldName;
|
||||
this.searchString = searchString;
|
||||
|
||||
if (entries.length > 0) {
|
||||
this.searchResult = Ci.nsIAutoCompleteResult.RESULT_SUCCESS;
|
||||
this.defaultIndex = 0;
|
||||
}
|
||||
}
|
||||
|
||||
FormAutoCompleteResult.prototype = {
|
||||
QueryInterface : XPCOMUtils.generateQI([Ci.nsIAutoCompleteResult,
|
||||
Ci.nsISupportsWeakReference]),
|
||||
|
||||
// private
|
||||
formHistory : null,
|
||||
entries : null,
|
||||
fieldName : null,
|
||||
|
||||
_checkIndexBounds : function (index) {
|
||||
if (index < 0 || index >= this.entries.length)
|
||||
Components.Exception("Index out of range.", Cr.NS_ERROR_ILLEGAL_VALUE);
|
||||
},
|
||||
|
||||
// Interfaces from idl...
|
||||
searchString : null,
|
||||
searchResult : Ci.nsIAutoCompleteResult.RESULT_NOMATCH,
|
||||
defaultIndex : -1,
|
||||
errorDescription : "",
|
||||
get matchCount() {
|
||||
return this.entries.length;
|
||||
},
|
||||
|
||||
getValueAt : function (index) {
|
||||
this._checkIndexBounds(index);
|
||||
return this.entries[index];
|
||||
},
|
||||
|
||||
getCommentAt : function (index) {
|
||||
this._checkIndexBounds(index);
|
||||
return "";
|
||||
},
|
||||
|
||||
getStyleAt : function (index) {
|
||||
this._checkIndexBounds(index);
|
||||
return "";
|
||||
},
|
||||
|
||||
getImageAt : function (index) {
|
||||
this._checkIndexBounds(index);
|
||||
return "";
|
||||
},
|
||||
|
||||
removeValueAt : function (index, removeFromDB) {
|
||||
this._checkIndexBounds(index);
|
||||
|
||||
let [removedEntry] = this.entries.splice(index, 1);
|
||||
|
||||
if (this.defaultIndex > this.entries.length)
|
||||
this.defaultIndex--;
|
||||
|
||||
if (removeFromDB)
|
||||
this.formHistory.removeEntry(this.fieldName, removedEntry);
|
||||
}
|
||||
};
|
||||
|
||||
let component = [FormAutoComplete];
|
||||
function NSGetModule (compMgr, fileSpec) {
|
||||
return XPCOMUtils.generateModule(component);
|
||||
}
|
@ -40,6 +40,7 @@
|
||||
#include "nsFormFillController.h"
|
||||
|
||||
#include "nsStorageFormHistory.h"
|
||||
#include "nsIFormAutoComplete.h"
|
||||
#include "nsIAutoCompleteSimpleResult.h"
|
||||
#include "nsString.h"
|
||||
#include "nsReadableUtils.h"
|
||||
@ -496,6 +497,7 @@ NS_IMETHODIMP
|
||||
nsFormFillController::StartSearch(const nsAString &aSearchString, const nsAString &aSearchParam,
|
||||
nsIAutoCompleteResult *aPreviousResult, nsIAutoCompleteObserver *aListener)
|
||||
{
|
||||
nsresult rv;
|
||||
nsCOMPtr<nsIAutoCompleteResult> result;
|
||||
|
||||
// If the login manager has indicated it's responsible for this field, let it
|
||||
@ -504,22 +506,21 @@ nsFormFillController::StartSearch(const nsAString &aSearchString, const nsAStrin
|
||||
if (mPwmgrInputs.Get(mFocusedInput, &dummy)) {
|
||||
// XXX aPreviousResult shouldn't ever be a historyResult type, since we're not letting
|
||||
// satchel manage the field?
|
||||
mLoginManager->AutoCompleteSearch(aSearchString,
|
||||
rv = mLoginManager->AutoCompleteSearch(aSearchString,
|
||||
aPreviousResult,
|
||||
mFocusedInput,
|
||||
getter_AddRefs(result));
|
||||
} else {
|
||||
nsCOMPtr<nsIAutoCompleteSimpleResult> historyResult;
|
||||
historyResult = do_QueryInterface(aPreviousResult);
|
||||
nsCOMPtr <nsIFormAutoComplete> formAutoComplete =
|
||||
do_GetService("@mozilla.org/satchel/form-autocomplete;1", &rv);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
nsFormHistory *history = nsFormHistory::GetInstance();
|
||||
if (history) {
|
||||
history->AutoCompleteSearch(aSearchParam,
|
||||
aSearchString,
|
||||
historyResult,
|
||||
getter_AddRefs(result));
|
||||
}
|
||||
rv = formAutoComplete->AutoCompleteSearch(aSearchParam,
|
||||
aSearchString,
|
||||
aPreviousResult,
|
||||
getter_AddRefs(result));
|
||||
}
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
aListener->OnSearchResult(this, result);
|
||||
|
||||
|
@ -61,7 +61,6 @@
|
||||
#include "nsCOMArray.h"
|
||||
#include "mozStorageHelper.h"
|
||||
#include "mozStorageCID.h"
|
||||
#include "nsIAutoCompleteSimpleResult.h"
|
||||
#include "nsTArray.h"
|
||||
#include "nsIPrivateBrowsingService.h"
|
||||
#include "nsNetCID.h"
|
||||
@ -78,75 +77,6 @@
|
||||
// Limit the number of fields saved in a form
|
||||
#define MAX_FIELDS_SAVED 100
|
||||
|
||||
// nsFormHistoryResult is a specialized autocomplete result class that knows
|
||||
// how to remove entries from the form history table.
|
||||
class nsFormHistoryResult : public nsIAutoCompleteSimpleResult
|
||||
{
|
||||
public:
|
||||
nsFormHistoryResult(const nsAString &aFieldName)
|
||||
: mFieldName(aFieldName) {}
|
||||
|
||||
nsresult Init();
|
||||
|
||||
NS_DECL_ISUPPORTS
|
||||
|
||||
// Forward everything except RemoveValueAt to the internal result
|
||||
NS_IMETHOD GetSearchString(nsAString &_result)
|
||||
{ return mResult->GetSearchString(_result); }
|
||||
NS_IMETHOD GetSearchResult(PRUint16 *_result)
|
||||
{ return mResult->GetSearchResult(_result); }
|
||||
NS_IMETHOD GetDefaultIndex(PRInt32 *_result)
|
||||
{ return mResult->GetDefaultIndex(_result); }
|
||||
NS_IMETHOD GetErrorDescription(nsAString &_result)
|
||||
{ return mResult->GetErrorDescription(_result); }
|
||||
NS_IMETHOD GetMatchCount(PRUint32 *_result)
|
||||
{ return mResult->GetMatchCount(_result); }
|
||||
NS_IMETHOD GetValueAt(PRInt32 aIndex, nsAString &_result)
|
||||
{ return mResult->GetValueAt(aIndex, _result); }
|
||||
NS_IMETHOD GetCommentAt(PRInt32 aIndex, nsAString &_result)
|
||||
{ return mResult->GetCommentAt(aIndex, _result); }
|
||||
NS_IMETHOD GetStyleAt(PRInt32 aIndex, nsAString &_result)
|
||||
{ return mResult->GetStyleAt(aIndex, _result); }
|
||||
NS_IMETHOD GetImageAt(PRInt32 aIndex, nsAString &_result)
|
||||
{ return mResult->GetImageAt(aIndex, _result); }
|
||||
NS_IMETHOD RemoveValueAt(PRInt32 aRowIndex, PRBool aRemoveFromDB);
|
||||
NS_FORWARD_NSIAUTOCOMPLETESIMPLERESULT(mResult->)
|
||||
|
||||
protected:
|
||||
nsCOMPtr<nsIAutoCompleteSimpleResult> mResult;
|
||||
nsString mFieldName;
|
||||
};
|
||||
|
||||
NS_IMPL_ISUPPORTS2(nsFormHistoryResult,
|
||||
nsIAutoCompleteResult, nsIAutoCompleteSimpleResult)
|
||||
|
||||
nsresult
|
||||
nsFormHistoryResult::Init()
|
||||
{
|
||||
nsresult rv;
|
||||
mResult = do_CreateInstance(NS_AUTOCOMPLETESIMPLERESULT_CONTRACTID, &rv);
|
||||
return rv;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsFormHistoryResult::RemoveValueAt(PRInt32 aRowIndex, PRBool aRemoveFromDB)
|
||||
{
|
||||
if (!aRemoveFromDB) {
|
||||
return mResult->RemoveValueAt(aRowIndex, aRemoveFromDB);
|
||||
}
|
||||
|
||||
nsAutoString value;
|
||||
nsresult rv = mResult->GetValueAt(aRowIndex, value);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
rv = mResult->RemoveValueAt(aRowIndex, aRemoveFromDB);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
nsFormHistory* fh = nsFormHistory::GetInstance();
|
||||
NS_ENSURE_TRUE(fh, NS_ERROR_OUT_OF_MEMORY);
|
||||
return fh->RemoveEntry(mFieldName, value);
|
||||
}
|
||||
|
||||
#define PREF_FORMFILL_BRANCH "browser.formfill."
|
||||
#define PREF_FORMFILL_ENABLE "enable"
|
||||
|
||||
@ -642,11 +572,6 @@ nsFormHistory::CreateStatements()
|
||||
getter_AddRefs(mDBFindEntryByName));
|
||||
NS_ENSURE_SUCCESS(rv,rv);
|
||||
|
||||
rv = mDBConn->CreateStatement(NS_LITERAL_CSTRING(
|
||||
"SELECT value FROM moz_formhistory WHERE fieldname=?1 ORDER BY UPPER(value) ASC"),
|
||||
getter_AddRefs(mDBGetMatchingField));
|
||||
NS_ENSURE_SUCCESS(rv,rv);
|
||||
|
||||
rv = mDBConn->CreateStatement(NS_LITERAL_CSTRING(
|
||||
"INSERT INTO moz_formhistory (fieldname, value, timesUsed, "
|
||||
"firstUsed, lastUsed) VALUES (?1, ?2, ?3, ?4, ?5)"),
|
||||
@ -880,73 +805,6 @@ nsFormHistory::dbAreExpectedColumnsPresent()
|
||||
}
|
||||
|
||||
|
||||
nsresult
|
||||
nsFormHistory::AutoCompleteSearch(const nsAString &aInputName,
|
||||
const nsAString &aInputValue,
|
||||
nsIAutoCompleteSimpleResult *aPrevResult,
|
||||
nsIAutoCompleteResult **aResult)
|
||||
{
|
||||
if (!FormHistoryEnabled())
|
||||
return NS_OK;
|
||||
|
||||
nsCOMPtr<nsIAutoCompleteSimpleResult> result;
|
||||
|
||||
if (aPrevResult) {
|
||||
result = aPrevResult;
|
||||
|
||||
PRUint32 matchCount;
|
||||
result->GetMatchCount(&matchCount);
|
||||
|
||||
for (PRInt32 i = matchCount - 1; i >= 0; --i) {
|
||||
nsAutoString match;
|
||||
result->GetValueAt(i, match);
|
||||
if (!StringBeginsWith(match, aInputValue,
|
||||
nsCaseInsensitiveStringComparator())) {
|
||||
result->RemoveValueAt(i, PR_FALSE);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
nsCOMPtr<nsFormHistoryResult> fhResult =
|
||||
new nsFormHistoryResult(aInputName);
|
||||
NS_ENSURE_TRUE(fhResult, NS_ERROR_OUT_OF_MEMORY);
|
||||
nsresult rv = fhResult->Init();
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
reinterpret_cast<nsCOMPtr<nsIAutoCompleteSimpleResult>*>(&fhResult)->swap(result);
|
||||
|
||||
result->SetSearchString(aInputValue);
|
||||
|
||||
// generates query string
|
||||
mozStorageStatementScoper scope(mDBGetMatchingField);
|
||||
rv = mDBGetMatchingField->BindStringParameter(0, aInputName);
|
||||
NS_ENSURE_SUCCESS(rv,rv);
|
||||
|
||||
PRBool hasMore = PR_FALSE;
|
||||
PRUint32 count = 0;
|
||||
while (NS_SUCCEEDED(mDBGetMatchingField->ExecuteStep(&hasMore)) &&
|
||||
hasMore) {
|
||||
nsAutoString entryString;
|
||||
mDBGetMatchingField->GetString(0, entryString);
|
||||
// filters out irrelevant results
|
||||
if(StringBeginsWith(entryString, aInputValue,
|
||||
nsCaseInsensitiveStringComparator())) {
|
||||
result->AppendMatch(entryString, EmptyString(), EmptyString(), EmptyString());
|
||||
++count;
|
||||
}
|
||||
}
|
||||
if (count > 0) {
|
||||
result->SetSearchResult(nsIAutoCompleteResult::RESULT_SUCCESS);
|
||||
result->SetDefaultIndex(0);
|
||||
} else {
|
||||
result->SetSearchResult(nsIAutoCompleteResult::RESULT_NOMATCH);
|
||||
result->SetDefaultIndex(-1);
|
||||
}
|
||||
}
|
||||
|
||||
*aResult = result;
|
||||
NS_IF_ADDREF(*aResult);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
#ifdef MOZ_MORKREADER
|
||||
|
||||
// Columns for form history rows
|
||||
|
@ -95,19 +95,6 @@ public:
|
||||
nsFormHistory();
|
||||
nsresult Init();
|
||||
|
||||
static nsFormHistory* GetInstance()
|
||||
{
|
||||
if (!gFormHistory) {
|
||||
nsCOMPtr<nsIFormHistory2> fh = do_GetService(NS_FORMHISTORY_CONTRACTID);
|
||||
}
|
||||
return gFormHistory;
|
||||
}
|
||||
|
||||
nsresult AutoCompleteSearch(const nsAString &aInputName,
|
||||
const nsAString &aInputValue,
|
||||
nsIAutoCompleteSimpleResult *aPrevResult,
|
||||
nsIAutoCompleteResult **aNewResult);
|
||||
|
||||
private:
|
||||
~nsFormHistory();
|
||||
|
||||
@ -137,7 +124,6 @@ public:
|
||||
|
||||
nsCOMPtr<nsIPrefBranch> mPrefBranch;
|
||||
nsCOMPtr<mozIStorageService> mStorageService;
|
||||
nsCOMPtr<mozIStorageStatement> mDBGetMatchingField;
|
||||
nsCOMPtr<mozIStorageStatement> mDBFindEntry;
|
||||
nsCOMPtr<mozIStorageStatement> mDBFindEntryByName;
|
||||
nsCOMPtr<mozIStorageStatement> mDBSelectEntries;
|
||||
|
@ -51,6 +51,7 @@ XPCSHELL_TESTS = \
|
||||
$(NULL)
|
||||
|
||||
MOCHI_TESTS = \
|
||||
test_form_autocomplete.html \
|
||||
test_form_submission.html \
|
||||
test_form_submission_cap.html \
|
||||
test_form_submission_cap2.html \
|
||||
|
@ -65,6 +65,28 @@ function $_(formNum, name) {
|
||||
return element;
|
||||
}
|
||||
|
||||
// Mochitest gives us a sendKey(), but it's targeted to a specific element.
|
||||
// This basically sends an untargeted key event, to whatever's focused.
|
||||
function doKey(aKey, modifier) {
|
||||
// Seems we need to enable this again, or sendKeyEvent() complaints.
|
||||
netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect');
|
||||
|
||||
var keyName = "DOM_VK_" + aKey.toUpperCase();
|
||||
var key = Components.interfaces.nsIDOMKeyEvent[keyName];
|
||||
|
||||
// undefined --> null
|
||||
if (!modifier)
|
||||
modifier = null;
|
||||
|
||||
// Window utils for sending fake sey events.
|
||||
var wutils = window.QueryInterface(Components.interfaces.nsIInterfaceRequestor).
|
||||
getInterface(Components.interfaces.nsIDOMWindowUtils);
|
||||
|
||||
wutils.sendKeyEvent("keydown", key, 0, modifier);
|
||||
wutils.sendKeyEvent("keypress", key, 0, modifier);
|
||||
wutils.sendKeyEvent("keyup", key, 0, modifier);
|
||||
}
|
||||
|
||||
function cleanUpFormHist() {
|
||||
netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
|
||||
var formhist = Components.classes["@mozilla.org/satchel/form-history;1"].
|
||||
|
482
toolkit/components/satchel/test/test_form_autocomplete.html
Normal file
482
toolkit/components/satchel/test/test_form_autocomplete.html
Normal file
@ -0,0 +1,482 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>Test for Form History Autocomplete</title>
|
||||
<script type="text/javascript" src="/MochiKit/packed.js"></script>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
|
||||
<script type="text/javascript" src="satchel_common.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
<body>
|
||||
Form History test: form field autocomplete
|
||||
<p id="display"></p>
|
||||
|
||||
<!-- we presumably can't hide the content for this test. -->
|
||||
<div id="content">
|
||||
|
||||
<!-- normal, basic form -->
|
||||
<form id="form1" onsubmit="return false;">
|
||||
<input type="text" name="field1">
|
||||
<button type="submit">Submit</button>
|
||||
</form>
|
||||
|
||||
<!-- normal, basic form (new fieldname) -->
|
||||
<form id="form2" onsubmit="return false;">
|
||||
<input type="text" name="field2">
|
||||
<button type="submit">Submit</button>
|
||||
</form>
|
||||
|
||||
<!-- form with autocomplete=off on input -->
|
||||
<form id="form3" onsubmit="return false;">
|
||||
<input type="text" name="field2" autocomplete="off">
|
||||
<button type="submit">Submit</button>
|
||||
</form>
|
||||
|
||||
<!-- form with autocomplete=off on form -->
|
||||
<form id="form4" autocomplete="off" onsubmit="return false;">
|
||||
<input type="text" name="field2">
|
||||
<button type="submit">Submit</button>
|
||||
</form>
|
||||
|
||||
<!-- normal form for testing filtering -->
|
||||
<form id="form5" onsubmit="return false;">
|
||||
<input type="text" name="field3">
|
||||
<button type="submit">Submit</button>
|
||||
</form>
|
||||
|
||||
</div>
|
||||
|
||||
<pre id="test">
|
||||
<script class="testbody" type="text/javascript">
|
||||
|
||||
/** Test for Form History autocomplete **/
|
||||
|
||||
netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect');
|
||||
|
||||
var input = $_(1, "field1");
|
||||
const shiftModifier = Components.interfaces.nsIDOMNSEvent.SHIFT_MASK;
|
||||
|
||||
// Get the form history service
|
||||
var fh = Components.classes["@mozilla.org/satchel/form-history;1"].
|
||||
getService(Components.interfaces.nsIFormHistory2);
|
||||
ok(fh != null, "got form history service");
|
||||
|
||||
fh.removeAllEntries();
|
||||
fh.addEntry("field1", "value1");
|
||||
fh.addEntry("field1", "value2");
|
||||
fh.addEntry("field1", "value3");
|
||||
fh.addEntry("field1", "value4");
|
||||
fh.addEntry("field2", "value1");
|
||||
fh.addEntry("field3", "a");
|
||||
fh.addEntry("field3", "aa");
|
||||
fh.addEntry("field3", "aa\xe6"); // 0xae == latin ae pair (0xc6 == AE)
|
||||
fh.addEntry("field3", "az");
|
||||
fh.addEntry("field3", "z");
|
||||
|
||||
// Restore the form to the default state.
|
||||
function restoreForm() {
|
||||
input.value = "";
|
||||
input.focus();
|
||||
}
|
||||
|
||||
// Check for expected form data.
|
||||
function checkForm(expectedValue) {
|
||||
var formID = input.parentNode.id;
|
||||
is(input.value, expectedValue, "Checking " + formID + " input");
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Main section of test...
|
||||
*
|
||||
* This is a bit hacky, because the events are either being sent or
|
||||
* processes asynchronously, so we need to interrupt our flow with lots of
|
||||
* setTimeout() calls. The case statements are executed in order, one per
|
||||
* timeout.
|
||||
*/
|
||||
function runTest(testNum) {
|
||||
// Seems we need to enable this again, or sendKeyEvent() complaints.
|
||||
netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect');
|
||||
ok(true, "Starting test #" + testNum);
|
||||
|
||||
switch(testNum) {
|
||||
case 1:
|
||||
// Make sure initial form is empty.
|
||||
checkForm("");
|
||||
// Trigger autocomplete popup
|
||||
restoreForm();
|
||||
doKey("down");
|
||||
break;
|
||||
|
||||
case 2:
|
||||
checkMenuEntries(["value1", "value2", "value3", "value4"]);
|
||||
// Check first entry
|
||||
doKey("down");
|
||||
checkForm(""); // value shouldn't update
|
||||
doKey("return"); // not "enter"!
|
||||
checkForm("value1");
|
||||
|
||||
// Trigger autocomplete popup
|
||||
restoreForm();
|
||||
doKey("down");
|
||||
break;
|
||||
|
||||
case 3:
|
||||
// Check second entry
|
||||
doKey("down");
|
||||
doKey("down");
|
||||
doKey("return"); // not "enter"!
|
||||
checkForm("value2");
|
||||
|
||||
// Trigger autocomplete popup
|
||||
restoreForm();
|
||||
doKey("down");
|
||||
break;
|
||||
|
||||
case 4:
|
||||
// Check third entry
|
||||
doKey("down");
|
||||
doKey("down");
|
||||
doKey("down");
|
||||
doKey("return");
|
||||
checkForm("value3");
|
||||
|
||||
// Trigger autocomplete popup
|
||||
restoreForm();
|
||||
doKey("down");
|
||||
break;
|
||||
|
||||
case 5:
|
||||
// Check fourth entry
|
||||
doKey("down");
|
||||
doKey("down");
|
||||
doKey("down");
|
||||
doKey("down");
|
||||
doKey("return");
|
||||
checkForm("value4");
|
||||
|
||||
// Trigger autocomplete popup
|
||||
restoreForm();
|
||||
doKey("down");
|
||||
break;
|
||||
|
||||
case 6:
|
||||
// Check first entry (wraparound)
|
||||
doKey("down");
|
||||
doKey("down");
|
||||
doKey("down");
|
||||
doKey("down");
|
||||
doKey("down"); // deselects
|
||||
doKey("down");
|
||||
doKey("return");
|
||||
checkForm("value1");
|
||||
|
||||
// Trigger autocomplete popup
|
||||
restoreForm();
|
||||
doKey("down");
|
||||
break;
|
||||
|
||||
case 7:
|
||||
// Check the last entry via arrow-up
|
||||
doKey("up");
|
||||
doKey("return");
|
||||
checkForm("value4");
|
||||
|
||||
// Trigger autocomplete popup
|
||||
restoreForm();
|
||||
doKey("down");
|
||||
break;
|
||||
|
||||
case 8:
|
||||
// Check the last entry via arrow-up
|
||||
doKey("down"); // select first entry
|
||||
doKey("up"); // selects nothing!
|
||||
doKey("up"); // select last entry
|
||||
doKey("return");
|
||||
checkForm("value4");
|
||||
|
||||
// Trigger autocomplete popup
|
||||
restoreForm();
|
||||
doKey("down");
|
||||
break;
|
||||
|
||||
case 9:
|
||||
// Check the last entry via arrow-up (wraparound)
|
||||
doKey("down");
|
||||
doKey("up"); // deselects
|
||||
doKey("up"); // last entry
|
||||
doKey("up");
|
||||
doKey("up");
|
||||
doKey("up"); // first entry
|
||||
doKey("up"); // deselects
|
||||
doKey("up"); // last entry
|
||||
doKey("return");
|
||||
checkForm("value4");
|
||||
|
||||
// Trigger autocomplete popup
|
||||
restoreForm();
|
||||
doKey("down");
|
||||
break;
|
||||
|
||||
case 10:
|
||||
// Set first entry w/o triggering autocomplete
|
||||
doKey("down");
|
||||
doKey("right");
|
||||
checkForm("value1");
|
||||
|
||||
// Trigger autocomplete popup
|
||||
restoreForm();
|
||||
doKey("down");
|
||||
break;
|
||||
|
||||
case 11:
|
||||
// Set first entry w/o triggering autocomplete
|
||||
doKey("down");
|
||||
doKey("left");
|
||||
checkForm("value1");
|
||||
|
||||
// Trigger autocomplete popup
|
||||
restoreForm();
|
||||
doKey("down");
|
||||
break;
|
||||
|
||||
case 12:
|
||||
// Check first entry (page up)
|
||||
doKey("down");
|
||||
doKey("down");
|
||||
doKey("page_up");
|
||||
doKey("return");
|
||||
checkForm("value1");
|
||||
|
||||
// Trigger autocomplete popup
|
||||
restoreForm();
|
||||
doKey("down");
|
||||
break;
|
||||
|
||||
case 13:
|
||||
// Check last entry (page down)
|
||||
doKey("down");
|
||||
doKey("page_down");
|
||||
doKey("return");
|
||||
checkForm("value4");
|
||||
|
||||
// Trigger autocomplete popup
|
||||
restoreForm();
|
||||
doKey("down");
|
||||
testNum = 49;
|
||||
break;
|
||||
|
||||
/* Test removing entries from the dropdown */
|
||||
|
||||
case 50:
|
||||
checkMenuEntries(["value1", "value2", "value3", "value4"]);
|
||||
// Delete the first entry (of 4)
|
||||
doKey("down");
|
||||
|
||||
// On OS X, shift-backspace and shift-delete work, just delete does not.
|
||||
// On Win/Linux, shift-backspace does not work, delete and shift-delete do.
|
||||
doKey("delete", shiftModifier);
|
||||
|
||||
checkForm("");
|
||||
ok(!fh.entryExists("field1", "value1"), "checking that f1/v1 was deleted");
|
||||
doKey("return");
|
||||
checkForm("value2");
|
||||
|
||||
// Trigger autocomplete popup
|
||||
restoreForm();
|
||||
doKey("down");
|
||||
break;
|
||||
|
||||
case 51:
|
||||
checkMenuEntries(["value2", "value3", "value4"]);
|
||||
// Check the new first entry (of 3)
|
||||
doKey("down");
|
||||
doKey("return");
|
||||
checkForm("value2");
|
||||
|
||||
// Trigger autocomplete popup
|
||||
restoreForm();
|
||||
doKey("down");
|
||||
break;
|
||||
|
||||
case 52:
|
||||
// Delete the second entry (of 3)
|
||||
doKey("down");
|
||||
doKey("down");
|
||||
doKey("delete", shiftModifier);
|
||||
checkForm("");
|
||||
ok(!fh.entryExists("field1", "value3"), "checking that f1/v3 was deleted");
|
||||
doKey("return");
|
||||
checkForm("value4")
|
||||
|
||||
// Trigger autocomplete popup
|
||||
restoreForm();
|
||||
doKey("down");
|
||||
break;
|
||||
|
||||
case 53:
|
||||
checkMenuEntries(["value2", "value4"]);
|
||||
// Check the new first entry (of 2)
|
||||
doKey("down");
|
||||
doKey("return");
|
||||
checkForm("value2");
|
||||
|
||||
// Trigger autocomplete popup
|
||||
restoreForm();
|
||||
doKey("down");
|
||||
break;
|
||||
|
||||
case 54:
|
||||
// Delete the last entry (of 2)
|
||||
doKey("down");
|
||||
doKey("down");
|
||||
doKey("delete", shiftModifier);
|
||||
checkForm("");
|
||||
ok(!fh.entryExists("field1", "value4"), "checking that f1/v4 was deleted");
|
||||
doKey("return");
|
||||
checkForm("value2");
|
||||
|
||||
// Trigger autocomplete popup
|
||||
restoreForm();
|
||||
doKey("down");
|
||||
break;
|
||||
|
||||
case 55:
|
||||
checkMenuEntries(["value2"]);
|
||||
// Check the new first entry (of 1)
|
||||
doKey("down");
|
||||
doKey("return");
|
||||
checkForm("value2");
|
||||
|
||||
// Trigger autocomplete popup
|
||||
restoreForm();
|
||||
doKey("down");
|
||||
break;
|
||||
|
||||
case 56:
|
||||
// Delete the only remaining entry
|
||||
doKey("down");
|
||||
doKey("delete", shiftModifier);
|
||||
checkForm("");
|
||||
ok(!fh.entryExists("field1", "value2"), "checking that f1/v2 was deleted");
|
||||
|
||||
// Look at form 2, trigger autocomplete popup
|
||||
input = $_(2, "field2");
|
||||
restoreForm();
|
||||
doKey("down");
|
||||
testNum = 99;
|
||||
break;
|
||||
|
||||
/* Test entries with autocomplete=off */
|
||||
|
||||
case 100:
|
||||
// Select first entry
|
||||
doKey("down");
|
||||
doKey("return");
|
||||
checkForm("value1");
|
||||
|
||||
// Look at form 3, try to trigger autocomplete popup
|
||||
input = $_(3, "field2");
|
||||
restoreForm();
|
||||
doKey("down");
|
||||
break;
|
||||
|
||||
case 101:
|
||||
// Ensure there's no autocomplete dropdown (autocomplete=off is present)
|
||||
doKey("down");
|
||||
doKey("return");
|
||||
checkForm("");
|
||||
|
||||
// Look at form 4, try to trigger autocomplete popup
|
||||
input = $_(4, "field2");
|
||||
restoreForm();
|
||||
doKey("down");
|
||||
break;
|
||||
|
||||
case 102:
|
||||
// Ensure there's no autocomplete dropdown (autocomplete=off is present)
|
||||
doKey("down");
|
||||
doKey("return");
|
||||
checkForm("");
|
||||
|
||||
// Look at form 5, try to trigger autocomplete popup
|
||||
input = $_(5, "field3");
|
||||
restoreForm();
|
||||
testNum = 199;
|
||||
sendChar("a", input);
|
||||
break;
|
||||
|
||||
/* Test filtering as characters are typed. */
|
||||
|
||||
case 200:
|
||||
checkMenuEntries(["a", "aa", "aa\xe6", "az"]);
|
||||
sendChar("a", input);
|
||||
break;
|
||||
|
||||
case 201:
|
||||
checkMenuEntries(["aa", "aa\xe6"]);
|
||||
sendChar("\xc6", input);
|
||||
break;
|
||||
|
||||
case 202:
|
||||
checkMenuEntries(["aa\xe6"]);
|
||||
doKey("escape");
|
||||
|
||||
SimpleTest.finish();
|
||||
return;
|
||||
|
||||
default:
|
||||
ok(false, "Unexpected invocation of test #" + testNum);
|
||||
SimpleTest.finish();
|
||||
return;
|
||||
}
|
||||
|
||||
setTimeout(runTest, 50, testNum + 1); // XXX 40ms was too slow, why?
|
||||
}
|
||||
|
||||
function checkMenuEntries(expectedValues) {
|
||||
var actualValues = getMenuEntries();
|
||||
is(actualValues.length, expectedValues.length, "Checking length of expected menu");
|
||||
for (var i = 0; i < expectedValues.length; i++)
|
||||
is(actualValues[i], expectedValues[i], "Checking menu entry #"+i);
|
||||
}
|
||||
|
||||
var autocompleteMenu;
|
||||
function getMenuEntries() {
|
||||
var entries = [];
|
||||
|
||||
// Could perhaps pull values directly from the controller, but it seems
|
||||
// more reliable to test the values that are actually in the tree?
|
||||
var column = autocompleteMenu.tree.columns[0];
|
||||
var numRows = autocompleteMenu.tree.view.rowCount;
|
||||
for (var i = 0; i < numRows; i++) {
|
||||
entries.push(autocompleteMenu.tree.view.getValueAt(i, column));
|
||||
}
|
||||
return entries;
|
||||
}
|
||||
|
||||
function startTest() {
|
||||
netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect');
|
||||
var Ci = Components.interfaces;
|
||||
chromeWin = window
|
||||
.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
.getInterface(Ci.nsIWebNavigation)
|
||||
.QueryInterface(Ci.nsIDocShellTreeItem)
|
||||
.rootTreeItem
|
||||
.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
.getInterface(Ci.nsIDOMWindow)
|
||||
.QueryInterface(Ci.nsIDOMChromeWindow);
|
||||
autocompleteMenu = chromeWin.document.getElementById("PopupAutoComplete");
|
||||
ok(autocompleteMenu, "Got autocomplete popup");
|
||||
runTest(1);
|
||||
}
|
||||
|
||||
window.onload = startTest;
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
||||
|
Loading…
x
Reference in New Issue
Block a user