Bug 1425827 - Allow "application/json" as well as "application/x-suggestions+json" as MIME types for Suggestions in OpenSearch r=mikedeboer
authorMark Banner <standard8@mozilla.com>
Fri, 31 Aug 2018 16:21:54 +0000
changeset 492013 a6f19768aa910330de9c07732b607cd36869d99c
parent 492012 ee165ec10257a3ddff93c126e56ffe5c430c8d9a
child 492014 79c83e9cc25496913956e534c07738d1b441517d
push id1815
push userffxbld-merge
push dateMon, 15 Oct 2018 10:40:45 +0000
treeherdermozilla-release@18d4c09e9378 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmikedeboer
bugs1425827
milestone63.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 1425827 - Allow "application/json" as well as "application/x-suggestions+json" as MIME types for Suggestions in OpenSearch r=mikedeboer Differential Revision: https://phabricator.services.mozilla.com/D3995
toolkit/components/search/nsSearchService.js
toolkit/components/search/tests/xpcshell/data/engineMaker.sjs
toolkit/components/search/tests/xpcshell/test_searchSuggest.js
--- a/toolkit/components/search/nsSearchService.js
+++ b/toolkit/components/search/nsSearchService.js
@@ -1773,25 +1773,36 @@ Engine.prototype = {
   _parseURL: function SRCH_ENG_parseURL(aElement) {
     var type     = aElement.getAttribute("type");
     // According to the spec, method is optional, defaulting to "GET" if not
     // specified
     var method   = aElement.getAttribute("method") || "GET";
     var template = aElement.getAttribute("template");
     var resultDomain = aElement.getAttribute("resultdomain");
 
+    let rels = [];
+    if (aElement.hasAttribute("rel")) {
+      rels = aElement.getAttribute("rel").toLowerCase().split(/\s+/);
+    }
+
+    // Support an alternate suggestion type, see bug 1425827 for details.
+    if (type == "application/json" && rels.includes("suggestions")) {
+      type = URLTYPE_SUGGEST_JSON;
+    }
+
     try {
       var url = new EngineURL(type, method, template, resultDomain);
     } catch (ex) {
       FAIL("_parseURL: failed to add " + template + " as a URL",
            Cr.NS_ERROR_FAILURE);
     }
 
-    if (aElement.hasAttribute("rel"))
-      url.rels = aElement.getAttribute("rel").toLowerCase().split(/\s+/);
+    if (rels.length) {
+      url.rels = rels;
+    }
 
     for (var i = 0; i < aElement.children.length; ++i) {
       var param = aElement.children[i];
       if (param.localName == "Param") {
         try {
           url.addParam(param.getAttribute("name"), param.getAttribute("value"));
         } catch (ex) {
           // Ignore failure
--- a/toolkit/components/search/tests/xpcshell/data/engineMaker.sjs
+++ b/toolkit/components/search/tests/xpcshell/data/engineMaker.sjs
@@ -28,25 +28,29 @@ function handleRequest(request, response
  */
 function createOpenSearchEngine(response, engineData) {
   let params = "", queryString = "";
   if (engineData.method == "POST") {
     params = "<Param name='q' value='{searchTerms}'/>";
   } else {
     queryString = "?q={searchTerms}";
   }
+  let type = "type='application/x-suggestions+json'";
+  if (engineData.alternativeJSONType) {
+    type = "type='application/json' rel='suggestions'";
+  }
 
-  let result = "<?xml version='1.0' encoding='utf-8'?>\
-<OpenSearchDescription xmlns='http://a9.com/-/spec/opensearch/1.1/'>\
-  <ShortName>" + engineData.name + "</ShortName>\
-  <Description>" + engineData.description + "</Description>\
-  <InputEncoding>UTF-8</InputEncoding>\
-  <LongName>" + engineData.name + "</LongName>\
-  <Url type='application/x-suggestions+json' method='" + engineData.method + "'\
-       template='" + engineData.baseURL + "searchSuggestions.sjs" + queryString + "'>\
-    " + params + "\
-  </Url>\
-  <Url type='text/html' method='" + engineData.method + "'\
-       template='" + engineData.baseURL + queryString + "'/>\
-</OpenSearchDescription>\
-";
+  let result = `<?xml version='1.0' encoding='utf-8'?>
+<OpenSearchDescription xmlns='http://a9.com/-/spec/opensearch/1.1/'>
+  <ShortName>${engineData.name}</ShortName>
+  <Description>${engineData.description}</Description>
+  <InputEncoding>UTF-8</InputEncoding>
+  <LongName>${engineData.name}</LongName>
+  <Url ${type} method='${engineData.method}'
+       template='${engineData.baseURL}searchSuggestions.sjs${queryString}'>
+    ${params}
+  </Url>
+  <Url type='text/html' method='${engineData.method}'
+       template='${engineData.baseURL}${queryString}'/>
+</OpenSearchDescription>
+`;
   response.write(result);
 }
--- a/toolkit/components/search/tests/xpcshell/test_searchSuggest.js
+++ b/toolkit/components/search/tests/xpcshell/test_searchSuggest.js
@@ -16,17 +16,17 @@ ChromeUtils.import("resource://gre/modul
 // We must make sure the FormHistoryStartup component is
 // initialized in order for it to respond to FormHistory
 // requests from nsFormAutoComplete.js.
 var formHistoryStartup = Cc["@mozilla.org/satchel/form-history-startup;1"].
                          getService(Ci.nsIObserver);
 formHistoryStartup.observe(null, "profile-after-change", null);
 
 var httpServer = new HttpServer();
-var getEngine, postEngine, unresolvableEngine;
+var getEngine, postEngine, unresolvableEngine, alternateJSONEngine;
 
 function run_test() {
   Services.prefs.setBoolPref("browser.search.suggest.enabled", true);
 
   let server = useHttpServer();
   server.registerContentType("sjs", "sjs");
 
   registerCleanupFunction(() => (async function cleanup() {
@@ -52,29 +52,40 @@ add_task(async function add_test_engines
   };
 
   let unresolvableEngineData = {
     baseURL: "http://example.invalid/",
     name: "Offline suggestion engine",
     method: "GET",
   };
 
-  [getEngine, postEngine, unresolvableEngine] = await addTestEngines([
+  let alternateJSONSuggestEngineData = {
+    baseURL: gDataUrl,
+    name: "Alternative JSON suggestion type",
+    method: "GET",
+    alternativeJSONType: true,
+  };
+
+  [getEngine, postEngine, unresolvableEngine, alternateJSONEngine] = await addTestEngines([
     {
       name: getEngineData.name,
       xmlFileName: "engineMaker.sjs?" + JSON.stringify(getEngineData),
     },
     {
       name: postEngineData.name,
       xmlFileName: "engineMaker.sjs?" + JSON.stringify(postEngineData),
     },
     {
       name: unresolvableEngineData.name,
       xmlFileName: "engineMaker.sjs?" + JSON.stringify(unresolvableEngineData),
     },
+    {
+      name: alternateJSONSuggestEngineData.name,
+      xmlFileName: "engineMaker.sjs?" + JSON.stringify(alternateJSONSuggestEngineData),
+    },
   ]);
 });
 
 
 // Begin tests
 
 add_task(async function simple_no_result_callback() {
   await new Promise(resolve => {
@@ -121,16 +132,27 @@ add_task(async function simple_remote_no
   Assert.equal(result.term, "mo");
   Assert.equal(result.local.length, 0);
   Assert.equal(result.remote.length, 3);
   Assert.equal(result.remote[0], "Mozilla");
   Assert.equal(result.remote[1], "modern");
   Assert.equal(result.remote[2], "mom");
 });
 
+add_task(async function simple_remote_no_local_result_alternative_type() {
+  let controller = new SearchSuggestionController();
+  let result = await controller.fetch("mo", false, alternateJSONEngine);
+  Assert.equal(result.term, "mo");
+  Assert.equal(result.local.length, 0);
+  Assert.equal(result.remote.length, 3);
+  Assert.equal(result.remote[0], "Mozilla");
+  Assert.equal(result.remote[1], "modern");
+  Assert.equal(result.remote[2], "mom");
+});
+
 add_task(async function remote_term_case_mismatch() {
   let controller = new SearchSuggestionController();
   let result = await controller.fetch("Query Case Mismatch", false, getEngine);
   Assert.equal(result.term, "Query Case Mismatch");
   Assert.equal(result.remote.length, 1);
   Assert.equal(result.remote[0], "Query Case Mismatch");
 });