Source code

Revision control

Other Tools

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 nsresult GetDefaultIcon(nsIChannel* aOriginalChannel,
49
nsIChannel** aChannel) {
50
nsCOMPtr<nsIURI> defaultIconURI;
51
nsresult rv = NS_NewURI(getter_AddRefs(defaultIconURI),
52
NS_LITERAL_CSTRING(FAVICON_DEFAULT_URL));
53
NS_ENSURE_SUCCESS(rv, rv);
54
nsCOMPtr<nsILoadInfo> loadInfo = aOriginalChannel->LoadInfo();
55
rv = NS_NewChannelInternal(aChannel, defaultIconURI, loadInfo);
56
NS_ENSURE_SUCCESS(rv, rv);
57
Unused << (*aChannel)->SetContentType(
58
NS_LITERAL_CSTRING(FAVICON_DEFAULT_MIMETYPE));
59
Unused << aOriginalChannel->SetContentType(
60
NS_LITERAL_CSTRING(FAVICON_DEFAULT_MIMETYPE));
61
return NS_OK;
62
}
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
public:
81
faviconAsyncLoader(nsIChannel* aChannel, nsIStreamListener* aListener,
82
uint16_t aPreferredSize)
83
: mChannel(aChannel),
84
mListener(aListener),
85
mPreferredSize(aPreferredSize) {
86
MOZ_ASSERT(aChannel, "Not providing a channel will result in crashes!");
87
MOZ_ASSERT(aListener,
88
"Not providing a stream listener will result in crashes!");
89
MOZ_ASSERT(aChannel, "Not providing a channel!");
90
}
91
92
//////////////////////////////////////////////////////////////////////////////
93
//// mozIStorageStatementCallback
94
95
NS_IMETHOD HandleResult(mozIStorageResultSet* aResultSet) override {
96
nsCOMPtr<mozIStorageRow> row;
97
while (NS_SUCCEEDED(aResultSet->GetNextRow(getter_AddRefs(row))) && row) {
98
int32_t width;
99
nsresult rv = row->GetInt32(1, &width);
100
NS_ENSURE_SUCCESS(rv, rv);
101
102
// Check if we already found an image >= than the preferred size,
103
// otherwise keep examining the next results.
104
if (width < mPreferredSize && !mData.IsEmpty()) {
105
return NS_OK;
106
}
107
108
// Eventually override the default mimeType for svg.
109
if (width == UINT16_MAX) {
110
rv = mChannel->SetContentType(NS_LITERAL_CSTRING(SVG_MIME_TYPE));
111
} else {
112
rv = mChannel->SetContentType(NS_LITERAL_CSTRING(PNG_MIME_TYPE));
113
}
114
NS_ENSURE_SUCCESS(rv, rv);
115
116
// Obtain the binary blob that contains our favicon data.
117
uint8_t* data;
118
uint32_t dataLen;
119
rv = row->GetBlob(0, &dataLen, &data);
120
NS_ENSURE_SUCCESS(rv, rv);
121
mData.Adopt(TO_CHARBUFFER(data), dataLen);
122
}
123
124
return NS_OK;
125
}
126
127
NS_IMETHOD HandleCompletion(uint16_t aReason) override {
128
MOZ_DIAGNOSTIC_ASSERT(mListener);
129
NS_ENSURE_TRUE(mListener, NS_ERROR_UNEXPECTED);
130
131
nsresult rv;
132
// Ensure we'll break possible cycles with the listener.
133
auto cleanup = MakeScopeExit([&]() { mListener = nullptr; });
134
135
nsCOMPtr<nsILoadInfo> loadInfo = mChannel->LoadInfo();
136
nsCOMPtr<nsIEventTarget> target =
137
nsContentUtils::GetEventTargetByLoadInfo(loadInfo, TaskCategory::Other);
138
if (!mData.IsEmpty()) {
139
nsCOMPtr<nsIInputStream> stream;
140
rv = NS_NewCStringInputStream(getter_AddRefs(stream), mData);
141
MOZ_ASSERT(NS_SUCCEEDED(rv));
142
if (NS_SUCCEEDED(rv)) {
143
RefPtr<nsInputStreamPump> pump;
144
rv = nsInputStreamPump::Create(getter_AddRefs(pump), stream, 0, 0, true,
145
target);
146
MOZ_ASSERT(NS_SUCCEEDED(rv));
147
if (NS_SUCCEEDED(rv)) {
148
return pump->AsyncRead(mListener, nullptr);
149
}
150
}
151
}
152
153
// Fallback to the default favicon.
154
// we should pass the loadInfo of the original channel along
155
// to the new channel. Note that mChannel can not be null,
156
// constructor checks that.
157
nsCOMPtr<nsIChannel> newChannel;
158
rv = GetDefaultIcon(mChannel, getter_AddRefs(newChannel));
159
if (NS_FAILED(rv)) {
160
mListener->OnStartRequest(mChannel);
161
mListener->OnStopRequest(mChannel, rv);
162
return rv;
163
}
164
return newChannel->AsyncOpen(mListener);
165
}
166
167
protected:
168
virtual ~faviconAsyncLoader() {}
169
170
private:
171
nsCOMPtr<nsIChannel> mChannel;
172
nsCOMPtr<nsIStreamListener> mListener;
173
nsCString mData;
174
uint16_t mPreferredSize;
175
};
176
177
} // namespace
178
179
////////////////////////////////////////////////////////////////////////////////
180
//// nsAnnoProtocolHandler
181
182
NS_IMPL_ISUPPORTS(nsAnnoProtocolHandler, nsIProtocolHandler)
183
184
// nsAnnoProtocolHandler::GetScheme
185
186
NS_IMETHODIMP
187
nsAnnoProtocolHandler::GetScheme(nsACString& aScheme) {
188
aScheme.AssignLiteral("moz-anno");
189
return NS_OK;
190
}
191
192
// nsAnnoProtocolHandler::GetDefaultPort
193
//
194
// There is no default port for annotation URLs
195
196
NS_IMETHODIMP
197
nsAnnoProtocolHandler::GetDefaultPort(int32_t* aDefaultPort) {
198
*aDefaultPort = -1;
199
return NS_OK;
200
}
201
202
// nsAnnoProtocolHandler::GetProtocolFlags
203
204
NS_IMETHODIMP
205
nsAnnoProtocolHandler::GetProtocolFlags(uint32_t* aProtocolFlags) {
206
*aProtocolFlags = (URI_NORELATIVE | URI_NOAUTH | URI_DANGEROUS_TO_LOAD |
207
URI_IS_LOCAL_RESOURCE);
208
return NS_OK;
209
}
210
211
// nsAnnoProtocolHandler::NewChannel
212
//
213
214
NS_IMETHODIMP
215
nsAnnoProtocolHandler::NewChannel(nsIURI* aURI, nsILoadInfo* aLoadInfo,
216
nsIChannel** _retval) {
217
NS_ENSURE_ARG_POINTER(aURI);
218
219
// annotation info
220
nsCOMPtr<nsIURI> annoURI;
221
nsAutoCString annoName;
222
nsresult rv = ParseAnnoURI(aURI, getter_AddRefs(annoURI), annoName);
223
NS_ENSURE_SUCCESS(rv, rv);
224
225
// Only favicon annotation are supported.
226
if (!annoName.EqualsLiteral(FAVICON_ANNOTATION_NAME))
227
return NS_ERROR_INVALID_ARG;
228
229
return NewFaviconChannel(aURI, annoURI, aLoadInfo, _retval);
230
}
231
232
// nsAnnoProtocolHandler::AllowPort
233
//
234
// Don't override any bans on bad ports.
235
236
NS_IMETHODIMP
237
nsAnnoProtocolHandler::AllowPort(int32_t port, const char* scheme,
238
bool* _retval) {
239
*_retval = false;
240
return NS_OK;
241
}
242
243
// nsAnnoProtocolHandler::ParseAnnoURI
244
//
245
// Splits an annotation URL into its URI and name parts
246
247
nsresult nsAnnoProtocolHandler::ParseAnnoURI(nsIURI* aURI, nsIURI** aResultURI,
248
nsCString& aName) {
249
nsresult rv;
250
nsAutoCString path;
251
rv = aURI->GetPathQueryRef(path);
252
NS_ENSURE_SUCCESS(rv, rv);
253
254
int32_t firstColon = path.FindChar(':');
255
if (firstColon <= 0) return NS_ERROR_MALFORMED_URI;
256
257
rv = NS_NewURI(aResultURI, Substring(path, firstColon + 1));
258
NS_ENSURE_SUCCESS(rv, rv);
259
260
aName = Substring(path, 0, firstColon);
261
return NS_OK;
262
}
263
264
nsresult nsAnnoProtocolHandler::NewFaviconChannel(nsIURI* aURI,
265
nsIURI* aAnnotationURI,
266
nsILoadInfo* aLoadInfo,
267
nsIChannel** _channel) {
268
// Create our channel. We'll call SetContentType with the right type when
269
// we know what it actually is.
270
nsCOMPtr<nsIChannel> channel = NS_NewSimpleChannel(
271
aURI, aLoadInfo, aAnnotationURI,
272
[](nsIStreamListener* listener, nsIChannel* channel,
273
nsIURI* annotationURI) {
274
auto fallback = [&]() -> RequestOrReason {
275
nsCOMPtr<nsIChannel> chan;
276
nsresult rv = GetDefaultIcon(channel, getter_AddRefs(chan));
277
NS_ENSURE_SUCCESS(rv, Err(rv));
278
279
rv = chan->AsyncOpen(listener);
280
NS_ENSURE_SUCCESS(rv, Err(rv));
281
282
return RequestOrReason(chan.forget());
283
};
284
285
// Now we go ahead and get our data asynchronously for the favicon.
286
// Ignore the ref part of the URI before querying the database because
287
// we may have added a size fragment for rendering purposes.
288
nsFaviconService* faviconService =
289
nsFaviconService::GetFaviconService();
290
nsAutoCString faviconSpec;
291
nsresult rv = annotationURI->GetSpecIgnoringRef(faviconSpec);
292
// Any failures fallback to the default icon channel.
293
if (NS_FAILED(rv) || !faviconService) return fallback();
294
295
uint16_t preferredSize = UINT16_MAX;
296
MOZ_ALWAYS_SUCCEEDS(faviconService->PreferredSizeFromURI(
297
annotationURI, &preferredSize));
298
nsCOMPtr<mozIStorageStatementCallback> callback =
299
new faviconAsyncLoader(channel, listener, preferredSize);
300
if (!callback) return fallback();
301
302
rv = faviconService->GetFaviconDataAsync(faviconSpec, callback);
303
if (NS_FAILED(rv)) return fallback();
304
305
return RequestOrReason(nullptr);
306
});
307
NS_ENSURE_TRUE(channel, NS_ERROR_OUT_OF_MEMORY);
308
309
channel.forget(_channel);
310
return NS_OK;
311
}