Bug 720501 - urlInlineComplete should not attempt to case-preserve results, since that interferes with the controller's case-preservation.
authorMarco Bonardo <mbonardo@mozilla.com>
Thu, 26 Jan 2012 00:54:25 +0100
changeset 86699 572cfaeede5cb713f544ccdf45c6504b2d8ecc12
parent 86698 ba57f7b6a2f3ed4202de5a6ce81ebc67dfd48cb2
child 86700 b0d7705cd271bfe9cf3506967eb4a5a9c0537224
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)
bugs720501
milestone12.0a1
Bug 720501 - urlInlineComplete should not attempt to case-preserve results, since that interferes with the controller's case-preservation. r=gavin
toolkit/components/places/nsPlacesAutoComplete.js
toolkit/components/places/tests/inline/head_autocomplete.js
toolkit/components/places/tests/inline/test_casing.js
toolkit/components/places/tests/inline/xpcshell.ini
--- a/toolkit/components/places/nsPlacesAutoComplete.js
+++ b/toolkit/components/places/nsPlacesAutoComplete.js
@@ -780,16 +780,17 @@ nsPlacesAutoComplete.prototype = {
     // Notify about results if we are supposed to.
     if (aNotify) {
       this._notifyResults(false);
     }
 
     // Clear our state
     delete this._originalSearchString;
     delete this._currentSearchString;
+    delete this._strippedPrefix;
     delete this._searchTokens;
     delete this._listener;
     delete this._result;
     delete this._usedPlaces;
     delete this._pendingQuery;
     this._secondPass = false;
     this._enableActions = false;
   },
@@ -1375,16 +1376,21 @@ urlInlineComplete.prototype = {
       this.stopSearch();
     }
 
     // We want to store the original string with no leading or trailing
     // whitespace for case sensitive searches.
     this._originalSearchString = aSearchString;
     this._currentSearchString =
       fixupSearchText(this._originalSearchString.toLowerCase());
+    // The protocol and the domain are lowercased by nsIURI, so it's fine to
+    // lowercase the typed prefix to add it back to the results later.
+    this._strippedPrefix = this._originalSearchString.slice(
+      0, this._originalSearchString.length - this._currentSearchString.length
+    ).toLowerCase();
 
     let result = Cc["@mozilla.org/autocomplete/simple-result;1"].
                  createInstance(Ci.nsIAutoCompleteSimpleResult);
     result.setSearchString(aSearchString);
     result.setTypeAheadResult(true);
 
     this._result = result;
     this._listener = aListener;
@@ -1414,18 +1420,17 @@ urlInlineComplete.prototype = {
           domain = query.getString(0);
         }
       } finally {
         query.reset();
       }
 
       if (hasDomainResult) {
         // We got a match for a domain, we can add it immediately.
-        let appendResult = domain.slice(this._currentSearchString.length);
-        result.appendMatch(aSearchString + appendResult, "");
+        result.appendMatch(this._strippedPrefix + domain, "");
 
         this._finishSearch();
         return;
       }
     }
 
     // We did not get a result from the synchronous domain search.
     // We now do an asynchronous search through places, and complete
