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"
9 #include <propvarutil.h>
13 #include "nsNetUtil.h"
16 #include "nsCExternalHandlerService.h"
17 #include "nsComponentManagerUtils.h"
18 #include "nsCycleCollectionParticipant.h"
19 #include "mozilla/Preferences.h"
20 #include "LegacyJumpListBuilder.h"
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
)
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
)
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
)
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
);
65 NS_IMETHODIMP
LegacyJumpListItemBase::Equals(nsILegacyJumpListItem
* aItem
,
67 NS_ENSURE_ARG_POINTER(aItem
);
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
;
84 NS_IMETHODIMP
LegacyJumpListLink::GetUri(nsIURI
** aURI
) {
85 NS_IF_ADDREF(*aURI
= mURI
);
90 NS_IMETHODIMP
LegacyJumpListLink::SetUri(nsIURI
* aURI
) {
96 NS_IMETHODIMP
LegacyJumpListLink::SetUriTitle(const nsAString
& aUriTitle
) {
97 mUriTitle
.Assign(aUriTitle
);
102 NS_IMETHODIMP
LegacyJumpListLink::GetUriTitle(nsAString
& aUriTitle
) {
103 aUriTitle
.Assign(mUriTitle
);
108 NS_IMETHODIMP
LegacyJumpListLink::Equals(nsILegacyJumpListItem
* aItem
,
110 NS_ENSURE_ARG_POINTER(aItem
);
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
;
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
;
133 if (NS_SUCCEEDED(link
->GetUri(getter_AddRefs(theUri
)))) {
135 if (!mURI
) *aResult
= true;
138 if (NS_SUCCEEDED(theUri
->Equals(mURI
, &equals
)) && equals
) {
148 NS_IMETHODIMP
LegacyJumpListShortcut::GetApp(nsILocalHandlerApp
** aApp
) {
149 NS_IF_ADDREF(*aApp
= mHandlerApp
);
154 NS_IMETHODIMP
LegacyJumpListShortcut::SetApp(nsILocalHandlerApp
* aApp
) {
159 NS_IMETHODIMP
LegacyJumpListShortcut::GetIconIndex(int32_t* aIconIndex
) {
160 NS_ENSURE_ARG_POINTER(aIconIndex
);
162 *aIconIndex
= mIconIndex
;
166 NS_IMETHODIMP
LegacyJumpListShortcut::SetIconIndex(int32_t aIconIndex
) {
167 mIconIndex
= aIconIndex
;
171 NS_IMETHODIMP
LegacyJumpListShortcut::GetFaviconPageUri(
172 nsIURI
** aFaviconPageURI
) {
173 NS_IF_ADDREF(*aFaviconPageURI
= mFaviconPageURI
);
178 NS_IMETHODIMP
LegacyJumpListShortcut::SetFaviconPageUri(
179 nsIURI
* aFaviconPageURI
) {
180 mFaviconPageURI
= aFaviconPageURI
;
184 NS_IMETHODIMP
LegacyJumpListShortcut::Equals(nsILegacyJumpListItem
* aItem
,
186 NS_ENSURE_ARG_POINTER(aItem
);
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
203 // shortcut->GetIconIndex(&idx);
204 // if (mIconIndex != idx)
206 // No need to check the icon page URI either
208 // Call the internal object's equals() method to check.
209 nsCOMPtr
<nsILocalHandlerApp
> theApp
;
211 if (NS_SUCCEEDED(shortcut
->GetApp(getter_AddRefs(theApp
)))) {
213 if (!mHandlerApp
) *aResult
= true;
216 if (NS_SUCCEEDED(theApp
->Equals(mHandlerApp
, &equals
)) && equals
) {
224 /* internal helpers */
226 // (static) Creates a ShellLink that encapsulate a separator.
227 nsresult
LegacyJumpListSeparator::GetSeparator(
228 RefPtr
<IShellLinkW
>& aShellLink
) {
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
;
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
);
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
) {
264 // http://msdn.microsoft.com/en-us/library/bb776891(VS.85).aspx
265 // http://msdn.microsoft.com/en-us/library/bb774950(VS.85).aspx
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;
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
298 handlerApp
->GetParameterCount(&count
);
299 for (uint32_t idx
= 0; idx
< count
; idx
++) {
300 if (idx
> 0) appArgs
.Append(' ');
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
) {
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
;
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());
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);
353 // We didn't use an ICO via URI so fall back to the app icon
355 psl
->SetIconLocation(appPath
.get(), appIconIndex
);
358 aShellLink
= dont_AddRef(psl
);
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
,
368 NS_ENSURE_ARG_POINTER(aPath
);
369 NS_ENSURE_ARG_POINTER(aSame
);
373 // Construct the path of our jump list cache
374 nsCOMPtr
<nsIFile
> jumpListCache
;
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
);
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
);
410 nsCOMPtr
<nsILocalHandlerApp
> handlerApp
=
411 do_CreateInstance(NS_LOCALHANDLERAPP_CONTRACTID
, &rv
);
412 NS_ENSURE_SUCCESS(rv
, rv
);
414 wchar_t buf
[MAX_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
);
429 hres
= pLink
->GetArguments(buf
, MAX_PATH
);
430 if (SUCCEEDED(hres
)) {
435 arglist
= ::CommandLineToArgvW(buf
, &numArgs
);
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
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
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.
460 if (NS_SUCCEEDED(IsPathInOurIconCache(aShortcut
, buf
, &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.
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;
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
);
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
;
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
;
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
);
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.
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
;
552 ::CoTaskMemFree(lpstrName
);
558 } // namespace widget
559 } // namespace mozilla