|
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 } |