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: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 #include "JumpListItem.h"
8 #include <shellapi.h>
9 #include <propvarutil.h>
10 #include <propkey.h>
12 #include "nsIFile.h"
13 #include "nsNetUtil.h"
14 #include "nsCRT.h"
15 #include "nsNetCID.h"
16 #include "nsCExternalHandlerService.h"
17 #include "nsCycleCollectionParticipant.h"
18 #include "mozilla/Preferences.h"
19 #include "JumpListBuilder.h"
20 #include "WinUtils.h"
22 namespace mozilla {
23 namespace widget {
25 // ISUPPORTS Impl's
26 NS_IMPL_ISUPPORTS(JumpListItem,
27 nsIJumpListItem)
29 NS_IMPL_ISUPPORTS_INHERITED(JumpListSeparator,
30 JumpListItem,
31 nsIJumpListSeparator)
33 NS_IMPL_ISUPPORTS_INHERITED(JumpListLink,
34 JumpListItem,
35 nsIJumpListLink)
37 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(JumpListShortcut)
38 NS_INTERFACE_MAP_ENTRY(nsIJumpListShortcut)
39 NS_INTERFACE_MAP_END_INHERITING(JumpListItem)
41 NS_IMPL_CYCLE_COLLECTION(JumpListShortcut, mHandlerApp)
43 NS_IMPL_CYCLE_COLLECTING_ADDREF(JumpListShortcut)
44 NS_IMPL_CYCLE_COLLECTING_RELEASE(JumpListShortcut)
46 /* attribute short type; */
47 NS_IMETHODIMP JumpListItem::GetType(int16_t *aType)
48 {
49 NS_ENSURE_ARG_POINTER(aType);
51 *aType = mItemType;
53 return NS_OK;
54 }
56 /* boolean equals(nsIJumpListItem item); */
57 NS_IMETHODIMP JumpListItem::Equals(nsIJumpListItem *aItem, bool *aResult)
58 {
59 NS_ENSURE_ARG_POINTER(aItem);
61 *aResult = false;
63 int16_t theType = nsIJumpListItem::JUMPLIST_ITEM_EMPTY;
64 if (NS_FAILED(aItem->GetType(&theType)))
65 return NS_OK;
67 // Make sure the types match.
68 if (Type() != theType)
69 return NS_OK;
71 *aResult = true;
73 return NS_OK;
74 }
76 /* link impl. */
78 /* attribute nsIURI uri; */
79 NS_IMETHODIMP JumpListLink::GetUri(nsIURI **aURI)
80 {
81 NS_IF_ADDREF(*aURI = mURI);
83 return NS_OK;
84 }
86 NS_IMETHODIMP JumpListLink::SetUri(nsIURI *aURI)
87 {
88 mURI = aURI;
90 return NS_OK;
91 }
93 /* attribute AString uriTitle; */
94 NS_IMETHODIMP JumpListLink::SetUriTitle(const nsAString &aUriTitle)
95 {
96 mUriTitle.Assign(aUriTitle);
98 return NS_OK;
99 }
101 NS_IMETHODIMP JumpListLink::GetUriTitle(nsAString& aUriTitle)
102 {
103 aUriTitle.Assign(mUriTitle);
105 return NS_OK;
106 }
108 /* readonly attribute long uriHash; */
109 NS_IMETHODIMP JumpListLink::GetUriHash(nsACString& aUriHash)
110 {
111 if (!mURI)
112 return NS_ERROR_NOT_AVAILABLE;
114 return mozilla::widget::FaviconHelper::HashURI(mCryptoHash, mURI, aUriHash);
115 }
117 /* boolean compareHash(in nsIURI uri); */
118 NS_IMETHODIMP JumpListLink::CompareHash(nsIURI *aUri, bool *aResult)
119 {
120 nsresult rv;
122 if (!mURI) {
123 *aResult = !aUri;
124 return NS_OK;
125 }
127 NS_ENSURE_ARG_POINTER(aUri);
129 nsAutoCString hash1, hash2;
131 rv = mozilla::widget::FaviconHelper::HashURI(mCryptoHash, mURI, hash1);
132 NS_ENSURE_SUCCESS(rv, rv);
133 rv = mozilla::widget::FaviconHelper::HashURI(mCryptoHash, aUri, hash2);
134 NS_ENSURE_SUCCESS(rv, rv);
136 *aResult = hash1.Equals(hash2);
138 return NS_OK;
139 }
141 /* boolean equals(nsIJumpListItem item); */
142 NS_IMETHODIMP JumpListLink::Equals(nsIJumpListItem *aItem, bool *aResult)
143 {
144 NS_ENSURE_ARG_POINTER(aItem);
146 nsresult rv;
148 *aResult = false;
150 int16_t theType = nsIJumpListItem::JUMPLIST_ITEM_EMPTY;
151 if (NS_FAILED(aItem->GetType(&theType)))
152 return NS_OK;
154 // Make sure the types match.
155 if (Type() != theType)
156 return NS_OK;
158 nsCOMPtr<nsIJumpListLink> link = do_QueryInterface(aItem, &rv);
159 if (NS_FAILED(rv))
160 return rv;
162 // Check the titles
163 nsAutoString title;
164 link->GetUriTitle(title);
165 if (!mUriTitle.Equals(title))
166 return NS_OK;
168 // Call the internal object's equals() method to check.
169 nsCOMPtr<nsIURI> theUri;
170 bool equals = false;
171 if (NS_SUCCEEDED(link->GetUri(getter_AddRefs(theUri)))) {
172 if (!theUri) {
173 if (!mURI)
174 *aResult = true;
175 return NS_OK;
176 }
177 if (NS_SUCCEEDED(theUri->Equals(mURI, &equals)) && equals) {
178 *aResult = true;
179 }
180 }
182 return NS_OK;
183 }
185 /* shortcut impl. */
187 /* attribute nsILocalHandlerApp app; */
188 NS_IMETHODIMP JumpListShortcut::GetApp(nsILocalHandlerApp **aApp)
189 {
190 NS_IF_ADDREF(*aApp = mHandlerApp);
192 return NS_OK;
193 }
195 NS_IMETHODIMP JumpListShortcut::SetApp(nsILocalHandlerApp *aApp)
196 {
197 mHandlerApp = aApp;
199 // Confirm the app is present on the system
200 if (!ExecutableExists(mHandlerApp))
201 return NS_ERROR_FILE_NOT_FOUND;
203 return NS_OK;
204 }
206 /* attribute long iconIndex; */
207 NS_IMETHODIMP JumpListShortcut::GetIconIndex(int32_t *aIconIndex)
208 {
209 NS_ENSURE_ARG_POINTER(aIconIndex);
211 *aIconIndex = mIconIndex;
212 return NS_OK;
213 }
215 NS_IMETHODIMP JumpListShortcut::SetIconIndex(int32_t aIconIndex)
216 {
217 mIconIndex = aIconIndex;
218 return NS_OK;
219 }
221 /* attribute long iconURI; */
222 NS_IMETHODIMP JumpListShortcut::GetFaviconPageUri(nsIURI **aFaviconPageURI)
223 {
224 NS_IF_ADDREF(*aFaviconPageURI = mFaviconPageURI);
226 return NS_OK;
227 }
229 NS_IMETHODIMP JumpListShortcut::SetFaviconPageUri(nsIURI *aFaviconPageURI)
230 {
231 mFaviconPageURI = aFaviconPageURI;
232 return NS_OK;
233 }
235 /* boolean equals(nsIJumpListItem item); */
236 NS_IMETHODIMP JumpListShortcut::Equals(nsIJumpListItem *aItem, bool *aResult)
237 {
238 NS_ENSURE_ARG_POINTER(aItem);
240 nsresult rv;
242 *aResult = false;
244 int16_t theType = nsIJumpListItem::JUMPLIST_ITEM_EMPTY;
245 if (NS_FAILED(aItem->GetType(&theType)))
246 return NS_OK;
248 // Make sure the types match.
249 if (Type() != theType)
250 return NS_OK;
252 nsCOMPtr<nsIJumpListShortcut> shortcut = do_QueryInterface(aItem, &rv);
253 if (NS_FAILED(rv))
254 return rv;
256 // Check the icon index
257 //int32_t idx;
258 //shortcut->GetIconIndex(&idx);
259 //if (mIconIndex != idx)
260 // return NS_OK;
261 // No need to check the icon page URI either
263 // Call the internal object's equals() method to check.
264 nsCOMPtr<nsILocalHandlerApp> theApp;
265 bool equals = false;
266 if (NS_SUCCEEDED(shortcut->GetApp(getter_AddRefs(theApp)))) {
267 if (!theApp) {
268 if (!mHandlerApp)
269 *aResult = true;
270 return NS_OK;
271 }
272 if (NS_SUCCEEDED(theApp->Equals(mHandlerApp, &equals)) && equals) {
273 *aResult = true;
274 }
275 }
277 return NS_OK;
278 }
280 /* internal helpers */
282 // (static) Creates a ShellLink that encapsulate a separator.
283 nsresult JumpListSeparator::GetSeparator(nsRefPtr<IShellLinkW>& aShellLink)
284 {
285 HRESULT hr;
286 IShellLinkW* psl;
288 // Create a IShellLink.
289 hr = CoCreateInstance(CLSID_ShellLink, nullptr, CLSCTX_INPROC_SERVER,
290 IID_IShellLinkW, (LPVOID*)&psl);
291 if (FAILED(hr))
292 return NS_ERROR_UNEXPECTED;
294 IPropertyStore* pPropStore = nullptr;
295 hr = psl->QueryInterface(IID_IPropertyStore, (LPVOID*)&pPropStore);
296 if (FAILED(hr))
297 return NS_ERROR_UNEXPECTED;
299 PROPVARIANT pv;
300 InitPropVariantFromBoolean(TRUE, &pv);
302 pPropStore->SetValue(PKEY_AppUserModel_IsDestListSeparator, pv);
303 pPropStore->Commit();
304 pPropStore->Release();
306 PropVariantClear(&pv);
308 aShellLink = dont_AddRef(psl);
310 return NS_OK;
311 }
313 // (static) Creates a ShellLink that encapsulate a shortcut to local apps.
314 nsresult JumpListShortcut::GetShellLink(nsCOMPtr<nsIJumpListItem>& item,
315 nsRefPtr<IShellLinkW>& aShellLink,
316 nsCOMPtr<nsIThread> &aIOThread)
317 {
318 HRESULT hr;
319 IShellLinkW* psl;
320 nsresult rv;
322 // Shell links:
323 // http://msdn.microsoft.com/en-us/library/bb776891(VS.85).aspx
324 // http://msdn.microsoft.com/en-us/library/bb774950(VS.85).aspx
326 int16_t type;
327 if (NS_FAILED(item->GetType(&type)))
328 return NS_ERROR_INVALID_ARG;
330 if (type != nsIJumpListItem::JUMPLIST_ITEM_SHORTCUT)
331 return NS_ERROR_INVALID_ARG;
333 nsCOMPtr<nsIJumpListShortcut> shortcut = do_QueryInterface(item, &rv);
334 NS_ENSURE_SUCCESS(rv, rv);
336 nsCOMPtr<nsILocalHandlerApp> handlerApp;
337 rv = shortcut->GetApp(getter_AddRefs(handlerApp));
338 NS_ENSURE_SUCCESS(rv, rv);
340 // Create a IShellLink
341 hr = CoCreateInstance(CLSID_ShellLink, nullptr, CLSCTX_INPROC_SERVER,
342 IID_IShellLinkW, (LPVOID*)&psl);
343 if (FAILED(hr))
344 return NS_ERROR_UNEXPECTED;
346 // Retrieve the app path, title, description and optional command line args.
347 nsAutoString appPath, appTitle, appDescription, appArgs;
348 int32_t appIconIndex = 0;
350 // Path
351 nsCOMPtr<nsIFile> executable;
352 handlerApp->GetExecutable(getter_AddRefs(executable));
354 rv = executable->GetPath(appPath);
355 NS_ENSURE_SUCCESS(rv, rv);
357 // Command line parameters
358 uint32_t count = 0;
359 handlerApp->GetParameterCount(&count);
360 for (uint32_t idx = 0; idx < count; idx++) {
361 if (idx > 0)
362 appArgs.Append(NS_LITERAL_STRING(" "));
363 nsAutoString param;
364 rv = handlerApp->GetParameter(idx, param);
365 if (NS_FAILED(rv))
366 return rv;
367 appArgs.Append(param);
368 }
370 handlerApp->GetName(appTitle);
371 handlerApp->GetDetailedDescription(appDescription);
373 bool useUriIcon = false; // if we want to use the URI icon
374 bool usedUriIcon = false; // if we did use the URI icon
375 shortcut->GetIconIndex(&appIconIndex);
377 nsCOMPtr<nsIURI> iconUri;
378 rv = shortcut->GetFaviconPageUri(getter_AddRefs(iconUri));
379 if (NS_SUCCEEDED(rv) && iconUri) {
380 useUriIcon = true;
381 }
383 // Store the title of the app
384 if (appTitle.Length() > 0) {
385 IPropertyStore* pPropStore = nullptr;
386 hr = psl->QueryInterface(IID_IPropertyStore, (LPVOID*)&pPropStore);
387 if (FAILED(hr))
388 return NS_ERROR_UNEXPECTED;
390 PROPVARIANT pv;
391 InitPropVariantFromString(appTitle.get(), &pv);
393 pPropStore->SetValue(PKEY_Title, pv);
394 pPropStore->Commit();
395 pPropStore->Release();
397 PropVariantClear(&pv);
398 }
400 // Store the rest of the params
401 psl->SetPath(appPath.get());
402 psl->SetDescription(appDescription.get());
403 psl->SetArguments(appArgs.get());
405 if (useUriIcon) {
406 nsString icoFilePath;
407 rv = mozilla::widget::FaviconHelper::ObtainCachedIconFile(iconUri,
408 icoFilePath,
409 aIOThread,
410 false);
411 if (NS_SUCCEEDED(rv)) {
412 // Always use the first icon in the ICO file
413 // our encoded icon only has 1 resource
414 psl->SetIconLocation(icoFilePath.get(), 0);
415 usedUriIcon = true;
416 }
417 }
419 // We didn't use an ICO via URI so fall back to the app icon
420 if (!usedUriIcon) {
421 psl->SetIconLocation(appPath.get(), appIconIndex);
422 }
424 aShellLink = dont_AddRef(psl);
426 return NS_OK;
427 }
429 // If successful fills in the aSame parameter
430 // aSame will be true if the path is in our icon cache
431 static nsresult IsPathInOurIconCache(nsCOMPtr<nsIJumpListShortcut>& aShortcut,
432 wchar_t *aPath, bool *aSame)
433 {
434 NS_ENSURE_ARG_POINTER(aPath);
435 NS_ENSURE_ARG_POINTER(aSame);
437 *aSame = false;
439 // Construct the path of our jump list cache
440 nsCOMPtr<nsIFile> jumpListCache;
441 nsresult rv = NS_GetSpecialDirectory("ProfLDS", getter_AddRefs(jumpListCache));
442 NS_ENSURE_SUCCESS(rv, rv);
443 rv = jumpListCache->AppendNative(nsDependentCString(FaviconHelper::kJumpListCacheDir));
444 NS_ENSURE_SUCCESS(rv, rv);
445 nsAutoString jumpListCachePath;
446 rv = jumpListCache->GetPath(jumpListCachePath);
447 NS_ENSURE_SUCCESS(rv, rv);
449 // Construct the parent path of the passed in path
450 nsCOMPtr<nsIFile> passedInFile = do_CreateInstance("@mozilla.org/file/local;1");
451 NS_ENSURE_TRUE(passedInFile, NS_ERROR_FAILURE);
452 nsAutoString passedInPath(aPath);
453 rv = passedInFile->InitWithPath(passedInPath);
454 nsCOMPtr<nsIFile> passedInParentFile;
455 passedInFile->GetParent(getter_AddRefs(passedInParentFile));
456 nsAutoString passedInParentPath;
457 rv = jumpListCache->GetPath(passedInParentPath);
458 NS_ENSURE_SUCCESS(rv, rv);
460 *aSame = jumpListCachePath.Equals(passedInParentPath);
461 return NS_OK;
462 }
464 // (static) For a given IShellLink, create and return a populated nsIJumpListShortcut.
465 nsresult JumpListShortcut::GetJumpListShortcut(IShellLinkW *pLink, nsCOMPtr<nsIJumpListShortcut>& aShortcut)
466 {
467 NS_ENSURE_ARG_POINTER(pLink);
469 nsresult rv;
470 HRESULT hres;
472 nsCOMPtr<nsILocalHandlerApp> handlerApp =
473 do_CreateInstance(NS_LOCALHANDLERAPP_CONTRACTID, &rv);
474 NS_ENSURE_SUCCESS(rv, rv);
476 wchar_t buf[MAX_PATH];
478 // Path
479 hres = pLink->GetPath(buf, MAX_PATH, nullptr, SLGP_UNCPRIORITY);
480 if (FAILED(hres))
481 return NS_ERROR_INVALID_ARG;
483 nsCOMPtr<nsIFile> file;
484 nsDependentString filepath(buf);
485 rv = NS_NewLocalFile(filepath, false, getter_AddRefs(file));
486 NS_ENSURE_SUCCESS(rv, rv);
488 rv = handlerApp->SetExecutable(file);
489 NS_ENSURE_SUCCESS(rv, rv);
491 // Parameters
492 hres = pLink->GetArguments(buf, MAX_PATH);
493 if (SUCCEEDED(hres)) {
494 LPWSTR *arglist;
495 int32_t numArgs;
496 int32_t idx;
498 arglist = ::CommandLineToArgvW(buf, &numArgs);
499 if(arglist) {
500 for (idx = 0; idx < numArgs; idx++) {
501 // szArglist[i] is null terminated
502 nsDependentString arg(arglist[idx]);
503 handlerApp->AppendParameter(arg);
504 }
505 ::LocalFree(arglist);
506 }
507 }
509 rv = aShortcut->SetApp(handlerApp);
510 NS_ENSURE_SUCCESS(rv, rv);
512 // Icon index or file location
513 int iconIdx = 0;
514 hres = pLink->GetIconLocation(buf, MAX_PATH, &iconIdx);
515 if (SUCCEEDED(hres)) {
516 // XXX How do we handle converting local files to images here? Do we need to?
517 aShortcut->SetIconIndex(iconIdx);
519 // Obtain the local profile directory and construct the output icon file path
520 // We only set the Icon Uri if we're sure it was from our icon cache.
521 bool isInOurCache;
522 if (NS_SUCCEEDED(IsPathInOurIconCache(aShortcut, buf, &isInOurCache)) &&
523 isInOurCache) {
524 nsCOMPtr<nsIURI> iconUri;
525 nsAutoString path(buf);
526 rv = NS_NewURI(getter_AddRefs(iconUri), path);
527 if (NS_SUCCEEDED(rv)) {
528 aShortcut->SetFaviconPageUri(iconUri);
529 }
530 }
531 }
533 // Do we need the title and description? Probably not since handler app doesn't compare
534 // these in equals.
536 return NS_OK;
537 }
539 // (static) ShellItems are used to encapsulate links to things. We currently only support URI links,
540 // but more support could be added, such as local file and directory links.
541 nsresult JumpListLink::GetShellItem(nsCOMPtr<nsIJumpListItem>& item, nsRefPtr<IShellItem2>& aShellItem)
542 {
543 IShellItem2 *psi = nullptr;
544 nsresult rv;
546 int16_t type;
547 if (NS_FAILED(item->GetType(&type)))
548 return NS_ERROR_INVALID_ARG;
550 if (type != nsIJumpListItem::JUMPLIST_ITEM_LINK)
551 return NS_ERROR_INVALID_ARG;
553 nsCOMPtr<nsIJumpListLink> link = do_QueryInterface(item, &rv);
554 NS_ENSURE_SUCCESS(rv, rv);
556 nsCOMPtr<nsIURI> uri;
557 rv = link->GetUri(getter_AddRefs(uri));
558 NS_ENSURE_SUCCESS(rv, rv);
560 nsAutoCString spec;
561 rv = uri->GetSpec(spec);
562 NS_ENSURE_SUCCESS(rv, rv);
564 // Create the IShellItem
565 if (FAILED(WinUtils::SHCreateItemFromParsingName(
566 NS_ConvertASCIItoUTF16(spec).get(),
567 nullptr, IID_PPV_ARGS(&psi)))) {
568 return NS_ERROR_INVALID_ARG;
569 }
571 // Set the title
572 nsAutoString linkTitle;
573 link->GetUriTitle(linkTitle);
575 IPropertyStore* pPropStore = nullptr;
576 HRESULT hres = psi->GetPropertyStore(GPS_DEFAULT, IID_IPropertyStore, (void**)&pPropStore);
577 if (FAILED(hres))
578 return NS_ERROR_UNEXPECTED;
580 PROPVARIANT pv;
581 InitPropVariantFromString(linkTitle.get(), &pv);
583 // May fail due to shell item access permissions.
584 pPropStore->SetValue(PKEY_ItemName, pv);
585 pPropStore->Commit();
586 pPropStore->Release();
588 PropVariantClear(&pv);
590 aShellItem = dont_AddRef(psi);
592 return NS_OK;
593 }
595 // (static) For a given IShellItem, create and return a populated nsIJumpListLink.
596 nsresult JumpListLink::GetJumpListLink(IShellItem *pItem, nsCOMPtr<nsIJumpListLink>& aLink)
597 {
598 NS_ENSURE_ARG_POINTER(pItem);
600 // We assume for now these are URI links, but through properties we could
601 // query and create other types.
602 nsresult rv;
603 LPWSTR lpstrName = nullptr;
605 if (SUCCEEDED(pItem->GetDisplayName(SIGDN_URL, &lpstrName))) {
606 nsCOMPtr<nsIURI> uri;
607 nsAutoString spec(lpstrName);
609 rv = NS_NewURI(getter_AddRefs(uri), NS_ConvertUTF16toUTF8(spec));
610 if (NS_FAILED(rv))
611 return NS_ERROR_INVALID_ARG;
613 aLink->SetUri(uri);
615 ::CoTaskMemFree(lpstrName);
616 }
618 return NS_OK;
619 }
621 // Confirm the app is on the system
622 bool JumpListShortcut::ExecutableExists(nsCOMPtr<nsILocalHandlerApp>& handlerApp)
623 {
624 nsresult rv;
626 if (!handlerApp)
627 return false;
629 nsCOMPtr<nsIFile> executable;
630 rv = handlerApp->GetExecutable(getter_AddRefs(executable));
631 if (NS_SUCCEEDED(rv) && executable) {
632 bool exists;
633 executable->Exists(&exists);
634 return exists;
635 }
636 return false;
637 }
639 } // namespace widget
640 } // namespace mozilla