widget/windows/JumpListItem.cpp

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

     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

mercurial