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