Bug 720110 - URL autocomplete breaks keyword bookmarks.
authorMarco Bonardo <mbonardo@mozilla.com>
Tue, 24 Jan 2012 20:37:46 +0100
changeset 85245 ddba5171787231891f5d600841921342b088ee68
parent 85244 360bd1a6e72bbc745b9d0ed270a4763b5edf57b3
child 85246 0a116f325333e5db37cb9cc2afff622bfd7bc564
push id5245
push usermak77@bonardo.net
push dateTue, 24 Jan 2012 19:38:12 +0000
treeherdermozilla-inbound@0a116f325333 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
bugs720110
milestone12.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 720110 - URL autocomplete breaks keyword bookmarks. r=gavin
testing/xpcshell/xpcshell.ini
toolkit/components/places/nsPlacesAutoComplete.js
toolkit/components/places/tests/Makefile.in
toolkit/components/places/tests/inline/head_autocomplete.js
toolkit/components/places/tests/inline/test_keywords.js
toolkit/components/places/tests/inline/xpcshell.ini
--- a/testing/xpcshell/xpcshell.ini
+++ b/testing/xpcshell/xpcshell.ini
@@ -15,16 +15,17 @@
 [include:embedding/tests/unit/xpcshell.ini]
 [include:toolkit/components/commandlines/test/unit/xpcshell.ini]
 [include:toolkit/components/contentprefs/tests/unit/xpcshell.ini]
 [include:toolkit/components/passwordmgr/test/unit/xpcshell.ini]
 # Bug 676989: tests hang on Android
 skip-if = os == "android"
 [include:toolkit/components/places/tests/migration/xpcshell.ini]
 [include:toolkit/components/places/tests/autocomplete/xpcshell.ini]
+[include:toolkit/components/places/tests/inline/xpcshell.ini]
 [include:toolkit/components/places/tests/expiration/xpcshell.ini]
 [include:toolkit/components/places/tests/sync/xpcshell.ini]
 [include:toolkit/components/places/tests/bookmarks/xpcshell.ini]
 [include:toolkit/components/places/tests/queries/xpcshell.ini]
 [include:toolkit/components/places/tests/unit/xpcshell.ini]
 [include:toolkit/components/places/tests/network/xpcshell.ini]
 [include:toolkit/components/urlformatter/tests/unit/xpcshell.ini]
 [include:toolkit/components/ctypes/tests/unit/xpcshell.ini]
--- a/toolkit/components/places/nsPlacesAutoComplete.js
+++ b/toolkit/components/places/nsPlacesAutoComplete.js
@@ -34,16 +34,18 @@
  * 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 ***** */
 
 Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
 Components.utils.import("resource://gre/modules/Services.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "PlacesUtils",
+                                  "resource://gre/modules/PlacesUtils.jsm");
 
 ////////////////////////////////////////////////////////////////////////////////
 //// Constants
 
 const Cc = Components.classes;
 const Ci = Components.interfaces;
 const Cr = Components.results;
 
