1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/image/decoders/icon/mac/nsIconChannelCocoa.mm Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,458 @@ 1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 4 -*- 1.5 + * 1.6 + * This Source Code Form is subject to the terms of the Mozilla Public 1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.9 + 1.10 +#include "nsIconChannel.h" 1.11 +#include "mozilla/Endian.h" 1.12 +#include "nsIIconURI.h" 1.13 +#include "nsIServiceManager.h" 1.14 +#include "nsIInterfaceRequestor.h" 1.15 +#include "nsIInterfaceRequestorUtils.h" 1.16 +#include "nsXPIDLString.h" 1.17 +#include "nsMimeTypes.h" 1.18 +#include "nsMemory.h" 1.19 +#include "nsIStringStream.h" 1.20 +#include "nsIURL.h" 1.21 +#include "nsNetUtil.h" 1.22 +#include "nsIMIMEService.h" 1.23 +#include "nsCExternalHandlerService.h" 1.24 +#include "nsILocalFileMac.h" 1.25 +#include "nsIFileURL.h" 1.26 +#include "nsTArray.h" 1.27 +#include "nsObjCExceptions.h" 1.28 + 1.29 +#include <Cocoa/Cocoa.h> 1.30 + 1.31 +// nsIconChannel methods 1.32 +nsIconChannel::nsIconChannel() 1.33 +{ 1.34 +} 1.35 + 1.36 +nsIconChannel::~nsIconChannel() 1.37 +{} 1.38 + 1.39 +NS_IMPL_ISUPPORTS(nsIconChannel, 1.40 + nsIChannel, 1.41 + nsIRequest, 1.42 + nsIRequestObserver, 1.43 + nsIStreamListener) 1.44 + 1.45 +nsresult nsIconChannel::Init(nsIURI* uri) 1.46 +{ 1.47 + NS_ASSERTION(uri, "no uri"); 1.48 + mUrl = uri; 1.49 + mOriginalURI = uri; 1.50 + nsresult rv; 1.51 + mPump = do_CreateInstance(NS_INPUTSTREAMPUMP_CONTRACTID, &rv); 1.52 + return rv; 1.53 +} 1.54 + 1.55 +//////////////////////////////////////////////////////////////////////////////// 1.56 +// nsIRequest methods: 1.57 + 1.58 +NS_IMETHODIMP nsIconChannel::GetName(nsACString &result) 1.59 +{ 1.60 + return mUrl->GetSpec(result); 1.61 +} 1.62 + 1.63 +NS_IMETHODIMP nsIconChannel::IsPending(bool *result) 1.64 +{ 1.65 + return mPump->IsPending(result); 1.66 +} 1.67 + 1.68 +NS_IMETHODIMP nsIconChannel::GetStatus(nsresult *status) 1.69 +{ 1.70 + return mPump->GetStatus(status); 1.71 +} 1.72 + 1.73 +NS_IMETHODIMP nsIconChannel::Cancel(nsresult status) 1.74 +{ 1.75 + return mPump->Cancel(status); 1.76 +} 1.77 + 1.78 +NS_IMETHODIMP nsIconChannel::Suspend(void) 1.79 +{ 1.80 + return mPump->Suspend(); 1.81 +} 1.82 + 1.83 +NS_IMETHODIMP nsIconChannel::Resume(void) 1.84 +{ 1.85 + return mPump->Resume(); 1.86 +} 1.87 + 1.88 +// nsIRequestObserver methods 1.89 +NS_IMETHODIMP nsIconChannel::OnStartRequest(nsIRequest* aRequest, nsISupports* aContext) 1.90 +{ 1.91 + if (mListener) 1.92 + return mListener->OnStartRequest(this, aContext); 1.93 + return NS_OK; 1.94 +} 1.95 + 1.96 +NS_IMETHODIMP nsIconChannel::OnStopRequest(nsIRequest* aRequest, nsISupports* aContext, nsresult aStatus) 1.97 +{ 1.98 + if (mListener) { 1.99 + mListener->OnStopRequest(this, aContext, aStatus); 1.100 + mListener = nullptr; 1.101 + } 1.102 + 1.103 + // Remove from load group 1.104 + if (mLoadGroup) 1.105 + mLoadGroup->RemoveRequest(this, nullptr, aStatus); 1.106 + 1.107 + return NS_OK; 1.108 +} 1.109 + 1.110 +// nsIStreamListener methods 1.111 +NS_IMETHODIMP nsIconChannel::OnDataAvailable(nsIRequest* aRequest, 1.112 + nsISupports* aContext, 1.113 + nsIInputStream* aStream, 1.114 + uint64_t aOffset, 1.115 + uint32_t aCount) 1.116 +{ 1.117 + if (mListener) 1.118 + return mListener->OnDataAvailable(this, aContext, aStream, aOffset, aCount); 1.119 + return NS_OK; 1.120 +} 1.121 + 1.122 +//////////////////////////////////////////////////////////////////////////////// 1.123 +// nsIChannel methods: 1.124 + 1.125 +NS_IMETHODIMP nsIconChannel::GetOriginalURI(nsIURI* *aURI) 1.126 +{ 1.127 + *aURI = mOriginalURI; 1.128 + NS_ADDREF(*aURI); 1.129 + return NS_OK; 1.130 +} 1.131 + 1.132 +NS_IMETHODIMP nsIconChannel::SetOriginalURI(nsIURI* aURI) 1.133 +{ 1.134 + NS_ENSURE_ARG_POINTER(aURI); 1.135 + mOriginalURI = aURI; 1.136 + return NS_OK; 1.137 +} 1.138 + 1.139 +NS_IMETHODIMP nsIconChannel::GetURI(nsIURI* *aURI) 1.140 +{ 1.141 + *aURI = mUrl; 1.142 + NS_IF_ADDREF(*aURI); 1.143 + return NS_OK; 1.144 +} 1.145 + 1.146 +NS_IMETHODIMP 1.147 +nsIconChannel::Open(nsIInputStream **_retval) 1.148 +{ 1.149 + return MakeInputStream(_retval, false); 1.150 +} 1.151 + 1.152 +nsresult nsIconChannel::ExtractIconInfoFromUrl(nsIFile ** aLocalFile, uint32_t * aDesiredImageSize, nsACString &aContentType, nsACString &aFileExtension) 1.153 +{ 1.154 + nsresult rv = NS_OK; 1.155 + nsCOMPtr<nsIMozIconURI> iconURI (do_QueryInterface(mUrl, &rv)); 1.156 + NS_ENSURE_SUCCESS(rv, rv); 1.157 + 1.158 + iconURI->GetImageSize(aDesiredImageSize); 1.159 + iconURI->GetContentType(aContentType); 1.160 + iconURI->GetFileExtension(aFileExtension); 1.161 + 1.162 + nsCOMPtr<nsIURL> url; 1.163 + rv = iconURI->GetIconURL(getter_AddRefs(url)); 1.164 + if (NS_FAILED(rv) || !url) return NS_OK; 1.165 + 1.166 + nsCOMPtr<nsIFileURL> fileURL = do_QueryInterface(url, &rv); 1.167 + if (NS_FAILED(rv) || !fileURL) return NS_OK; 1.168 + 1.169 + nsCOMPtr<nsIFile> file; 1.170 + rv = fileURL->GetFile(getter_AddRefs(file)); 1.171 + if (NS_FAILED(rv) || !file) return NS_OK; 1.172 + 1.173 + nsCOMPtr<nsILocalFileMac> localFileMac (do_QueryInterface(file, &rv)); 1.174 + if (NS_FAILED(rv) || !localFileMac) return NS_OK; 1.175 + 1.176 + *aLocalFile = file; 1.177 + NS_IF_ADDREF(*aLocalFile); 1.178 + 1.179 + return NS_OK; 1.180 +} 1.181 + 1.182 +NS_IMETHODIMP nsIconChannel::AsyncOpen(nsIStreamListener *aListener, nsISupports *ctxt) 1.183 +{ 1.184 + nsCOMPtr<nsIInputStream> inStream; 1.185 + nsresult rv = MakeInputStream(getter_AddRefs(inStream), true); 1.186 + NS_ENSURE_SUCCESS(rv, rv); 1.187 + 1.188 + // Init our stream pump 1.189 + rv = mPump->Init(inStream, int64_t(-1), int64_t(-1), 0, 0, false); 1.190 + NS_ENSURE_SUCCESS(rv, rv); 1.191 + 1.192 + rv = mPump->AsyncRead(this, ctxt); 1.193 + if (NS_SUCCEEDED(rv)) { 1.194 + // Store our real listener 1.195 + mListener = aListener; 1.196 + // Add ourself to the load group, if available 1.197 + if (mLoadGroup) 1.198 + mLoadGroup->AddRequest(this, nullptr); 1.199 + } 1.200 + 1.201 + return rv; 1.202 +} 1.203 + 1.204 +nsresult nsIconChannel::MakeInputStream(nsIInputStream** _retval, bool nonBlocking) 1.205 +{ 1.206 + NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT; 1.207 + 1.208 + nsXPIDLCString contentType; 1.209 + nsAutoCString fileExt; 1.210 + nsCOMPtr<nsIFile> fileloc; // file we want an icon for 1.211 + uint32_t desiredImageSize; 1.212 + nsresult rv = ExtractIconInfoFromUrl(getter_AddRefs(fileloc), &desiredImageSize, contentType, fileExt); 1.213 + NS_ENSURE_SUCCESS(rv, rv); 1.214 + 1.215 + bool fileExists = false; 1.216 + if (fileloc) { 1.217 + // ensure that we DO NOT resolve aliases, very important for file views 1.218 + fileloc->SetFollowLinks(false); 1.219 + fileloc->Exists(&fileExists); 1.220 + } 1.221 + 1.222 + NSImage* iconImage = nil; 1.223 + 1.224 + // first try to get the icon from the file if it exists 1.225 + if (fileExists) { 1.226 + nsCOMPtr<nsILocalFileMac> localFileMac(do_QueryInterface(fileloc, &rv)); 1.227 + NS_ENSURE_SUCCESS(rv, rv); 1.228 + 1.229 + CFURLRef macURL; 1.230 + if (NS_SUCCEEDED(localFileMac->GetCFURL(&macURL))) { 1.231 + iconImage = [[NSWorkspace sharedWorkspace] iconForFile:[(NSURL*)macURL path]]; 1.232 + ::CFRelease(macURL); 1.233 + } 1.234 + } 1.235 + 1.236 + // if we don't have an icon yet try to get one by extension 1.237 + if (!iconImage && !fileExt.IsEmpty()) { 1.238 + NSString* fileExtension = [NSString stringWithUTF8String:fileExt.get()]; 1.239 + iconImage = [[NSWorkspace sharedWorkspace] iconForFileType:fileExtension]; 1.240 + } 1.241 + 1.242 + // If we still don't have an icon, get the generic document icon. 1.243 + if (!iconImage) 1.244 + iconImage = [[NSWorkspace sharedWorkspace] iconForFileType:NSFileTypeUnknown]; 1.245 + 1.246 + if (!iconImage) 1.247 + return NS_ERROR_FAILURE; 1.248 + 1.249 + // we have an icon now, size it 1.250 + NSRect desiredSizeRect = NSMakeRect(0, 0, desiredImageSize, desiredImageSize); 1.251 + [iconImage setSize:desiredSizeRect.size]; 1.252 + 1.253 + [iconImage lockFocus]; 1.254 + NSBitmapImageRep* bitmapRep = [[[NSBitmapImageRep alloc] initWithFocusedViewRect:desiredSizeRect] autorelease]; 1.255 + [iconImage unlockFocus]; 1.256 + 1.257 + // we expect the following things to be true about our bitmapRep 1.258 + NS_ENSURE_TRUE(![bitmapRep isPlanar] && 1.259 + // Not necessarily: on a HiDPI-capable system, we'll get a 2x bitmap 1.260 + // (unsigned int)[bitmapRep bytesPerPlane] == desiredImageSize * desiredImageSize * 4 && 1.261 + [bitmapRep bitsPerPixel] == 32 && 1.262 + [bitmapRep samplesPerPixel] == 4 && 1.263 + [bitmapRep hasAlpha] == YES, 1.264 + NS_ERROR_UNEXPECTED); 1.265 + 1.266 + // check what size we actually got, and ensure it isn't too big to return 1.267 + uint32_t actualImageSize = [bitmapRep bytesPerRow] / 4; 1.268 + NS_ENSURE_TRUE(actualImageSize < 256, NS_ERROR_UNEXPECTED); 1.269 + 1.270 + // now we can validate the amount of data 1.271 + NS_ENSURE_TRUE((unsigned int)[bitmapRep bytesPerPlane] == actualImageSize * actualImageSize * 4, 1.272 + NS_ERROR_UNEXPECTED); 1.273 + 1.274 + // rgba, pre-multiplied data 1.275 + uint8_t* bitmapRepData = (uint8_t*)[bitmapRep bitmapData]; 1.276 + 1.277 + // create our buffer 1.278 + int32_t bufferCapacity = 2 + [bitmapRep bytesPerPlane]; 1.279 + nsAutoTArray<uint8_t, 3 + 16 * 16 * 5> iconBuffer; // initial size is for 16x16 1.280 + iconBuffer.SetLength(bufferCapacity); 1.281 + 1.282 + uint8_t* iconBufferPtr = iconBuffer.Elements(); 1.283 + 1.284 + // write header data into buffer 1.285 + *iconBufferPtr++ = actualImageSize; 1.286 + *iconBufferPtr++ = actualImageSize; 1.287 + 1.288 + uint32_t dataCount = [bitmapRep bytesPerPlane]; 1.289 + uint32_t index = 0; 1.290 + while (index < dataCount) { 1.291 + // get data from the bitmap 1.292 + uint8_t r = bitmapRepData[index++]; 1.293 + uint8_t g = bitmapRepData[index++]; 1.294 + uint8_t b = bitmapRepData[index++]; 1.295 + uint8_t a = bitmapRepData[index++]; 1.296 + 1.297 + // write data out to our buffer 1.298 + // non-cairo uses native image format, but the A channel is ignored. 1.299 + // cairo uses ARGB (highest to lowest bits) 1.300 +#if MOZ_LITTLE_ENDIAN 1.301 + *iconBufferPtr++ = b; 1.302 + *iconBufferPtr++ = g; 1.303 + *iconBufferPtr++ = r; 1.304 + *iconBufferPtr++ = a; 1.305 +#else 1.306 + *iconBufferPtr++ = a; 1.307 + *iconBufferPtr++ = r; 1.308 + *iconBufferPtr++ = g; 1.309 + *iconBufferPtr++ = b; 1.310 +#endif 1.311 + } 1.312 + 1.313 + NS_ASSERTION(iconBufferPtr == iconBuffer.Elements() + bufferCapacity, 1.314 + "buffer size miscalculation"); 1.315 + 1.316 + // Now, create a pipe and stuff our data into it 1.317 + nsCOMPtr<nsIInputStream> inStream; 1.318 + nsCOMPtr<nsIOutputStream> outStream; 1.319 + rv = NS_NewPipe(getter_AddRefs(inStream), getter_AddRefs(outStream), 1.320 + bufferCapacity, bufferCapacity, nonBlocking); 1.321 + 1.322 + if (NS_SUCCEEDED(rv)) { 1.323 + uint32_t written; 1.324 + rv = outStream->Write((char*)iconBuffer.Elements(), bufferCapacity, &written); 1.325 + if (NS_SUCCEEDED(rv)) 1.326 + NS_IF_ADDREF(*_retval = inStream); 1.327 + } 1.328 + 1.329 + // Drop notification callbacks to prevent cycles. 1.330 + mCallbacks = nullptr; 1.331 + 1.332 + return NS_OK; 1.333 + 1.334 + NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT; 1.335 +} 1.336 + 1.337 +NS_IMETHODIMP nsIconChannel::GetLoadFlags(uint32_t *aLoadAttributes) 1.338 +{ 1.339 + return mPump->GetLoadFlags(aLoadAttributes); 1.340 +} 1.341 + 1.342 +NS_IMETHODIMP nsIconChannel::SetLoadFlags(uint32_t aLoadAttributes) 1.343 +{ 1.344 + return mPump->SetLoadFlags(aLoadAttributes); 1.345 +} 1.346 + 1.347 +NS_IMETHODIMP nsIconChannel::GetContentType(nsACString &aContentType) 1.348 +{ 1.349 + aContentType.AssignLiteral(IMAGE_ICON_MS); 1.350 + return NS_OK; 1.351 +} 1.352 + 1.353 +NS_IMETHODIMP 1.354 +nsIconChannel::SetContentType(const nsACString &aContentType) 1.355 +{ 1.356 + //It doesn't make sense to set the content-type on this type 1.357 + // of channel... 1.358 + return NS_ERROR_FAILURE; 1.359 +} 1.360 + 1.361 +NS_IMETHODIMP nsIconChannel::GetContentCharset(nsACString &aContentCharset) 1.362 +{ 1.363 + aContentCharset.AssignLiteral(IMAGE_ICON_MS); 1.364 + return NS_OK; 1.365 +} 1.366 + 1.367 +NS_IMETHODIMP 1.368 +nsIconChannel::SetContentCharset(const nsACString &aContentCharset) 1.369 +{ 1.370 + //It doesn't make sense to set the content-type on this type 1.371 + // of channel... 1.372 + return NS_ERROR_FAILURE; 1.373 +} 1.374 + 1.375 +NS_IMETHODIMP 1.376 +nsIconChannel::GetContentDisposition(uint32_t *aContentDisposition) 1.377 +{ 1.378 + return NS_ERROR_NOT_AVAILABLE; 1.379 +} 1.380 + 1.381 +NS_IMETHODIMP 1.382 +nsIconChannel::SetContentDisposition(uint32_t aContentDisposition) 1.383 +{ 1.384 + return NS_ERROR_NOT_AVAILABLE; 1.385 +} 1.386 + 1.387 +NS_IMETHODIMP 1.388 +nsIconChannel::GetContentDispositionFilename(nsAString &aContentDispositionFilename) 1.389 +{ 1.390 + return NS_ERROR_NOT_AVAILABLE; 1.391 +} 1.392 + 1.393 +NS_IMETHODIMP 1.394 +nsIconChannel::SetContentDispositionFilename(const nsAString &aContentDispositionFilename) 1.395 +{ 1.396 + return NS_ERROR_NOT_AVAILABLE; 1.397 +} 1.398 + 1.399 +NS_IMETHODIMP 1.400 +nsIconChannel::GetContentDispositionHeader(nsACString &aContentDispositionHeader) 1.401 +{ 1.402 + return NS_ERROR_NOT_AVAILABLE; 1.403 +} 1.404 + 1.405 +NS_IMETHODIMP nsIconChannel::GetContentLength(int64_t *aContentLength) 1.406 +{ 1.407 + *aContentLength = mContentLength; 1.408 + return NS_OK; 1.409 +} 1.410 + 1.411 +NS_IMETHODIMP nsIconChannel::SetContentLength(int64_t aContentLength) 1.412 +{ 1.413 + NS_NOTREACHED("nsIconChannel::SetContentLength"); 1.414 + return NS_ERROR_NOT_IMPLEMENTED; 1.415 +} 1.416 + 1.417 +NS_IMETHODIMP nsIconChannel::GetLoadGroup(nsILoadGroup* *aLoadGroup) 1.418 +{ 1.419 + *aLoadGroup = mLoadGroup; 1.420 + NS_IF_ADDREF(*aLoadGroup); 1.421 + return NS_OK; 1.422 +} 1.423 + 1.424 +NS_IMETHODIMP nsIconChannel::SetLoadGroup(nsILoadGroup* aLoadGroup) 1.425 +{ 1.426 + mLoadGroup = aLoadGroup; 1.427 + return NS_OK; 1.428 +} 1.429 + 1.430 +NS_IMETHODIMP nsIconChannel::GetOwner(nsISupports* *aOwner) 1.431 +{ 1.432 + *aOwner = mOwner.get(); 1.433 + NS_IF_ADDREF(*aOwner); 1.434 + return NS_OK; 1.435 +} 1.436 + 1.437 +NS_IMETHODIMP nsIconChannel::SetOwner(nsISupports* aOwner) 1.438 +{ 1.439 + mOwner = aOwner; 1.440 + return NS_OK; 1.441 +} 1.442 + 1.443 +NS_IMETHODIMP nsIconChannel::GetNotificationCallbacks(nsIInterfaceRequestor* *aNotificationCallbacks) 1.444 +{ 1.445 + *aNotificationCallbacks = mCallbacks.get(); 1.446 + NS_IF_ADDREF(*aNotificationCallbacks); 1.447 + return NS_OK; 1.448 +} 1.449 + 1.450 +NS_IMETHODIMP nsIconChannel::SetNotificationCallbacks(nsIInterfaceRequestor* aNotificationCallbacks) 1.451 +{ 1.452 + mCallbacks = aNotificationCallbacks; 1.453 + return NS_OK; 1.454 +} 1.455 + 1.456 +NS_IMETHODIMP nsIconChannel::GetSecurityInfo(nsISupports * *aSecurityInfo) 1.457 +{ 1.458 + *aSecurityInfo = nullptr; 1.459 + return NS_OK; 1.460 +} 1.461 +