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"
9 #include <propvarutil.h>
13 #include "nsNetUtil.h"
16 #include "nsCExternalHandlerService.h"
17 #include "nsCycleCollectionParticipant.h"
18 #include "mozilla/Preferences.h"
19 #include "JumpListBuilder.h"
26 NS_IMPL_ISUPPORTS(JumpListItem
, nsIJumpListItem
)
28 NS_INTERFACE_MAP_BEGIN(JumpListSeparator
)
29 NS_INTERFACE_MAP_ENTRY(nsIJumpListSeparator
)
30 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsIJumpListItem
, JumpListItemBase
)
31 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports
, JumpListItemBase
)
33 NS_IMPL_ADDREF(JumpListSeparator
)
34 NS_IMPL_RELEASE(JumpListSeparator
)
36 NS_INTERFACE_MAP_BEGIN(JumpListLink
)
37 NS_INTERFACE_MAP_ENTRY(nsIJumpListLink
)
38 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsIJumpListItem
, JumpListItemBase
)
39 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports
, JumpListItemBase
)
41 NS_IMPL_ADDREF(JumpListLink
)
42 NS_IMPL_RELEASE(JumpListLink
)
44 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(JumpListShortcut
)
45 NS_INTERFACE_MAP_ENTRY(nsIJumpListShortcut
)
46 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsIJumpListItem
, JumpListItemBase
)
47 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports
, nsIJumpListShortcut
)
49 NS_IMPL_CYCLE_COLLECTING_ADDREF(JumpListShortcut
)
50 NS_IMPL_CYCLE_COLLECTING_RELEASE(JumpListShortcut
)
51 NS_IMPL_CYCLE_COLLECTION(JumpListShortcut
, mHandlerApp
)
53 NS_IMETHODIMP
JumpListItemBase::GetType(int16_t* aType
) {
54 NS_ENSURE_ARG_POINTER(aType
);
61 NS_IMETHODIMP
JumpListItemBase::Equals(nsIJumpListItem
* aItem
, bool* aResult
) {
62 NS_ENSURE_ARG_POINTER(aItem
);
66 int16_t theType
= nsIJumpListItem::JUMPLIST_ITEM_EMPTY
;
67 if (NS_FAILED(aItem
->GetType(&theType
))) return NS_OK
;
69 // Make sure the types match.
70 if (Type() != theType
) return NS_OK
;
79 NS_IMETHODIMP
JumpListLink::GetUri(nsIURI
** aURI
) {
80 NS_IF_ADDREF(*aURI
= mURI
);
85 NS_IMETHODIMP
JumpListLink::SetUri(nsIURI
* aURI
) {
91 NS_IMETHODIMP
JumpListLink::SetUriTitle(const nsAString
& aUriTitle
) {
92 mUriTitle
.Assign(aUriTitle
);
97 NS_IMETHODIMP
JumpListLink::GetUriTitle(nsAString
& aUriTitle
) {
98 aUriTitle
.Assign(mUriTitle
);
103 NS_IMETHODIMP
JumpListLink::GetUriHash(nsACString
& aUriHash
) {
104 if (!mURI
) return NS_ERROR_NOT_AVAILABLE
;
106 return mozilla::widget::FaviconHelper::HashURI(mCryptoHash
, mURI
, aUriHash
);
109 NS_IMETHODIMP
JumpListLink::CompareHash(nsIURI
* aUri
, bool* aResult
) {
117 NS_ENSURE_ARG_POINTER(aUri
);
119 nsAutoCString hash1
, hash2
;
121 rv
= mozilla::widget::FaviconHelper::HashURI(mCryptoHash
, mURI
, hash1
);
122 NS_ENSURE_SUCCESS(rv
, rv
);
123 rv
= mozilla::widget::FaviconHelper::HashURI(mCryptoHash
, aUri
, hash2
);
124 NS_ENSURE_SUCCESS(rv
, rv
);
126 *aResult
= hash1
.Equals(hash2
);
131 NS_IMETHODIMP
JumpListLink::Equals(nsIJumpListItem
* aItem
, bool* aResult
) {
132 NS_ENSURE_ARG_POINTER(aItem
);
138 int16_t theType
= nsIJumpListItem::JUMPLIST_ITEM_EMPTY
;
139 if (NS_FAILED(aItem
->GetType(&theType
))) return NS_OK
;
141 // Make sure the types match.
142 if (Type() != theType
) return NS_OK
;
144 nsCOMPtr
<nsIJumpListLink
> link
= do_QueryInterface(aItem
, &rv
);
145 if (NS_FAILED(rv
)) return rv
;
149 link
->GetUriTitle(title
);
150 if (!mUriTitle
.Equals(title
)) return NS_OK
;
152 // Call the internal object's equals() method to check.
153 nsCOMPtr
<nsIURI
> theUri
;
155 if (NS_SUCCEEDED(link
->GetUri(getter_AddRefs(theUri
)))) {
157 if (!mURI
) *aResult
= true;
160 if (NS_SUCCEEDED(theUri
->Equals(mURI
, &equals
)) && equals
) {
170 NS_IMETHODIMP
JumpListShortcut::GetApp(nsILocalHandlerApp
** aApp
) {
171 NS_IF_ADDREF(*aApp
= mHandlerApp
);
176 NS_IMETHODIMP
JumpListShortcut::SetApp(nsILocalHandlerApp
* aApp
) {
181 NS_IMETHODIMP
JumpListShortcut::GetIconIndex(int32_t* aIconIndex
) {
182 NS_ENSURE_ARG_POINTER(aIconIndex
);
184 *aIconIndex
= mIconIndex
;
188 NS_IMETHODIMP
JumpListShortcut::SetIconIndex(int32_t aIconIndex
) {
189 mIconIndex
= aIconIndex
;
193 NS_IMETHODIMP
JumpListShortcut::GetFaviconPageUri(nsIURI
** aFaviconPageURI
) {
194 NS_IF_ADDREF(*aFaviconPageURI
= mFaviconPageURI
);
199 NS_IMETHODIMP
JumpListShortcut::SetFaviconPageUri(nsIURI
* aFaviconPageURI
) {
200 mFaviconPageURI
= aFaviconPageURI
;
204 NS_IMETHODIMP
JumpListShortcut::Equals(nsIJumpListItem
* aItem
, bool* aResult
) {
205 NS_ENSURE_ARG_POINTER(aItem
);
211 int16_t theType
= nsIJumpListItem::JUMPLIST_ITEM_EMPTY
;
212 if (NS_FAILED(aItem
->GetType(&theType
))) return NS_OK
;
214 // Make sure the types match.
215 if (Type() != theType
) return NS_OK
;
217 nsCOMPtr
<nsIJumpListShortcut
> shortcut
= do_QueryInterface(aItem
, &rv
);
218 if (NS_FAILED(rv
)) return rv
;
220 // Check the icon index
222 // shortcut->GetIconIndex(&idx);
223 // if (mIconIndex != idx)
225 // No need to check the icon page URI either
227 // Call the internal object's equals() method to check.
228 nsCOMPtr
<nsILocalHandlerApp
> theApp
;
230 if (NS_SUCCEEDED(shortcut
->GetApp(getter_AddRefs(theApp
)))) {
232 if (!mHandlerApp
) *aResult
= true;
235 if (NS_SUCCEEDED(theApp
->Equals(mHandlerApp
, &equals
)) && equals
) {
243 /* internal helpers */
245 // (static) Creates a ShellLink that encapsulate a separator.
246 nsresult
JumpListSeparator::GetSeparator(RefPtr
<IShellLinkW
>& aShellLink
) {
250 // Create a IShellLink.
251 hr
= CoCreateInstance(CLSID_ShellLink
, nullptr, CLSCTX_INPROC_SERVER
,
252 IID_IShellLinkW
, (LPVOID
*)&psl
);
253 if (FAILED(hr
)) return NS_ERROR_UNEXPECTED
;
255 IPropertyStore
* pPropStore
= nullptr;
256 hr
= psl
->QueryInterface(IID_IPropertyStore
, (LPVOID
*)&pPropStore
);
257 if (FAILED(hr
)) return NS_ERROR_UNEXPECTED
;
260 InitPropVariantFromBoolean(TRUE
, &pv
);
262 pPropStore
->SetValue(PKEY_AppUserModel_IsDestListSeparator
, pv
);
263 pPropStore
->Commit();
264 pPropStore
->Release();
266 PropVariantClear(&pv
);
268 aShellLink
= dont_AddRef(psl
);
273 // (static) Creates a ShellLink that encapsulate a shortcut to local apps.
274 nsresult
JumpListShortcut::GetShellLink(nsCOMPtr
<nsIJumpListItem
>& item
,
275 RefPtr
<IShellLinkW
>& aShellLink
,
276 nsCOMPtr
<nsIThread
>& aIOThread
) {
282 // http://msdn.microsoft.com/en-us/library/bb776891(VS.85).aspx
283 // http://msdn.microsoft.com/en-us/library/bb774950(VS.85).aspx
286 if (NS_FAILED(item
->GetType(&type
))) return NS_ERROR_INVALID_ARG
;
288 if (type
!= nsIJumpListItem::JUMPLIST_ITEM_SHORTCUT
)
289 return NS_ERROR_INVALID_ARG
;
291 nsCOMPtr
<nsIJumpListShortcut
> shortcut
= do_QueryInterface(item
, &rv
);
292 NS_ENSURE_SUCCESS(rv
, rv
);
294 nsCOMPtr
<nsILocalHandlerApp
> handlerApp
;
295 rv
= shortcut
->GetApp(getter_AddRefs(handlerApp
));
296 NS_ENSURE_SUCCESS(rv
, rv
);
298 // Create a IShellLink
299 hr
= CoCreateInstance(CLSID_ShellLink
, nullptr, CLSCTX_INPROC_SERVER
,
300 IID_IShellLinkW
, (LPVOID
*)&psl
);
301 if (FAILED(hr
)) return NS_ERROR_UNEXPECTED
;
303 // Retrieve the app path, title, description and optional command line args.
304 nsAutoString appPath
, appTitle
, appDescription
, appArgs
;
305 int32_t appIconIndex
= 0;
308 nsCOMPtr
<nsIFile
> executable
;
309 handlerApp
->GetExecutable(getter_AddRefs(executable
));
311 rv
= executable
->GetPath(appPath
);
312 NS_ENSURE_SUCCESS(rv
, rv
);
314 // Command line parameters
316 handlerApp
->GetParameterCount(&count
);
317 for (uint32_t idx
= 0; idx
< count
; idx
++) {
318 if (idx
> 0) appArgs
.Append(' ');
320 rv
= handlerApp
->GetParameter(idx
, param
);
321 if (NS_FAILED(rv
)) return rv
;
322 appArgs
.Append(param
);
325 handlerApp
->GetName(appTitle
);
326 handlerApp
->GetDetailedDescription(appDescription
);
328 bool useUriIcon
= false; // if we want to use the URI icon
329 bool usedUriIcon
= false; // if we did use the URI icon
330 shortcut
->GetIconIndex(&appIconIndex
);
332 nsCOMPtr
<nsIURI
> iconUri
;
333 rv
= shortcut
->GetFaviconPageUri(getter_AddRefs(iconUri
));
334 if (NS_SUCCEEDED(rv
) && iconUri
) {
338 // Store the title of the app
339 if (appTitle
.Length() > 0) {
340 IPropertyStore
* pPropStore
= nullptr;
341 hr
= psl
->QueryInterface(IID_IPropertyStore
, (LPVOID
*)&pPropStore
);
342 if (FAILED(hr
)) return NS_ERROR_UNEXPECTED
;
345 InitPropVariantFromString(appTitle
.get(), &pv
);
347 pPropStore
->SetValue(PKEY_Title
, pv
);
348 pPropStore
->Commit();
349 pPropStore
->Release();
351 PropVariantClear(&pv
);
354 // Store the rest of the params
355 psl
->SetPath(appPath
.get());
356 psl
->SetDescription(appDescription
.get());
357 psl
->SetArguments(appArgs
.get());
360 nsString icoFilePath
;
361 rv
= mozilla::widget::FaviconHelper::ObtainCachedIconFile(
362 iconUri
, icoFilePath
, aIOThread
, false);
363 if (NS_SUCCEEDED(rv
)) {
364 // Always use the first icon in the ICO file
365 // our encoded icon only has 1 resource
366 psl
->SetIconLocation(icoFilePath
.get(), 0);
371 // We didn't use an ICO via URI so fall back to the app icon
373 psl
->SetIconLocation(appPath
.get(), appIconIndex
);
376 aShellLink
= dont_AddRef(psl
);
381 // If successful fills in the aSame parameter
382 // aSame will be true if the path is in our icon cache
383 static nsresult
IsPathInOurIconCache(nsCOMPtr
<nsIJumpListShortcut
>& aShortcut
,
384 wchar_t* aPath
, bool* aSame
) {
385 NS_ENSURE_ARG_POINTER(aPath
);
386 NS_ENSURE_ARG_POINTER(aSame
);
390 // Construct the path of our jump list cache
391 nsCOMPtr
<nsIFile
> jumpListCache
;
393 NS_GetSpecialDirectory("ProfLDS", getter_AddRefs(jumpListCache
));
394 NS_ENSURE_SUCCESS(rv
, rv
);
395 rv
= jumpListCache
->AppendNative(
396 nsDependentCString(FaviconHelper::kJumpListCacheDir
));
397 NS_ENSURE_SUCCESS(rv
, rv
);
398 nsAutoString jumpListCachePath
;
399 rv
= jumpListCache
->GetPath(jumpListCachePath
);
400 NS_ENSURE_SUCCESS(rv
, rv
);
402 // Construct the parent path of the passed in path
403 nsCOMPtr
<nsIFile
> passedInFile
=
404 do_CreateInstance("@mozilla.org/file/local;1");
405 NS_ENSURE_TRUE(passedInFile
, NS_ERROR_FAILURE
);
406 nsAutoString
passedInPath(aPath
);
407 rv
= passedInFile
->InitWithPath(passedInPath
);
408 nsCOMPtr
<nsIFile
> passedInParentFile
;
409 passedInFile
->GetParent(getter_AddRefs(passedInParentFile
));
410 nsAutoString passedInParentPath
;
411 rv
= jumpListCache
->GetPath(passedInParentPath
);
412 NS_ENSURE_SUCCESS(rv
, rv
);
414 *aSame
= jumpListCachePath
.Equals(passedInParentPath
);
418 // (static) For a given IShellLink, create and return a populated
419 // nsIJumpListShortcut.
420 nsresult
JumpListShortcut::GetJumpListShortcut(
421 IShellLinkW
* pLink
, nsCOMPtr
<nsIJumpListShortcut
>& aShortcut
) {
422 NS_ENSURE_ARG_POINTER(pLink
);
427 nsCOMPtr
<nsILocalHandlerApp
> handlerApp
=
428 do_CreateInstance(NS_LOCALHANDLERAPP_CONTRACTID
, &rv
);
429 NS_ENSURE_SUCCESS(rv
, rv
);
431 wchar_t buf
[MAX_PATH
];
434 hres
= pLink
->GetPath(buf
, MAX_PATH
, nullptr, SLGP_UNCPRIORITY
);
435 if (FAILED(hres
)) return NS_ERROR_INVALID_ARG
;
437 nsCOMPtr
<nsIFile
> file
;
438 nsDependentString
filepath(buf
);
439 rv
= NS_NewLocalFile(filepath
, false, getter_AddRefs(file
));
440 NS_ENSURE_SUCCESS(rv
, rv
);
442 rv
= handlerApp
->SetExecutable(file
);
443 NS_ENSURE_SUCCESS(rv
, rv
);
446 hres
= pLink
->GetArguments(buf
, MAX_PATH
);
447 if (SUCCEEDED(hres
)) {
452 arglist
= ::CommandLineToArgvW(buf
, &numArgs
);
454 for (idx
= 0; idx
< numArgs
; idx
++) {
455 // szArglist[i] is null terminated
456 nsDependentString
arg(arglist
[idx
]);
457 handlerApp
->AppendParameter(arg
);
459 ::LocalFree(arglist
);
463 rv
= aShortcut
->SetApp(handlerApp
);
464 NS_ENSURE_SUCCESS(rv
, rv
);
466 // Icon index or file location
468 hres
= pLink
->GetIconLocation(buf
, MAX_PATH
, &iconIdx
);
469 if (SUCCEEDED(hres
)) {
470 // XXX How do we handle converting local files to images here? Do we need
472 aShortcut
->SetIconIndex(iconIdx
);
474 // Obtain the local profile directory and construct the output icon file
475 // path We only set the Icon Uri if we're sure it was from our icon cache.
477 if (NS_SUCCEEDED(IsPathInOurIconCache(aShortcut
, buf
, &isInOurCache
)) &&
479 nsCOMPtr
<nsIURI
> iconUri
;
480 nsAutoString
path(buf
);
481 rv
= NS_NewURI(getter_AddRefs(iconUri
), path
);
482 if (NS_SUCCEEDED(rv
)) {
483 aShortcut
->SetFaviconPageUri(iconUri
);
488 // Do we need the title and description? Probably not since handler app
489 // doesn't compare these in equals.
494 // (static) ShellItems are used to encapsulate links to things. We currently
495 // only support URI links, but more support could be added, such as local file
496 // and directory links.
497 nsresult
JumpListLink::GetShellItem(nsCOMPtr
<nsIJumpListItem
>& item
,
498 RefPtr
<IShellItem2
>& aShellItem
) {
499 IShellItem2
* psi
= nullptr;
503 if (NS_FAILED(item
->GetType(&type
))) return NS_ERROR_INVALID_ARG
;
505 if (type
!= nsIJumpListItem::JUMPLIST_ITEM_LINK
) return NS_ERROR_INVALID_ARG
;
507 nsCOMPtr
<nsIJumpListLink
> link
= do_QueryInterface(item
, &rv
);
508 NS_ENSURE_SUCCESS(rv
, rv
);
510 nsCOMPtr
<nsIURI
> uri
;
511 rv
= link
->GetUri(getter_AddRefs(uri
));
512 NS_ENSURE_SUCCESS(rv
, rv
);
515 rv
= uri
->GetSpec(spec
);
516 NS_ENSURE_SUCCESS(rv
, rv
);
518 // Create the IShellItem
519 if (FAILED(SHCreateItemFromParsingName(NS_ConvertASCIItoUTF16(spec
).get(),
520 nullptr, IID_PPV_ARGS(&psi
)))) {
521 return NS_ERROR_INVALID_ARG
;
525 nsAutoString linkTitle
;
526 link
->GetUriTitle(linkTitle
);
528 IPropertyStore
* pPropStore
= nullptr;
529 HRESULT hres
= psi
->GetPropertyStore(GPS_DEFAULT
, IID_IPropertyStore
,
530 (void**)&pPropStore
);
531 if (FAILED(hres
)) return NS_ERROR_UNEXPECTED
;
534 InitPropVariantFromString(linkTitle
.get(), &pv
);
536 // May fail due to shell item access permissions.
537 pPropStore
->SetValue(PKEY_ItemName
, pv
);
538 pPropStore
->Commit();
539 pPropStore
->Release();
541 PropVariantClear(&pv
);
543 aShellItem
= dont_AddRef(psi
);
548 // (static) For a given IShellItem, create and return a populated
550 nsresult
JumpListLink::GetJumpListLink(IShellItem
* pItem
,
551 nsCOMPtr
<nsIJumpListLink
>& aLink
) {
552 NS_ENSURE_ARG_POINTER(pItem
);
554 // We assume for now these are URI links, but through properties we could
555 // query and create other types.
557 LPWSTR lpstrName
= nullptr;
559 if (SUCCEEDED(pItem
->GetDisplayName(SIGDN_URL
, &lpstrName
))) {
560 nsCOMPtr
<nsIURI
> uri
;
561 nsAutoString
spec(lpstrName
);
563 rv
= NS_NewURI(getter_AddRefs(uri
), NS_ConvertUTF16toUTF8(spec
));
564 if (NS_FAILED(rv
)) return NS_ERROR_INVALID_ARG
;
568 ::CoTaskMemFree(lpstrName
);
574 } // namespace widget
575 } // namespace mozilla