Thu, 15 Jan 2015 15:59:08 +0100
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 }