Bug 720110 - URL autocomplete breaks keyword bookmarks.
authorMarco Bonardo <mbonardo@mozilla.com>
Tue, 24 Jan 2012 20:37:46 +0100
changeset 86489 ddba5171787231891f5d600841921342b088ee68
parent 86488 360bd1a6e72bbc745b9d0ed270a4763b5edf57b3
child 86490 0a116f325333e5db37cb9cc2afff622bfd7bc564
push id805
push userakeybl@mozilla.com
push dateWed, 01 Feb 2012 18:17:35 +0000
treeherdermozilla-aurora@6fb3bf232436 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
bugs720110
milestone12.0a1
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]