Bug 900865: Make about:home call nsISearchEngine.getSubmission for all searches. r=gavin
authorMike de Boer <mdeboer@mozilla.com>
Fri, 30 Aug 2013 18:20:22 +0200
changeset 145135 75d9df61d0e16d4d5ef3549a8c78b1fe0bad263d
parent 145048 4732468202263313825992705432952478beb497
child 145136 c489910a28c3210091485329b8f713ad03aa33a4
push id25195
push userryanvm@gmail.com
push dateSat, 31 Aug 2013 01:00:12 +0000
treeherdermozilla-central@4ba8dda1ee31 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersgavin
bugs900865
milestone26.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 900865: Make about:home call nsISearchEngine.getSubmission for all searches. r=gavin
browser/base/content/abouthome/aboutHome.js
browser/base/content/content.js
browser/base/content/test/browser_aboutHome.js
browser/modules/AboutHome.jsm
netwerk/base/public/nsIBrowserSearchService.idl
toolkit/components/search/nsSearchService.js
--- a/browser/base/content/abouthome/aboutHome.js
+++ b/browser/base/content/abouthome/aboutHome.js
@@ -148,17 +148,17 @@ const SNIPPETS_UPDATE_INTERVAL_MS = 8640
 const DATABASE_NAME = "abouthome";
 const DATABASE_VERSION = 1;
 const SNIPPETS_OBJECTSTORE_NAME = "snippets";
 
 // This global tracks if the page has been set up before, to prevent double inits
 let gInitialized = false;
 let gObserver = new MutationObserver(function (mutations) {
   for (let mutation of mutations) {
-    if (mutation.attributeName == "searchEngineURL") {
+    if (mutation.attributeName == "searchEngineName") {
       setupSearchEngine();
       if (!gInitialized) {
         ensureSnippetsMapThen(loadSnippets);
         gInitialized = true;
       }
       return;
     }
   }
@@ -290,62 +290,27 @@ function ensureSnippetsMapThen(aCallback
       setTimeout(invokeCallbacks, 0);
     }
   }
 }
 
 function onSearchSubmit(aEvent)
 {
   let searchTerms = document.getElementById("searchText").value;
-  let searchURL = document.documentElement.getAttribute("searchEngineURL");
-
-  if (searchURL && searchTerms.length > 0) {
-    // Send an event that a search was performed. This was originally
-    // added so Firefox Health Report could record that a search from
-    // about:home had occurred.
-    let engineName = document.documentElement.getAttribute("searchEngineName");
-    let event = new CustomEvent("AboutHomeSearchEvent", {detail: engineName});
-    document.dispatchEvent(event);
-
-    const SEARCH_TOKEN = "_searchTerms_";
-    let searchPostData = document.documentElement.getAttribute("searchEnginePostData");
-    if (searchPostData) {
-      // Check if a post form already exists. If so, remove it.
-      const POST_FORM_NAME = "searchFormPost";
-      let form = document.forms[POST_FORM_NAME];
-      if (form) {
-        form.parentNode.removeChild(form);
-      }
+  let engineName = document.documentElement.getAttribute("searchEngineName");
 
-      // Create a new post form.
-      form = document.body.appendChild(document.createElement("form"));
-      form.setAttribute("name", POST_FORM_NAME);
-      // Set the URL to submit the form to.
-      form.setAttribute("action", searchURL.replace(SEARCH_TOKEN, searchTerms));
-      form.setAttribute("method", "post");
-
-      // Create new <input type=hidden> elements for search param.
-      searchPostData = searchPostData.split("&");
-      for (let postVar of searchPostData) {
-        let [name, value] = postVar.split("=");
-        if (value == SEARCH_TOKEN) {
-          value = searchTerms;
-        }
-        let input = document.createElement("input");
-        input.setAttribute("type", "hidden");
-        input.setAttribute("name", name);
-        input.setAttribute("value", value);
-        form.appendChild(input);
-      }
-      // Submit the form.
-      form.submit();
-   } else {
-      searchURL = searchURL.replace(SEARCH_TOKEN, encodeURIComponent(searchTerms));
-      window.location.href = searchURL;
-    }
+  if (engineName && searchTerms.length > 0) {
+    // Send an event that will perform a search and Firefox Health Report will
+    // record that a search from about:home has occurred.
+    let eventData = JSON.stringify({
+      engineName: engineName,
+      searchTerms: searchTerms
+    });
+    let event = new CustomEvent("AboutHomeSearchEvent", {detail: eventData});
+    document.dispatchEvent(event);
   }
 
   aEvent.preventDefault();
 }
 
 
 function setupSearchEngine()
 {
--- a/browser/base/content/content.js
+++ b/browser/base/content/content.js
@@ -85,29 +85,23 @@ let AboutHomeListener = {
     if (doc.documentURI.toLowerCase() != "about:home")
       return;
 
     if (aData.showRestoreLastSession && !PrivateBrowsingUtils.isWindowPrivate(content))
       doc.getElementById("launcher").setAttribute("session", "true");
 
     // Inject search engine and snippets URL.
     let docElt = doc.documentElement;
-    // set the following attributes BEFORE searchEngineURL, which triggers to
+    // set the following attributes BEFORE searchEngineName, which triggers to
     // show the snippets when it's set.
     docElt.setAttribute("snippetsURL", aData.snippetsURL);
     if (aData.showKnowYourRights)
       docElt.setAttribute("showKnowYourRights", "true");
     docElt.setAttribute("snippetsVersion", aData.snippetsVersion);
-
-    let engine = aData.defaultSearchEngine;
-    docElt.setAttribute("searchEngineName", engine.name);
-    docElt.setAttribute("searchEnginePostData", engine.postDataString || "");
-    // Again, keep the searchEngineURL as the last attribute, because the
-    // mutation observer in aboutHome.js is counting on that.
-    docElt.setAttribute("searchEngineURL", engine.searchURL);
+    docElt.setAttribute("searchEngineName", Services.search.defaultEngine.name);
   },
 
   onPageLoad: function() {
     let doc = content.document;
     if (doc.documentURI.toLowerCase() != "about:home" ||
         doc.documentElement.hasAttribute("hasBrowserHandlers")) {
       return;
     }
@@ -130,17 +124,17 @@ let AboutHomeListener = {
     // the hidden attribute set on the apps button in aboutHome.xhtml
     if (Services.prefs.getPrefType("browser.aboutHome.apps") == Services.prefs.PREF_BOOL &&
         Services.prefs.getBoolPref("browser.aboutHome.apps"))
       doc.getElementById("apps").removeAttribute("hidden");
 
     sendAsyncMessage("AboutHome:RequestUpdate");
 
     doc.addEventListener("AboutHomeSearchEvent", function onSearch(e) {
-      sendAsyncMessage("AboutHome:Search", { engineName: e.detail });
+      sendAsyncMessage("AboutHome:Search", { searchData: e.detail });
     }, true, true);
   },
 
   onClick: function(aEvent) {
     if (!aEvent.isTrusted || // Don't trust synthetic events
         aEvent.button == 2 || aEvent.target.localName != "button") {
       return;
     }
@@ -275,9 +269,9 @@ let ClickEventHandler = {
       node = node.parentNode;
     }
 
     // In case of XLink, we don't return the node we got href from since
     // callers expect <a>-like elements.
     return [href ? makeURLAbsolute(baseURI, href) : null, null];
   }
 };
-ClickEventHandler.init();
\ No newline at end of file
+ClickEventHandler.init();
--- a/browser/base/content/test/browser_aboutHome.js
+++ b/browser/base/content/test/browser_aboutHome.js
@@ -102,17 +102,18 @@ let gTests = [
     }
 
     let numSearchesBefore = 0;
     let deferred = Promise.defer();
     let doc = gBrowser.contentDocument;
     let engineName = doc.documentElement.getAttribute("searchEngineName");
 
     doc.addEventListener("AboutHomeSearchEvent", function onSearch(e) {
-      is(e.detail, engineName, "Detail is search engine name");
+      let data = JSON.parse(e.detail);
+      is(data.engineName, engineName, "Detail is search engine name");
 
       // We use executeSoon() to ensure that this code runs after the
       // count has been updated in browser.js, since it uses the same
       // event.
       executeSoon(function () {
         getNumberOfSearches(engineName).then(num => {
           is(num, numSearchesBefore + 1, "One more search recorded.");
           deferred.resolve();
@@ -282,17 +283,17 @@ let gTests = [
       let needle = "Search for something awesome.";
       let document = gBrowser.selectedTab.linkedBrowser.contentDocument;
       let searchText = document.getElementById("searchText");
 
       // We're about to change the search engine. Once the change has
       // propagated to the about:home content, we want to perform a search.
       let mutationObserver = new MutationObserver(function (mutations) {
         for (let mutation of mutations) {
-          if (mutation.attributeName == "searchEngineURL") {
+          if (mutation.attributeName == "searchEngineName") {
             searchText.value = needle;
             searchText.focus();
             EventUtils.synthesizeKey("VK_RETURN", {});
           }
         }
       });
       mutationObserver.observe(document.documentElement, { attributes: true });
 
@@ -440,17 +441,17 @@ function promiseBrowserAttributes(aTab)
       info("Got attribute mutation: " + mutation.attributeName +
                                     " from " + mutation.oldValue); 
       if (mutation.attributeName == "snippetsURL" &&
           docElt.getAttribute("snippetsURL") != "nonexistent://test") {
         docElt.setAttribute("snippetsURL", "nonexistent://test");
       }
 
       // Now we just have to wait for the last attribute.
-      if (mutation.attributeName == "searchEngineURL") {
+      if (mutation.attributeName == "searchEngineName") {
         info("Remove attributes observer");
         observer.disconnect();
         // Must be sure to continue after the page mutation observer.
         executeSoon(function() deferred.resolve());
         break;
       }
     }
   });
--- a/browser/modules/AboutHome.jsm
+++ b/browser/modules/AboutHome.jsm
@@ -20,31 +20,16 @@ XPCOMUtils.defineLazyModuleGetter(this, 
 const SNIPPETS_URL_PREF = "browser.aboutHomeSnippets.updateUrl";
 
 // Should be bumped up if the snippets content format changes.
 const STARTPAGE_VERSION = 4;
 
 this.AboutHomeUtils = {
   get snippetsVersion() STARTPAGE_VERSION,
 
-  /**
-   * Returns an object containing the name and searchURL of the original default
-   * search engine.
-   */
-  get defaultSearchEngine() {
-    let defaultEngine = Services.search.defaultEngine;
-    let submission = defaultEngine.getSubmission("_searchTerms_", null, "homepage");
-
-    return Object.freeze({
-      name: defaultEngine.name,
-      searchURL: submission.uri.spec,
-      postDataString: submission.postDataString
-    });
-  },
-
   /*
    * showKnowYourRights - Determines if the user should be shown the
    * about:rights notification. The notification should *not* be shown if
    * we've already shown the current version, or if the override pref says to
    * never show it. The notification *should* be shown if it's never been seen
    * before, if a newer version is available, or if the override pref says to
    * always show it.
    */
@@ -168,34 +153,43 @@ let AboutHome = {
         window.openPreferences();
         break;
 
       case "AboutHome:RequestUpdate":
         this.sendAboutHomeData(aMessage.target);
         break;
 
       case "AboutHome:Search":
+        let data;
+        try {
+          data = JSON.parse(aMessage.data.searchData);
+        } catch(ex) {
+          Cu.reportError(ex);
+          break;
+        }
 #ifdef MOZ_SERVICES_HEALTHREPORT
-        window.BrowserSearch.recordSearchInHealthReport(aMessage.data.engineName, "abouthome");
+        window.BrowserSearch.recordSearchInHealthReport(data.engineName, "abouthome");
 #endif
+        // Trigger a search through nsISearchEngine.getSubmission()
+        let submission = Services.search.currentEngine.getSubmission(data.searchTerms);
+        window.loadURI(submission.uri.spec, null, submission.postData);
         break;
     }
   },
 
   // Send all the chrome-privileged data needed by about:home. This
   // gets re-sent when the search engine changes.
   sendAboutHomeData: function(target) {
     let ss = Cc["@mozilla.org/browser/sessionstore;1"].
                getService(Ci.nsISessionStore);
     let data = {
       showRestoreLastSession: ss.canRestoreLastSession,
       snippetsURL: AboutHomeUtils.snippetsURL,
       showKnowYourRights: AboutHomeUtils.showKnowYourRights,
-      snippetsVersion: AboutHomeUtils.snippetsVersion,
-      defaultSearchEngine: AboutHomeUtils.defaultSearchEngine
+      snippetsVersion: AboutHomeUtils.snippetsVersion
     };
 
     if (AboutHomeUtils.showKnowYourRights) {
       // Set pref to indicate we've shown the notification.
       let currentVersion = Services.prefs.getIntPref("browser.rights.version");
       Services.prefs.setBoolPref("browser.rights." + currentVersion + ".shown", true);
     }
 
--- a/netwerk/base/public/nsIBrowserSearchService.idl
+++ b/netwerk/base/public/nsIBrowserSearchService.idl
@@ -2,32 +2,26 @@
  * 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/. */
 
 #include "nsISupports.idl"
 
 interface nsIURI;
 interface nsIInputStream;
 
-[scriptable, uuid(82ec6ee8-68b5-49ef-87b7-0d5240f8a183)]
+[scriptable, uuid(5799251f-5b55-4df7-a9e7-0c27812c469a)]
 interface nsISearchSubmission : nsISupports
 {
   /**
    * The POST data associated with a search submission, wrapped in a MIME
    * input stream. May be null.
    */
   readonly attribute nsIInputStream postData;
 
   /**
-   * The POST data associated with a search submission as an
-   * application/x-www-form-urlencoded string. May be null.
-   */
-  readonly attribute AString postDataString;
-
-  /**
    * The URI to submit a search to.
    */
   readonly attribute nsIURI uri;
 };
 
 [scriptable, uuid(ccf6aa20-10a9-4a0c-a81d-31b10ea846de)]
 interface nsISearchEngine : nsISupports
 {
--- a/toolkit/components/search/nsSearchService.js
+++ b/toolkit/components/search/nsSearchService.js
@@ -926,38 +926,37 @@ EngineURL.prototype = {
         continue;
 
       var value = ParamSubstitution(param.value, aSearchTerms, aEngine);
 
       dataString += (i > 0 ? "&" : "") + param.name + "=" + value;
     }
 
     var postData = null;
-    let postDataString = null;
     if (this.method == "GET") {
       // GET method requests have no post data, and append the encoded
       // query string to the url...
       if (url.indexOf("?") == -1 && dataString)
         url += "?";
       url += dataString;
     } else if (this.method == "POST") {
-      // For POST requests, specify the data as a MIME stream as well as a string.
-      postDataString = dataString;
+      // POST method requests must wrap the encoded text in a MIME
+      // stream and supply that as POSTDATA.
       var stringStream = Cc["@mozilla.org/io/string-input-stream;1"].
                          createInstance(Ci.nsIStringInputStream);
       stringStream.data = dataString;
 
       postData = Cc["@mozilla.org/network/mime-input-stream;1"].
                  createInstance(Ci.nsIMIMEInputStream);
       postData.addHeader("Content-Type", "application/x-www-form-urlencoded");
       postData.addContentLength = true;
       postData.setData(stringStream);
     }
 
-    return new Submission(makeURI(url), postData, postDataString);
+    return new Submission(makeURI(url), postData);
   },
 
   _hasRelation: function SRC_EURL__hasRelation(aRel)
     this.rels.some(function(e) e == aRel.toLowerCase()),
 
   _initWithJSON: function SRC_EURL__initWithJSON(aJson, aEngine) {
     if (!aJson.params)
       return;
@@ -2539,17 +2538,17 @@ Engine.prototype = {
 
     var url = this._getURLOfType(aResponseType);
 
     if (!url)
       return null;
 
     if (!aData) {
       // Return a dummy submission object with our searchForm attribute
-      return new Submission(makeURI(this.searchForm));
+      return new Submission(makeURI(this.searchForm), null);
     }
 
     LOG("getSubmission: In data: \"" + aData + "\"; Purpose: \"" + aPurpose + "\"");
     var textToSubURI = Cc["@mozilla.org/intl/texttosuburi;1"].
                        getService(Ci.nsITextToSubURI);
     var data = "";
     try {
       data = textToSubURI.ConvertAndEscape(this.queryCharset, aData);
@@ -2576,31 +2575,27 @@ Engine.prototype = {
 
   get wrappedJSObject() {
     return this;
   }
 
 };
 
 // nsISearchSubmission
-function Submission(aURI, aPostData = null, aPostDataString = null) {
+function Submission(aURI, aPostData = null) {
   this._uri = aURI;
   this._postData = aPostData;
-  this._postDataString = aPostDataString;
 }
 Submission.prototype = {
   get uri() {
     return this._uri;
   },
   get postData() {
     return this._postData;
   },
-  get postDataString() {
-    return this._postDataString;
-  },
   QueryInterface: function SRCH_SUBM_QI(aIID) {
     if (aIID.equals(Ci.nsISearchSubmission) ||
         aIID.equals(Ci.nsISupports))
       return this;
     throw Cr.NS_ERROR_NO_INTERFACE;
   }
 }