Backed out changeset 2450366cf7ca (bug 1891629) for causing win msix mochitest failures
[gecko.git] / widget / windows / LegacyJumpListItem.cpp
blob3ffddf11f98b46c198a043e368366935397325de
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 "LegacyJumpListItem.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 "nsComponentManagerUtils.h"
18 #include "nsCycleCollectionParticipant.h"
19 #include "mozilla/Preferences.h"
20 #include "LegacyJumpListBuilder.h"
21 #include "WinUtils.h"
23 namespace mozilla {
24 namespace widget {
26 // ISUPPORTS Impl's
27 NS_IMPL_ISUPPORTS(LegacyJumpListItem, nsILegacyJumpListItem)
29 NS_INTERFACE_MAP_BEGIN(LegacyJumpListSeparator)
30 NS_INTERFACE_MAP_ENTRY(nsILegacyJumpListSeparator)
31 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsILegacyJumpListItem,
32 LegacyJumpListItemBase)
33 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, LegacyJumpListItemBase)
34 NS_INTERFACE_MAP_END
35 NS_IMPL_ADDREF(LegacyJumpListSeparator)
36 NS_IMPL_RELEASE(LegacyJumpListSeparator)
38 NS_INTERFACE_MAP_BEGIN(LegacyJumpListLink)
39 NS_INTERFACE_MAP_ENTRY(nsILegacyJumpListLink)
40 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsILegacyJumpListItem,
41 LegacyJumpListItemBase)
42 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, LegacyJumpListItemBase)
43 NS_INTERFACE_MAP_END
44 NS_IMPL_ADDREF(LegacyJumpListLink)
45 NS_IMPL_RELEASE(LegacyJumpListLink)
47 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(LegacyJumpListShortcut)
48 NS_INTERFACE_MAP_ENTRY(nsILegacyJumpListShortcut)
49 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsILegacyJumpListItem,
50 LegacyJumpListItemBase)
51 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsILegacyJumpListShortcut)
52 NS_INTERFACE_MAP_END
53 NS_IMPL_CYCLE_COLLECTING_ADDREF(LegacyJumpListShortcut)
54 NS_IMPL_CYCLE_COLLECTING_RELEASE(LegacyJumpListShortcut)
55 NS_IMPL_CYCLE_COLLECTION(LegacyJumpListShortcut, mHandlerApp)
57 NS_IMETHODIMP LegacyJumpListItemBase::GetType(int16_t* aType) {
58 NS_ENSURE_ARG_POINTER(aType);
60 *aType = mItemType;
62 return NS_OK;
65 NS_IMETHODIMP LegacyJumpListItemBase::Equals(nsILegacyJumpListItem* aItem,
66 bool* aResult) {
67 NS_ENSURE_ARG_POINTER(aItem);
69 *aResult = false;
71 int16_t theType = nsILegacyJumpListItem::JUMPLIST_ITEM_EMPTY;
72 if (NS_FAILED(aItem->GetType(&theType))) return NS_OK;
74 // Make sure the types match.
75 if (Type() != theType) return NS_OK;
77 *aResult = true;
79 return NS_OK;
82 /* link impl. */
84 NS_IMETHODIMP LegacyJumpListLink::GetUri(nsIURI** aURI) {
85 NS_IF_ADDREF(*aURI = mURI);
87 return NS_OK;
90 NS_IMETHODIMP LegacyJumpListLink::SetUri(nsIURI* aURI) {
91 mURI = aURI;
93 return NS_OK;
96 NS_IMETHODIMP LegacyJumpListLink::SetUriTitle(const nsAString& aUriTitle) {
97 mUriTitle.Assign(aUriTitle);
99 return NS_OK;
102 NS_IMETHODIMP LegacyJumpListLink::GetUriTitle(nsAString& aUriTitle) {
103 aUriTitle.Assign(mUriTitle);
105 return NS_OK;
108 NS_IMETHODIMP LegacyJumpListLink::Equals(nsILegacyJumpListItem* aItem,
109 bool* aResult) {
110 NS_ENSURE_ARG_POINTER(aItem);
112 nsresult rv;
114 *aResult = false;
116 int16_t theType = nsILegacyJumpListItem::JUMPLIST_ITEM_EMPTY;
117 if (NS_FAILED(aItem->GetType(&theType))) return NS_OK;
119 // Make sure the types match.
120 if (Type() != theType) return NS_OK;
122 nsCOMPtr<nsILegacyJumpListLink> link = do_QueryInterface(aItem, &rv);
123 if (NS_FAILED(rv)) return rv;
125 // Check the titles
126 nsAutoString title;
127 link->GetUriTitle(title);
128 if (!mUriTitle.Equals(title)) return NS_OK;
130 // Call the internal object's equals() method to check.
131 nsCOMPtr<nsIURI> theUri;
132 bool equals = false;
133 if (NS_SUCCEEDED(link->GetUri(getter_AddRefs(theUri)))) {
134 if (!theUri) {
135 if (!mURI) *aResult = true;
136 return NS_OK;
138 if (NS_SUCCEEDED(theUri->Equals(mURI, &equals)) && equals) {
139 *aResult = true;
143 return NS_OK;
146 /* shortcut impl. */
148 NS_IMETHODIMP LegacyJumpListShortcut::GetApp(nsILocalHandlerApp** aApp) {
149 NS_IF_ADDREF(*aApp = mHandlerApp);
151 return NS_OK;
154 NS_IMETHODIMP LegacyJumpListShortcut::SetApp(nsILocalHandlerApp* aApp) {
155 mHandlerApp = aApp;
156 return NS_OK;
159 NS_IMETHODIMP LegacyJumpListShortcut::GetIconIndex(int32_t* aIconIndex) {
160 NS_ENSURE_ARG_POINTER(aIconIndex);
162 *aIconIndex = mIconIndex;
163 return NS_OK;
166 NS_IMETHODIMP LegacyJumpListShortcut::SetIconIndex(int32_t aIconIndex) {
167 mIconIndex = aIconIndex;
168 return NS_OK;
171 NS_IMETHODIMP LegacyJumpListShortcut::GetFaviconPageUri(
172 nsIURI** aFaviconPageURI) {
173 NS_IF_ADDREF(*aFaviconPageURI = mFaviconPageURI);
175 return NS_OK;
178 NS_IMETHODIMP LegacyJumpListShortcut::SetFaviconPageUri(
179 nsIURI* aFaviconPageURI) {
180 mFaviconPageURI = aFaviconPageURI;
181 return NS_OK;
184 NS_IMETHODIMP LegacyJumpListShortcut::Equals(nsILegacyJumpListItem* aItem,
185 bool* aResult) {
186 NS_ENSURE_ARG_POINTER(aItem);
188 nsresult rv;
190 *aResult = false;
192 int16_t theType = nsILegacyJumpListItem::JUMPLIST_ITEM_EMPTY;
193 if (NS_FAILED(aItem->GetType(&theType))) return NS_OK;
195 // Make sure the types match.
196 if (Type() != theType) return NS_OK;
198 nsCOMPtr<nsILegacyJumpListShortcut> shortcut = do_QueryInterface(aItem, &rv);
199 if (NS_FAILED(rv)) return rv;
201 // Check the icon index
202 // int32_t idx;
203 // shortcut->GetIconIndex(&idx);
204 // if (mIconIndex != idx)
205 // return NS_OK;
206 // No need to check the icon page URI either
208 // Call the internal object's equals() method to check.
209 nsCOMPtr<nsILocalHandlerApp> theApp;
210 bool equals = false;
211 if (NS_SUCCEEDED(shortcut->GetApp(getter_AddRefs(theApp)))) {
212 if (!theApp) {
213 if (!mHandlerApp) *aResult = true;
214 return NS_OK;
216 if (NS_SUCCEEDED(theApp->Equals(mHandlerApp, &equals)) && equals) {
217 *aResult = true;
221 return NS_OK;
224 /* internal helpers */
226 // (static) Creates a ShellLink that encapsulate a separator.
227 nsresult LegacyJumpListSeparator::GetSeparator(
228 RefPtr<IShellLinkW>& aShellLink) {
229 HRESULT hr;
230 IShellLinkW* psl;
232 // Create a IShellLink.
233 hr = CoCreateInstance(CLSID_ShellLink, nullptr, CLSCTX_INPROC_SERVER,
234 IID_IShellLinkW, (LPVOID*)&psl);
235 if (FAILED(hr)) return NS_ERROR_UNEXPECTED;
237 IPropertyStore* pPropStore = nullptr;
238 hr = psl->QueryInterface(IID_IPropertyStore, (LPVOID*)&pPropStore);
239 if (FAILED(hr)) return NS_ERROR_UNEXPECTED;
241 PROPVARIANT pv;
242 InitPropVariantFromBoolean(TRUE, &pv);
244 pPropStore->SetValue(PKEY_AppUserModel_IsDestListSeparator, pv);
245 pPropStore->Commit();
246 pPropStore->Release();
248 PropVariantClear(&pv);
250 aShellLink = dont_AddRef(psl);
252 return NS_OK;
255 // (static) Creates a ShellLink that encapsulate a shortcut to local apps.
256 nsresult LegacyJumpListShortcut::GetShellLink(
257 nsCOMPtr<nsILegacyJumpListItem>& item, RefPtr<IShellLinkW>& aShellLink,
258 RefPtr<LazyIdleThread>& aIOThread) {
259 HRESULT hr;
260 IShellLinkW* psl;
261 nsresult rv;
263 // Shell links:
264 // http://msdn.microsoft.com/en-us/library/bb776891(VS.85).aspx
265 // http://msdn.microsoft.com/en-us/library/bb774950(VS.85).aspx
267 int16_t type;
268 if (NS_FAILED(item->GetType(&type))) return NS_ERROR_INVALID_ARG;
270 if (type != nsILegacyJumpListItem::JUMPLIST_ITEM_SHORTCUT)
271 return NS_ERROR_INVALID_ARG;
273 nsCOMPtr<nsILegacyJumpListShortcut> shortcut = do_QueryInterface(item, &rv);
274 NS_ENSURE_SUCCESS(rv, rv);
276 nsCOMPtr<nsILocalHandlerApp> handlerApp;
277 rv = shortcut->GetApp(getter_AddRefs(handlerApp));
278 NS_ENSURE_SUCCESS(rv, rv);
280 // Create a IShellLink
281 hr = CoCreateInstance(CLSID_ShellLink, nullptr, CLSCTX_INPROC_SERVER,
282 IID_IShellLinkW, (LPVOID*)&psl);
283 if (FAILED(hr)) return NS_ERROR_UNEXPECTED;
285 // Retrieve the app path, title, description and optional command line args.
286 nsAutoString appPath, appTitle, appDescription, appArgs;
287 int32_t appIconIndex = 0;
289 // Path
290 nsCOMPtr<nsIFile> executable;
291 handlerApp->GetExecutable(getter_AddRefs(executable));
293 rv = executable->GetPath(appPath);
294 NS_ENSURE_SUCCESS(rv, rv);
296 // Command line parameters
297 uint32_t count = 0;
298 handlerApp->GetParameterCount(&count);
299 for (uint32_t idx = 0; idx < count; idx++) {
300 if (idx > 0) appArgs.Append(' ');
301 nsAutoString param;
302 rv = handlerApp->GetParameter(idx, param);
303 if (NS_FAILED(rv)) return rv;
304 appArgs.Append(param);
307 handlerApp->GetName(appTitle);
308 handlerApp->GetDetailedDescription(appDescription);
310 bool useUriIcon = false; // if we want to use the URI icon
311 bool usedUriIcon = false; // if we did use the URI icon
312 shortcut->GetIconIndex(&appIconIndex);
314 nsCOMPtr<nsIURI> iconUri;
315 rv = shortcut->GetFaviconPageUri(getter_AddRefs(iconUri));
316 if (NS_SUCCEEDED(rv) && iconUri) {
317 useUriIcon = true;
320 // Store the title of the app
321 if (appTitle.Length() > 0) {
322 IPropertyStore* pPropStore = nullptr;
323 hr = psl->QueryInterface(IID_IPropertyStore, (LPVOID*)&pPropStore);
324 if (FAILED(hr)) return NS_ERROR_UNEXPECTED;
326 PROPVARIANT pv;
327 InitPropVariantFromString(appTitle.get(), &pv);
329 pPropStore->SetValue(PKEY_Title, pv);
330 pPropStore->Commit();
331 pPropStore->Release();
333 PropVariantClear(&pv);
336 // Store the rest of the params
337 psl->SetPath(appPath.get());
338 psl->SetDescription(appDescription.get());
339 psl->SetArguments(appArgs.get());
341 if (useUriIcon) {
342 nsString icoFilePath;
343 rv = mozilla::widget::FaviconHelper::ObtainCachedIconFile(
344 iconUri, icoFilePath, aIOThread, false);
345 if (NS_SUCCEEDED(rv)) {
346 // Always use the first icon in the ICO file
347 // our encoded icon only has 1 resource
348 psl->SetIconLocation(icoFilePath.get(), 0);
349 usedUriIcon = true;
353 // We didn't use an ICO via URI so fall back to the app icon
354 if (!usedUriIcon) {
355 psl->SetIconLocation(appPath.get(), appIconIndex);
358 aShellLink = dont_AddRef(psl);
360 return NS_OK;
363 // If successful fills in the aSame parameter
364 // aSame will be true if the path is in our icon cache
365 static nsresult IsPathInOurIconCache(
366 nsCOMPtr<nsILegacyJumpListShortcut>& aShortcut, wchar_t* aPath,
367 bool* aSame) {
368 NS_ENSURE_ARG_POINTER(aPath);
369 NS_ENSURE_ARG_POINTER(aSame);
371 *aSame = false;
373 // Construct the path of our jump list cache
374 nsCOMPtr<nsIFile> jumpListCache;
375 nsresult rv =
376 NS_GetSpecialDirectory("ProfLDS", getter_AddRefs(jumpListCache));
377 NS_ENSURE_SUCCESS(rv, rv);
378 rv = jumpListCache->AppendNative(
379 nsDependentCString(FaviconHelper::kJumpListCacheDir));
380 NS_ENSURE_SUCCESS(rv, rv);
381 nsAutoString jumpListCachePath;
382 rv = jumpListCache->GetPath(jumpListCachePath);
383 NS_ENSURE_SUCCESS(rv, rv);
385 // Construct the parent path of the passed in path
386 nsCOMPtr<nsIFile> passedInFile =
387 do_CreateInstance("@mozilla.org/file/local;1");
388 NS_ENSURE_TRUE(passedInFile, NS_ERROR_FAILURE);
389 nsAutoString passedInPath(aPath);
390 rv = passedInFile->InitWithPath(passedInPath);
391 nsCOMPtr<nsIFile> passedInParentFile;
392 passedInFile->GetParent(getter_AddRefs(passedInParentFile));
393 nsAutoString passedInParentPath;
394 rv = jumpListCache->GetPath(passedInParentPath);
395 NS_ENSURE_SUCCESS(rv, rv);
397 *aSame = jumpListCachePath.Equals(passedInParentPath);
398 return NS_OK;
401 // (static) For a given IShellLink, create and return a populated
402 // nsILegacyJumpListShortcut.
403 nsresult LegacyJumpListShortcut::GetJumpListShortcut(
404 IShellLinkW* pLink, nsCOMPtr<nsILegacyJumpListShortcut>& aShortcut) {
405 NS_ENSURE_ARG_POINTER(pLink);
407 nsresult rv;
408 HRESULT hres;
410 nsCOMPtr<nsILocalHandlerApp> handlerApp =
411 do_CreateInstance(NS_LOCALHANDLERAPP_CONTRACTID, &rv);
412 NS_ENSURE_SUCCESS(rv, rv);
414 wchar_t buf[MAX_PATH];
416 // Path
417 hres = pLink->GetPath(buf, MAX_PATH, nullptr, SLGP_UNCPRIORITY);
418 if (FAILED(hres)) return NS_ERROR_INVALID_ARG;
420 nsCOMPtr<nsIFile> file;
421 nsDependentString filepath(buf);
422 rv = NS_NewLocalFile(filepath, false, getter_AddRefs(file));
423 NS_ENSURE_SUCCESS(rv, rv);
425 rv = handlerApp->SetExecutable(file);
426 NS_ENSURE_SUCCESS(rv, rv);
428 // Parameters
429 hres = pLink->GetArguments(buf, MAX_PATH);
430 if (SUCCEEDED(hres)) {
431 LPWSTR* arglist;
432 int32_t numArgs;
433 int32_t idx;
435 arglist = ::CommandLineToArgvW(buf, &numArgs);
436 if (arglist) {
437 for (idx = 0; idx < numArgs; idx++) {
438 // szArglist[i] is null terminated
439 nsDependentString arg(arglist[idx]);
440 handlerApp->AppendParameter(arg);
442 ::LocalFree(arglist);
446 rv = aShortcut->SetApp(handlerApp);
447 NS_ENSURE_SUCCESS(rv, rv);
449 // Icon index or file location
450 int iconIdx = 0;
451 hres = pLink->GetIconLocation(buf, MAX_PATH, &iconIdx);
452 if (SUCCEEDED(hres)) {
453 // XXX How do we handle converting local files to images here? Do we need
454 // to?
455 aShortcut->SetIconIndex(iconIdx);
457 // Obtain the local profile directory and construct the output icon file
458 // path We only set the Icon Uri if we're sure it was from our icon cache.
459 bool isInOurCache;
460 if (NS_SUCCEEDED(IsPathInOurIconCache(aShortcut, buf, &isInOurCache)) &&
461 isInOurCache) {
462 nsCOMPtr<nsIURI> iconUri;
463 nsAutoString path(buf);
464 rv = NS_NewURI(getter_AddRefs(iconUri), path);
465 if (NS_SUCCEEDED(rv)) {
466 aShortcut->SetFaviconPageUri(iconUri);
471 // Do we need the title and description? Probably not since handler app
472 // doesn't compare these in equals.
474 return NS_OK;
477 // (static) ShellItems are used to encapsulate links to things. We currently
478 // only support URI links, but more support could be added, such as local file
479 // and directory links.
480 nsresult LegacyJumpListLink::GetShellItem(nsCOMPtr<nsILegacyJumpListItem>& item,
481 RefPtr<IShellItem2>& aShellItem) {
482 IShellItem2* psi = nullptr;
483 nsresult rv;
485 int16_t type;
486 if (NS_FAILED(item->GetType(&type))) return NS_ERROR_INVALID_ARG;
488 if (type != nsILegacyJumpListItem::JUMPLIST_ITEM_LINK)
489 return NS_ERROR_INVALID_ARG;
491 nsCOMPtr<nsILegacyJumpListLink> link = do_QueryInterface(item, &rv);
492 NS_ENSURE_SUCCESS(rv, rv);
494 nsCOMPtr<nsIURI> uri;
495 rv = link->GetUri(getter_AddRefs(uri));
496 NS_ENSURE_SUCCESS(rv, rv);
498 nsAutoCString spec;
499 rv = uri->GetSpec(spec);
500 NS_ENSURE_SUCCESS(rv, rv);
502 // Create the IShellItem
503 if (FAILED(SHCreateItemFromParsingName(NS_ConvertASCIItoUTF16(spec).get(),
504 nullptr, IID_PPV_ARGS(&psi)))) {
505 return NS_ERROR_INVALID_ARG;
508 // Set the title
509 nsAutoString linkTitle;
510 link->GetUriTitle(linkTitle);
512 IPropertyStore* pPropStore = nullptr;
513 HRESULT hres = psi->GetPropertyStore(GPS_DEFAULT, IID_IPropertyStore,
514 (void**)&pPropStore);
515 if (FAILED(hres)) return NS_ERROR_UNEXPECTED;
517 PROPVARIANT pv;
518 InitPropVariantFromString(linkTitle.get(), &pv);
520 // May fail due to shell item access permissions.
521 pPropStore->SetValue(PKEY_ItemName, pv);
522 pPropStore->Commit();
523 pPropStore->Release();
525 PropVariantClear(&pv);
527 aShellItem = dont_AddRef(psi);
529 return NS_OK;
532 // (static) For a given IShellItem, create and return a populated
533 // nsILegacyJumpListLink.
534 nsresult LegacyJumpListLink::GetJumpListLink(
535 IShellItem* pItem, nsCOMPtr<nsILegacyJumpListLink>& aLink) {
536 NS_ENSURE_ARG_POINTER(pItem);
538 // We assume for now these are URI links, but through properties we could
539 // query and create other types.
540 nsresult rv;
541 LPWSTR lpstrName = nullptr;
543 if (SUCCEEDED(pItem->GetDisplayName(SIGDN_URL, &lpstrName))) {
544 nsCOMPtr<nsIURI> uri;
545 nsAutoString spec(lpstrName);
547 rv = NS_NewURI(getter_AddRefs(uri), NS_ConvertUTF16toUTF8(spec));
548 if (NS_FAILED(rv)) return NS_ERROR_INVALID_ARG;
550 aLink->SetUri(uri);
552 ::CoTaskMemFree(lpstrName);
555 return NS_OK;
558 } // namespace widget
559 } // namespace mozilla