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/. */
5 
6 #include "JumpListItem.h"
7 
8 #include <shellapi.h>
9 #include <propvarutil.h>
10 #include <propkey.h>
11 
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"
21 
22 namespace mozilla {
23 namespace widget {
24 
25 // ISUPPORTS Impl's
NS_IMPL_ISUPPORTS(JumpListItem,nsIJumpListItem)26 NS_IMPL_ISUPPORTS(JumpListItem, nsIJumpListItem)
27 
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)
32 NS_INTERFACE_MAP_END
33 NS_IMPL_ADDREF(JumpListSeparator)
34 NS_IMPL_RELEASE(JumpListSeparator)
35 
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)
40 NS_INTERFACE_MAP_END
41 NS_IMPL_ADDREF(JumpListLink)
42 NS_IMPL_RELEASE(JumpListLink)
43 
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)
48 NS_INTERFACE_MAP_END
49 NS_IMPL_CYCLE_COLLECTING_ADDREF(JumpListShortcut)
50 NS_IMPL_CYCLE_COLLECTING_RELEASE(JumpListShortcut)
51 NS_IMPL_CYCLE_COLLECTION(JumpListShortcut, mHandlerApp)
52 
53 NS_IMETHODIMP JumpListItemBase::GetType(int16_t* aType) {
54   NS_ENSURE_ARG_POINTER(aType);
55 
56   *aType = mItemType;
57 
58   return NS_OK;
59 }
60 
Equals(nsIJumpListItem * aItem,bool * aResult)61 NS_IMETHODIMP JumpListItemBase::Equals(nsIJumpListItem* aItem, bool* aResult) {
62   NS_ENSURE_ARG_POINTER(aItem);
63 
64   *aResult = false;
65 
66   int16_t theType = nsIJumpListItem::JUMPLIST_ITEM_EMPTY;
67   if (NS_FAILED(aItem->GetType(&theType))) return NS_OK;
68 
69   // Make sure the types match.
70   if (Type() != theType) return NS_OK;
71 
72   *aResult = true;
73 
74   return NS_OK;
75 }
76 
77 /* link impl. */
78 
GetUri(nsIURI ** aURI)79 NS_IMETHODIMP JumpListLink::GetUri(nsIURI** aURI) {
80   NS_IF_ADDREF(*aURI = mURI);
81 
82   return NS_OK;
83 }
84 
SetUri(nsIURI * aURI)85 NS_IMETHODIMP JumpListLink::SetUri(nsIURI* aURI) {
86   mURI = aURI;
87 
88   return NS_OK;
89 }
90 
SetUriTitle(const nsAString & aUriTitle)91 NS_IMETHODIMP JumpListLink::SetUriTitle(const nsAString& aUriTitle) {
92   mUriTitle.Assign(aUriTitle);
93 
94   return NS_OK;
95 }
96 
GetUriTitle(nsAString & aUriTitle)97 NS_IMETHODIMP JumpListLink::GetUriTitle(nsAString& aUriTitle) {
98   aUriTitle.Assign(mUriTitle);
99 
100   return NS_OK;
101 }
102 
GetUriHash(nsACString & aUriHash)103 NS_IMETHODIMP JumpListLink::GetUriHash(nsACString& aUriHash) {
104   if (!mURI) return NS_ERROR_NOT_AVAILABLE;
105 
106   return mozilla::widget::FaviconHelper::HashURI(mCryptoHash, mURI, aUriHash);
107 }
108 
CompareHash(nsIURI * aUri,bool * aResult)109 NS_IMETHODIMP JumpListLink::CompareHash(nsIURI* aUri, bool* aResult) {
110   nsresult rv;
111 
112   if (!mURI) {
113     *aResult = !aUri;
114     return NS_OK;
115   }
116 
117   NS_ENSURE_ARG_POINTER(aUri);
118 
119   nsAutoCString hash1, hash2;
120 
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);
125 
126   *aResult = hash1.Equals(hash2);
127 
128   return NS_OK;
129 }
130 
Equals(nsIJumpListItem * aItem,bool * aResult)131 NS_IMETHODIMP JumpListLink::Equals(nsIJumpListItem* aItem, bool* aResult) {
132   NS_ENSURE_ARG_POINTER(aItem);
133 
134   nsresult rv;
135 
136   *aResult = false;
137 
138   int16_t theType = nsIJumpListItem::JUMPLIST_ITEM_EMPTY;
139   if (NS_FAILED(aItem->GetType(&theType))) return NS_OK;
140 
141   // Make sure the types match.
142   if (Type() != theType) return NS_OK;
143 
144   nsCOMPtr<nsIJumpListLink> link = do_QueryInterface(aItem, &rv);
145   if (NS_FAILED(rv)) return rv;
146 
147   // Check the titles
148   nsAutoString title;
149   link->GetUriTitle(title);
150   if (!mUriTitle.Equals(title)) return NS_OK;
151 
152   // Call the internal object's equals() method to check.
153   nsCOMPtr<nsIURI> theUri;
154   bool equals = false;
155   if (NS_SUCCEEDED(link->GetUri(getter_AddRefs(theUri)))) {
156     if (!theUri) {
157       if (!mURI) *aResult = true;
158       return NS_OK;
159     }
160     if (NS_SUCCEEDED(theUri->Equals(mURI, &equals)) && equals) {
161       *aResult = true;
162     }
163   }
164 
165   return NS_OK;
166 }
167 
168 /* shortcut impl. */
169 
GetApp(nsILocalHandlerApp ** aApp)170 NS_IMETHODIMP JumpListShortcut::GetApp(nsILocalHandlerApp** aApp) {
171   NS_IF_ADDREF(*aApp = mHandlerApp);
172 
173   return NS_OK;
174 }
175 
SetApp(nsILocalHandlerApp * aApp)176 NS_IMETHODIMP JumpListShortcut::SetApp(nsILocalHandlerApp* aApp) {
177   mHandlerApp = aApp;
178   return NS_OK;
179 }
180 
GetIconIndex(int32_t * aIconIndex)181 NS_IMETHODIMP JumpListShortcut::GetIconIndex(int32_t* aIconIndex) {
182   NS_ENSURE_ARG_POINTER(aIconIndex);
183 
184   *aIconIndex = mIconIndex;
185   return NS_OK;
186 }
187 
SetIconIndex(int32_t aIconIndex)188 NS_IMETHODIMP JumpListShortcut::SetIconIndex(int32_t aIconIndex) {
189   mIconIndex = aIconIndex;
190   return NS_OK;
191 }
192 
GetFaviconPageUri(nsIURI ** aFaviconPageURI)193 NS_IMETHODIMP JumpListShortcut::GetFaviconPageUri(nsIURI** aFaviconPageURI) {
194   NS_IF_ADDREF(*aFaviconPageURI = mFaviconPageURI);
195 
196   return NS_OK;
197 }
198 
SetFaviconPageUri(nsIURI * aFaviconPageURI)199 NS_IMETHODIMP JumpListShortcut::SetFaviconPageUri(nsIURI* aFaviconPageURI) {
200   mFaviconPageURI = aFaviconPageURI;
201   return NS_OK;
202 }
203 
Equals(nsIJumpListItem * aItem,bool * aResult)204 NS_IMETHODIMP JumpListShortcut::Equals(nsIJumpListItem* aItem, bool* aResult) {
205   NS_ENSURE_ARG_POINTER(aItem);
206 
207   nsresult rv;
208 
209   *aResult = false;
210 
211   int16_t theType = nsIJumpListItem::JUMPLIST_ITEM_EMPTY;
212   if (NS_FAILED(aItem->GetType(&theType))) return NS_OK;
213 
214   // Make sure the types match.
215   if (Type() != theType) return NS_OK;
216 
217   nsCOMPtr<nsIJumpListShortcut> shortcut = do_QueryInterface(aItem, &rv);
218   if (NS_FAILED(rv)) return rv;
219 
220   // Check the icon index
221   // int32_t idx;
222   // shortcut->GetIconIndex(&idx);
223   // if (mIconIndex != idx)
224   //  return NS_OK;
225   // No need to check the icon page URI either
226 
227   // Call the internal object's equals() method to check.
228   nsCOMPtr<nsILocalHandlerApp> theApp;
229   bool equals = false;
230   if (NS_SUCCEEDED(shortcut->GetApp(getter_AddRefs(theApp)))) {
231     if (!theApp) {
232       if (!mHandlerApp) *aResult = true;
233       return NS_OK;
234     }
235     if (NS_SUCCEEDED(theApp->Equals(mHandlerApp, &equals)) && equals) {
236       *aResult = true;
237     }
238   }
239 
240   return NS_OK;
241 }
242 
243 /* internal helpers */
244 
245 // (static) Creates a ShellLink that encapsulate a separator.
GetSeparator(RefPtr<IShellLinkW> & aShellLink)246 nsresult JumpListSeparator::GetSeparator(RefPtr<IShellLinkW>& aShellLink) {
247   HRESULT hr;
248   IShellLinkW* psl;
249 
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;
254 
255   IPropertyStore* pPropStore = nullptr;
256   hr = psl->QueryInterface(IID_IPropertyStore, (LPVOID*)&pPropStore);
257   if (FAILED(hr)) return NS_ERROR_UNEXPECTED;
258 
259   PROPVARIANT pv;
260   InitPropVariantFromBoolean(TRUE, &pv);
261 
262   pPropStore->SetValue(PKEY_AppUserModel_IsDestListSeparator, pv);
263   pPropStore->Commit();
264   pPropStore->Release();
265 
266   PropVariantClear(&pv);
267 
268   aShellLink = dont_AddRef(psl);
269 
270   return NS_OK;
271 }
272 
273 // (static) Creates a ShellLink that encapsulate a shortcut to local apps.
GetShellLink(nsCOMPtr<nsIJumpListItem> & item,RefPtr<IShellLinkW> & aShellLink,nsCOMPtr<nsIThread> & aIOThread)274 nsresult JumpListShortcut::GetShellLink(nsCOMPtr<nsIJumpListItem>& item,
275                                         RefPtr<IShellLinkW>& aShellLink,
276                                         nsCOMPtr<nsIThread>& aIOThread) {
277   HRESULT hr;
278   IShellLinkW* psl;
279   nsresult rv;
280 
281   // Shell links:
282   // http://msdn.microsoft.com/en-us/library/bb776891(VS.85).aspx
283   // http://msdn.microsoft.com/en-us/library/bb774950(VS.85).aspx
284 
285   int16_t type;
286   if (NS_FAILED(item->GetType(&type))) return NS_ERROR_INVALID_ARG;
287 
288   if (type != nsIJumpListItem::JUMPLIST_ITEM_SHORTCUT)
289     return NS_ERROR_INVALID_ARG;
290 
291   nsCOMPtr<nsIJumpListShortcut> shortcut = do_QueryInterface(item, &rv);
292   NS_ENSURE_SUCCESS(rv, rv);
293 
294   nsCOMPtr<nsILocalHandlerApp> handlerApp;
295   rv = shortcut->GetApp(getter_AddRefs(handlerApp));
296   NS_ENSURE_SUCCESS(rv, rv);
297 
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;
302 
303   // Retrieve the app path, title, description and optional command line args.
304   nsAutoString appPath, appTitle, appDescription, appArgs;
305   int32_t appIconIndex = 0;
306 
307   // Path
308   nsCOMPtr<nsIFile> executable;
309   handlerApp->GetExecutable(getter_AddRefs(executable));
310 
311   rv = executable->GetPath(appPath);
312   NS_ENSURE_SUCCESS(rv, rv);
313 
314   // Command line parameters
315   uint32_t count = 0;
316   handlerApp->GetParameterCount(&count);
317   for (uint32_t idx = 0; idx < count; idx++) {
318     if (idx > 0) appArgs.Append(' ');
319     nsAutoString param;
320     rv = handlerApp->GetParameter(idx, param);
321     if (NS_FAILED(rv)) return rv;
322     appArgs.Append(param);
323   }
324 
325   handlerApp->GetName(appTitle);
326   handlerApp->GetDetailedDescription(appDescription);
327 
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);
331 
332   nsCOMPtr<nsIURI> iconUri;
333   rv = shortcut->GetFaviconPageUri(getter_AddRefs(iconUri));
334   if (NS_SUCCEEDED(rv) && iconUri) {
335     useUriIcon = true;
336   }
337 
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;
343 
344     PROPVARIANT pv;
345     InitPropVariantFromString(appTitle.get(), &pv);
346 
347     pPropStore->SetValue(PKEY_Title, pv);
348     pPropStore->Commit();
349     pPropStore->Release();
350 
351     PropVariantClear(&pv);
352   }
353 
354   // Store the rest of the params
355   psl->SetPath(appPath.get());
356   psl->SetDescription(appDescription.get());
357   psl->SetArguments(appArgs.get());
358 
359   if (useUriIcon) {
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);
367       usedUriIcon = true;
368     }
369   }
370 
371   // We didn't use an ICO via URI so fall back to the app icon
372   if (!usedUriIcon) {
373     psl->SetIconLocation(appPath.get(), appIconIndex);
374   }
375 
376   aShellLink = dont_AddRef(psl);
377 
378   return NS_OK;
379 }
380 
381 // If successful fills in the aSame parameter
382 // aSame will be true if the path is in our icon cache
IsPathInOurIconCache(nsCOMPtr<nsIJumpListShortcut> & aShortcut,wchar_t * aPath,bool * aSame)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);
387 
388   *aSame = false;
389 
390   // Construct the path of our jump list cache
391   nsCOMPtr<nsIFile> jumpListCache;
392   nsresult rv =
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);
401 
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);
413 
414   *aSame = jumpListCachePath.Equals(passedInParentPath);
415   return NS_OK;
416 }
417 
418 // (static) For a given IShellLink, create and return a populated
419 // nsIJumpListShortcut.
GetJumpListShortcut(IShellLinkW * pLink,nsCOMPtr<nsIJumpListShortcut> & aShortcut)420 nsresult JumpListShortcut::GetJumpListShortcut(
421     IShellLinkW* pLink, nsCOMPtr<nsIJumpListShortcut>& aShortcut) {
422   NS_ENSURE_ARG_POINTER(pLink);
423 
424   nsresult rv;
425   HRESULT hres;
426 
427   nsCOMPtr<nsILocalHandlerApp> handlerApp =
428       do_CreateInstance(NS_LOCALHANDLERAPP_CONTRACTID, &rv);
429   NS_ENSURE_SUCCESS(rv, rv);
430 
431   wchar_t buf[MAX_PATH];
432 
433   // Path
434   hres = pLink->GetPath(buf, MAX_PATH, nullptr, SLGP_UNCPRIORITY);
435   if (FAILED(hres)) return NS_ERROR_INVALID_ARG;
436 
437   nsCOMPtr<nsIFile> file;
438   nsDependentString filepath(buf);
439   rv = NS_NewLocalFile(filepath, false, getter_AddRefs(file));
440   NS_ENSURE_SUCCESS(rv, rv);
441 
442   rv = handlerApp->SetExecutable(file);
443   NS_ENSURE_SUCCESS(rv, rv);
444 
445   // Parameters
446   hres = pLink->GetArguments(buf, MAX_PATH);
447   if (SUCCEEDED(hres)) {
448     LPWSTR* arglist;
449     int32_t numArgs;
450     int32_t idx;
451 
452     arglist = ::CommandLineToArgvW(buf, &numArgs);
453     if (arglist) {
454       for (idx = 0; idx < numArgs; idx++) {
455         // szArglist[i] is null terminated
456         nsDependentString arg(arglist[idx]);
457         handlerApp->AppendParameter(arg);
458       }
459       ::LocalFree(arglist);
460     }
461   }
462 
463   rv = aShortcut->SetApp(handlerApp);
464   NS_ENSURE_SUCCESS(rv, rv);
465 
466   // Icon index or file location
467   int iconIdx = 0;
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
471     // to?
472     aShortcut->SetIconIndex(iconIdx);
473 
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.
476     bool isInOurCache;
477     if (NS_SUCCEEDED(IsPathInOurIconCache(aShortcut, buf, &isInOurCache)) &&
478         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);
484       }
485     }
486   }
487 
488   // Do we need the title and description? Probably not since handler app
489   // doesn't compare these in equals.
490 
491   return NS_OK;
492 }
493 
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.
GetShellItem(nsCOMPtr<nsIJumpListItem> & item,RefPtr<IShellItem2> & aShellItem)497 nsresult JumpListLink::GetShellItem(nsCOMPtr<nsIJumpListItem>& item,
498                                     RefPtr<IShellItem2>& aShellItem) {
499   IShellItem2* psi = nullptr;
500   nsresult rv;
501 
502   int16_t type;
503   if (NS_FAILED(item->GetType(&type))) return NS_ERROR_INVALID_ARG;
504 
505   if (type != nsIJumpListItem::JUMPLIST_ITEM_LINK) return NS_ERROR_INVALID_ARG;
506 
507   nsCOMPtr<nsIJumpListLink> link = do_QueryInterface(item, &rv);
508   NS_ENSURE_SUCCESS(rv, rv);
509 
510   nsCOMPtr<nsIURI> uri;
511   rv = link->GetUri(getter_AddRefs(uri));
512   NS_ENSURE_SUCCESS(rv, rv);
513 
514   nsAutoCString spec;
515   rv = uri->GetSpec(spec);
516   NS_ENSURE_SUCCESS(rv, rv);
517 
518   // Create the IShellItem
519   if (FAILED(SHCreateItemFromParsingName(NS_ConvertASCIItoUTF16(spec).get(),
520                                          nullptr, IID_PPV_ARGS(&psi)))) {
521     return NS_ERROR_INVALID_ARG;
522   }
523 
524   // Set the title
525   nsAutoString linkTitle;
526   link->GetUriTitle(linkTitle);
527 
528   IPropertyStore* pPropStore = nullptr;
529   HRESULT hres = psi->GetPropertyStore(GPS_DEFAULT, IID_IPropertyStore,
530                                        (void**)&pPropStore);
531   if (FAILED(hres)) return NS_ERROR_UNEXPECTED;
532 
533   PROPVARIANT pv;
534   InitPropVariantFromString(linkTitle.get(), &pv);
535 
536   // May fail due to shell item access permissions.
537   pPropStore->SetValue(PKEY_ItemName, pv);
538   pPropStore->Commit();
539   pPropStore->Release();
540 
541   PropVariantClear(&pv);
542 
543   aShellItem = dont_AddRef(psi);
544 
545   return NS_OK;
546 }
547 
548 // (static) For a given IShellItem, create and return a populated
549 // nsIJumpListLink.
GetJumpListLink(IShellItem * pItem,nsCOMPtr<nsIJumpListLink> & aLink)550 nsresult JumpListLink::GetJumpListLink(IShellItem* pItem,
551                                        nsCOMPtr<nsIJumpListLink>& aLink) {
552   NS_ENSURE_ARG_POINTER(pItem);
553 
554   // We assume for now these are URI links, but through properties we could
555   // query and create other types.
556   nsresult rv;
557   LPWSTR lpstrName = nullptr;
558 
559   if (SUCCEEDED(pItem->GetDisplayName(SIGDN_URL, &lpstrName))) {
560     nsCOMPtr<nsIURI> uri;
561     nsAutoString spec(lpstrName);
562 
563     rv = NS_NewURI(getter_AddRefs(uri), NS_ConvertUTF16toUTF8(spec));
564     if (NS_FAILED(rv)) return NS_ERROR_INVALID_ARG;
565 
566     aLink->SetUri(uri);
567 
568     ::CoTaskMemFree(lpstrName);
569   }
570 
571   return NS_OK;
572 }
573 
574 }  // namespace widget
575 }  // namespace mozilla
576