image/decoders/icon/mac/nsIconChannelCocoa.mm

Thu, 15 Jan 2015 15:59:08 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 15 Jan 2015 15:59:08 +0100
branch
TOR_BUG_9701
changeset 10
ac0c01689b40
permissions
-rw-r--r--

Implement a real Private Browsing Mode condition by changing the API/ABI;
This solves Tor bug #9701, complying with disk avoidance documented in
https://www.torproject.org/projects/torbrowser/design/#disk-avoidance.

     1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 4 -*-
     2  *
     3  * This Source Code Form is subject to the terms of the Mozilla Public
     4  * License, v. 2.0. If a copy of the MPL was not distributed with this
     5  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     7 #include "nsIconChannel.h"
     8 #include "mozilla/Endian.h"
     9 #include "nsIIconURI.h"
    10 #include "nsIServiceManager.h"
    11 #include "nsIInterfaceRequestor.h"
    12 #include "nsIInterfaceRequestorUtils.h"
    13 #include "nsXPIDLString.h"
    14 #include "nsMimeTypes.h"
    15 #include "nsMemory.h"
    16 #include "nsIStringStream.h"
    17 #include "nsIURL.h"
    18 #include "nsNetUtil.h"
    19 #include "nsIMIMEService.h"
    20 #include "nsCExternalHandlerService.h"
    21 #include "nsILocalFileMac.h"
    22 #include "nsIFileURL.h"
    23 #include "nsTArray.h"
    24 #include "nsObjCExceptions.h"
    26 #include <Cocoa/Cocoa.h>
    28 // nsIconChannel methods
    29 nsIconChannel::nsIconChannel()
    30 {
    31 }
    33 nsIconChannel::~nsIconChannel() 
    34 {}
    36 NS_IMPL_ISUPPORTS(nsIconChannel, 
    37                   nsIChannel, 
    38                   nsIRequest,
    39 			       nsIRequestObserver,
    40 			       nsIStreamListener)
    42 nsresult nsIconChannel::Init(nsIURI* uri)
    43 {
    44   NS_ASSERTION(uri, "no uri");
    45   mUrl = uri;
    46   mOriginalURI = uri;
    47   nsresult rv;
    48   mPump = do_CreateInstance(NS_INPUTSTREAMPUMP_CONTRACTID, &rv);
    49   return rv;
    50 }
    52 ////////////////////////////////////////////////////////////////////////////////
    53 // nsIRequest methods:
    55 NS_IMETHODIMP nsIconChannel::GetName(nsACString &result)
    56 {
    57   return mUrl->GetSpec(result);
    58 }
    60 NS_IMETHODIMP nsIconChannel::IsPending(bool *result)
    61 {
    62   return mPump->IsPending(result);
    63 }
    65 NS_IMETHODIMP nsIconChannel::GetStatus(nsresult *status)
    66 {
    67   return mPump->GetStatus(status);
    68 }
    70 NS_IMETHODIMP nsIconChannel::Cancel(nsresult status)
    71 {
    72   return mPump->Cancel(status);
    73 }
    75 NS_IMETHODIMP nsIconChannel::Suspend(void)
    76 {
    77   return mPump->Suspend();
    78 }
    80 NS_IMETHODIMP nsIconChannel::Resume(void)
    81 {
    82   return mPump->Resume();
    83 }
    85 // nsIRequestObserver methods
    86 NS_IMETHODIMP nsIconChannel::OnStartRequest(nsIRequest* aRequest, nsISupports* aContext)
    87 {
    88   if (mListener)
    89     return mListener->OnStartRequest(this, aContext);
    90   return NS_OK;
    91 }
    93 NS_IMETHODIMP nsIconChannel::OnStopRequest(nsIRequest* aRequest, nsISupports* aContext, nsresult aStatus)
    94 {
    95   if (mListener) {
    96     mListener->OnStopRequest(this, aContext, aStatus);
    97     mListener = nullptr;
    98   }
   100   // Remove from load group
   101   if (mLoadGroup)
   102     mLoadGroup->RemoveRequest(this, nullptr, aStatus);
   104   return NS_OK;
   105 }
   107 // nsIStreamListener methods
   108 NS_IMETHODIMP nsIconChannel::OnDataAvailable(nsIRequest* aRequest,
   109                                              nsISupports* aContext,
   110                                              nsIInputStream* aStream,
   111                                              uint64_t aOffset,
   112                                              uint32_t aCount)
   113 {
   114   if (mListener)
   115     return mListener->OnDataAvailable(this, aContext, aStream, aOffset, aCount);
   116   return NS_OK;
   117 }
   119 ////////////////////////////////////////////////////////////////////////////////
   120 // nsIChannel methods:
   122 NS_IMETHODIMP nsIconChannel::GetOriginalURI(nsIURI* *aURI)
   123 {
   124   *aURI = mOriginalURI;
   125   NS_ADDREF(*aURI);
   126   return NS_OK;
   127 }
   129 NS_IMETHODIMP nsIconChannel::SetOriginalURI(nsIURI* aURI)
   130 {
   131   NS_ENSURE_ARG_POINTER(aURI);
   132   mOriginalURI = aURI;
   133   return NS_OK;
   134 }
   136 NS_IMETHODIMP nsIconChannel::GetURI(nsIURI* *aURI)
   137 {
   138   *aURI = mUrl;
   139   NS_IF_ADDREF(*aURI);
   140   return NS_OK;
   141 }
   143 NS_IMETHODIMP
   144 nsIconChannel::Open(nsIInputStream **_retval)
   145 {
   146   return MakeInputStream(_retval, false);
   147 }
   149 nsresult nsIconChannel::ExtractIconInfoFromUrl(nsIFile ** aLocalFile, uint32_t * aDesiredImageSize, nsACString &aContentType, nsACString &aFileExtension)
   150 {
   151   nsresult rv = NS_OK;
   152   nsCOMPtr<nsIMozIconURI> iconURI (do_QueryInterface(mUrl, &rv));
   153   NS_ENSURE_SUCCESS(rv, rv);
   155   iconURI->GetImageSize(aDesiredImageSize);
   156   iconURI->GetContentType(aContentType);
   157   iconURI->GetFileExtension(aFileExtension);
   159   nsCOMPtr<nsIURL> url;
   160   rv = iconURI->GetIconURL(getter_AddRefs(url));
   161   if (NS_FAILED(rv) || !url) return NS_OK;
   163   nsCOMPtr<nsIFileURL> fileURL = do_QueryInterface(url, &rv);
   164   if (NS_FAILED(rv) || !fileURL) return NS_OK;
   166   nsCOMPtr<nsIFile> file;
   167   rv = fileURL->GetFile(getter_AddRefs(file));
   168   if (NS_FAILED(rv) || !file) return NS_OK;
   170   nsCOMPtr<nsILocalFileMac> localFileMac (do_QueryInterface(file, &rv));
   171   if (NS_FAILED(rv) || !localFileMac) return NS_OK;
   173   *aLocalFile = file;
   174   NS_IF_ADDREF(*aLocalFile);
   176   return NS_OK;
   177 }
   179 NS_IMETHODIMP nsIconChannel::AsyncOpen(nsIStreamListener *aListener, nsISupports *ctxt)
   180 {
   181   nsCOMPtr<nsIInputStream> inStream;
   182   nsresult rv = MakeInputStream(getter_AddRefs(inStream), true);
   183   NS_ENSURE_SUCCESS(rv, rv);
   185   // Init our stream pump
   186   rv = mPump->Init(inStream, int64_t(-1), int64_t(-1), 0, 0, false);
   187   NS_ENSURE_SUCCESS(rv, rv);
   189   rv = mPump->AsyncRead(this, ctxt);
   190   if (NS_SUCCEEDED(rv)) {
   191     // Store our real listener
   192     mListener = aListener;
   193     // Add ourself to the load group, if available
   194     if (mLoadGroup)
   195       mLoadGroup->AddRequest(this, nullptr);
   196   }
   198   return rv;
   199 }
   201 nsresult nsIconChannel::MakeInputStream(nsIInputStream** _retval, bool nonBlocking)
   202 {
   203   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
   205   nsXPIDLCString contentType;
   206   nsAutoCString fileExt;
   207   nsCOMPtr<nsIFile> fileloc; // file we want an icon for
   208   uint32_t desiredImageSize;
   209   nsresult rv = ExtractIconInfoFromUrl(getter_AddRefs(fileloc), &desiredImageSize, contentType, fileExt);
   210   NS_ENSURE_SUCCESS(rv, rv);
   212   bool fileExists = false;
   213   if (fileloc) {
   214     // ensure that we DO NOT resolve aliases, very important for file views
   215     fileloc->SetFollowLinks(false);
   216     fileloc->Exists(&fileExists);
   217   }
   219   NSImage* iconImage = nil;
   221   // first try to get the icon from the file if it exists
   222   if (fileExists) {
   223     nsCOMPtr<nsILocalFileMac> localFileMac(do_QueryInterface(fileloc, &rv));
   224     NS_ENSURE_SUCCESS(rv, rv);
   226     CFURLRef macURL;
   227     if (NS_SUCCEEDED(localFileMac->GetCFURL(&macURL))) {
   228       iconImage = [[NSWorkspace sharedWorkspace] iconForFile:[(NSURL*)macURL path]];
   229       ::CFRelease(macURL);
   230     }
   231   }
   233   // if we don't have an icon yet try to get one by extension
   234   if (!iconImage && !fileExt.IsEmpty()) {
   235     NSString* fileExtension = [NSString stringWithUTF8String:fileExt.get()];
   236     iconImage = [[NSWorkspace sharedWorkspace] iconForFileType:fileExtension];
   237   }
   239   // If we still don't have an icon, get the generic document icon.
   240   if (!iconImage)
   241     iconImage = [[NSWorkspace sharedWorkspace] iconForFileType:NSFileTypeUnknown];
   243   if (!iconImage)
   244     return NS_ERROR_FAILURE;
   246   // we have an icon now, size it
   247   NSRect desiredSizeRect = NSMakeRect(0, 0, desiredImageSize, desiredImageSize);
   248   [iconImage setSize:desiredSizeRect.size];
   250   [iconImage lockFocus];
   251   NSBitmapImageRep* bitmapRep = [[[NSBitmapImageRep alloc] initWithFocusedViewRect:desiredSizeRect] autorelease];
   252   [iconImage unlockFocus];
   254   // we expect the following things to be true about our bitmapRep
   255   NS_ENSURE_TRUE(![bitmapRep isPlanar] &&
   256                  // Not necessarily: on a HiDPI-capable system, we'll get a 2x bitmap
   257                  // (unsigned int)[bitmapRep bytesPerPlane] == desiredImageSize * desiredImageSize * 4 &&
   258                  [bitmapRep bitsPerPixel] == 32 &&
   259                  [bitmapRep samplesPerPixel] == 4 &&
   260                  [bitmapRep hasAlpha] == YES,
   261                  NS_ERROR_UNEXPECTED);
   263   // check what size we actually got, and ensure it isn't too big to return
   264   uint32_t actualImageSize = [bitmapRep bytesPerRow] / 4;
   265   NS_ENSURE_TRUE(actualImageSize < 256, NS_ERROR_UNEXPECTED);
   267   // now we can validate the amount of data
   268   NS_ENSURE_TRUE((unsigned int)[bitmapRep bytesPerPlane] == actualImageSize * actualImageSize * 4,
   269                  NS_ERROR_UNEXPECTED);
   271   // rgba, pre-multiplied data
   272   uint8_t* bitmapRepData = (uint8_t*)[bitmapRep bitmapData];
   274   // create our buffer
   275   int32_t bufferCapacity = 2 + [bitmapRep bytesPerPlane];
   276   nsAutoTArray<uint8_t, 3 + 16 * 16 * 5> iconBuffer; // initial size is for 16x16
   277   iconBuffer.SetLength(bufferCapacity);
   279   uint8_t* iconBufferPtr = iconBuffer.Elements();
   281   // write header data into buffer
   282   *iconBufferPtr++ = actualImageSize;
   283   *iconBufferPtr++ = actualImageSize;
   285   uint32_t dataCount = [bitmapRep bytesPerPlane];
   286   uint32_t index = 0;
   287   while (index < dataCount) {
   288     // get data from the bitmap
   289     uint8_t r = bitmapRepData[index++];
   290     uint8_t g = bitmapRepData[index++];
   291     uint8_t b = bitmapRepData[index++];
   292     uint8_t a = bitmapRepData[index++];
   294     // write data out to our buffer
   295     // non-cairo uses native image format, but the A channel is ignored.
   296     // cairo uses ARGB (highest to lowest bits)
   297 #if MOZ_LITTLE_ENDIAN
   298     *iconBufferPtr++ = b;
   299     *iconBufferPtr++ = g;
   300     *iconBufferPtr++ = r;
   301     *iconBufferPtr++ = a;
   302 #else
   303     *iconBufferPtr++ = a;
   304     *iconBufferPtr++ = r;
   305     *iconBufferPtr++ = g;
   306     *iconBufferPtr++ = b;
   307 #endif
   308   }
   310   NS_ASSERTION(iconBufferPtr == iconBuffer.Elements() + bufferCapacity,
   311                "buffer size miscalculation");
   313   // Now, create a pipe and stuff our data into it
   314   nsCOMPtr<nsIInputStream> inStream;
   315   nsCOMPtr<nsIOutputStream> outStream;
   316   rv = NS_NewPipe(getter_AddRefs(inStream), getter_AddRefs(outStream),
   317                   bufferCapacity, bufferCapacity, nonBlocking);
   319   if (NS_SUCCEEDED(rv)) {
   320     uint32_t written;
   321     rv = outStream->Write((char*)iconBuffer.Elements(), bufferCapacity, &written);
   322     if (NS_SUCCEEDED(rv))
   323       NS_IF_ADDREF(*_retval = inStream);
   324   }
   326   // Drop notification callbacks to prevent cycles.
   327   mCallbacks = nullptr;
   329   return NS_OK;
   331   NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
   332 }
   334 NS_IMETHODIMP nsIconChannel::GetLoadFlags(uint32_t *aLoadAttributes)
   335 {
   336   return mPump->GetLoadFlags(aLoadAttributes);
   337 }
   339 NS_IMETHODIMP nsIconChannel::SetLoadFlags(uint32_t aLoadAttributes)
   340 {
   341   return mPump->SetLoadFlags(aLoadAttributes);
   342 }
   344 NS_IMETHODIMP nsIconChannel::GetContentType(nsACString &aContentType) 
   345 {
   346   aContentType.AssignLiteral(IMAGE_ICON_MS);
   347   return NS_OK;
   348 }
   350 NS_IMETHODIMP
   351 nsIconChannel::SetContentType(const nsACString &aContentType)
   352 {
   353   //It doesn't make sense to set the content-type on this type
   354   // of channel...
   355   return NS_ERROR_FAILURE;
   356 }
   358 NS_IMETHODIMP nsIconChannel::GetContentCharset(nsACString &aContentCharset) 
   359 {
   360   aContentCharset.AssignLiteral(IMAGE_ICON_MS);
   361   return NS_OK;
   362 }
   364 NS_IMETHODIMP
   365 nsIconChannel::SetContentCharset(const nsACString &aContentCharset)
   366 {
   367   //It doesn't make sense to set the content-type on this type
   368   // of channel...
   369   return NS_ERROR_FAILURE;
   370 }
   372 NS_IMETHODIMP
   373 nsIconChannel::GetContentDisposition(uint32_t *aContentDisposition)
   374 {
   375   return NS_ERROR_NOT_AVAILABLE;
   376 }
   378 NS_IMETHODIMP
   379 nsIconChannel::SetContentDisposition(uint32_t aContentDisposition)
   380 {
   381   return NS_ERROR_NOT_AVAILABLE;
   382 }
   384 NS_IMETHODIMP
   385 nsIconChannel::GetContentDispositionFilename(nsAString &aContentDispositionFilename)
   386 {
   387   return NS_ERROR_NOT_AVAILABLE;
   388 }
   390 NS_IMETHODIMP
   391 nsIconChannel::SetContentDispositionFilename(const nsAString &aContentDispositionFilename)
   392 {
   393   return NS_ERROR_NOT_AVAILABLE;
   394 }
   396 NS_IMETHODIMP
   397 nsIconChannel::GetContentDispositionHeader(nsACString &aContentDispositionHeader)
   398 {
   399   return NS_ERROR_NOT_AVAILABLE;
   400 }
   402 NS_IMETHODIMP nsIconChannel::GetContentLength(int64_t *aContentLength)
   403 {
   404   *aContentLength = mContentLength;
   405   return NS_OK;
   406 }
   408 NS_IMETHODIMP nsIconChannel::SetContentLength(int64_t aContentLength)
   409 {
   410   NS_NOTREACHED("nsIconChannel::SetContentLength");
   411   return NS_ERROR_NOT_IMPLEMENTED;
   412 }
   414 NS_IMETHODIMP nsIconChannel::GetLoadGroup(nsILoadGroup* *aLoadGroup)
   415 {
   416   *aLoadGroup = mLoadGroup;
   417   NS_IF_ADDREF(*aLoadGroup);
   418   return NS_OK;
   419 }
   421 NS_IMETHODIMP nsIconChannel::SetLoadGroup(nsILoadGroup* aLoadGroup)
   422 {
   423   mLoadGroup = aLoadGroup;
   424   return NS_OK;
   425 }
   427 NS_IMETHODIMP nsIconChannel::GetOwner(nsISupports* *aOwner)
   428 {
   429   *aOwner = mOwner.get();
   430   NS_IF_ADDREF(*aOwner);
   431   return NS_OK;
   432 }
   434 NS_IMETHODIMP nsIconChannel::SetOwner(nsISupports* aOwner)
   435 {
   436   mOwner = aOwner;
   437   return NS_OK;
   438 }
   440 NS_IMETHODIMP nsIconChannel::GetNotificationCallbacks(nsIInterfaceRequestor* *aNotificationCallbacks)
   441 {
   442   *aNotificationCallbacks = mCallbacks.get();
   443   NS_IF_ADDREF(*aNotificationCallbacks);
   444   return NS_OK;
   445 }
   447 NS_IMETHODIMP nsIconChannel::SetNotificationCallbacks(nsIInterfaceRequestor* aNotificationCallbacks)
   448 {
   449   mCallbacks = aNotificationCallbacks;
   450   return NS_OK;
   451 }
   453 NS_IMETHODIMP nsIconChannel::GetSecurityInfo(nsISupports * *aSecurityInfo)
   454 {
   455   *aSecurityInfo = nullptr;
   456   return NS_OK;
   457 }

mercurial