mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-28 15:23:51 +00:00
Bug 1283825 - Add a page-icon protocol to fetch the best icon for a url. r=adw
MozReview-Commit-ID: 3exDniH8Hkm --HG-- extra : rebase_source : 299b288933f1a826aadffef4e9fab9559a7950ff
This commit is contained in:
parent
d98fe32356
commit
0bd3c0c381
@ -442,6 +442,7 @@
|
||||
@RESPATH@/components/nsTaggingService.js
|
||||
@RESPATH@/components/UnifiedComplete.js
|
||||
@RESPATH@/components/nsPlacesExpiration.js
|
||||
@RESPATH@/components/PageIconProtocolHandler.js
|
||||
@RESPATH@/components/PlacesCategoriesStarter.js
|
||||
@RESPATH@/components/ColorAnalyzer.js
|
||||
@RESPATH@/components/PageThumbsProtocol.js
|
||||
|
@ -613,7 +613,7 @@ AsyncFetchAndSetIconForPage::OnStopRequest(nsIRequest* aRequest,
|
||||
|
||||
// If over the maximum size allowed, don't save data to the database to
|
||||
// avoid bloating it.
|
||||
if (mIcon.data.Length() > MAX_FAVICON_SIZE) {
|
||||
if (mIcon.data.Length() > nsIFaviconService::MAX_FAVICON_SIZE) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
128
toolkit/components/places/PageIconProtocolHandler.js
Normal file
128
toolkit/components/places/PageIconProtocolHandler.js
Normal file
@ -0,0 +1,128 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* 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/. */
|
||||
|
||||
"use strict";
|
||||
|
||||
const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
|
||||
|
||||
Cu.import('resource://gre/modules/XPCOMUtils.jsm');
|
||||
Cu.import('resource://gre/modules/Services.jsm');
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "PlacesUtils",
|
||||
"resource://gre/modules/PlacesUtils.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "NetUtil",
|
||||
"resource://gre/modules/NetUtil.jsm");
|
||||
|
||||
function makeDefaultFaviconChannel(uri, loadInfo) {
|
||||
let channel = Services.io.newChannelFromURIWithLoadInfo(
|
||||
PlacesUtils.favicons.defaultFavicon, loadInfo);
|
||||
channel.originalURI = uri;
|
||||
return channel;
|
||||
}
|
||||
|
||||
function streamDefaultFavicon(uri, loadInfo, outputStream) {
|
||||
try {
|
||||
// Open up a new channel to get that data, and push it to our output stream.
|
||||
// Create a listener to hand data to the pipe's output stream.
|
||||
let listener = Cc["@mozilla.org/network/simple-stream-listener;1"]
|
||||
.createInstance(Ci.nsISimpleStreamListener);
|
||||
listener.init(outputStream, {
|
||||
onStartRequest(request, context) {},
|
||||
onStopRequest(request, context, statusCode) {
|
||||
// We must close the outputStream regardless.
|
||||
outputStream.close();
|
||||
}
|
||||
});
|
||||
let defaultIconChannel = makeDefaultFaviconChannel(uri, loadInfo);
|
||||
defaultIconChannel.asyncOpen2(listener);
|
||||
} catch (ex) {
|
||||
Cu.reportError(ex);
|
||||
outputStream.close();
|
||||
}
|
||||
}
|
||||
|
||||
function PageIconProtocolHandler() {
|
||||
}
|
||||
|
||||
PageIconProtocolHandler.prototype = {
|
||||
get scheme() {
|
||||
return "page-icon";
|
||||
},
|
||||
|
||||
get defaultPort() {
|
||||
return -1;
|
||||
},
|
||||
|
||||
get protocolFlags() {
|
||||
return Ci.nsIProtocolHandler.URI_NORELATIVE |
|
||||
Ci.nsIProtocolHandler.URI_NOAUTH |
|
||||
Ci.nsIProtocolHandler.URI_DANGEROUS_TO_LOAD |
|
||||
Ci.nsIProtocolHandler.URI_IS_LOCAL_RESOURCE;
|
||||
},
|
||||
|
||||
newURI(spec, originCharset, baseURI) {
|
||||
let uri = Cc["@mozilla.org/network/simple-uri;1"].createInstance(Ci.nsIURI);
|
||||
uri.spec = spec;
|
||||
return uri;
|
||||
},
|
||||
|
||||
newChannel2(uri, loadInfo) {
|
||||
try {
|
||||
// Create a pipe that will give us an output stream that we can use once
|
||||
// we got all the favicon data.
|
||||
let pipe = Cc["@mozilla.org/pipe;1"]
|
||||
.createInstance(Ci.nsIPipe);
|
||||
pipe.init(true, true, 0, Ci.nsIFaviconService.MAX_FAVICON_SIZE);
|
||||
|
||||
// Create our channel.
|
||||
let channel = Cc['@mozilla.org/network/input-stream-channel;1']
|
||||
.createInstance(Ci.nsIInputStreamChannel);
|
||||
channel.QueryInterface(Ci.nsIChannel);
|
||||
channel.setURI(uri);
|
||||
channel.contentStream = pipe.inputStream;
|
||||
channel.loadInfo = loadInfo;
|
||||
|
||||
let pageURI = NetUtil.newURI(uri.path);
|
||||
PlacesUtils.favicons.getFaviconDataForPage(pageURI, (iconuri, len, data, mime) => {
|
||||
if (len == 0) {
|
||||
channel.contentType = "image/png";
|
||||
streamDefaultFavicon(uri, loadInfo, pipe.outputStream);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
channel.contentType = mime;
|
||||
// Pass the icon data to the output stream.
|
||||
let stream = Cc["@mozilla.org/binaryoutputstream;1"]
|
||||
.createInstance(Ci.nsIBinaryOutputStream);
|
||||
stream.setOutputStream(pipe.outputStream);
|
||||
stream.writeByteArray(data, len);
|
||||
stream.close();
|
||||
pipe.outputStream.close();
|
||||
} catch (ex) {
|
||||
channel.contentType = "image/png";
|
||||
streamDefaultFavicon(uri, loadInfo, pipe.outputStream);
|
||||
}
|
||||
});
|
||||
|
||||
return channel;
|
||||
} catch (ex) {
|
||||
return makeDefaultFaviconChannel(uri, loadInfo);
|
||||
}
|
||||
},
|
||||
|
||||
newChannel(uri) {
|
||||
return this.newChannel2(uri, null);
|
||||
},
|
||||
|
||||
allowPort(port, scheme) {
|
||||
return false;
|
||||
},
|
||||
|
||||
classID: Components.ID("{60a1f7c6-4ff9-4a42-84d3-5a185faa6f32}"),
|
||||
QueryInterface: XPCOMUtils.generateQI([
|
||||
Ci.nsIProtocolHandler
|
||||
])
|
||||
};
|
||||
|
||||
this.NSGetFactory = XPCOMUtils.generateNSGetFactory([PageIconProtocolHandler]);
|
@ -78,6 +78,7 @@ if CONFIG['MOZ_PLACES']:
|
||||
'nsLivemarkService.js',
|
||||
'nsPlacesExpiration.js',
|
||||
'nsTaggingService.js',
|
||||
'PageIconProtocolHandler.js',
|
||||
'PlacesCategoriesStarter.js',
|
||||
'toolkitplaces.manifest',
|
||||
'UnifiedComplete.js',
|
||||
|
@ -336,8 +336,7 @@ nsAnnoProtocolHandler::NewFaviconChannel(nsIURI *aURI, nsIURI *aAnnotationURI,
|
||||
nsCOMPtr<nsIOutputStream> outputStream;
|
||||
nsresult rv = NS_NewPipe(getter_AddRefs(inputStream),
|
||||
getter_AddRefs(outputStream),
|
||||
MAX_FAVICON_SIZE, MAX_FAVICON_SIZE, true,
|
||||
true);
|
||||
0, nsIFaviconService::MAX_FAVICON_SIZE, true, true);
|
||||
NS_ENSURE_SUCCESS(rv, GetDefaultIcon(aLoadInfo, _channel));
|
||||
|
||||
// Create our channel. We'll call SetContentType with the right type when
|
||||
|
@ -347,7 +347,7 @@ nsFaviconService::ReplaceFaviconData(nsIURI* aFaviconURI,
|
||||
rv = OptimizeFaviconImage(aData, aDataLen, aMimeType, iconData->data, iconData->mimeType);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
if (iconData->data.Length() > MAX_FAVICON_SIZE) {
|
||||
if (iconData->data.Length() > nsIFaviconService::MAX_FAVICON_SIZE) {
|
||||
// We cannot optimize this favicon size and we are over the maximum size
|
||||
// allowed, so we will not save data to the db to avoid bloating it.
|
||||
mUnassociatedIcons.RemoveEntry(aFaviconURI);
|
||||
|
@ -23,11 +23,6 @@
|
||||
|
||||
#include "FaviconHelpers.h"
|
||||
|
||||
// Favicons bigger than this size should not be saved to the db to avoid
|
||||
// bloating it with large image blobs.
|
||||
// This still allows us to accept a favicon even if we cannot optimize it.
|
||||
#define MAX_FAVICON_SIZE 10240
|
||||
|
||||
// Most icons will be smaller than this rough estimate of the size of an
|
||||
// uncompressed 16x16 RGBA image of the same dimensions.
|
||||
#define MAX_ICON_FILESIZE(s) ((uint32_t) s*s*4)
|
||||
|
@ -15,6 +15,12 @@ interface nsIFaviconService : nsISupports
|
||||
// The favicon is being loaded from a non-private browsing window
|
||||
const unsigned long FAVICON_LOAD_NON_PRIVATE = 2;
|
||||
|
||||
/**
|
||||
* Favicons bigger than this size in bytes, won't be saved to the database to
|
||||
* avoid bloating it with large image blobs.
|
||||
*/
|
||||
const unsigned long MAX_FAVICON_SIZE = 10240;
|
||||
|
||||
/**
|
||||
* For a given icon URI, this will return a URI that will result in the image.
|
||||
* In most cases, this is an annotation URI. For chrome URIs, this will do
|
||||
|
@ -1145,17 +1145,18 @@ nsNavHistory::CanAddURI(nsIURI* aURI, bool* canAdd)
|
||||
|
||||
// now check for all bad things
|
||||
if (scheme.EqualsLiteral("about") ||
|
||||
scheme.EqualsLiteral("blob") ||
|
||||
scheme.EqualsLiteral("chrome") ||
|
||||
scheme.EqualsLiteral("data") ||
|
||||
scheme.EqualsLiteral("imap") ||
|
||||
scheme.EqualsLiteral("news") ||
|
||||
scheme.EqualsLiteral("javascript") ||
|
||||
scheme.EqualsLiteral("mailbox") ||
|
||||
scheme.EqualsLiteral("moz-anno") ||
|
||||
scheme.EqualsLiteral("view-source") ||
|
||||
scheme.EqualsLiteral("chrome") ||
|
||||
scheme.EqualsLiteral("news") ||
|
||||
scheme.EqualsLiteral("page-icon") ||
|
||||
scheme.EqualsLiteral("resource") ||
|
||||
scheme.EqualsLiteral("data") ||
|
||||
scheme.EqualsLiteral("wyciwyg") ||
|
||||
scheme.EqualsLiteral("javascript") ||
|
||||
scheme.EqualsLiteral("blob")) {
|
||||
scheme.EqualsLiteral("view-source") ||
|
||||
scheme.EqualsLiteral("wyciwyg")) {
|
||||
return NS_OK;
|
||||
}
|
||||
*canAdd = true;
|
||||
|
@ -0,0 +1,66 @@
|
||||
const ICON_DATA = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAAAAAA6fptVAAAACklEQVQI12NgAAAAAgAB4iG8MwAAAABJRU5ErkJggg==";
|
||||
const TEST_URI = NetUtil.newURI("http://mozilla.org/");
|
||||
const ICON_URI = NetUtil.newURI("http://mozilla.org/favicon.ico");
|
||||
|
||||
function fetchIconForSpec(spec) {
|
||||
return new Promise((resolve, reject) => {
|
||||
NetUtil.asyncFetch({
|
||||
uri: NetUtil.newURI("page-icon:" + TEST_URI.spec),
|
||||
loadUsingSystemPrincipal: true,
|
||||
contentPolicyType: Ci.nsIContentPolicy.TYPE_INTERNAL_IMAGE
|
||||
}, (input, status, request) => {
|
||||
if (!Components.isSuccessCode(status)) {
|
||||
reject(new Error("unable to load icon"));
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
let data = NetUtil.readInputStreamToString(input, input.available());
|
||||
let contentType = request.QueryInterface(Ci.nsIChannel).contentType;
|
||||
input.close();
|
||||
resolve({ data, contentType });
|
||||
} catch (ex) {
|
||||
reject(ex);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
var gDefaultFavicon;
|
||||
var gFavicon;
|
||||
|
||||
add_task(function* setup() {
|
||||
PlacesTestUtils.addVisits({ uri: TEST_URI });
|
||||
|
||||
PlacesUtils.favicons.replaceFaviconDataFromDataURL(
|
||||
ICON_URI, ICON_DATA, (Date.now() + 8640000) * 1000,
|
||||
Services.scriptSecurityManager.getSystemPrincipal());
|
||||
|
||||
yield new Promise(resolve => {
|
||||
PlacesUtils.favicons.setAndFetchFaviconForPage(
|
||||
TEST_URI, ICON_URI, false,
|
||||
PlacesUtils.favicons.FAVICON_LOAD_NON_PRIVATE,
|
||||
resolve, Services.scriptSecurityManager.getSystemPrincipal());
|
||||
});
|
||||
|
||||
gDefaultFavicon = yield fetchIconForSpec(PlacesUtils.favicons.defaultFavicon);
|
||||
gFavicon = yield fetchIconForSpec(ICON_DATA);
|
||||
});
|
||||
|
||||
add_task(function* known_url() {
|
||||
let {data, contentType} = yield fetchIconForSpec(TEST_URI.spec);
|
||||
Assert.equal(contentType, gFavicon.contentType);
|
||||
Assert.ok(data == gFavicon.data, "Got the favicon data");
|
||||
});
|
||||
|
||||
add_task(function* unknown_url() {
|
||||
let {data, contentType} = yield fetchIconForSpec("http://www.moz.org/");
|
||||
Assert.equal(contentType, gDefaultFavicon.contentType);
|
||||
Assert.ok(data == gDefaultFavicon.data, "Got the default favicon data");
|
||||
});
|
||||
|
||||
add_task(function* invalid_url() {
|
||||
let {data, contentType} = yield fetchIconForSpec("test");
|
||||
Assert.equal(contentType, gDefaultFavicon.contentType);
|
||||
Assert.ok(data == gDefaultFavicon.data, "Got the default favicon data");
|
||||
});
|
@ -27,6 +27,7 @@ fail-if = os == "android"
|
||||
[test_getFaviconDataForPage.js]
|
||||
[test_getFaviconURLForPage.js]
|
||||
[test_moz-anno_favicon_mime_type.js]
|
||||
[test_page-icon_protocol.js]
|
||||
[test_query_result_favicon_changed_on_child.js]
|
||||
[test_replaceFaviconData.js]
|
||||
[test_replaceFaviconDataFromDataURL.js]
|
||||
|
@ -26,3 +26,7 @@ contract @mozilla.org/places/colorAnalyzer;1 {d056186c-28a0-494e-aacc-9e433772b1
|
||||
# UnifiedComplete.js
|
||||
component {f964a319-397a-4d21-8be6-5cdd1ee3e3ae} UnifiedComplete.js
|
||||
contract @mozilla.org/autocomplete/search;1?name=unifiedcomplete {f964a319-397a-4d21-8be6-5cdd1ee3e3ae}
|
||||
|
||||
# PageIconProtocolHandler.js
|
||||
component {60a1f7c6-4ff9-4a42-84d3-5a185faa6f32} PageIconProtocolHandler.js
|
||||
contract @mozilla.org/network/protocol;1?name=page-icon {60a1f7c6-4ff9-4a42-84d3-5a185faa6f32}
|
||||
|
Loading…
Reference in New Issue
Block a user