@@ -1382,17 +1384,22 @@ urlInlineComplete.prototype = {
     let result = Cc["@mozilla.org/autocomplete/simple-result;1"].
                  createInstance(Ci.nsIAutoCompleteSimpleResult);
     result.setSearchString(aSearchString);
     result.setTypeAheadResult(true);
 
     this._result = result;
     this._listener = aListener;
 
-    if (this._currentSearchString.length == 0 || !this._db) {
+    // Don't autoFill if the search term is recognized as a keyword, otherwise
+    // it will override default keywords behavior.  Note that keywords are
+    // hashed on first use, so while the first query may delay a little bit,
+    // next ones will just hit the memory hash.
+    if (this._currentSearchString.length == 0 || !this._db ||
+        PlacesUtils.bookmarks.getURIForKeyword(this._currentSearchString)) {
       this._finishSearch();
       return;
     }
 
     // Do a synchronous search on the table of domains.
     let query = this._syncQuery;
     query.params.search_string = this._currentSearchString.toLowerCase();
 
--- a/toolkit/components/places/tests/Makefile.in
+++ b/toolkit/components/places/tests/Makefile.in
@@ -42,29 +42,30 @@ srcdir		= @srcdir@
 VPATH		= @srcdir@
 relativesrcdir	= toolkit/components/places/tests
 
 include $(DEPTH)/config/autoconf.mk
 
 MODULE		= test_places
 
 XPCSHELL_TESTS = \
-	autocomplete \
-	expiration \
-	bookmarks \
-	queries \
-	unit \
-	network \
-	migration \
-	$(NULL)
+  autocomplete \
+  bookmarks \
+  expiration \
+  inline \
+  migration \
+  network \
+  queries \
+  unit \
+  $(NULL)
 
 # Files in the main "tests" folder.
 XPCSHELL_TESTS_COMMON = \
   head_common.js \
-	$(NULL)
+  $(NULL)
 
 # Simple MochiTests
 MOCHI_TESTS = \
 	mochitest/test_bug_411966.html \
 	mochitest/test_bug_461710.html \
 	$(NULL)
 
 DIRS = \
new file mode 100644
--- /dev/null
+++ b/toolkit/components/places/tests/inline/head_autocomplete.js
@@ -0,0 +1,166 @@
+/* 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/. */
+
+const Ci = Components.interfaces;
+const Cc = Components.classes;
+const Cr = Components.results;
+const Cu = Components.utils;
+
+Cu.import("resource://gre/modules/Services.jsm");
+
+// Import common head.
+let (commonFile = do_get_file("../head_common.js", false)) {
+  let uri = Services.io.newFileURI(commonFile);
+  Services.scriptloader.loadSubScript(uri.spec, this);
+}
+
+// Put any other stuff relative to this test folder below.
+
+XPCOMUtils.defineLazyServiceGetter(this, "gHistory",
+                                   "@mozilla.org/browser/history;1",
+                                   "mozIAsyncHistory");
+
+/**
+ * @param aSearches
+ *        Array of AutoCompleteSearch names. 
+ */
+function AutoCompleteInput(aSearches) {
+  this.searches = aSearches;
+}
+AutoCompleteInput.prototype = {
+  searches: null,
+  minResultsForPopup: 0,
+  timeout: 10,
+  searchParam: "",
+  textValue: "",
+  disableAutoComplete: false,
+
+  completeDefaultIndex: true,
+  defaultIndex: 0,
+
+  // Text selection range
+  _selStart: 0,
+  _selEnd: 0,
+  get selectionStart() {
+    return this._selStart;
+  },
+  get selectionEnd() {
+    return this._selEnd;
+  },
+  selectTextRange: function(aStart, aEnd) {
+    this._selStart = aStart;
+    this._selEnd = aEnd;
+  },
+
+  get searchCount() {
+    return this.searches.length;
+  },
+  getSearchAt: function(aIndex) {
+    return this.searches[aIndex];
+  },
+
+  onSearchBegin: function () {},
+  onSearchComplete: function () {},
+
+  popupOpen: false,
+
+  popup: {
+    selectedIndex: 0,
+    invalidate: function () {},
+
+    QueryInterface: XPCOMUtils.generateQI([Ci.nsIAutoCompletePopup])
+  },
+
+  QueryInterface: XPCOMUtils.generateQI([Ci.nsIAutoCompleteInput])
+}
+
+/**
+ * @param aSearchString
+ *        String to search.
+ * @param aExpectedValue
+ *        Expected value returned by autoFill.
+ */
+function ensure_results(aSearchString, aExpectedValue) {
+  // Make an AutoCompleteInput that uses our searches and confirms results.
+  let input = new AutoCompleteInput(["urlinline"]);
+  input.textValue = aSearchString;
+
+  // Caret must be at the end for autoFill to happen.
+  let strLen = aSearchString.length;
+  input.selectTextRange(strLen, strLen);
+  do_check_eq(input.selectionStart, strLen);
+  do_check_eq(input.selectionEnd, strLen);
+
+  let controller = Cc["@mozilla.org/autocomplete/controller;1"].
+                   getService(Ci.nsIAutoCompleteController);
+  controller.input = input;
+
+  let numSearchesStarted = 0;
+  input.onSearchBegin = function() {
+    numSearchesStarted++;
+    do_check_eq(numSearchesStarted, 1);
+  };
+
+  input.onSearchComplete = function() {
+    // We should be running only one query.
+    do_check_eq(numSearchesStarted, 1);
+
+    // Check the autoFilled result.
+    do_check_eq(input.textValue, aExpectedValue);
+
+    waitForCleanup(run_next_test);
+  };
+
+  do_log_info("Searching for: '" + aSearchString + "'");
+  controller.startSearch(aSearchString);
+}
+
+function run_test() {
+  Services.prefs.setBoolPref("browser.urlbar.autoFill", true);
+  do_register_cleanup(function () {
+    Services.prefs.clearUserPref("browser.urlbar.autoFill");
+  });
+
+  gAutoCompleteTests.forEach(function (testData) {
+    let [description, searchString, expectedValue, setupFunc] = testData;
+    add_test(function () {
+      do_log_info(description);
+      if (setupFunc) {
+        setupFunc();
+      }
+
+      // At this point frecency could still be updating due to latest pages
+      // updates.
+      // This is not a problem in real life, but autocomplete tests should
+      // return reliable resultsets, thus we have to wait.
+      waitForAsyncUpdates(ensure_results, this, [searchString, expectedValue]);
+    })
+  }, this);
+
+  run_next_test();
+}
+
+let gAutoCompleteTests = [];
+function add_autocomplete_test(aTestData) {
+  gAutoCompleteTests.push(aTestData);
+}
+
+function waitForCleanup(aCallback) {
+  remove_all_bookmarks();
+  waitForClearHistory(aCallback);
+}
+
+function addBookmark(aBookmarkObj) {
+  do_check_true(!!aBookmarkObj.url);
+  let parentId = aBookmarkObj.parentId ? aBookmarkObj.parentId
+                                       : PlacesUtils.unfiledBookmarksFolderId;
+  let itemId = PlacesUtils.bookmarks
+                          .insertBookmark(parentId,
+                                          NetUtil.newURI(aBookmarkObj.url),
+                                          PlacesUtils.bookmarks.DEFAULT_INDEX,
+                                          "A bookmark");
+  if (aBookmarkObj.keyword) {
+    PlacesUtils.bookmarks.setKeywordForBookmark(itemId, aBookmarkObj.keyword);
+  }
+}
new file mode 100644
--- /dev/null
+++ b/toolkit/components/places/tests/inline/test_keywords.js
@@ -0,0 +1,48 @@
+/* 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/. */
+
+add_autocomplete_test([
+  "Searching for non-keyworded entry should autoFill it",
+  "moz",
+  "mozilla.org/",
+  function () {
+    addBookmark({ url: "http://mozilla.org/test/" });
+  }
+]);
+
+add_autocomplete_test([
+  "Searching for keyworded entry should not autoFill it",
+  "moz",
+  "moz",
+  function () {
+    addBookmark({ url: "http://mozilla.org/test/", keyword: "moz" });
+  }
+]);
+
+add_autocomplete_test([
+  "Searching for more than keyworded entry should autoFill it",
+  "mozi",
+  "mozilla.org/",
+  function () {
+    addBookmark({ url: "http://mozilla.org/test/", keyword: "moz" });
+  }
+]);
+
+add_autocomplete_test([
+  "Searching for less than keyworded entry should autoFill it",
+  "mo",
+  "mozilla.org/",
+  function () {
+    addBookmark({ url: "http://mozilla.org/test/", keyword: "moz" });
+  }
+]);
+
+add_autocomplete_test([
+  "Searching for keyworded entry is case-insensitive",
+  "MoZ",
+  "MoZ",
+  function () {
+    addBookmark({ url: "http://mozilla.org/test/", keyword: "moz" });
+  }
+]);
new file mode 100644
--- /dev/null
+++ b/toolkit/components/places/tests/inline/xpcshell.ini
@@ -0,0 +1,5 @@
+[DEFAULT]
+head = head_autocomplete.js
+tail = 
+
+[test_keywords.js]