/* 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"; ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm"); ChromeUtils.import("resource://gre/modules/Services.jsm"); ChromeUtils.defineModuleGetter(this, "PlacesUtils", "resource://gre/modules/PlacesUtils.jsm"); ChromeUtils.defineModuleGetter(this, "NetUtil", "resource://gre/modules/NetUtil.jsm"); function makeDefaultFaviconChannel(uri, loadInfo) { let channel = Services.io.newChannelFromURIWithLoadInfo( PlacesUtils.favicons.defaultFavicon, loadInfo); channel.originalURI = uri; channel.contentType = PlacesUtils.favicons.defaultFaviconMimeType; return channel; } function streamDefaultFavicon(uri, loadInfo, outputStream, originalChannel) { 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(); }, }); originalChannel.contentType = PlacesUtils.favicons.defaultFaviconMimeType; let defaultIconChannel = makeDefaultFaviconChannel(uri, loadInfo); defaultIconChannel.asyncOpen2(listener); } catch (ex) { Cu.reportError(ex); outputStream.close(); } } function serveIcon(pipe, data, len) { // 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(); } 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) { return Cc["@mozilla.org/network/simple-uri-mutator;1"] .createInstance(Ci.nsIURIMutator) .setSpec(spec) .finalize(); }, 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_BUFFER_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.pathQueryRef.replace(/[&#]size=[^&]+$/, "")); let preferredSize = PlacesUtils.favicons.preferredSizeFromURI(uri); PlacesUtils.favicons.getFaviconDataForPage(pageURI, (iconURI, len, data, mimeType) => { if (len == 0) { streamDefaultFavicon(uri, loadInfo, pipe.outputStream, channel); } else { try { channel.contentType = mimeType; channel.contentLength = len; serveIcon(pipe, data, len); } catch (ex) { streamDefaultFavicon(uri, loadInfo, pipe.outputStream, channel); } } }, preferredSize); 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: ChromeUtils.generateQI([ Ci.nsIProtocolHandler, Ci.nsISupportsWeakReference, ]), }; this.NSGetFactory = XPCOMUtils.generateNSGetFactory([PageIconProtocolHandler]);