Bug 1240729 - No favicon displayed in Search bar panel after adding an engine with an SVG icon, r=adw.
authorFlorian Quèze <florian@queze.net>
Fri, 11 Mar 2016 12:16:46 +0100
changeset 288077 f7ed458ec73dc42d58fda3d69b1036738d968833
parent 288076 0901ef091aabd804386e9838bc85e806b7e16707
child 288078 971c1817df4c69ac35e12d12aac6a7cfa4066103
push id18132
push userflorian@queze.net
push dateFri, 11 Mar 2016 11:17:10 +0000
treeherderfx-team@f7ed458ec73d [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersadw
bugs1240729
milestone48.0a1
Bug 1240729 - No favicon displayed in Search bar panel after adding an engine with an SVG icon, r=adw.
toolkit/components/search/nsSearchService.js
toolkit/components/search/tests/xpcshell/test_svg_icon.js
toolkit/components/search/tests/xpcshell/xpcshell.ini
--- a/toolkit/components/search/nsSearchService.js
+++ b/toolkit/components/search/nsSearchService.js
@@ -96,18 +96,16 @@ const LAZY_SERIALIZE_DELAY = 100;
 const CACHE_INVALIDATION_DELAY = 1000;
 
 // Current cache version. This should be incremented if the format of the cache
 // file is modified.
 const CACHE_VERSION = 1;
 
 const CACHE_FILENAME = "search.json.mozlz4";
 
-const ICON_DATAURL_PREFIX = "data:image/x-icon;base64,";
-
 const NEW_LINES = /(\r\n|\r|\n)/;
 
 // Set an arbitrary cap on the maximum icon size. Without this, large icons can
 // cause big delays when loading them at startup.
 const MAX_ICON_SIZE   = 10000;
 
 // Default charset to use for sending search parameters. ISO-8859-1 is used to
 // match previous nsInternetSearchService behavior.
@@ -1789,17 +1787,16 @@ Engine.prototype = {
 
         if (aWidth && aHeight) {
           this._addIconToMap(aWidth, aHeight, aIconURL)
         }
         break;
       case "http":
       case "https":
       case "ftp":
-        // No use downloading the icon if the engine file is read-only
         LOG("_setIcon: Downloading icon: \"" + uri.spec +
             "\" for engine: \"" + this.name + "\"");
         var chan = NetUtil.newChannel({
                      uri: uri,
                      loadUsingSystemPrincipal: true
                    });
 
         let iconLoadCallback = function (aByteArray, aEngine) {
@@ -1808,27 +1805,31 @@ Engine.prototype = {
           if (aEngine._hasPreferredIcon && !aIsPreferred)
             return;
 
           if (!aByteArray || aByteArray.length > MAX_ICON_SIZE) {
             LOG("iconLoadCallback: load failed, or the icon was too large!");
             return;
           }
 
-          var str = btoa(String.fromCharCode.apply(null, aByteArray));
-          let dataURL = ICON_DATAURL_PREFIX + str;
+          let type = chan.contentType;
+          if (!type.startsWith("image/"))
+            type = "image/x-icon";
+          let dataURL = "data:" + type + ";base64," +
+            btoa(String.fromCharCode.apply(null, aByteArray));
+
           aEngine._iconURI = makeURI(dataURL);
 
           if (aWidth && aHeight) {
             aEngine._addIconToMap(aWidth, aHeight, dataURL)
           }
 
           notifyAction(aEngine, SEARCH_ENGINE_CHANGED);
           aEngine._hasPreferredIcon = aIsPreferred;
-        }
+        };
 
         // If we're currently acting as an "update engine", then the callback
         // should set the icon on the engine we're updating and not us, since
         // |this| might be gone by the time the callback runs.
         var engineToSet = this._engineToUpdate || this;
 
         var listener = new loadListener(chan, engineToSet, iconLoadCallback);
         chan.notificationCallbacks = listener;
new file mode 100644
--- /dev/null
+++ b/toolkit/components/search/tests/xpcshell/test_svg_icon.js
@@ -0,0 +1,52 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+var url;
+var requestHandled;
+
+const icon =
+  '<?xml version="1.0" encoding="UTF-8" standalone="no"?>' +
+  '<svg xmlns="http://www.w3.org/2000/svg" ' +
+       'width="16" height="16" viewBox="0 0 16 16">' +
+   '<rect x="4" y="4" width="8px" height="8px" style="fill: blue"/>' +
+  '</svg>';
+
+function run_test() {
+  updateAppInfo();
+  useHttpServer(); // Unused, but required to call addTestEngines.
+
+  requestHandled = new Promise(resolve => {
+    let srv = new HttpServer();
+    srv.registerPathHandler("/icon.svg", (metadata, response) => {
+      response.setStatusLine("1.0", 200, "OK");
+      response.setHeader("Content-Type", "image/svg+xml", false);
+
+      response.write(icon);
+      resolve();
+    });
+    srv.start(-1);
+    do_register_cleanup(() => srv.stop(() => {}));
+
+    url = "http://localhost:" + srv.identity.primaryPort + "/icon.svg";
+  });
+
+  run_next_test();
+}
+
+add_task(function* test_svg_icon() {
+  yield asyncInit();
+
+  let [engine] = yield addTestEngines([
+    { name: "SVGIcon", details: [url, "", "SVG icon", "GET",
+                                 "http://icon.svg/search?q={searchTerms}"] },
+  ]);
+
+  yield requestHandled;
+  yield promiseAfterCache();
+
+  ok(engine.iconURI, "the engine has an icon");
+  ok(engine.iconURI.spec.startsWith("data:image/svg+xml"),
+     "the icon is saved as an SVG data url");
+});
--- a/toolkit/components/search/tests/xpcshell/xpcshell.ini
+++ b/toolkit/components/search/tests/xpcshell/xpcshell.ini
@@ -83,8 +83,9 @@ tags = addons
 [test_sync_migration.js]
 [test_sync_profile_engine.js]
 [test_rel_searchform.js]
 [test_remove_profile_engine.js]
 [test_selectedEngine.js]
 [test_geodefaults.js]
 [test_hidden.js]
 [test_currentEngine_fallback.js]
+[test_svg_icon.js]