/src/mozilla-central/toolkit/components/places/nsAnnoProtocolHandler.cpp
Line | Count | Source (jump to first uncovered line) |
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 "nsIInputStream.h" |
24 | | #include "nsISupportsUtils.h" |
25 | | #include "nsIURI.h" |
26 | | #include "nsIURIMutator.h" |
27 | | #include "nsNetUtil.h" |
28 | | #include "nsIOutputStream.h" |
29 | | #include "nsInputStreamPump.h" |
30 | | #include "nsContentUtils.h" |
31 | | #include "nsServiceManagerUtils.h" |
32 | | #include "nsStringStream.h" |
33 | | #include "SimpleChannel.h" |
34 | | #include "mozilla/ScopeExit.h" |
35 | | #include "mozilla/storage.h" |
36 | | #include "Helpers.h" |
37 | | #include "FaviconHelpers.h" |
38 | | |
39 | | using namespace mozilla; |
40 | | using namespace mozilla::places; |
41 | | |
42 | | //////////////////////////////////////////////////////////////////////////////// |
43 | | //// Global Functions |
44 | | |
45 | | /** |
46 | | * Creates a channel to obtain the default favicon. |
47 | | */ |
48 | | static |
49 | | nsresult |
50 | | GetDefaultIcon(nsIChannel *aOriginalChannel, nsIChannel **aChannel) |
51 | 0 | { |
52 | 0 | nsCOMPtr<nsIURI> defaultIconURI; |
53 | 0 | nsresult rv = NS_NewURI(getter_AddRefs(defaultIconURI), |
54 | 0 | NS_LITERAL_CSTRING(FAVICON_DEFAULT_URL)); |
55 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
56 | 0 | nsCOMPtr<nsILoadInfo> loadInfo = aOriginalChannel->GetLoadInfo(); |
57 | 0 | rv = NS_NewChannelInternal(aChannel, defaultIconURI, loadInfo); |
58 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
59 | 0 | Unused << (*aChannel)->SetContentType(NS_LITERAL_CSTRING(FAVICON_DEFAULT_MIMETYPE)); |
60 | 0 | Unused << aOriginalChannel->SetContentType(NS_LITERAL_CSTRING(FAVICON_DEFAULT_MIMETYPE)); |
61 | 0 | return NS_OK; |
62 | 0 | } |
63 | | |
64 | | //////////////////////////////////////////////////////////////////////////////// |
65 | | //// faviconAsyncLoader |
66 | | |
67 | | namespace { |
68 | | |
69 | | /** |
70 | | * An instance of this class is passed to the favicon service as the callback |
71 | | * for getting favicon data from the database. We'll get this data back in |
72 | | * HandleResult, and on HandleCompletion, we'll close our output stream which |
73 | | * will close the original channel for the favicon request. |
74 | | * |
75 | | * However, if an error occurs at any point and we don't have mData, we will |
76 | | * just fallback to the default favicon. If anything happens at that point, the |
77 | | * world must be against us, so we can do nothing. |
78 | | */ |
79 | | class faviconAsyncLoader : public AsyncStatementCallback |
80 | | { |
81 | | public: |
82 | | faviconAsyncLoader(nsIChannel *aChannel, nsIStreamListener *aListener, |
83 | | uint16_t aPreferredSize) |
84 | | : mChannel(aChannel) |
85 | | , mListener(aListener) |
86 | | , mPreferredSize(aPreferredSize) |
87 | 0 | { |
88 | 0 | MOZ_ASSERT(aChannel, "Not providing a channel will result in crashes!"); |
89 | 0 | MOZ_ASSERT(aListener, "Not providing a stream listener will result in crashes!"); |
90 | 0 | MOZ_ASSERT(aChannel, "Not providing a channel!"); |
91 | 0 | } |
92 | | |
93 | | ////////////////////////////////////////////////////////////////////////////// |
94 | | //// mozIStorageStatementCallback |
95 | | |
96 | | NS_IMETHOD HandleResult(mozIStorageResultSet *aResultSet) override |
97 | 0 | { |
98 | 0 | nsCOMPtr<mozIStorageRow> row; |
99 | 0 | while (NS_SUCCEEDED(aResultSet->GetNextRow(getter_AddRefs(row))) && row) { |
100 | 0 | int32_t width; |
101 | 0 | nsresult rv = row->GetInt32(1, &width); |
102 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
103 | 0 |
|
104 | 0 | // Check if we already found an image >= than the preferred size, |
105 | 0 | // otherwise keep examining the next results. |
106 | 0 | if (width < mPreferredSize && !mData.IsEmpty()) { |
107 | 0 | return NS_OK; |
108 | 0 | } |
109 | 0 | |
110 | 0 | // Eventually override the default mimeType for svg. |
111 | 0 | if (width == UINT16_MAX) { |
112 | 0 | rv = mChannel->SetContentType(NS_LITERAL_CSTRING(SVG_MIME_TYPE)); |
113 | 0 | } else { |
114 | 0 | rv = mChannel->SetContentType(NS_LITERAL_CSTRING(PNG_MIME_TYPE)); |
115 | 0 | } |
116 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
117 | 0 |
|
118 | 0 | // Obtain the binary blob that contains our favicon data. |
119 | 0 | uint8_t *data; |
120 | 0 | uint32_t dataLen; |
121 | 0 | rv = row->GetBlob(0, &dataLen, &data); |
122 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
123 | 0 | mData.Adopt(TO_CHARBUFFER(data), dataLen); |
124 | 0 | } |
125 | 0 |
|
126 | 0 | return NS_OK; |
127 | 0 | } |
128 | | |
129 | | NS_IMETHOD HandleCompletion(uint16_t aReason) override |
130 | 0 | { |
131 | 0 | MOZ_DIAGNOSTIC_ASSERT(mListener); |
132 | 0 | NS_ENSURE_TRUE(mListener, NS_ERROR_UNEXPECTED); |
133 | 0 |
|
134 | 0 | nsresult rv; |
135 | 0 | // Ensure we'll break possible cycles with the listener. |
136 | 0 | auto cleanup = MakeScopeExit([&] () { |
137 | 0 | mListener = nullptr; |
138 | 0 | }); |
139 | 0 |
|
140 | 0 | nsCOMPtr<nsILoadInfo> loadInfo = mChannel->GetLoadInfo(); |
141 | 0 | nsCOMPtr<nsIEventTarget> target = |
142 | 0 | nsContentUtils::GetEventTargetByLoadInfo(loadInfo, TaskCategory::Other); |
143 | 0 | if (!mData.IsEmpty()) { |
144 | 0 | nsCOMPtr<nsIInputStream> stream; |
145 | 0 | rv = NS_NewCStringInputStream(getter_AddRefs(stream), mData); |
146 | 0 | MOZ_ASSERT(NS_SUCCEEDED(rv)); |
147 | 0 | if (NS_SUCCEEDED(rv)) { |
148 | 0 | RefPtr<nsInputStreamPump> pump; |
149 | 0 | rv = nsInputStreamPump::Create(getter_AddRefs(pump), stream, 0, 0, true, |
150 | 0 | target); |
151 | 0 | MOZ_ASSERT(NS_SUCCEEDED(rv)); |
152 | 0 | if (NS_SUCCEEDED(rv)) { |
153 | 0 | return pump->AsyncRead(mListener, nullptr); |
154 | 0 | } |
155 | 0 | } |
156 | 0 | } |
157 | 0 | |
158 | 0 | // Fallback to the default favicon. |
159 | 0 | // we should pass the loadInfo of the original channel along |
160 | 0 | // to the new channel. Note that mChannel can not be null, |
161 | 0 | // constructor checks that. |
162 | 0 | nsCOMPtr<nsIChannel> newChannel; |
163 | 0 | rv = GetDefaultIcon(mChannel, getter_AddRefs(newChannel)); |
164 | 0 | if (NS_FAILED(rv)) { |
165 | 0 | mListener->OnStartRequest(mChannel, nullptr); |
166 | 0 | mListener->OnStopRequest(mChannel, nullptr, rv); |
167 | 0 | return rv; |
168 | 0 | } |
169 | 0 | return newChannel->AsyncOpen2(mListener); |
170 | 0 | } |
171 | | |
172 | | protected: |
173 | 0 | virtual ~faviconAsyncLoader() {} |
174 | | |
175 | | private: |
176 | | nsCOMPtr<nsIChannel> mChannel; |
177 | | nsCOMPtr<nsIStreamListener> mListener; |
178 | | nsCString mData; |
179 | | uint16_t mPreferredSize; |
180 | | }; |
181 | | |
182 | | } // namespace |
183 | | |
184 | | //////////////////////////////////////////////////////////////////////////////// |
185 | | //// nsAnnoProtocolHandler |
186 | | |
187 | | NS_IMPL_ISUPPORTS(nsAnnoProtocolHandler, nsIProtocolHandler) |
188 | | |
189 | | // nsAnnoProtocolHandler::GetScheme |
190 | | |
191 | | NS_IMETHODIMP |
192 | | nsAnnoProtocolHandler::GetScheme(nsACString& aScheme) |
193 | 0 | { |
194 | 0 | aScheme.AssignLiteral("moz-anno"); |
195 | 0 | return NS_OK; |
196 | 0 | } |
197 | | |
198 | | |
199 | | // nsAnnoProtocolHandler::GetDefaultPort |
200 | | // |
201 | | // There is no default port for annotation URLs |
202 | | |
203 | | NS_IMETHODIMP |
204 | | nsAnnoProtocolHandler::GetDefaultPort(int32_t *aDefaultPort) |
205 | 0 | { |
206 | 0 | *aDefaultPort = -1; |
207 | 0 | return NS_OK; |
208 | 0 | } |
209 | | |
210 | | |
211 | | // nsAnnoProtocolHandler::GetProtocolFlags |
212 | | |
213 | | NS_IMETHODIMP |
214 | | nsAnnoProtocolHandler::GetProtocolFlags(uint32_t *aProtocolFlags) |
215 | 0 | { |
216 | 0 | *aProtocolFlags = (URI_NORELATIVE | URI_NOAUTH | URI_DANGEROUS_TO_LOAD | |
217 | 0 | URI_IS_LOCAL_RESOURCE); |
218 | 0 | return NS_OK; |
219 | 0 | } |
220 | | |
221 | | |
222 | | // nsAnnoProtocolHandler::NewURI |
223 | | |
224 | | NS_IMETHODIMP |
225 | | nsAnnoProtocolHandler::NewURI(const nsACString& aSpec, |
226 | | const char *aOriginCharset, |
227 | | nsIURI *aBaseURI, nsIURI **_retval) |
228 | 0 | { |
229 | 0 | *_retval = nullptr; |
230 | 0 | return NS_MutateURI(NS_SIMPLEURIMUTATOR_CONTRACTID) |
231 | 0 | .SetSpec(aSpec) |
232 | 0 | .Finalize(_retval); |
233 | 0 | } |
234 | | |
235 | | |
236 | | // nsAnnoProtocolHandler::NewChannel |
237 | | // |
238 | | |
239 | | NS_IMETHODIMP |
240 | | nsAnnoProtocolHandler::NewChannel2(nsIURI* aURI, |
241 | | nsILoadInfo* aLoadInfo, |
242 | | nsIChannel** _retval) |
243 | 0 | { |
244 | 0 | NS_ENSURE_ARG_POINTER(aURI); |
245 | 0 |
|
246 | 0 | // annotation info |
247 | 0 | nsCOMPtr<nsIURI> annoURI; |
248 | 0 | nsAutoCString annoName; |
249 | 0 | nsresult rv = ParseAnnoURI(aURI, getter_AddRefs(annoURI), annoName); |
250 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
251 | 0 |
|
252 | 0 | // Only favicon annotation are supported. |
253 | 0 | if (!annoName.EqualsLiteral(FAVICON_ANNOTATION_NAME)) |
254 | 0 | return NS_ERROR_INVALID_ARG; |
255 | 0 | |
256 | 0 | return NewFaviconChannel(aURI, annoURI, aLoadInfo, _retval); |
257 | 0 | } |
258 | | |
259 | | NS_IMETHODIMP |
260 | | nsAnnoProtocolHandler::NewChannel(nsIURI *aURI, nsIChannel **_retval) |
261 | 0 | { |
262 | 0 | return NewChannel2(aURI, nullptr, _retval); |
263 | 0 | } |
264 | | |
265 | | |
266 | | // nsAnnoProtocolHandler::AllowPort |
267 | | // |
268 | | // Don't override any bans on bad ports. |
269 | | |
270 | | NS_IMETHODIMP |
271 | | nsAnnoProtocolHandler::AllowPort(int32_t port, const char *scheme, |
272 | | bool *_retval) |
273 | 0 | { |
274 | 0 | *_retval = false; |
275 | 0 | return NS_OK; |
276 | 0 | } |
277 | | |
278 | | |
279 | | // nsAnnoProtocolHandler::ParseAnnoURI |
280 | | // |
281 | | // Splits an annotation URL into its URI and name parts |
282 | | |
283 | | nsresult |
284 | | nsAnnoProtocolHandler::ParseAnnoURI(nsIURI* aURI, |
285 | | nsIURI** aResultURI, nsCString& aName) |
286 | 0 | { |
287 | 0 | nsresult rv; |
288 | 0 | nsAutoCString path; |
289 | 0 | rv = aURI->GetPathQueryRef(path); |
290 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
291 | 0 |
|
292 | 0 | int32_t firstColon = path.FindChar(':'); |
293 | 0 | if (firstColon <= 0) |
294 | 0 | return NS_ERROR_MALFORMED_URI; |
295 | 0 | |
296 | 0 | rv = NS_NewURI(aResultURI, Substring(path, firstColon + 1)); |
297 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
298 | 0 |
|
299 | 0 | aName = Substring(path, 0, firstColon); |
300 | 0 | return NS_OK; |
301 | 0 | } |
302 | | |
303 | | nsresult |
304 | | nsAnnoProtocolHandler::NewFaviconChannel(nsIURI *aURI, nsIURI *aAnnotationURI, |
305 | | nsILoadInfo* aLoadInfo, nsIChannel **_channel) |
306 | 0 | { |
307 | 0 | // Create our channel. We'll call SetContentType with the right type when |
308 | 0 | // we know what it actually is. |
309 | 0 | nsCOMPtr<nsIChannel> channel = NS_NewSimpleChannel( |
310 | 0 | aURI, aLoadInfo, aAnnotationURI, |
311 | 0 | [] (nsIStreamListener* listener, nsIChannel* channel, nsIURI* annotationURI) { |
312 | 0 | auto fallback = [&] () -> RequestOrReason { |
313 | 0 | nsCOMPtr<nsIChannel> chan; |
314 | 0 | nsresult rv = GetDefaultIcon(channel, getter_AddRefs(chan)); |
315 | 0 | NS_ENSURE_SUCCESS(rv, Err(rv)); |
316 | 0 |
|
317 | 0 | rv = chan->AsyncOpen2(listener); |
318 | 0 | NS_ENSURE_SUCCESS(rv, Err(rv)); |
319 | 0 |
|
320 | 0 | return RequestOrReason(chan.forget()); |
321 | 0 | }; |
322 | 0 |
|
323 | 0 | // Now we go ahead and get our data asynchronously for the favicon. |
324 | 0 | // Ignore the ref part of the URI before querying the database because |
325 | 0 | // we may have added a size fragment for rendering purposes. |
326 | 0 | nsFaviconService* faviconService = nsFaviconService::GetFaviconService(); |
327 | 0 | nsAutoCString faviconSpec; |
328 | 0 | nsresult rv = annotationURI->GetSpecIgnoringRef(faviconSpec); |
329 | 0 | // Any failures fallback to the default icon channel. |
330 | 0 | if (NS_FAILED(rv) || !faviconService) |
331 | 0 | return fallback(); |
332 | 0 | |
333 | 0 | uint16_t preferredSize = UINT16_MAX; |
334 | 0 | MOZ_ALWAYS_SUCCEEDS(faviconService->PreferredSizeFromURI(annotationURI, &preferredSize)); |
335 | 0 | nsCOMPtr<mozIStorageStatementCallback> callback = |
336 | 0 | new faviconAsyncLoader(channel, listener, preferredSize); |
337 | 0 | if (!callback) |
338 | 0 | return fallback(); |
339 | 0 | |
340 | 0 | rv = faviconService->GetFaviconDataAsync(faviconSpec, callback); |
341 | 0 | if (NS_FAILED(rv)) |
342 | 0 | return fallback(); |
343 | 0 | |
344 | 0 | return RequestOrReason(nullptr); |
345 | 0 | }); |
346 | 0 | NS_ENSURE_TRUE(channel, NS_ERROR_OUT_OF_MEMORY); |
347 | 0 |
|
348 | 0 | channel.forget(_channel); |
349 | 0 | return NS_OK; |
350 | 0 | } |