toolkit/components/places/nsAnnoProtocolHandler.cpp

branch
TOR_BUG_9701
changeset 15
b8a032363ba2
equal deleted inserted replaced
-1:000000000000 0:b390bd3468c6
1 //* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5
6 /**
7 * Implementation of moz-anno: URLs for accessing favicons. The urls are sent
8 * to the favicon service. If the favicon service doesn't have the
9 * data, a stream containing the default favicon will be returned.
10 *
11 * The reference to annotations ("moz-anno") is a leftover from previous
12 * iterations of this component. As of now the moz-anno protocol is independent
13 * of annotations.
14 */
15
16 #include "nsAnnoProtocolHandler.h"
17 #include "nsFaviconService.h"
18 #include "nsIChannel.h"
19 #include "nsIInputStreamChannel.h"
20 #include "nsILoadGroup.h"
21 #include "nsIStandardURL.h"
22 #include "nsIStringStream.h"
23 #include "nsISupportsUtils.h"
24 #include "nsIURI.h"
25 #include "nsNetUtil.h"
26 #include "nsServiceManagerUtils.h"
27 #include "nsStringStream.h"
28 #include "mozilla/storage.h"
29 #include "nsIPipe.h"
30 #include "Helpers.h"
31
32 using namespace mozilla;
33 using namespace mozilla::places;
34
35 ////////////////////////////////////////////////////////////////////////////////
36 //// Global Functions
37
38 /**
39 * Creates a channel to obtain the default favicon.
40 */
41 static
42 nsresult
43 GetDefaultIcon(nsIChannel **aChannel)
44 {
45 nsCOMPtr<nsIURI> defaultIconURI;
46 nsresult rv = NS_NewURI(getter_AddRefs(defaultIconURI),
47 NS_LITERAL_CSTRING(FAVICON_DEFAULT_URL));
48 NS_ENSURE_SUCCESS(rv, rv);
49 return NS_NewChannel(aChannel, defaultIconURI);
50 }
51
52 ////////////////////////////////////////////////////////////////////////////////
53 //// faviconAsyncLoader
54
55 namespace {
56
57 /**
58 * An instance of this class is passed to the favicon service as the callback
59 * for getting favicon data from the database. We'll get this data back in
60 * HandleResult, and on HandleCompletion, we'll close our output stream which
61 * will close the original channel for the favicon request.
62 *
63 * However, if an error occurs at any point, we do not set mReturnDefaultIcon to
64 * false, so we will open up another channel to get the default favicon, and
65 * pass that along to our output stream in HandleCompletion. If anything
66 * happens at that point, the world must be against us, so we return nothing.
67 */
68 class faviconAsyncLoader : public AsyncStatementCallback
69 , public nsIRequestObserver
70 {
71 public:
72 NS_DECL_ISUPPORTS_INHERITED
73
74 faviconAsyncLoader(nsIChannel *aChannel, nsIOutputStream *aOutputStream) :
75 mChannel(aChannel)
76 , mOutputStream(aOutputStream)
77 , mReturnDefaultIcon(true)
78 {
79 NS_ASSERTION(aChannel,
80 "Not providing a channel will result in crashes!");
81 NS_ASSERTION(aOutputStream,
82 "Not providing an output stream will result in crashes!");
83 }
84
85 //////////////////////////////////////////////////////////////////////////////
86 //// mozIStorageStatementCallback
87
88 NS_IMETHOD HandleResult(mozIStorageResultSet *aResultSet)
89 {
90 // We will only get one row back in total, so we do not need to loop.
91 nsCOMPtr<mozIStorageRow> row;
92 nsresult rv = aResultSet->GetNextRow(getter_AddRefs(row));
93 NS_ENSURE_SUCCESS(rv, rv);
94
95 // We do not allow favicons without a MIME type, so we'll return the default
96 // icon.
97 nsAutoCString mimeType;
98 (void)row->GetUTF8String(1, mimeType);
99 NS_ENSURE_FALSE(mimeType.IsEmpty(), NS_OK);
100
101 // Set our mimeType now that we know it.
102 rv = mChannel->SetContentType(mimeType);
103 NS_ENSURE_SUCCESS(rv, rv);
104
105 // Obtain the binary blob that contains our favicon data.
106 uint8_t *favicon;
107 uint32_t size = 0;
108 rv = row->GetBlob(0, &size, &favicon);
109 NS_ENSURE_SUCCESS(rv, rv);
110
111 uint32_t totalWritten = 0;
112 do {
113 uint32_t bytesWritten;
114 rv = mOutputStream->Write(
115 &(reinterpret_cast<const char *>(favicon)[totalWritten]),
116 size - totalWritten,
117 &bytesWritten
118 );
119 if (NS_FAILED(rv) || !bytesWritten)
120 break;
121 totalWritten += bytesWritten;
122 } while (size != totalWritten);
123 NS_ASSERTION(NS_FAILED(rv) || size == totalWritten,
124 "Failed to write all of our data out to the stream!");
125
126 // Free our favicon array.
127 NS_Free(favicon);
128
129 // Handle an error to write if it occurred, but only after we've freed our
130 // favicon.
131 NS_ENSURE_SUCCESS(rv, rv);
132
133 // At this point, we should have written out all of our data to our stream.
134 // HandleCompletion will close the output stream, so we are done here.
135 mReturnDefaultIcon = false;
136 return NS_OK;
137 }
138
139 NS_IMETHOD HandleCompletion(uint16_t aReason)
140 {
141 if (!mReturnDefaultIcon)
142 return mOutputStream->Close();
143
144 // We need to return our default icon, so we'll open up a new channel to get
145 // that data, and push it to our output stream. If at any point we get an
146 // error, we can't do anything, so we'll just close our output stream.
147 nsCOMPtr<nsIStreamListener> listener;
148 nsresult rv = NS_NewSimpleStreamListener(getter_AddRefs(listener),
149 mOutputStream, this);
150 NS_ENSURE_SUCCESS(rv, mOutputStream->Close());
151
152 nsCOMPtr<nsIChannel> newChannel;
153 rv = GetDefaultIcon(getter_AddRefs(newChannel));
154 NS_ENSURE_SUCCESS(rv, mOutputStream->Close());
155
156 rv = newChannel->AsyncOpen(listener, nullptr);
157 NS_ENSURE_SUCCESS(rv, mOutputStream->Close());
158
159 return NS_OK;
160 }
161
162 //////////////////////////////////////////////////////////////////////////////
163 //// nsIRequestObserver
164
165 NS_IMETHOD OnStartRequest(nsIRequest *, nsISupports *)
166 {
167 return NS_OK;
168 }
169
170 NS_IMETHOD OnStopRequest(nsIRequest *, nsISupports *, nsresult aStatusCode)
171 {
172 // We always need to close our output stream, regardless of the status code.
173 (void)mOutputStream->Close();
174
175 // But, we'll warn about it not being successful if it wasn't.
176 NS_WARN_IF_FALSE(NS_SUCCEEDED(aStatusCode),
177 "Got an error when trying to load our default favicon!");
178
179 return NS_OK;
180 }
181
182 private:
183 nsCOMPtr<nsIChannel> mChannel;
184 nsCOMPtr<nsIOutputStream> mOutputStream;
185 bool mReturnDefaultIcon;
186 };
187
188 NS_IMPL_ISUPPORTS_INHERITED(
189 faviconAsyncLoader,
190 AsyncStatementCallback,
191 nsIRequestObserver
192 )
193
194 } // anonymous namespace
195
196 ////////////////////////////////////////////////////////////////////////////////
197 //// nsAnnoProtocolHandler
198
199 NS_IMPL_ISUPPORTS(nsAnnoProtocolHandler, nsIProtocolHandler)
200
201 // nsAnnoProtocolHandler::GetScheme
202
203 NS_IMETHODIMP
204 nsAnnoProtocolHandler::GetScheme(nsACString& aScheme)
205 {
206 aScheme.AssignLiteral("moz-anno");
207 return NS_OK;
208 }
209
210
211 // nsAnnoProtocolHandler::GetDefaultPort
212 //
213 // There is no default port for annotation URLs
214
215 NS_IMETHODIMP
216 nsAnnoProtocolHandler::GetDefaultPort(int32_t *aDefaultPort)
217 {
218 *aDefaultPort = -1;
219 return NS_OK;
220 }
221
222
223 // nsAnnoProtocolHandler::GetProtocolFlags
224
225 NS_IMETHODIMP
226 nsAnnoProtocolHandler::GetProtocolFlags(uint32_t *aProtocolFlags)
227 {
228 *aProtocolFlags = (URI_NORELATIVE | URI_NOAUTH | URI_DANGEROUS_TO_LOAD |
229 URI_IS_LOCAL_RESOURCE);
230 return NS_OK;
231 }
232
233
234 // nsAnnoProtocolHandler::NewURI
235
236 NS_IMETHODIMP
237 nsAnnoProtocolHandler::NewURI(const nsACString& aSpec,
238 const char *aOriginCharset,
239 nsIURI *aBaseURI, nsIURI **_retval)
240 {
241 nsCOMPtr <nsIURI> uri = do_CreateInstance(NS_SIMPLEURI_CONTRACTID);
242 if (!uri)
243 return NS_ERROR_OUT_OF_MEMORY;
244 nsresult rv = uri->SetSpec(aSpec);
245 NS_ENSURE_SUCCESS(rv, rv);
246
247 *_retval = nullptr;
248 uri.swap(*_retval);
249 return NS_OK;
250 }
251
252
253 // nsAnnoProtocolHandler::NewChannel
254 //
255
256 NS_IMETHODIMP
257 nsAnnoProtocolHandler::NewChannel(nsIURI *aURI, nsIChannel **_retval)
258 {
259 NS_ENSURE_ARG_POINTER(aURI);
260
261 // annotation info
262 nsCOMPtr<nsIURI> annoURI;
263 nsAutoCString annoName;
264 nsresult rv = ParseAnnoURI(aURI, getter_AddRefs(annoURI), annoName);
265 NS_ENSURE_SUCCESS(rv, rv);
266
267 // Only favicon annotation are supported.
268 if (!annoName.EqualsLiteral(FAVICON_ANNOTATION_NAME))
269 return NS_ERROR_INVALID_ARG;
270
271 return NewFaviconChannel(aURI, annoURI, _retval);
272 }
273
274
275 // nsAnnoProtocolHandler::AllowPort
276 //
277 // Don't override any bans on bad ports.
278
279 NS_IMETHODIMP
280 nsAnnoProtocolHandler::AllowPort(int32_t port, const char *scheme,
281 bool *_retval)
282 {
283 *_retval = false;
284 return NS_OK;
285 }
286
287
288 // nsAnnoProtocolHandler::ParseAnnoURI
289 //
290 // Splits an annotation URL into its URI and name parts
291
292 nsresult
293 nsAnnoProtocolHandler::ParseAnnoURI(nsIURI* aURI,
294 nsIURI** aResultURI, nsCString& aName)
295 {
296 nsresult rv;
297 nsAutoCString path;
298 rv = aURI->GetPath(path);
299 NS_ENSURE_SUCCESS(rv, rv);
300
301 int32_t firstColon = path.FindChar(':');
302 if (firstColon <= 0)
303 return NS_ERROR_MALFORMED_URI;
304
305 rv = NS_NewURI(aResultURI, Substring(path, firstColon + 1));
306 NS_ENSURE_SUCCESS(rv, rv);
307
308 aName = Substring(path, 0, firstColon);
309 return NS_OK;
310 }
311
312 nsresult
313 nsAnnoProtocolHandler::NewFaviconChannel(nsIURI *aURI, nsIURI *aAnnotationURI,
314 nsIChannel **_channel)
315 {
316 // Create our pipe. This will give us our input stream and output stream
317 // that will be written to when we get data from the database.
318 nsCOMPtr<nsIInputStream> inputStream;
319 nsCOMPtr<nsIOutputStream> outputStream;
320 nsresult rv = NS_NewPipe(getter_AddRefs(inputStream),
321 getter_AddRefs(outputStream),
322 MAX_FAVICON_SIZE, MAX_FAVICON_SIZE, true,
323 true);
324 NS_ENSURE_SUCCESS(rv, GetDefaultIcon(_channel));
325
326 // Create our channel. We'll call SetContentType with the right type when
327 // we know what it actually is.
328 nsCOMPtr<nsIChannel> channel;
329 rv = NS_NewInputStreamChannel(getter_AddRefs(channel), aURI, inputStream,
330 EmptyCString());
331 NS_ENSURE_SUCCESS(rv, GetDefaultIcon(_channel));
332
333 // Now we go ahead and get our data asynchronously for the favicon.
334 nsCOMPtr<mozIStorageStatementCallback> callback =
335 new faviconAsyncLoader(channel, outputStream);
336 NS_ENSURE_TRUE(callback, GetDefaultIcon(_channel));
337 nsFaviconService* faviconService = nsFaviconService::GetFaviconService();
338 NS_ENSURE_TRUE(faviconService, GetDefaultIcon(_channel));
339
340 rv = faviconService->GetFaviconDataAsync(aAnnotationURI, callback);
341 NS_ENSURE_SUCCESS(rv, GetDefaultIcon(_channel));
342
343 channel.forget(_channel);
344 return NS_OK;
345 }

mercurial