summaryrefslogtreecommitdiffstats
path: root/toolkit/components/thumbnails/PageThumbsProtocol.js
blob: 41dfe96be189272a8c559ba0b58c8a2c2a1d9c03 (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
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
/* 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/. */

/**
 * PageThumbsProtocol.js
 *
 * This file implements the moz-page-thumb:// protocol and the corresponding
 * channel delivering cached thumbnails.
 *
 * URL structure:
 *
 * moz-page-thumb://thumbnail/?url=http%3A%2F%2Fwww.mozilla.org%2F&revision=XX
 *
 * This URL requests an image for 'http://www.mozilla.org/'.
 * The value of the revision key may change when the stored thumbnail changes.
 */

"use strict";

const Cu = Components.utils;
const Cc = Components.classes;
const Cr = Components.results;
const Ci = Components.interfaces;

Cu.import("resource://gre/modules/PageThumbs.jsm");
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource://gre/modules/osfile.jsm", this);

XPCOMUtils.defineLazyModuleGetter(this, "Services",
  "resource://gre/modules/Services.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "FileUtils",
  "resource://gre/modules/FileUtils.jsm");

const SUBSTITUTING_URL_CID = "{dea9657c-18cf-4984-bde9-ccef5d8ab473}";

/**
 * Implements the thumbnail protocol handler responsible for moz-page-thumb: URLs.
 */
function Protocol() {
}

Protocol.prototype = {
  /**
   * The scheme used by this protocol.
   */
  get scheme() {
    return PageThumbs.scheme;
  },

  /**
   * The default port for this protocol (we don't support ports).
   */
  get defaultPort() {
    return -1;
  },

  /**
   * The flags specific to this protocol implementation.
   */
  get protocolFlags() {
    return Ci.nsIProtocolHandler.URI_DANGEROUS_TO_LOAD |
           Ci.nsIProtocolHandler.URI_IS_LOCAL_RESOURCE |
           Ci.nsIProtocolHandler.URI_NORELATIVE |
           Ci.nsIProtocolHandler.URI_NOAUTH;
  },

  /**
   * Creates a new URI object that is suitable for loading by this protocol.
   * @param aSpec The URI string in UTF8 encoding.
   * @param aOriginCharset The charset of the document from which the URI originated.
   * @return The newly created URI.
   */
  newURI: function Proto_newURI(aSpec, aOriginCharset) {
    let uri = Components.classesByID[SUBSTITUTING_URL_CID].createInstance(Ci.nsIURL);
    uri.spec = aSpec;
    return uri;
  },

  /**
   * Constructs a new channel from the given URI for this protocol handler.
   * @param aURI The URI for which to construct a channel.
   * @param aLoadInfo The Loadinfo which to use on the channel.
   * @return The newly created channel.
   */
  newChannel2: function Proto_newChannel2(aURI, aLoadInfo) {
    let {file} = aURI.QueryInterface(Ci.nsIFileURL);
    let fileuri = Services.io.newFileURI(file);
    let channel = Services.io.newChannelFromURIWithLoadInfo(fileuri, aLoadInfo);
    channel.originalURI = aURI;
    return channel;
  },

  newChannel: function Proto_newChannel(aURI) {
    return this.newChannel2(aURI, null);
  },

  /**
   * Decides whether to allow a blacklisted port.
   * @return Always false, we'll never allow ports.
   */
  allowPort: () => false,

  // nsISubstitutingProtocolHandler methods

  /*
   * Substituting the scheme and host isn't enough, we also transform the path.
   * So declare no-op implementations for (get|set|has)Substitution methods and
   * do all the work in resolveURI.
   */

  setSubstitution(root, baseURI) {},

  getSubstitution(root) {
    throw Cr.NS_ERROR_NOT_AVAILABLE;
  },

  hasSubstitution(root) {
    return false;
  },

  resolveURI(resURI) {
    let {url} = parseURI(resURI);
    let path = PageThumbsStorage.getFilePathForURL(url);
    return OS.Path.toFileURI(path);
  },

  // xpcom machinery
  classID: Components.ID("{5a4ae9b5-f475-48ae-9dce-0b4c1d347884}"),
  QueryInterface: XPCOMUtils.generateQI([Ci.nsIProtocolHandler,
                                         Ci.nsISubstitutingProtocolHandler])
};

this.NSGetFactory = XPCOMUtils.generateNSGetFactory([Protocol]);

/**
 * Parses a given URI and extracts all parameters relevant to this protocol.
 * @param aURI The URI to parse.
 * @return The parsed parameters.
 */
function parseURI(aURI) {
  if (aURI.host != PageThumbs.staticHost)
    throw Cr.NS_ERROR_NOT_AVAILABLE;

  let {query} = aURI.QueryInterface(Ci.nsIURL);
  let params = {};

  query.split("&").forEach(function (aParam) {
    let [key, value] = aParam.split("=").map(decodeURIComponent);
    params[key.toLowerCase()] = value;
  });

  return params;
}