summaryrefslogtreecommitdiffstats
path: root/toolkit/components/places/PageIconProtocolHandler.js
blob: 05e43ccf3e371f62506ad784aaceb448ca706dfb (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
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_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.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]);