1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/toolkit/components/places/nsAnnoProtocolHandler.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,345 @@ 1.4 +//* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.8 + 1.9 +/** 1.10 + * Implementation of moz-anno: URLs for accessing favicons. The urls are sent 1.11 + * to the favicon service. If the favicon service doesn't have the 1.12 + * data, a stream containing the default favicon will be returned. 1.13 + * 1.14 + * The reference to annotations ("moz-anno") is a leftover from previous 1.15 + * iterations of this component. As of now the moz-anno protocol is independent 1.16 + * of annotations. 1.17 + */ 1.18 + 1.19 +#include "nsAnnoProtocolHandler.h" 1.20 +#include "nsFaviconService.h" 1.21 +#include "nsIChannel.h" 1.22 +#include "nsIInputStreamChannel.h" 1.23 +#include "nsILoadGroup.h" 1.24 +#include "nsIStandardURL.h" 1.25 +#include "nsIStringStream.h" 1.26 +#include "nsISupportsUtils.h" 1.27 +#include "nsIURI.h" 1.28 +#include "nsNetUtil.h" 1.29 +#include "nsServiceManagerUtils.h" 1.30 +#include "nsStringStream.h" 1.31 +#include "mozilla/storage.h" 1.32 +#include "nsIPipe.h" 1.33 +#include "Helpers.h" 1.34 + 1.35 +using namespace mozilla; 1.36 +using namespace mozilla::places; 1.37 + 1.38 +//////////////////////////////////////////////////////////////////////////////// 1.39 +//// Global Functions 1.40 + 1.41 +/** 1.42 + * Creates a channel to obtain the default favicon. 1.43 + */ 1.44 +static 1.45 +nsresult 1.46 +GetDefaultIcon(nsIChannel **aChannel) 1.47 +{ 1.48 + nsCOMPtr<nsIURI> defaultIconURI; 1.49 + nsresult rv = NS_NewURI(getter_AddRefs(defaultIconURI), 1.50 + NS_LITERAL_CSTRING(FAVICON_DEFAULT_URL)); 1.51 + NS_ENSURE_SUCCESS(rv, rv); 1.52 + return NS_NewChannel(aChannel, defaultIconURI); 1.53 +} 1.54 + 1.55 +//////////////////////////////////////////////////////////////////////////////// 1.56 +//// faviconAsyncLoader 1.57 + 1.58 +namespace { 1.59 + 1.60 +/** 1.61 + * An instance of this class is passed to the favicon service as the callback 1.62 + * for getting favicon data from the database. We'll get this data back in 1.63 + * HandleResult, and on HandleCompletion, we'll close our output stream which 1.64 + * will close the original channel for the favicon request. 1.65 + * 1.66 + * However, if an error occurs at any point, we do not set mReturnDefaultIcon to 1.67 + * false, so we will open up another channel to get the default favicon, and 1.68 + * pass that along to our output stream in HandleCompletion. If anything 1.69 + * happens at that point, the world must be against us, so we return nothing. 1.70 + */ 1.71 +class faviconAsyncLoader : public AsyncStatementCallback 1.72 + , public nsIRequestObserver 1.73 +{ 1.74 +public: 1.75 + NS_DECL_ISUPPORTS_INHERITED 1.76 + 1.77 + faviconAsyncLoader(nsIChannel *aChannel, nsIOutputStream *aOutputStream) : 1.78 + mChannel(aChannel) 1.79 + , mOutputStream(aOutputStream) 1.80 + , mReturnDefaultIcon(true) 1.81 + { 1.82 + NS_ASSERTION(aChannel, 1.83 + "Not providing a channel will result in crashes!"); 1.84 + NS_ASSERTION(aOutputStream, 1.85 + "Not providing an output stream will result in crashes!"); 1.86 + } 1.87 + 1.88 + ////////////////////////////////////////////////////////////////////////////// 1.89 + //// mozIStorageStatementCallback 1.90 + 1.91 + NS_IMETHOD HandleResult(mozIStorageResultSet *aResultSet) 1.92 + { 1.93 + // We will only get one row back in total, so we do not need to loop. 1.94 + nsCOMPtr<mozIStorageRow> row; 1.95 + nsresult rv = aResultSet->GetNextRow(getter_AddRefs(row)); 1.96 + NS_ENSURE_SUCCESS(rv, rv); 1.97 + 1.98 + // We do not allow favicons without a MIME type, so we'll return the default 1.99 + // icon. 1.100 + nsAutoCString mimeType; 1.101 + (void)row->GetUTF8String(1, mimeType); 1.102 + NS_ENSURE_FALSE(mimeType.IsEmpty(), NS_OK); 1.103 + 1.104 + // Set our mimeType now that we know it. 1.105 + rv = mChannel->SetContentType(mimeType); 1.106 + NS_ENSURE_SUCCESS(rv, rv); 1.107 + 1.108 + // Obtain the binary blob that contains our favicon data. 1.109 + uint8_t *favicon; 1.110 + uint32_t size = 0; 1.111 + rv = row->GetBlob(0, &size, &favicon); 1.112 + NS_ENSURE_SUCCESS(rv, rv); 1.113 + 1.114 + uint32_t totalWritten = 0; 1.115 + do { 1.116 + uint32_t bytesWritten; 1.117 + rv = mOutputStream->Write( 1.118 + &(reinterpret_cast<const char *>(favicon)[totalWritten]), 1.119 + size - totalWritten, 1.120 + &bytesWritten 1.121 + ); 1.122 + if (NS_FAILED(rv) || !bytesWritten) 1.123 + break; 1.124 + totalWritten += bytesWritten; 1.125 + } while (size != totalWritten); 1.126 + NS_ASSERTION(NS_FAILED(rv) || size == totalWritten, 1.127 + "Failed to write all of our data out to the stream!"); 1.128 + 1.129 + // Free our favicon array. 1.130 + NS_Free(favicon); 1.131 + 1.132 + // Handle an error to write if it occurred, but only after we've freed our 1.133 + // favicon. 1.134 + NS_ENSURE_SUCCESS(rv, rv); 1.135 + 1.136 + // At this point, we should have written out all of our data to our stream. 1.137 + // HandleCompletion will close the output stream, so we are done here. 1.138 + mReturnDefaultIcon = false; 1.139 + return NS_OK; 1.140 + } 1.141 + 1.142 + NS_IMETHOD HandleCompletion(uint16_t aReason) 1.143 + { 1.144 + if (!mReturnDefaultIcon) 1.145 + return mOutputStream->Close(); 1.146 + 1.147 + // We need to return our default icon, so we'll open up a new channel to get 1.148 + // that data, and push it to our output stream. If at any point we get an 1.149 + // error, we can't do anything, so we'll just close our output stream. 1.150 + nsCOMPtr<nsIStreamListener> listener; 1.151 + nsresult rv = NS_NewSimpleStreamListener(getter_AddRefs(listener), 1.152 + mOutputStream, this); 1.153 + NS_ENSURE_SUCCESS(rv, mOutputStream->Close()); 1.154 + 1.155 + nsCOMPtr<nsIChannel> newChannel; 1.156 + rv = GetDefaultIcon(getter_AddRefs(newChannel)); 1.157 + NS_ENSURE_SUCCESS(rv, mOutputStream->Close()); 1.158 + 1.159 + rv = newChannel->AsyncOpen(listener, nullptr); 1.160 + NS_ENSURE_SUCCESS(rv, mOutputStream->Close()); 1.161 + 1.162 + return NS_OK; 1.163 + } 1.164 + 1.165 + ////////////////////////////////////////////////////////////////////////////// 1.166 + //// nsIRequestObserver 1.167 + 1.168 + NS_IMETHOD OnStartRequest(nsIRequest *, nsISupports *) 1.169 + { 1.170 + return NS_OK; 1.171 + } 1.172 + 1.173 + NS_IMETHOD OnStopRequest(nsIRequest *, nsISupports *, nsresult aStatusCode) 1.174 + { 1.175 + // We always need to close our output stream, regardless of the status code. 1.176 + (void)mOutputStream->Close(); 1.177 + 1.178 + // But, we'll warn about it not being successful if it wasn't. 1.179 + NS_WARN_IF_FALSE(NS_SUCCEEDED(aStatusCode), 1.180 + "Got an error when trying to load our default favicon!"); 1.181 + 1.182 + return NS_OK; 1.183 + } 1.184 + 1.185 +private: 1.186 + nsCOMPtr<nsIChannel> mChannel; 1.187 + nsCOMPtr<nsIOutputStream> mOutputStream; 1.188 + bool mReturnDefaultIcon; 1.189 +}; 1.190 + 1.191 +NS_IMPL_ISUPPORTS_INHERITED( 1.192 + faviconAsyncLoader, 1.193 + AsyncStatementCallback, 1.194 + nsIRequestObserver 1.195 +) 1.196 + 1.197 +} // anonymous namespace 1.198 + 1.199 +//////////////////////////////////////////////////////////////////////////////// 1.200 +//// nsAnnoProtocolHandler 1.201 + 1.202 +NS_IMPL_ISUPPORTS(nsAnnoProtocolHandler, nsIProtocolHandler) 1.203 + 1.204 +// nsAnnoProtocolHandler::GetScheme 1.205 + 1.206 +NS_IMETHODIMP 1.207 +nsAnnoProtocolHandler::GetScheme(nsACString& aScheme) 1.208 +{ 1.209 + aScheme.AssignLiteral("moz-anno"); 1.210 + return NS_OK; 1.211 +} 1.212 + 1.213 + 1.214 +// nsAnnoProtocolHandler::GetDefaultPort 1.215 +// 1.216 +// There is no default port for annotation URLs 1.217 + 1.218 +NS_IMETHODIMP 1.219 +nsAnnoProtocolHandler::GetDefaultPort(int32_t *aDefaultPort) 1.220 +{ 1.221 + *aDefaultPort = -1; 1.222 + return NS_OK; 1.223 +} 1.224 + 1.225 + 1.226 +// nsAnnoProtocolHandler::GetProtocolFlags 1.227 + 1.228 +NS_IMETHODIMP 1.229 +nsAnnoProtocolHandler::GetProtocolFlags(uint32_t *aProtocolFlags) 1.230 +{ 1.231 + *aProtocolFlags = (URI_NORELATIVE | URI_NOAUTH | URI_DANGEROUS_TO_LOAD | 1.232 + URI_IS_LOCAL_RESOURCE); 1.233 + return NS_OK; 1.234 +} 1.235 + 1.236 + 1.237 +// nsAnnoProtocolHandler::NewURI 1.238 + 1.239 +NS_IMETHODIMP 1.240 +nsAnnoProtocolHandler::NewURI(const nsACString& aSpec, 1.241 + const char *aOriginCharset, 1.242 + nsIURI *aBaseURI, nsIURI **_retval) 1.243 +{ 1.244 + nsCOMPtr <nsIURI> uri = do_CreateInstance(NS_SIMPLEURI_CONTRACTID); 1.245 + if (!uri) 1.246 + return NS_ERROR_OUT_OF_MEMORY; 1.247 + nsresult rv = uri->SetSpec(aSpec); 1.248 + NS_ENSURE_SUCCESS(rv, rv); 1.249 + 1.250 + *_retval = nullptr; 1.251 + uri.swap(*_retval); 1.252 + return NS_OK; 1.253 +} 1.254 + 1.255 + 1.256 +// nsAnnoProtocolHandler::NewChannel 1.257 +// 1.258 + 1.259 +NS_IMETHODIMP 1.260 +nsAnnoProtocolHandler::NewChannel(nsIURI *aURI, nsIChannel **_retval) 1.261 +{ 1.262 + NS_ENSURE_ARG_POINTER(aURI); 1.263 + 1.264 + // annotation info 1.265 + nsCOMPtr<nsIURI> annoURI; 1.266 + nsAutoCString annoName; 1.267 + nsresult rv = ParseAnnoURI(aURI, getter_AddRefs(annoURI), annoName); 1.268 + NS_ENSURE_SUCCESS(rv, rv); 1.269 + 1.270 + // Only favicon annotation are supported. 1.271 + if (!annoName.EqualsLiteral(FAVICON_ANNOTATION_NAME)) 1.272 + return NS_ERROR_INVALID_ARG; 1.273 + 1.274 + return NewFaviconChannel(aURI, annoURI, _retval); 1.275 +} 1.276 + 1.277 + 1.278 +// nsAnnoProtocolHandler::AllowPort 1.279 +// 1.280 +// Don't override any bans on bad ports. 1.281 + 1.282 +NS_IMETHODIMP 1.283 +nsAnnoProtocolHandler::AllowPort(int32_t port, const char *scheme, 1.284 + bool *_retval) 1.285 +{ 1.286 + *_retval = false; 1.287 + return NS_OK; 1.288 +} 1.289 + 1.290 + 1.291 +// nsAnnoProtocolHandler::ParseAnnoURI 1.292 +// 1.293 +// Splits an annotation URL into its URI and name parts 1.294 + 1.295 +nsresult 1.296 +nsAnnoProtocolHandler::ParseAnnoURI(nsIURI* aURI, 1.297 + nsIURI** aResultURI, nsCString& aName) 1.298 +{ 1.299 + nsresult rv; 1.300 + nsAutoCString path; 1.301 + rv = aURI->GetPath(path); 1.302 + NS_ENSURE_SUCCESS(rv, rv); 1.303 + 1.304 + int32_t firstColon = path.FindChar(':'); 1.305 + if (firstColon <= 0) 1.306 + return NS_ERROR_MALFORMED_URI; 1.307 + 1.308 + rv = NS_NewURI(aResultURI, Substring(path, firstColon + 1)); 1.309 + NS_ENSURE_SUCCESS(rv, rv); 1.310 + 1.311 + aName = Substring(path, 0, firstColon); 1.312 + return NS_OK; 1.313 +} 1.314 + 1.315 +nsresult 1.316 +nsAnnoProtocolHandler::NewFaviconChannel(nsIURI *aURI, nsIURI *aAnnotationURI, 1.317 + nsIChannel **_channel) 1.318 +{ 1.319 + // Create our pipe. This will give us our input stream and output stream 1.320 + // that will be written to when we get data from the database. 1.321 + nsCOMPtr<nsIInputStream> inputStream; 1.322 + nsCOMPtr<nsIOutputStream> outputStream; 1.323 + nsresult rv = NS_NewPipe(getter_AddRefs(inputStream), 1.324 + getter_AddRefs(outputStream), 1.325 + MAX_FAVICON_SIZE, MAX_FAVICON_SIZE, true, 1.326 + true); 1.327 + NS_ENSURE_SUCCESS(rv, GetDefaultIcon(_channel)); 1.328 + 1.329 + // Create our channel. We'll call SetContentType with the right type when 1.330 + // we know what it actually is. 1.331 + nsCOMPtr<nsIChannel> channel; 1.332 + rv = NS_NewInputStreamChannel(getter_AddRefs(channel), aURI, inputStream, 1.333 + EmptyCString()); 1.334 + NS_ENSURE_SUCCESS(rv, GetDefaultIcon(_channel)); 1.335 + 1.336 + // Now we go ahead and get our data asynchronously for the favicon. 1.337 + nsCOMPtr<mozIStorageStatementCallback> callback = 1.338 + new faviconAsyncLoader(channel, outputStream); 1.339 + NS_ENSURE_TRUE(callback, GetDefaultIcon(_channel)); 1.340 + nsFaviconService* faviconService = nsFaviconService::GetFaviconService(); 1.341 + NS_ENSURE_TRUE(faviconService, GetDefaultIcon(_channel)); 1.342 + 1.343 + rv = faviconService->GetFaviconDataAsync(aAnnotationURI, callback); 1.344 + NS_ENSURE_SUCCESS(rv, GetDefaultIcon(_channel)); 1.345 + 1.346 + channel.forget(_channel); 1.347 + return NS_OK; 1.348 +}