CLOSED TREE: TraceMonkey merge head. (a=blockers)
[mozilla-central.git] / widget / src / windows / JumpListItem.cpp
blobbd82a0e37143084e0245bc65fa3728f7f04f1192
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* ***** BEGIN LICENSE BLOCK *****
3 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
5 * The contents of this file are subject to the Mozilla Public License Version
6 * 1.1 (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 * http://www.mozilla.org/MPL/
10 * Software distributed under the License is distributed on an "AS IS" basis,
11 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 * for the specific language governing rights and limitations under the
13 * License.
15 * The Original Code is mozilla.org code.
17 * The Initial Developer of the Original Code is
18 * Mozilla Foundation.
19 * Portions created by the Initial Developer are Copyright (C) 2009
20 * the Initial Developer. All Rights Reserved.
22 * Contributor(s):
23 * Jim Mathies <jmathies@mozilla.com>
25 * Alternatively, the contents of this file may be used under the terms of
26 * either the GNU General Public License Version 2 or later (the "GPL"), or
27 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
28 * in which case the provisions of the GPL or the LGPL are applicable instead
29 * of those above. If you wish to allow use of your version of this file only
30 * under the terms of either the GPL or the LGPL, and not to allow others to
31 * use your version of this file under the terms of the MPL, indicate your
32 * decision by deleting the provisions above and replace them with the notice
33 * and other provisions required by the GPL or the LGPL. If you do not delete
34 * the provisions above, a recipient may use your version of this file under
35 * the terms of any one of the MPL, the GPL or the LGPL.
37 * ***** END LICENSE BLOCK ***** */
39 #if MOZ_WINSDK_TARGETVER >= MOZ_NTDDI_WIN7
41 #include "JumpListItem.h"
43 #include <shellapi.h>
44 #include <propvarutil.h>
45 #include <propkey.h>
47 #include "nsIFile.h"
48 #include "nsILocalFile.h"
49 #include "nsNetUtil.h"
50 #include "nsCRT.h"
51 #include "nsNetCID.h"
52 #include "nsCExternalHandlerService.h"
53 #include "nsCycleCollectionParticipant.h"
55 namespace mozilla {
56 namespace widget {
58 // SHCreateItemFromParsingName is only available on vista and up. We only load this if we
59 // need to call it on win7+.
60 JumpListLink::SHCreateItemFromParsingNamePtr JumpListLink::createItemFromParsingName = nsnull;
61 const PRUnichar JumpListLink::kSehllLibraryName[] = L"shell32.dll";
62 HMODULE JumpListLink::sShellDll = nsnull;
64 // ISUPPORTS Impl's
65 NS_IMPL_ISUPPORTS1(JumpListItem,
66 nsIJumpListItem)
68 NS_IMPL_ISUPPORTS_INHERITED1(JumpListSeparator,
69 JumpListItem,
70 nsIJumpListSeparator)
72 NS_IMPL_ISUPPORTS_INHERITED1(JumpListLink,
73 JumpListItem,
74 nsIJumpListLink)
76 NS_IMPL_CYCLE_COLLECTION_CLASS(JumpListShortcut)
78 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(JumpListShortcut)
79 NS_INTERFACE_MAP_ENTRY(nsIJumpListShortcut)
80 NS_INTERFACE_MAP_END_INHERITING(JumpListItem)
82 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(JumpListShortcut)
83 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mHandlerApp)
84 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
86 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(JumpListShortcut)
87 NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mHandlerApp)
88 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
90 NS_IMPL_CYCLE_COLLECTING_ADDREF(JumpListShortcut)
91 NS_IMPL_CYCLE_COLLECTING_RELEASE(JumpListShortcut)
93 /* attribute short type; */
94 NS_IMETHODIMP JumpListItem::GetType(PRInt16 *aType)
96 NS_ENSURE_ARG_POINTER(aType);
98 *aType = mItemType;
100 return NS_OK;
103 /* boolean equals(nsIJumpListItem item); */
104 NS_IMETHODIMP JumpListItem::Equals(nsIJumpListItem *aItem, PRBool *aResult)
106 NS_ENSURE_ARG_POINTER(aItem);
108 *aResult = PR_FALSE;
110 PRInt16 theType = nsIJumpListItem::JUMPLIST_ITEM_EMPTY;
111 if (NS_FAILED(aItem->GetType(&theType)))
112 return NS_OK;
114 // Make sure the types match.
115 if (Type() != theType)
116 return NS_OK;
118 *aResult = PR_TRUE;
120 return NS_OK;
123 /* link impl. */
125 /* attribute nsIURI uri; */
126 NS_IMETHODIMP JumpListLink::GetUri(nsIURI **aURI)
128 NS_IF_ADDREF(*aURI = mURI);
130 return NS_OK;
133 NS_IMETHODIMP JumpListLink::SetUri(nsIURI *aURI)
135 mURI = aURI;
137 return NS_OK;
140 /* attribute AString uriTitle; */
141 NS_IMETHODIMP JumpListLink::SetUriTitle(const nsAString &aUriTitle)
143 mUriTitle.Assign(aUriTitle);
145 return NS_OK;
148 NS_IMETHODIMP JumpListLink::GetUriTitle(nsAString& aUriTitle)
150 aUriTitle.Assign(mUriTitle);
152 return NS_OK;
155 /* readonly attribute long uriHash; */
156 NS_IMETHODIMP JumpListLink::GetUriHash(nsACString& aUriHash)
158 if (!mURI)
159 return NS_ERROR_NOT_AVAILABLE;
161 return HashURI(mURI, aUriHash);
164 /* boolean compareHash(in nsIURI uri); */
165 NS_IMETHODIMP JumpListLink::CompareHash(nsIURI *aUri, PRBool *aResult)
167 nsresult rv;
169 if (!mURI) {
170 *aResult = !aUri;
171 return NS_OK;
174 NS_ENSURE_ARG_POINTER(aUri);
176 nsCAutoString hash1, hash2;
178 rv = HashURI(mURI, hash1);
179 NS_ENSURE_SUCCESS(rv, rv);
180 rv = HashURI(aUri, hash2);
181 NS_ENSURE_SUCCESS(rv, rv);
183 *aResult = hash1.Equals(hash2);
185 return NS_OK;
188 /* boolean equals(nsIJumpListItem item); */
189 NS_IMETHODIMP JumpListLink::Equals(nsIJumpListItem *aItem, PRBool *aResult)
191 NS_ENSURE_ARG_POINTER(aItem);
193 nsresult rv;
195 *aResult = PR_FALSE;
197 PRInt16 theType = nsIJumpListItem::JUMPLIST_ITEM_EMPTY;
198 if (NS_FAILED(aItem->GetType(&theType)))
199 return NS_OK;
201 // Make sure the types match.
202 if (Type() != theType)
203 return NS_OK;
205 nsCOMPtr<nsIJumpListLink> link = do_QueryInterface(aItem, &rv);
206 if (NS_FAILED(rv))
207 return rv;
209 // Check the titles
210 nsAutoString title;
211 link->GetUriTitle(title);
212 if (!mUriTitle.Equals(title))
213 return NS_OK;
215 // Call the internal object's equals() method to check.
216 nsCOMPtr<nsIURI> theUri;
217 PRBool equals = PR_FALSE;
218 if (NS_SUCCEEDED(link->GetUri(getter_AddRefs(theUri)))) {
219 if (!theUri) {
220 if (!mURI)
221 *aResult = PR_TRUE;
222 return NS_OK;
224 if (NS_SUCCEEDED(theUri->Equals(mURI, &equals)) && equals) {
225 *aResult = PR_TRUE;
229 return NS_OK;
232 /* shortcut impl. */
234 /* attribute nsILocalHandlerApp app; */
235 NS_IMETHODIMP JumpListShortcut::GetApp(nsILocalHandlerApp **aApp)
237 NS_IF_ADDREF(*aApp = mHandlerApp);
239 return NS_OK;
242 NS_IMETHODIMP JumpListShortcut::SetApp(nsILocalHandlerApp *aApp)
244 mHandlerApp = aApp;
246 // Confirm the app is present on the system
247 if (!ExecutableExists(mHandlerApp))
248 return NS_ERROR_FILE_NOT_FOUND;
250 return NS_OK;
253 /* attribute long iconIndex; */
254 NS_IMETHODIMP JumpListShortcut::GetIconIndex(PRInt32 *aIconIndex)
256 NS_ENSURE_ARG_POINTER(aIconIndex);
258 *aIconIndex = mIconIndex;
259 return NS_OK;
262 NS_IMETHODIMP JumpListShortcut::SetIconIndex(PRInt32 aIconIndex)
264 mIconIndex = aIconIndex;
265 return NS_OK;
268 /* boolean equals(nsIJumpListItem item); */
269 NS_IMETHODIMP JumpListShortcut::Equals(nsIJumpListItem *aItem, PRBool *aResult)
271 NS_ENSURE_ARG_POINTER(aItem);
273 nsresult rv;
275 *aResult = PR_FALSE;
277 PRInt16 theType = nsIJumpListItem::JUMPLIST_ITEM_EMPTY;
278 if (NS_FAILED(aItem->GetType(&theType)))
279 return NS_OK;
281 // Make sure the types match.
282 if (Type() != theType)
283 return NS_OK;
285 nsCOMPtr<nsIJumpListShortcut> shortcut = do_QueryInterface(aItem, &rv);
286 if (NS_FAILED(rv))
287 return rv;
289 // Check the icon index
290 //PRInt32 idx;
291 //shortcut->GetIconIndex(&idx);
292 //if (mIconIndex != idx)
293 // return NS_OK;
295 // Call the internal object's equals() method to check.
296 nsCOMPtr<nsILocalHandlerApp> theApp;
297 PRBool equals = PR_FALSE;
298 if (NS_SUCCEEDED(shortcut->GetApp(getter_AddRefs(theApp)))) {
299 if (!theApp) {
300 if (!mHandlerApp)
301 *aResult = PR_TRUE;
302 return NS_OK;
304 if (NS_SUCCEEDED(theApp->Equals(mHandlerApp, &equals)) && equals) {
305 *aResult = PR_TRUE;
309 return NS_OK;
312 /* internal helpers */
314 // (static) Creates a ShellLink that encapsulate a separator.
315 nsresult JumpListSeparator::GetSeparator(nsRefPtr<IShellLinkW>& aShellLink)
317 HRESULT hr;
318 IShellLinkW* psl;
320 // Create a IShellLink.
321 hr = CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER,
322 IID_IShellLinkW, (LPVOID*)&psl);
323 if (FAILED(hr))
324 return NS_ERROR_UNEXPECTED;
326 IPropertyStore* pPropStore = nsnull;
327 hr = psl->QueryInterface(IID_IPropertyStore, (LPVOID*)&pPropStore);
328 if (FAILED(hr))
329 return NS_ERROR_UNEXPECTED;
331 PROPVARIANT pv;
332 InitPropVariantFromBoolean(TRUE, &pv);
334 pPropStore->SetValue(PKEY_AppUserModel_IsDestListSeparator, pv);
335 pPropStore->Commit();
336 pPropStore->Release();
338 PropVariantClear(&pv);
340 aShellLink = dont_AddRef(psl);
342 return NS_OK;
345 // (static) Creates a ShellLink that encapsulate a shortcut to local apps.
346 nsresult JumpListShortcut::GetShellLink(nsCOMPtr<nsIJumpListItem>& item, nsRefPtr<IShellLinkW>& aShellLink)
348 HRESULT hr;
349 IShellLinkW* psl;
350 nsresult rv;
352 // Shell links:
353 // http://msdn.microsoft.com/en-us/library/bb776891(VS.85).aspx
354 // http://msdn.microsoft.com/en-us/library/bb774950(VS.85).aspx
356 PRInt16 type;
357 if (NS_FAILED(item->GetType(&type)))
358 return NS_ERROR_INVALID_ARG;
360 if (type != nsIJumpListItem::JUMPLIST_ITEM_SHORTCUT)
361 return NS_ERROR_INVALID_ARG;
363 nsCOMPtr<nsIJumpListShortcut> shortcut = do_QueryInterface(item, &rv);
364 NS_ENSURE_SUCCESS(rv, rv);
366 nsCOMPtr<nsILocalHandlerApp> handlerApp;
367 rv = shortcut->GetApp(getter_AddRefs(handlerApp));
368 NS_ENSURE_SUCCESS(rv, rv);
370 // Create a IShellLink
371 hr = CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER,
372 IID_IShellLinkW, (LPVOID*)&psl);
373 if (FAILED(hr))
374 return NS_ERROR_UNEXPECTED;
376 // Retrieve the app path, title, description and optional command line args.
377 nsAutoString appPath, appTitle, appDescription, appArgs;
378 PRInt32 appIconIndex = 0;
380 // Path
381 nsCOMPtr<nsIFile> executable;
382 handlerApp->GetExecutable(getter_AddRefs(executable));
383 nsCOMPtr<nsILocalFile> localFile = do_QueryInterface(executable, &rv);
384 NS_ENSURE_SUCCESS(rv, rv);
386 rv = localFile->GetPath(appPath);
387 NS_ENSURE_SUCCESS(rv, rv);
389 // Command line parameters
390 PRUint32 count = 0;
391 handlerApp->GetParameterCount(&count);
392 for (PRUint32 idx = 0; idx < count; idx++) {
393 if (idx > 0)
394 appArgs.Append(NS_LITERAL_STRING(" "));
395 nsAutoString param;
396 rv = handlerApp->GetParameter(idx, param);
397 if (NS_FAILED(rv))
398 return rv;
399 appArgs.Append(param);
402 handlerApp->GetName(appTitle);
403 handlerApp->GetDetailedDescription(appDescription);
404 shortcut->GetIconIndex(&appIconIndex);
406 // Store the title of the app
407 if (appTitle.Length() > 0) {
408 IPropertyStore* pPropStore = nsnull;
409 hr = psl->QueryInterface(IID_IPropertyStore, (LPVOID*)&pPropStore);
410 if (FAILED(hr))
411 return NS_ERROR_UNEXPECTED;
413 PROPVARIANT pv;
414 InitPropVariantFromString(appTitle.get(), &pv);
416 pPropStore->SetValue(PKEY_Title, pv);
417 pPropStore->Commit();
418 pPropStore->Release();
420 PropVariantClear(&pv);
423 // Store the rest of the params
424 psl->SetPath(appPath.get());
425 psl->SetDescription(appDescription.get());
426 psl->SetArguments(appArgs.get());
427 psl->SetIconLocation(appPath.get(), appIconIndex);
429 aShellLink = dont_AddRef(psl);
431 return NS_OK;
434 // (static) For a given IShellLink, create and return a populated nsIJumpListShortcut.
435 nsresult JumpListShortcut::GetJumpListShortcut(IShellLinkW *pLink, nsCOMPtr<nsIJumpListShortcut>& aShortcut)
437 NS_ENSURE_ARG_POINTER(pLink);
439 nsresult rv;
440 HRESULT hres;
442 nsCOMPtr<nsILocalHandlerApp> handlerApp =
443 do_CreateInstance(NS_LOCALHANDLERAPP_CONTRACTID, &rv);
444 NS_ENSURE_SUCCESS(rv, rv);
446 PRUnichar buf[MAX_PATH];
448 // Path
449 hres = pLink->GetPath((LPWSTR)&buf, MAX_PATH, NULL, SLGP_UNCPRIORITY);
450 if (FAILED(hres))
451 return NS_ERROR_INVALID_ARG;
453 nsCOMPtr<nsILocalFile> file;
454 nsDependentString filepath(buf);
455 rv = NS_NewLocalFile(filepath, PR_FALSE, getter_AddRefs(file));
456 NS_ENSURE_SUCCESS(rv, rv);
458 rv = handlerApp->SetExecutable(file);
459 NS_ENSURE_SUCCESS(rv, rv);
461 // Parameters
462 hres = pLink->GetArguments((LPWSTR)&buf, MAX_PATH);
463 if (SUCCEEDED(hres)) {
464 LPWSTR *arglist;
465 PRInt32 numArgs;
466 PRInt32 idx;
468 arglist = ::CommandLineToArgvW(buf, &numArgs);
469 if(arglist) {
470 for (idx = 0; idx < numArgs; idx++) {
471 // szArglist[i] is null terminated
472 nsDependentString arg(arglist[idx]);
473 handlerApp->AppendParameter(arg);
475 ::LocalFree(arglist);
479 rv = aShortcut->SetApp(handlerApp);
480 NS_ENSURE_SUCCESS(rv, rv);
482 // Icon index or file location
483 int iconIdx = 0;
484 hres = pLink->GetIconLocation((LPWSTR)&buf, MAX_PATH, &iconIdx);
485 if (SUCCEEDED(hres)) {
486 // XXX How do we handle converting local files to images here? Do we need to?
487 aShortcut->SetIconIndex(iconIdx);
490 // Do we need the title and description? Probably not since handler app doesn't compare
491 // these in equals.
493 return NS_OK;
496 // (static) ShellItems are used to encapsulate links to things. We currently only support URI links,
497 // but more support could be added, such as local file and directory links.
498 nsresult JumpListLink::GetShellItem(nsCOMPtr<nsIJumpListItem>& item, nsRefPtr<IShellItem2>& aShellItem)
500 IShellItem2 *psi = nsnull;
501 nsresult rv;
503 PRInt16 type;
504 if (NS_FAILED(item->GetType(&type)))
505 return NS_ERROR_INVALID_ARG;
507 if (type != nsIJumpListItem::JUMPLIST_ITEM_LINK)
508 return NS_ERROR_INVALID_ARG;
510 nsCOMPtr<nsIJumpListLink> link = do_QueryInterface(item, &rv);
511 NS_ENSURE_SUCCESS(rv, rv);
513 nsCOMPtr<nsIURI> uri;
514 rv = link->GetUri(getter_AddRefs(uri));
515 NS_ENSURE_SUCCESS(rv, rv);
517 nsCAutoString spec;
518 rv = uri->GetSpec(spec);
519 NS_ENSURE_SUCCESS(rv, rv);
521 // Load vista+ SHCreateItemFromParsingName
522 if (createItemFromParsingName == nsnull) {
523 if (sShellDll)
524 return NS_ERROR_UNEXPECTED;
525 sShellDll = ::LoadLibraryW(kSehllLibraryName);
526 if (sShellDll)
527 createItemFromParsingName = (SHCreateItemFromParsingNamePtr)GetProcAddress(sShellDll, "SHCreateItemFromParsingName");
528 if (createItemFromParsingName == nsnull)
529 return NS_ERROR_UNEXPECTED;
532 // Create the IShellItem
533 if (FAILED(createItemFromParsingName(NS_ConvertASCIItoUTF16(spec).get(),
534 NULL, IID_PPV_ARGS(&psi))))
535 return NS_ERROR_INVALID_ARG;
537 // Set the title
538 nsAutoString linkTitle;
539 link->GetUriTitle(linkTitle);
541 IPropertyStore* pPropStore = nsnull;
542 HRESULT hres = psi->GetPropertyStore(GPS_DEFAULT, IID_IPropertyStore, (void**)&pPropStore);
543 if (FAILED(hres))
544 return NS_ERROR_UNEXPECTED;
546 PROPVARIANT pv;
547 InitPropVariantFromString(linkTitle.get(), &pv);
549 // May fail due to shell item access permissions.
550 pPropStore->SetValue(PKEY_ItemName, pv);
551 pPropStore->Commit();
552 pPropStore->Release();
554 PropVariantClear(&pv);
556 aShellItem = dont_AddRef(psi);
558 return NS_OK;
561 // (static) For a given IShellItem, create and return a populated nsIJumpListLink.
562 nsresult JumpListLink::GetJumpListLink(IShellItem *pItem, nsCOMPtr<nsIJumpListLink>& aLink)
564 NS_ENSURE_ARG_POINTER(pItem);
566 // We assume for now these are URI links, but through properties we could
567 // query and create other types.
568 nsresult rv;
569 LPWSTR lpstrName = NULL;
571 if (SUCCEEDED(pItem->GetDisplayName(SIGDN_URL, &lpstrName))) {
572 nsCOMPtr<nsIURI> uri;
573 nsAutoString spec(lpstrName);
575 rv = NS_NewURI(getter_AddRefs(uri), NS_ConvertUTF16toUTF8(spec));
576 if (NS_FAILED(rv))
577 return NS_ERROR_INVALID_ARG;
579 aLink->SetUri(uri);
581 ::CoTaskMemFree(lpstrName);
584 return NS_OK;
587 // Confirm the app is on the system
588 PRBool JumpListShortcut::ExecutableExists(nsCOMPtr<nsILocalHandlerApp>& handlerApp)
590 nsresult rv;
592 if (!handlerApp)
593 return PR_FALSE;
595 nsCOMPtr<nsIFile> executable;
596 rv = handlerApp->GetExecutable(getter_AddRefs(executable));
597 if (NS_SUCCEEDED(rv) && executable) {
598 PRBool exists;
599 executable->Exists(&exists);
600 return exists;
602 return PR_FALSE;
605 nsresult JumpListLink::HashURI(nsIURI *aUri, nsACString& aUriHash)
607 nsresult rv;
609 if (!aUri)
610 return NS_ERROR_INVALID_ARG;
612 nsCAutoString spec;
613 rv = aUri->GetSpec(spec);
614 NS_ENSURE_SUCCESS(rv, rv);
616 if (!mCryptoHash) {
617 mCryptoHash = do_CreateInstance(NS_CRYPTO_HASH_CONTRACTID, &rv);
618 NS_ENSURE_SUCCESS(rv, rv);
621 rv = mCryptoHash->Init(nsICryptoHash::MD5);
622 NS_ENSURE_SUCCESS(rv, rv);
623 rv = mCryptoHash->Update(reinterpret_cast<const PRUint8*>(spec.BeginReading()), spec.Length());
624 NS_ENSURE_SUCCESS(rv, rv);
625 rv = mCryptoHash->Finish(PR_TRUE, aUriHash);
626 NS_ENSURE_SUCCESS(rv, rv);
628 return NS_OK;
631 } // namespace widget
632 } // namespace mozilla
634 #endif // MOZ_WINSDK_TARGETVER >= MOZ_NTDDI_WIN7