Bug 361923 - Can't use remote search engine icons larger than 10KB. r=florian

MozReview-Commit-ID: D0lmhAwabb3

--HG--
extra : rebase_source : 403b76132df74af3aeddc1ad4a00d85ea03d929f
This commit is contained in:
Marco Bonardo 2017-10-31 17:21:35 +01:00
Родитель 708f0175c1
Коммит 0296cb4328
5 изменённых файлов: 82 добавлений и 11 удалений

Просмотреть файл

@ -31,6 +31,13 @@ XPCOMUtils.defineLazyServiceGetters(this, {
gChromeReg: ["@mozilla.org/chrome/chrome-registry;1", "nsIChromeRegistry"],
});
const ArrayBufferInputStream = Components.Constructor(
"@mozilla.org/io/arraybuffer-input-stream;1",
"nsIArrayBufferInputStream", "setData");
const BinaryInputStream = Components.Constructor(
"@mozilla.org/binaryinputstream;1",
"nsIBinaryInputStream", "setInputStream");
Cu.importGlobalProperties(["XMLHttpRequest"]);
// A text encoder to UTF8, used whenever we commit the cache to disk.
@ -98,7 +105,7 @@ 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;
const MAX_ICON_SIZE = 20000;
// Default charset to use for sending search parameters. ISO-8859-1 is used to
// match previous nsInternetSearchService behavior as a URL parameter. Label
@ -306,11 +313,11 @@ loadListener.prototype = {
if (requestFailed || this._countRead == 0) {
LOG("loadListener: request failed!");
// send null so the callback can deal with the failure
this._callback(null, this._engine);
} else
this._callback(this._bytes, this._engine);
this._bytes = null;
}
this._callback(this._bytes, this._engine);
this._channel = null;
this._engine = null;
this._engine = null;
},
// nsIStreamListener
@ -341,6 +348,29 @@ loadListener.prototype = {
onStatus(aRequest, aContext, aStatus, aStatusArg) {}
};
/**
* Tries to rescale an icon to a given size.
*
* @param aByteArray Byte array containing the icon payload.
* @param aContentType Mime type of the payload.
* @param [optional] aSize desired icon size.
* @throws if the icon cannot be rescaled or the rescaled icon is too big.
*/
function rescaleIcon(aByteArray, aContentType, aSize = 32) {
if (aContentType == "image/svg+xml")
throw new Error("Cannot rescale SVG image");
let buffer = Uint8Array.from(aByteArray).buffer;
let imgTools = Cc["@mozilla.org/image/tools;1"].getService(Ci.imgITools);
let input = new ArrayBufferInputStream(buffer, 0, buffer.byteLength);
let container = imgTools.decodeImage(input, aContentType);
let stream = imgTools.encodeScaledImage(container, "image/png", aSize, aSize);
let size = stream.available();
if (size > MAX_ICON_SIZE)
throw new Error("Icon is too big");
let bis = new BinaryInputStream(stream);
return [bis.readByteArray(size), "image/png"];
}
function isPartnerBuild() {
try {
let distroID = Services.prefs.getCharPref("distribution.id");
@ -1740,15 +1770,26 @@ Engine.prototype = {
if (aEngine._hasPreferredIcon && !aIsPreferred)
return;
if (!aByteArray || aByteArray.length > MAX_ICON_SIZE) {
LOG("iconLoadCallback: load failed, or the icon was too large!");
if (!aByteArray) {
LOG("iconLoadCallback: load failed");
return;
}
let type = chan.contentType;
if (!type.startsWith("image/"))
type = "image/x-icon";
let dataURL = "data:" + type + ";base64," +
let contentType = chan.contentType;
if (aByteArray.length > MAX_ICON_SIZE) {
try {
LOG("iconLoadCallback: rescaling icon");
[aByteArray, contentType] = rescaleIcon(aByteArray, contentType);
} catch (ex) {
LOG("iconLoadCallback: got exception: " + ex);
Cu.reportError("Unable to set an icon for the search engine because: " + ex);
return;
}
}
if (!contentType.startsWith("image/"))
contentType = "image/x-icon";
let dataURL = "data:" + contentType + ";base64," +
btoa(String.fromCharCode.apply(null, aByteArray));
aEngine._iconURI = makeURI(dataURL);

Двоичные данные
toolkit/components/search/tests/xpcshell/data/big_icon.ico Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 55 KiB

Просмотреть файл

@ -10,6 +10,8 @@ Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource://testing-common/AppInfo.jsm");
Cu.import("resource://testing-common/httpd.js");
XPCOMUtils.defineLazyModuleGetter(this, "TestUtils",
"resource://testing-common/TestUtils.jsm");
const BROWSER_SEARCH_PREF = "browser.search.";
const NS_APP_SEARCH_DIR = "SrchPlugns";

Просмотреть файл

@ -0,0 +1,26 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
add_task(async function test_big_icon() {
let srv = useHttpServer();
srv.registerContentType("ico", "image/x-icon");
await asyncInit();
let promiseChanged = TestUtils.topicObserved("browser-search-engine-modified",
(engine, verb) => verb == "engine-changed" && engine.name == "BigIcon" && engine.iconURI);
let iconUrl = gDataUrl + "big_icon.ico";
await addTestEngines([
{ name: "BigIcon",
details: [iconUrl, "", "Big icon", "GET",
"http://test_big_icon/search?q={searchTerms}"] },
]);
await promiseAfterCache();
let [engine] = await promiseChanged;
Assert.ok(engine.iconURI.spec.startsWith("data:image/png"),
"The icon is saved as a PNG data url");
});

Просмотреть файл

@ -3,6 +3,7 @@ head = head_search.js
firefox-appdir = browser
skip-if = toolkit == 'android'
support-files =
data/big_icon.ico
data/chrome.manifest
data/engine.xml
data/engine2.xml
@ -32,6 +33,7 @@ support-files =
[test_nocache.js]
[test_645970.js]
[test_big_icon.js]
[test_bug930456.js]
[test_bug930456_child.js]
[test_engine_set_alias.js]