widget/windows/JumpListBuilder.cpp

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: 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 "JumpListBuilder.h"
     8 #include "nsError.h"
     9 #include "nsCOMPtr.h"
    10 #include "nsServiceManagerUtils.h"
    11 #include "nsAutoPtr.h"
    12 #include "nsString.h"
    13 #include "nsArrayUtils.h"
    14 #include "nsIMutableArray.h"
    15 #include "nsWidgetsCID.h"
    16 #include "WinTaskbar.h"
    17 #include "nsDirectoryServiceUtils.h"
    18 #include "nsISimpleEnumerator.h"
    19 #include "mozilla/Preferences.h"
    20 #include "nsStringStream.h"
    21 #include "nsNetUtil.h"
    22 #include "nsThreadUtils.h"
    23 #include "mozilla/LazyIdleThread.h"
    25 #include "WinUtils.h"
    27 // The amount of time, in milliseconds, that our IO thread will stay alive after the last event it processes.
    28 #define DEFAULT_THREAD_TIMEOUT_MS 30000
    30 namespace mozilla {
    31 namespace widget {
    33 static NS_DEFINE_CID(kJumpListItemCID,     NS_WIN_JUMPLISTITEM_CID);
    34 static NS_DEFINE_CID(kJumpListLinkCID,     NS_WIN_JUMPLISTLINK_CID);
    35 static NS_DEFINE_CID(kJumpListShortcutCID, NS_WIN_JUMPLISTSHORTCUT_CID);
    37 // defined in WinTaskbar.cpp
    38 extern const wchar_t *gMozillaJumpListIDGeneric;
    40 bool JumpListBuilder::sBuildingList = false;
    41 const char kPrefTaskbarEnabled[] = "browser.taskbar.lists.enabled";
    43 NS_IMPL_ISUPPORTS(JumpListBuilder, nsIJumpListBuilder, nsIObserver)
    45 JumpListBuilder::JumpListBuilder() :
    46   mMaxItems(0),
    47   mHasCommit(false)
    48 {
    49   ::CoInitialize(nullptr);
    51   CoCreateInstance(CLSID_DestinationList, nullptr, CLSCTX_INPROC_SERVER,
    52                    IID_ICustomDestinationList, getter_AddRefs(mJumpListMgr));
    54   // Make a lazy thread for any IO
    55   mIOThread = new LazyIdleThread(DEFAULT_THREAD_TIMEOUT_MS,
    56                                  NS_LITERAL_CSTRING("Jump List"),
    57                                  LazyIdleThread::ManualShutdown);
    58   Preferences::AddStrongObserver(this, kPrefTaskbarEnabled);
    59 }
    61 JumpListBuilder::~JumpListBuilder()
    62 {
    63   mIOThread->Shutdown();
    64   Preferences::RemoveObserver(this, kPrefTaskbarEnabled);
    65   mJumpListMgr = nullptr;
    66   ::CoUninitialize();
    67 }
    69 /* readonly attribute short available; */
    70 NS_IMETHODIMP JumpListBuilder::GetAvailable(int16_t *aAvailable)
    71 {
    72   *aAvailable = false;
    74   if (mJumpListMgr)
    75     *aAvailable = true;
    77   return NS_OK;
    78 }
    80 /* readonly attribute boolean isListCommitted; */
    81 NS_IMETHODIMP JumpListBuilder::GetIsListCommitted(bool *aCommit)
    82 {
    83   *aCommit = mHasCommit;
    85   return NS_OK;
    86 }
    88 /* readonly attribute short maxItems; */
    89 NS_IMETHODIMP JumpListBuilder::GetMaxListItems(int16_t *aMaxItems)
    90 {
    91   if (!mJumpListMgr)
    92     return NS_ERROR_NOT_AVAILABLE;
    94   *aMaxItems = 0;
    96   if (sBuildingList) {
    97     *aMaxItems = mMaxItems;
    98     return NS_OK;
    99   }
   101   IObjectArray *objArray;
   102   if (SUCCEEDED(mJumpListMgr->BeginList(&mMaxItems, IID_PPV_ARGS(&objArray)))) {
   103     *aMaxItems = mMaxItems;
   105     if (objArray)
   106       objArray->Release();
   108     mJumpListMgr->AbortList();
   109   }
   111   return NS_OK;
   112 }
   114 /* boolean initListBuild(in nsIMutableArray removedItems); */
   115 NS_IMETHODIMP JumpListBuilder::InitListBuild(nsIMutableArray *removedItems, bool *_retval)
   116 {
   117   NS_ENSURE_ARG_POINTER(removedItems);
   119   *_retval = false;
   121   if (!mJumpListMgr)
   122     return NS_ERROR_NOT_AVAILABLE;
   124   if(sBuildingList)
   125     AbortListBuild();
   127   IObjectArray *objArray;
   129   if (SUCCEEDED(mJumpListMgr->BeginList(&mMaxItems, IID_PPV_ARGS(&objArray)))) {
   130     if (objArray) {
   131       TransferIObjectArrayToIMutableArray(objArray, removedItems);
   132       objArray->Release();
   133     }
   135     RemoveIconCacheForItems(removedItems);
   137     sBuildingList = true;
   138     *_retval = true;
   139     return NS_OK;
   140   }
   142   return NS_OK;
   143 }
   145 // Ensures that we don't have old ICO files that aren't in our jump lists 
   146 // anymore left over in the cache.
   147 nsresult JumpListBuilder::RemoveIconCacheForItems(nsIMutableArray *items) 
   148 {
   149   NS_ENSURE_ARG_POINTER(items);
   151   nsresult rv;
   152   uint32_t length;
   153   items->GetLength(&length);
   154   for (uint32_t i = 0; i < length; ++i) {
   156     //Obtain an IJumpListItem and get the type
   157     nsCOMPtr<nsIJumpListItem> item = do_QueryElementAt(items, i);
   158     if (!item) {
   159       continue;
   160     }
   161     int16_t type;
   162     if (NS_FAILED(item->GetType(&type))) {
   163       continue;
   164     }
   166     // If the item is a shortcut, remove its associated icon if any
   167     if (type == nsIJumpListItem::JUMPLIST_ITEM_SHORTCUT) {
   168       nsCOMPtr<nsIJumpListShortcut> shortcut = do_QueryInterface(item);
   169       if (shortcut) {
   170         nsCOMPtr<nsIURI> uri;
   171         rv = shortcut->GetFaviconPageUri(getter_AddRefs(uri));
   172         if (NS_SUCCEEDED(rv) && uri) {
   174           // The local file path is stored inside the nsIURI
   175           // Get the nsIURI spec which stores the local path for the icon to remove
   176           nsAutoCString spec;
   177           nsresult rv = uri->GetSpec(spec);
   178           NS_ENSURE_SUCCESS(rv, rv);
   180           nsCOMPtr<nsIRunnable> event 
   181             = new mozilla::widget::AsyncDeleteIconFromDisk(NS_ConvertUTF8toUTF16(spec));
   182           mIOThread->Dispatch(event, NS_DISPATCH_NORMAL);
   184           // The shortcut was generated from an IShellLinkW so IShellLinkW can
   185           // only tell us what the original icon is and not the URI.
   186           // So this field was used only temporarily as the actual icon file
   187           // path.  It should be cleared.
   188           shortcut->SetFaviconPageUri(nullptr);
   189         }
   190       }
   191     }
   193   } // end for
   195   return NS_OK;
   196 }
   198 // Ensures that we have no old ICO files left in the jump list cache
   199 nsresult JumpListBuilder::RemoveIconCacheForAllItems() 
   200 {
   201   // Construct the path of our jump list cache
   202   nsCOMPtr<nsIFile> jumpListCacheDir;
   203   nsresult rv = NS_GetSpecialDirectory("ProfLDS", 
   204                                        getter_AddRefs(jumpListCacheDir));
   205   NS_ENSURE_SUCCESS(rv, rv);
   206   rv = jumpListCacheDir->AppendNative(nsDependentCString(
   207                          mozilla::widget::FaviconHelper::kJumpListCacheDir));
   208   NS_ENSURE_SUCCESS(rv, rv);
   209   nsCOMPtr<nsISimpleEnumerator> entries;
   210   rv = jumpListCacheDir->GetDirectoryEntries(getter_AddRefs(entries));
   211   NS_ENSURE_SUCCESS(rv, rv);
   213   // Loop through each directory entry and remove all ICO files found
   214   do {
   215     bool hasMore = false;
   216     if (NS_FAILED(entries->HasMoreElements(&hasMore)) || !hasMore)
   217       break;
   219     nsCOMPtr<nsISupports> supp;
   220     if (NS_FAILED(entries->GetNext(getter_AddRefs(supp))))
   221       break;
   223     nsCOMPtr<nsIFile> currFile(do_QueryInterface(supp));
   224     nsAutoString path;
   225     if (NS_FAILED(currFile->GetPath(path)))
   226       continue;
   228     int32_t len = path.Length();
   229     if (StringTail(path, 4).LowerCaseEqualsASCII(".ico")) {
   230       // Check if the cached ICO file exists
   231       bool exists;
   232       if (NS_FAILED(currFile->Exists(&exists)) || !exists)
   233         continue;
   235       // We found an ICO file that exists, so we should remove it
   236       currFile->Remove(false);
   237     }
   238   } while(true);
   240   return NS_OK;
   241 }
   243 /* boolean addListToBuild(in short aCatType, [optional] in nsIArray items, [optional] in AString catName); */
   244 NS_IMETHODIMP JumpListBuilder::AddListToBuild(int16_t aCatType, nsIArray *items, const nsAString &catName, bool *_retval)
   245 {
   246   nsresult rv;
   248   *_retval = false;
   250   if (!mJumpListMgr)
   251     return NS_ERROR_NOT_AVAILABLE;
   253   switch(aCatType) {
   254     case nsIJumpListBuilder::JUMPLIST_CATEGORY_TASKS:
   255     {
   256       NS_ENSURE_ARG_POINTER(items);
   258       HRESULT hr;
   259       nsRefPtr<IObjectCollection> collection;
   260       hr = CoCreateInstance(CLSID_EnumerableObjectCollection, nullptr,
   261                             CLSCTX_INPROC_SERVER, IID_IObjectCollection,
   262                             getter_AddRefs(collection));
   263       if (FAILED(hr))
   264         return NS_ERROR_UNEXPECTED;
   266       // Build the list
   267       uint32_t length;
   268       items->GetLength(&length);
   269       for (uint32_t i = 0; i < length; ++i) {
   270         nsCOMPtr<nsIJumpListItem> item = do_QueryElementAt(items, i);
   271         if (!item)
   272           continue;
   273         // Check for separators 
   274         if (IsSeparator(item)) {
   275           nsRefPtr<IShellLinkW> link;
   276           rv = JumpListSeparator::GetSeparator(link);
   277           if (NS_FAILED(rv))
   278             return rv;
   279           collection->AddObject(link);
   280           continue;
   281         }
   282         // These should all be ShellLinks
   283         nsRefPtr<IShellLinkW> link;
   284         rv = JumpListShortcut::GetShellLink(item, link, mIOThread);
   285         if (NS_FAILED(rv))
   286           return rv;
   287         collection->AddObject(link);
   288       }
   290       // We need IObjectArray to submit
   291       nsRefPtr<IObjectArray> pArray;
   292       hr = collection->QueryInterface(IID_IObjectArray, getter_AddRefs(pArray));
   293       if (FAILED(hr))
   294         return NS_ERROR_UNEXPECTED;
   296       // Add the tasks
   297       hr = mJumpListMgr->AddUserTasks(pArray);
   298       if (SUCCEEDED(hr))
   299         *_retval = true;
   300       return NS_OK;
   301     }
   302     break;
   303     case nsIJumpListBuilder::JUMPLIST_CATEGORY_RECENT:
   304     {
   305       if (SUCCEEDED(mJumpListMgr->AppendKnownCategory(KDC_RECENT)))
   306         *_retval = true;
   307       return NS_OK;
   308     }
   309     break;
   310     case nsIJumpListBuilder::JUMPLIST_CATEGORY_FREQUENT:
   311     {
   312       if (SUCCEEDED(mJumpListMgr->AppendKnownCategory(KDC_FREQUENT)))
   313         *_retval = true;
   314       return NS_OK;
   315     }
   316     break;
   317     case nsIJumpListBuilder::JUMPLIST_CATEGORY_CUSTOMLIST:
   318     {
   319       NS_ENSURE_ARG_POINTER(items);
   321       if (catName.IsEmpty())
   322         return NS_ERROR_INVALID_ARG;
   324       HRESULT hr;
   325       nsRefPtr<IObjectCollection> collection;
   326       hr = CoCreateInstance(CLSID_EnumerableObjectCollection, nullptr,
   327                             CLSCTX_INPROC_SERVER, IID_IObjectCollection,
   328                             getter_AddRefs(collection));
   329       if (FAILED(hr))
   330         return NS_ERROR_UNEXPECTED;
   332       uint32_t length;
   333       items->GetLength(&length);
   334       for (uint32_t i = 0; i < length; ++i) {
   335         nsCOMPtr<nsIJumpListItem> item = do_QueryElementAt(items, i);
   336         if (!item)
   337           continue;
   338         int16_t type;
   339         if (NS_FAILED(item->GetType(&type)))
   340           continue;
   341         switch(type) {
   342           case nsIJumpListItem::JUMPLIST_ITEM_SEPARATOR:
   343           {
   344             nsRefPtr<IShellLinkW> shellItem;
   345             rv = JumpListSeparator::GetSeparator(shellItem);
   346             if (NS_FAILED(rv))
   347               return rv;
   348             collection->AddObject(shellItem);
   349           }
   350           break;
   351           case nsIJumpListItem::JUMPLIST_ITEM_LINK:
   352           {
   353             nsRefPtr<IShellItem2> shellItem;
   354             rv = JumpListLink::GetShellItem(item, shellItem);
   355             if (NS_FAILED(rv))
   356               return rv;
   357             collection->AddObject(shellItem);
   358           }
   359           break;
   360           case nsIJumpListItem::JUMPLIST_ITEM_SHORTCUT:
   361           {
   362             nsRefPtr<IShellLinkW> shellItem;
   363             rv = JumpListShortcut::GetShellLink(item, shellItem, mIOThread);
   364             if (NS_FAILED(rv))
   365               return rv;
   366             collection->AddObject(shellItem);
   367           }
   368           break;
   369         }
   370       }
   372       // We need IObjectArray to submit
   373       nsRefPtr<IObjectArray> pArray;
   374       hr = collection->QueryInterface(IID_IObjectArray, (LPVOID*)&pArray);
   375       if (FAILED(hr))
   376         return NS_ERROR_UNEXPECTED;
   378       // Add the tasks
   379       hr = mJumpListMgr->AppendCategory(reinterpret_cast<const wchar_t*>(catName.BeginReading()), pArray);
   380       if (SUCCEEDED(hr))
   381         *_retval = true;
   382       return NS_OK;
   383     }
   384     break;
   385   }
   386   return NS_OK;
   387 }
   389 /* void abortListBuild(); */
   390 NS_IMETHODIMP JumpListBuilder::AbortListBuild()
   391 {
   392   if (!mJumpListMgr)
   393     return NS_ERROR_NOT_AVAILABLE;
   395   mJumpListMgr->AbortList();
   396   sBuildingList = false;
   398   return NS_OK;
   399 }
   401 /* boolean commitListBuild(); */
   402 NS_IMETHODIMP JumpListBuilder::CommitListBuild(bool *_retval)
   403 {
   404   *_retval = false;
   406   if (!mJumpListMgr)
   407     return NS_ERROR_NOT_AVAILABLE;
   409   HRESULT hr = mJumpListMgr->CommitList();
   410   sBuildingList = false;
   412   // XXX We might want some specific error data here.
   413   if (SUCCEEDED(hr)) {
   414     *_retval = true;
   415     mHasCommit = true;
   416   }
   418   return NS_OK;
   419 }
   421 /* boolean deleteActiveList(); */
   422 NS_IMETHODIMP JumpListBuilder::DeleteActiveList(bool *_retval)
   423 {
   424   *_retval = false;
   426   if (!mJumpListMgr)
   427     return NS_ERROR_NOT_AVAILABLE;
   429   if(sBuildingList)
   430     AbortListBuild();
   432   nsAutoString uid;
   433   if (!WinTaskbar::GetAppUserModelID(uid))
   434     return NS_OK;
   436   if (SUCCEEDED(mJumpListMgr->DeleteList(uid.get())))
   437     *_retval = true;
   439   return NS_OK;
   440 }
   442 /* internal */
   444 bool JumpListBuilder::IsSeparator(nsCOMPtr<nsIJumpListItem>& item)
   445 {
   446   int16_t type;
   447   item->GetType(&type);
   448   if (NS_FAILED(item->GetType(&type)))
   449     return false;
   451   if (type == nsIJumpListItem::JUMPLIST_ITEM_SEPARATOR)
   452     return true;
   453   return false;
   454 }
   456 // TransferIObjectArrayToIMutableArray - used in converting removed items
   457 // to our objects.
   458 nsresult JumpListBuilder::TransferIObjectArrayToIMutableArray(IObjectArray *objArray, nsIMutableArray *removedItems)
   459 {
   460   NS_ENSURE_ARG_POINTER(objArray);
   461   NS_ENSURE_ARG_POINTER(removedItems);
   463   nsresult rv;
   465   uint32_t count = 0;
   466   objArray->GetCount(&count);
   468   nsCOMPtr<nsIJumpListItem> item;
   470   for (uint32_t idx = 0; idx < count; idx++) {
   471     IShellLinkW * pLink = nullptr;
   472     IShellItem * pItem = nullptr;
   474     if (SUCCEEDED(objArray->GetAt(idx, IID_IShellLinkW, (LPVOID*)&pLink))) {
   475       nsCOMPtr<nsIJumpListShortcut> shortcut = 
   476         do_CreateInstance(kJumpListShortcutCID, &rv);
   477       if (NS_FAILED(rv))
   478         return NS_ERROR_UNEXPECTED;
   479       rv = JumpListShortcut::GetJumpListShortcut(pLink, shortcut);
   480       item = do_QueryInterface(shortcut);
   481     }
   482     else if (SUCCEEDED(objArray->GetAt(idx, IID_IShellItem, (LPVOID*)&pItem))) {
   483       nsCOMPtr<nsIJumpListLink> link = 
   484         do_CreateInstance(kJumpListLinkCID, &rv);
   485       if (NS_FAILED(rv))
   486         return NS_ERROR_UNEXPECTED;
   487       rv = JumpListLink::GetJumpListLink(pItem, link);
   488       item = do_QueryInterface(link);
   489     }
   491     if (pLink)
   492       pLink->Release();
   493     if (pItem)
   494       pItem->Release();
   496     if (NS_SUCCEEDED(rv)) {
   497       removedItems->AppendElement(item, false);
   498     }
   499   }
   500   return NS_OK;
   501 }
   503 NS_IMETHODIMP JumpListBuilder::Observe(nsISupports* aSubject,
   504                                         const char* aTopic,
   505                                         const char16_t* aData)
   506 {
   507   if (nsDependentString(aData).EqualsASCII(kPrefTaskbarEnabled)) {
   508     bool enabled = Preferences::GetBool(kPrefTaskbarEnabled, true);
   509     if (!enabled) {
   511       nsCOMPtr<nsIRunnable> event = 
   512         new mozilla::widget::AsyncDeleteAllFaviconsFromDisk();
   513       mIOThread->Dispatch(event, NS_DISPATCH_NORMAL);
   514     }
   515   }
   516   return NS_OK;
   517 }
   519 } // namespace widget
   520 } // namespace mozilla

mercurial