@@ -1488,27 +1493,28 @@ urlInlineComplete.prototype = {
   //// mozIStorageStatementCallback
 
   handleResult: function UIC_handleResult(aResultSet)
   {
     let row = aResultSet.getNextRow();
     let url = fixupSearchText(row.getResultByIndex(0));
 
     // We must complete the URL up to the next separator (which is /, ? or #).
-    let appendText = url.slice(this._currentSearchString.length);
-    let separatorIndex = appendText.search(/[\/\?\#]/);
+    let separatorIndex = url.slice(this._currentSearchString.length)
+                            .search(/[\/\?\#]/);
     if (separatorIndex != -1) {
-      if (appendText[separatorIndex] == "/") {
+      separatorIndex += this._currentSearchString.length;
+      if (url[separatorIndex] == "/") {
         separatorIndex++; // Include the "/" separator
       }
-      appendText = appendText.slice(0, separatorIndex);
+      url = url.slice(0, separatorIndex);
     }
 
     // Add the result
-    this._result.appendMatch(this._originalSearchString + appendText, "");
+    this._result.appendMatch(this._strippedPrefix + url, "");
 
     // handleCompletion() will cause the result listener to be called, and
     // will display the result in the UI.
   },
 
   handleError: function UIC_handleError(aError)
   {
     Components.utils.reportError("URL Inline Complete: An async statement encountered an " +
--- a/toolkit/components/places/tests/inline/head_autocomplete.js
+++ b/toolkit/components/places/tests/inline/head_autocomplete.js
@@ -48,45 +48,64 @@ AutoCompleteInput.prototype = {
   get selectionEnd() {
     return this._selEnd;
   },
   selectTextRange: function(aStart, aEnd) {
     this._selStart = aStart;
     this._selEnd = aEnd;
   },
 
+  onTextEntered: function() false,
+  onTextReverted: function() false,
+
   get searchCount() {
     return this.searches.length;
   },
   getSearchAt: function(aIndex) {
     return this.searches[aIndex];
   },
 
   onSearchBegin: function () {},
   onSearchComplete: function () {},
 
   popupOpen: false,
 
   popup: {
-    selectedIndex: 0,
+    selectedIndex: -1,
     invalidate: function () {},
 
     QueryInterface: XPCOMUtils.generateQI([Ci.nsIAutoCompletePopup])
   },
 
   QueryInterface: XPCOMUtils.generateQI([Ci.nsIAutoCompleteInput])
 }
 
 /**
  * @param aSearchString
  *        String to search.
  * @param aExpectedValue
  *        Expected value returned by autoFill.
+ *        May be a string, or an object like
+ *        {
+ *          autoFilled: the value suggested by autofill,
+ *          completed: the value completed on user's confirmation
+ *        }
+ *        In the latter case this will also check that on user's confirmation
+ *        the result's casing is correctly applied.
  */
 function ensure_results(aSearchString, aExpectedValue) {
+  let autoFilledValue, completedValue;
+  if (typeof(aExpectedValue) == "string") {
+    autoFilledValue = aExpectedValue;
+  }
+  else {
+    autoFilledValue = aExpectedValue.autoFilled;
+    completedValue = aExpectedValue.completed;
+  }
+
   // 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);
@@ -102,17 +121,25 @@ function ensure_results(aSearchString, a
     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);
+    do_check_eq(input.textValue, autoFilledValue);
+
+    if (completedValue) {
+      // Now force completion and check correct casing of the result.
+      // This ensures the controller is able to do its magic case-preserving
+      // stuff and correct replacement of the user's casing with result's one.
+      controller.handleEnter(false);
+      do_check_eq(input.textValue, completedValue);
+    }
 
     waitForCleanup(run_next_test);
   };
 
   do_log_info("Searching for: '" + aSearchString + "'");
   controller.startSearch(aSearchString);
 }
 
new file mode 100644
--- /dev/null
+++ b/toolkit/components/places/tests/inline/test_casing.js
@@ -0,0 +1,66 @@
+/* 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 cased entry 1",
+  "MOZ",
+  { autoFilled: "MOZilla.org/", completed: "mozilla.org/" },
+  function () {
+    addBookmark({ url: "http://mozilla.org/test/" });
+  }
+]);
+
+add_autocomplete_test([
+  "Searching for cased entry 2",
+  "mozilla.org/T",
+  { autoFilled: "mozilla.org/Test/", completed: "mozilla.org/test/" },
+  function () {
+    addBookmark({ url: "http://mozilla.org/test/" });
+  }
+]);
+
+add_autocomplete_test([
+  "Searching for cased entry 3",
+  "mOzilla.org/t",
+  { autoFilled: "mOzilla.org/test/", completed: "mozilla.org/Test/" },
+  function () {
+    addBookmark({ url: "http://mozilla.org/Test/" });
+  },
+]);
+
+add_autocomplete_test([
+  "Searching for untrimmed cased entry",
+  "http://mOz",
+  { autoFilled: "http://mOzilla.org/", completed: "http://mozilla.org/" },
+  function () {
+    addBookmark({ url: "http://mozilla.org/Test/" });
+  },
+]);
+
+add_autocomplete_test([
+  "Searching for untrimmed cased entry with www",
+  "http://www.mOz",
+  { autoFilled: "http://www.mOzilla.org/", completed: "http://www.mozilla.org/" },
+  function () {
+    addBookmark({ url: "http://www.mozilla.org/Test/" });
+  },
+]);
+
+add_autocomplete_test([
+  "Searching for untrimmed cased entry with path",
+  "http://mOzilla.org/t",
+  { autoFilled: "http://mOzilla.org/test/", completed: "http://mozilla.org/Test/" },
+  function () {
+    addBookmark({ url: "http://mozilla.org/Test/" });
+  },
+]);
+
+add_autocomplete_test([
+  "Searching for untrimmed cased entry with www and path",
+  "http://www.mOzilla.org/t",
+  { autoFilled: "http://www.mOzilla.org/test/", completed: "http://www.mozilla.org/Test/" },
+  function () {
+    addBookmark({ url: "http://www.mozilla.org/Test/" });
+  },
+]);
--- a/toolkit/components/places/tests/inline/xpcshell.ini
+++ b/toolkit/components/places/tests/inline/xpcshell.ini
@@ -1,5 +1,6 @@
 [DEFAULT]
 head = head_autocomplete.js
 tail = 
 
+[test_casing.js]
 [test_keywords.js]