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 288241 f7ed458ec73dc42d58fda3d69b1036738d968833
parent 288240 0901ef091aabd804386e9838bc85e806b7e16707
child 288242 971c1817df4c69ac35e12d12aac6a7cfa4066103
push id30078
push userryanvm@gmail.com
push dateSat, 12 Mar 2016 18:00:11 +0000
treeherdermozilla-central@b8efc6dc729e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersadw
bugs1240729
milestone48.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 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]