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 "JumpListBuilder.h"
7
8 #include "nsError.h"
9 #include "nsCOMPtr.h"
10 #include "nsServiceManagerUtils.h"
11 #include "nsString.h"
12 #include "nsArrayUtils.h"
13 #include "nsWidgetsCID.h"
14 #include "WinTaskbar.h"
15 #include "nsDirectoryServiceUtils.h"
16 #include "mozilla/Preferences.h"
17 #include "nsStringStream.h"
18 #include "nsThreadUtils.h"
19 #include "mozilla/LazyIdleThread.h"
20 #include "nsIObserverService.h"
21 #include "mozilla/ScopeExit.h"
22 #include "mozilla/Unused.h"
23 #include "mozilla/dom/Promise.h"
24 #include "mozilla/mscom/ApartmentRegion.h"
25 #include "mozilla/mscom/EnsureMTA.h"
26
27 #include <shellapi.h>
28 #include "WinUtils.h"
29
30 using mozilla::dom::Promise;
31
32 // The amount of time, in milliseconds, that our IO thread will stay alive after
33 // the last event it processes.
34 #define DEFAULT_THREAD_TIMEOUT_MS 30000
35
36 namespace mozilla {
37 namespace widget {
38
39 // defined in WinTaskbar.cpp
40 extern const wchar_t* gMozillaJumpListIDGeneric;
41
42 Atomic<bool> JumpListBuilder::sBuildingList(false);
43 const char kPrefTaskbarEnabled[] = "browser.taskbar.lists.enabled";
44
45 NS_IMPL_ISUPPORTS(JumpListBuilder, nsIJumpListBuilder, nsIObserver)
46 #define TOPIC_PROFILE_BEFORE_CHANGE "profile-before-change"
47 #define TOPIC_CLEAR_PRIVATE_DATA "clear-private-data"
48
49 namespace detail {
50
51 class DoneCommitListBuildCallback final : public nsIRunnable {
52 NS_DECL_THREADSAFE_ISUPPORTS
53
54 public:
DoneCommitListBuildCallback(nsIJumpListCommittedCallback * aCallback,JumpListBuilder * aBuilder)55 DoneCommitListBuildCallback(nsIJumpListCommittedCallback* aCallback,
56 JumpListBuilder* aBuilder)
57 : mCallback(aCallback), mBuilder(aBuilder), mResult(false) {}
58
Run()59 NS_IMETHOD Run() override {
60 MOZ_ASSERT(NS_IsMainThread());
61 if (mCallback) {
62 Unused << mCallback->Done(mResult);
63 }
64 // Ensure we are releasing on the main thread.
65 Destroy();
66 return NS_OK;
67 }
68
SetResult(bool aResult)69 void SetResult(bool aResult) { mResult = aResult; }
70
71 private:
~DoneCommitListBuildCallback()72 ~DoneCommitListBuildCallback() {
73 // Destructor does not always call on the main thread.
74 MOZ_ASSERT(!mCallback);
75 MOZ_ASSERT(!mBuilder);
76 }
77
Destroy()78 void Destroy() {
79 MOZ_ASSERT(NS_IsMainThread());
80 mCallback = nullptr;
81 mBuilder = nullptr;
82 }
83
84 // These two references MUST be released on the main thread.
85 RefPtr<nsIJumpListCommittedCallback> mCallback;
86 RefPtr<JumpListBuilder> mBuilder;
87 bool mResult;
88 };
89
90 NS_IMPL_ISUPPORTS(DoneCommitListBuildCallback, nsIRunnable);
91
92 } // namespace detail
93
JumpListBuilder()94 JumpListBuilder::JumpListBuilder()
95 : mMaxItems(0), mHasCommit(false), mMonitor("JumpListBuilderMonitor") {
96 MOZ_ASSERT(NS_IsMainThread());
97
98 // Instantiate mJumpListMgr in the multithreaded apartment so that proxied
99 // calls on that object do not need to interact with the main thread's message
100 // pump.
101 mscom::EnsureMTA([&]() {
102 RefPtr<ICustomDestinationList> jumpListMgr;
103 HRESULT hr = ::CoCreateInstance(
104 CLSID_DestinationList, nullptr, CLSCTX_INPROC_SERVER,
105 IID_ICustomDestinationList, getter_AddRefs(jumpListMgr));
106 if (FAILED(hr)) {
107 return;
108 }
109
110 // Since we are accessing mJumpListMgr across different threads
111 // (ie, different apartments), mJumpListMgr must be an agile reference.
112 mJumpListMgr = jumpListMgr;
113 });
114
115 if (!mJumpListMgr) {
116 return;
117 }
118
119 // Make a lazy thread for any IO
120 mIOThread = new LazyIdleThread(DEFAULT_THREAD_TIMEOUT_MS, "Jump List"_ns,
121 LazyIdleThread::ManualShutdown);
122 Preferences::AddStrongObserver(this, kPrefTaskbarEnabled);
123
124 nsCOMPtr<nsIObserverService> observerService =
125 do_GetService("@mozilla.org/observer-service;1");
126 if (observerService) {
127 observerService->AddObserver(this, TOPIC_PROFILE_BEFORE_CHANGE, false);
128 observerService->AddObserver(this, TOPIC_CLEAR_PRIVATE_DATA, false);
129 }
130
131 RefPtr<ICustomDestinationList> jumpListMgr = mJumpListMgr;
132 if (!jumpListMgr) {
133 return;
134 }
135
136 // GetAppUserModelID can only be called once we're back on the main thread.
137 nsString modelId;
138 if (mozilla::widget::WinTaskbar::GetAppUserModelID(modelId)) {
139 jumpListMgr->SetAppID(modelId.get());
140 }
141 }
142
~JumpListBuilder()143 JumpListBuilder::~JumpListBuilder() {
144 Preferences::RemoveObserver(this, kPrefTaskbarEnabled);
145 }
146
GetAvailable(int16_t * aAvailable)147 NS_IMETHODIMP JumpListBuilder::GetAvailable(int16_t* aAvailable) {
148 *aAvailable = false;
149
150 ReentrantMonitorAutoEnter lock(mMonitor);
151 if (mJumpListMgr) *aAvailable = true;
152
153 return NS_OK;
154 }
155
GetIsListCommitted(bool * aCommit)156 NS_IMETHODIMP JumpListBuilder::GetIsListCommitted(bool* aCommit) {
157 *aCommit = mHasCommit;
158
159 return NS_OK;
160 }
161
GetMaxListItems(int16_t * aMaxItems)162 NS_IMETHODIMP JumpListBuilder::GetMaxListItems(int16_t* aMaxItems) {
163 ReentrantMonitorAutoEnter lock(mMonitor);
164 if (!mJumpListMgr) return NS_ERROR_NOT_AVAILABLE;
165
166 *aMaxItems = 0;
167
168 if (sBuildingList) {
169 *aMaxItems = mMaxItems;
170 return NS_OK;
171 }
172
173 RefPtr<ICustomDestinationList> jumpListMgr = mJumpListMgr;
174 if (!jumpListMgr) {
175 return NS_ERROR_UNEXPECTED;
176 }
177
178 IObjectArray* objArray;
179 if (SUCCEEDED(jumpListMgr->BeginList(&mMaxItems, IID_PPV_ARGS(&objArray)))) {
180 *aMaxItems = mMaxItems;
181
182 if (objArray) objArray->Release();
183
184 jumpListMgr->AbortList();
185 }
186
187 return NS_OK;
188 }
189
InitListBuild(JSContext * aCx,Promise ** aPromise)190 NS_IMETHODIMP JumpListBuilder::InitListBuild(JSContext* aCx,
191 Promise** aPromise) {
192 ReentrantMonitorAutoEnter lock(mMonitor);
193 if (!mJumpListMgr) {
194 return NS_ERROR_NOT_AVAILABLE;
195 }
196
197 nsIGlobalObject* globalObject = xpc::CurrentNativeGlobal(aCx);
198 if (NS_WARN_IF(!globalObject)) {
199 return NS_ERROR_FAILURE;
200 }
201
202 ErrorResult result;
203 RefPtr<Promise> promise = Promise::Create(globalObject, result);
204 if (NS_WARN_IF(result.Failed())) {
205 return result.StealNSResult();
206 }
207
208 nsCOMPtr<nsIRunnable> runnable =
209 NewRunnableMethod<StoreCopyPassByRRef<RefPtr<Promise>>>(
210 "InitListBuild", this, &JumpListBuilder::DoInitListBuild, promise);
211 nsresult rv = mIOThread->Dispatch(runnable, NS_DISPATCH_NORMAL);
212 if (NS_WARN_IF(NS_FAILED(rv))) {
213 return rv;
214 }
215
216 promise.forget(aPromise);
217 return NS_OK;
218 }
219
DoInitListBuild(RefPtr<Promise> && aPromise)220 void JumpListBuilder::DoInitListBuild(RefPtr<Promise>&& aPromise) {
221 // Since we're invoking COM interfaces to talk to the shell on a background
222 // thread, we need to be running inside a multithreaded apartment.
223 mscom::MTARegion mta;
224 MOZ_ASSERT(mta.IsValid());
225
226 ReentrantMonitorAutoEnter lock(mMonitor);
227 MOZ_ASSERT(mJumpListMgr);
228
229 if (sBuildingList) {
230 AbortListBuild();
231 }
232
233 HRESULT hr = E_UNEXPECTED;
234 auto errorHandler = MakeScopeExit([&aPromise, &hr]() {
235 if (SUCCEEDED(hr)) {
236 return;
237 }
238
239 NS_DispatchToMainThread(NS_NewRunnableFunction(
240 "InitListBuildReject", [promise = std::move(aPromise)]() {
241 promise->MaybeReject(NS_ERROR_FAILURE);
242 }));
243 });
244
245 RefPtr<ICustomDestinationList> jumpListMgr = mJumpListMgr;
246 if (!jumpListMgr) {
247 return;
248 }
249
250 nsTArray<nsString> urisToRemove;
251 RefPtr<IObjectArray> objArray;
252 hr = jumpListMgr->BeginList(
253 &mMaxItems,
254 IID_PPV_ARGS(static_cast<IObjectArray**>(getter_AddRefs(objArray))));
255 if (FAILED(hr)) {
256 return;
257 }
258
259 // The returned objArray of removed items are for manually removed items.
260 // This does not return items which are removed because they were previously
261 // part of the jump list but are no longer part of the jump list.
262 sBuildingList = true;
263 RemoveIconCacheAndGetJumplistShortcutURIs(objArray, urisToRemove);
264
265 NS_DispatchToMainThread(NS_NewRunnableFunction(
266 "InitListBuildResolve", [urisToRemove = std::move(urisToRemove),
267 promise = std::move(aPromise)]() {
268 promise->MaybeResolve(urisToRemove);
269 }));
270 }
271
272 // Ensures that we have no old ICO files left in the jump list cache
RemoveIconCacheForAllItems()273 nsresult JumpListBuilder::RemoveIconCacheForAllItems() {
274 // Construct the path of our jump list cache
275 nsCOMPtr<nsIFile> jumpListCacheDir;
276 nsresult rv =
277 NS_GetSpecialDirectory("ProfLDS", getter_AddRefs(jumpListCacheDir));
278 NS_ENSURE_SUCCESS(rv, rv);
279 rv = jumpListCacheDir->AppendNative(
280 nsDependentCString(mozilla::widget::FaviconHelper::kJumpListCacheDir));
281 NS_ENSURE_SUCCESS(rv, rv);
282
283 nsCOMPtr<nsIDirectoryEnumerator> entries;
284 rv = jumpListCacheDir->GetDirectoryEntries(getter_AddRefs(entries));
285 NS_ENSURE_SUCCESS(rv, rv);
286
287 // Loop through each directory entry and remove all ICO files found
288 do {
289 nsCOMPtr<nsIFile> currFile;
290 if (NS_FAILED(entries->GetNextFile(getter_AddRefs(currFile))) || !currFile)
291 break;
292
293 nsAutoString path;
294 if (NS_FAILED(currFile->GetPath(path))) continue;
295
296 if (StringTail(path, 4).LowerCaseEqualsASCII(".ico")) {
297 // Check if the cached ICO file exists
298 bool exists;
299 if (NS_FAILED(currFile->Exists(&exists)) || !exists) continue;
300
301 // We found an ICO file that exists, so we should remove it
302 currFile->Remove(false);
303 }
304 } while (true);
305
306 return NS_OK;
307 }
308
AddListToBuild(int16_t aCatType,nsIArray * items,const nsAString & catName,bool * _retval)309 NS_IMETHODIMP JumpListBuilder::AddListToBuild(int16_t aCatType, nsIArray* items,
310 const nsAString& catName,
311 bool* _retval) {
312 nsresult rv;
313
314 *_retval = false;
315
316 ReentrantMonitorAutoEnter lock(mMonitor);
317 if (!mJumpListMgr) return NS_ERROR_NOT_AVAILABLE;
318
319 RefPtr<ICustomDestinationList> jumpListMgr = mJumpListMgr;
320 if (!jumpListMgr) {
321 return NS_ERROR_UNEXPECTED;
322 }
323
324 switch (aCatType) {
325 case nsIJumpListBuilder::JUMPLIST_CATEGORY_TASKS: {
326 NS_ENSURE_ARG_POINTER(items);
327
328 HRESULT hr;
329 RefPtr<IObjectCollection> collection;
330 hr = CoCreateInstance(CLSID_EnumerableObjectCollection, nullptr,
331 CLSCTX_INPROC_SERVER, IID_IObjectCollection,
332 getter_AddRefs(collection));
333 if (FAILED(hr)) return NS_ERROR_UNEXPECTED;
334
335 // Build the list
336 uint32_t length;
337 items->GetLength(&length);
338 for (uint32_t i = 0; i < length; ++i) {
339 nsCOMPtr<nsIJumpListItem> item = do_QueryElementAt(items, i);
340 if (!item) continue;
341 // Check for separators
342 if (IsSeparator(item)) {
343 RefPtr<IShellLinkW> link;
344 rv = JumpListSeparator::GetSeparator(link);
345 if (NS_FAILED(rv)) return rv;
346 collection->AddObject(link);
347 continue;
348 }
349 // These should all be ShellLinks
350 RefPtr<IShellLinkW> link;
351 rv = JumpListShortcut::GetShellLink(item, link, mIOThread);
352 if (NS_FAILED(rv)) return rv;
353 collection->AddObject(link);
354 }
355
356 // We need IObjectArray to submit
357 RefPtr<IObjectArray> pArray;
358 hr = collection->QueryInterface(IID_IObjectArray, getter_AddRefs(pArray));
359 if (FAILED(hr)) return NS_ERROR_UNEXPECTED;
360
361 // Add the tasks
362 hr = jumpListMgr->AddUserTasks(pArray);
363 if (SUCCEEDED(hr)) *_retval = true;
364 return NS_OK;
365 } break;
366 case nsIJumpListBuilder::JUMPLIST_CATEGORY_RECENT: {
367 if (SUCCEEDED(jumpListMgr->AppendKnownCategory(KDC_RECENT)))
368 *_retval = true;
369 return NS_OK;
370 } break;
371 case nsIJumpListBuilder::JUMPLIST_CATEGORY_FREQUENT: {
372 if (SUCCEEDED(jumpListMgr->AppendKnownCategory(KDC_FREQUENT)))
373 *_retval = true;
374 return NS_OK;
375 } break;
376 case nsIJumpListBuilder::JUMPLIST_CATEGORY_CUSTOMLIST: {
377 NS_ENSURE_ARG_POINTER(items);
378
379 if (catName.IsEmpty()) return NS_ERROR_INVALID_ARG;
380
381 HRESULT hr;
382 RefPtr<IObjectCollection> collection;
383 hr = CoCreateInstance(CLSID_EnumerableObjectCollection, nullptr,
384 CLSCTX_INPROC_SERVER, IID_IObjectCollection,
385 getter_AddRefs(collection));
386 if (FAILED(hr)) return NS_ERROR_UNEXPECTED;
387
388 uint32_t length;
389 items->GetLength(&length);
390 for (uint32_t i = 0; i < length; ++i) {
391 nsCOMPtr<nsIJumpListItem> item = do_QueryElementAt(items, i);
392 if (!item) continue;
393 int16_t type;
394 if (NS_FAILED(item->GetType(&type))) continue;
395 switch (type) {
396 case nsIJumpListItem::JUMPLIST_ITEM_SEPARATOR: {
397 RefPtr<IShellLinkW> shellItem;
398 rv = JumpListSeparator::GetSeparator(shellItem);
399 if (NS_FAILED(rv)) return rv;
400 collection->AddObject(shellItem);
401 } break;
402 case nsIJumpListItem::JUMPLIST_ITEM_LINK: {
403 RefPtr<IShellItem2> shellItem;
404 rv = JumpListLink::GetShellItem(item, shellItem);
405 if (NS_FAILED(rv)) return rv;
406 collection->AddObject(shellItem);
407 } break;
408 case nsIJumpListItem::JUMPLIST_ITEM_SHORTCUT: {
409 RefPtr<IShellLinkW> shellItem;
410 rv = JumpListShortcut::GetShellLink(item, shellItem, mIOThread);
411 if (NS_FAILED(rv)) return rv;
412 collection->AddObject(shellItem);
413 } break;
414 }
415 }
416
417 // We need IObjectArray to submit
418 RefPtr<IObjectArray> pArray;
419 hr = collection->QueryInterface(IID_IObjectArray, (LPVOID*)&pArray);
420 if (FAILED(hr)) return NS_ERROR_UNEXPECTED;
421
422 // Add the tasks
423 hr = jumpListMgr->AppendCategory(
424 reinterpret_cast<const wchar_t*>(catName.BeginReading()), pArray);
425 if (SUCCEEDED(hr)) *_retval = true;
426
427 // Get rid of the old icons
428 nsCOMPtr<nsIRunnable> event =
429 new mozilla::widget::AsyncDeleteAllFaviconsFromDisk(true);
430 mIOThread->Dispatch(event, NS_DISPATCH_NORMAL);
431
432 return NS_OK;
433 } break;
434 }
435 return NS_OK;
436 }
437
AbortListBuild()438 NS_IMETHODIMP JumpListBuilder::AbortListBuild() {
439 ReentrantMonitorAutoEnter lock(mMonitor);
440 if (!mJumpListMgr) return NS_ERROR_NOT_AVAILABLE;
441
442 RefPtr<ICustomDestinationList> jumpListMgr = mJumpListMgr;
443 if (!jumpListMgr) {
444 return NS_ERROR_UNEXPECTED;
445 }
446
447 jumpListMgr->AbortList();
448 sBuildingList = false;
449
450 return NS_OK;
451 }
452
CommitListBuild(nsIJumpListCommittedCallback * aCallback)453 NS_IMETHODIMP JumpListBuilder::CommitListBuild(
454 nsIJumpListCommittedCallback* aCallback) {
455 ReentrantMonitorAutoEnter lock(mMonitor);
456 if (!mJumpListMgr) return NS_ERROR_NOT_AVAILABLE;
457
458 // Also holds a strong reference to this to prevent use-after-free.
459 RefPtr<detail::DoneCommitListBuildCallback> callback =
460 new detail::DoneCommitListBuildCallback(aCallback, this);
461
462 // The builder has a strong reference in the callback already, so we do not
463 // need to do it for this runnable again.
464 RefPtr<nsIRunnable> event =
465 NewNonOwningRunnableMethod<RefPtr<detail::DoneCommitListBuildCallback>>(
466 "JumpListBuilder::DoCommitListBuild", this,
467 &JumpListBuilder::DoCommitListBuild, std::move(callback));
468 Unused << mIOThread->Dispatch(event, NS_DISPATCH_NORMAL);
469
470 return NS_OK;
471 }
472
DoCommitListBuild(RefPtr<detail::DoneCommitListBuildCallback> aCallback)473 void JumpListBuilder::DoCommitListBuild(
474 RefPtr<detail::DoneCommitListBuildCallback> aCallback) {
475 // Since we're invoking COM interfaces to talk to the shell on a background
476 // thread, we need to be running inside a multithreaded apartment.
477 mscom::MTARegion mta;
478 MOZ_ASSERT(mta.IsValid());
479
480 ReentrantMonitorAutoEnter lock(mMonitor);
481 MOZ_ASSERT(mJumpListMgr);
482 MOZ_ASSERT(aCallback);
483
484 HRESULT hr = E_UNEXPECTED;
485 auto onExit = MakeScopeExit([&hr, &aCallback]() {
486 // XXX We might want some specific error data here.
487 aCallback->SetResult(SUCCEEDED(hr));
488 Unused << NS_DispatchToMainThread(aCallback);
489 });
490
491 RefPtr<ICustomDestinationList> jumpListMgr = mJumpListMgr;
492 if (!jumpListMgr) {
493 return;
494 }
495
496 hr = jumpListMgr->CommitList();
497 sBuildingList = false;
498
499 if (SUCCEEDED(hr)) {
500 mHasCommit = true;
501 }
502 }
503
DeleteActiveList(bool * _retval)504 NS_IMETHODIMP JumpListBuilder::DeleteActiveList(bool* _retval) {
505 *_retval = false;
506
507 ReentrantMonitorAutoEnter lock(mMonitor);
508 if (!mJumpListMgr) return NS_ERROR_NOT_AVAILABLE;
509
510 if (sBuildingList) {
511 AbortListBuild();
512 }
513
514 nsAutoString uid;
515 if (!WinTaskbar::GetAppUserModelID(uid)) return NS_OK;
516
517 RefPtr<ICustomDestinationList> jumpListMgr = mJumpListMgr;
518 if (!jumpListMgr) {
519 return NS_ERROR_UNEXPECTED;
520 }
521
522 if (SUCCEEDED(jumpListMgr->DeleteList(uid.get()))) {
523 *_retval = true;
524 }
525
526 return NS_OK;
527 }
528
529 /* internal */
530
IsSeparator(nsCOMPtr<nsIJumpListItem> & item)531 bool JumpListBuilder::IsSeparator(nsCOMPtr<nsIJumpListItem>& item) {
532 int16_t type;
533 item->GetType(&type);
534 if (NS_FAILED(item->GetType(&type))) return false;
535
536 if (type == nsIJumpListItem::JUMPLIST_ITEM_SEPARATOR) return true;
537 return false;
538 }
539
540 // RemoveIconCacheAndGetJumplistShortcutURIs - does multiple things to
541 // avoid unnecessary extra XPCOM incantations. For each object in the input
542 // array, if it's an IShellLinkW, this deletes the cached icon and adds the
543 // url param to a list of URLs to be removed from the places database.
RemoveIconCacheAndGetJumplistShortcutURIs(IObjectArray * aObjArray,nsTArray<nsString> & aURISpecs)544 void JumpListBuilder::RemoveIconCacheAndGetJumplistShortcutURIs(
545 IObjectArray* aObjArray, nsTArray<nsString>& aURISpecs) {
546 MOZ_ASSERT(!NS_IsMainThread());
547
548 // Early return here just in case some versions of Windows don't populate this
549 if (!aObjArray) {
550 return;
551 }
552
553 uint32_t count = 0;
554 aObjArray->GetCount(&count);
555
556 for (uint32_t idx = 0; idx < count; idx++) {
557 RefPtr<IShellLinkW> pLink;
558
559 if (FAILED(aObjArray->GetAt(idx, IID_IShellLinkW,
560 static_cast<void**>(getter_AddRefs(pLink))))) {
561 continue;
562 }
563
564 wchar_t buf[MAX_PATH];
565 HRESULT hres = pLink->GetArguments(buf, MAX_PATH);
566 if (SUCCEEDED(hres)) {
567 LPWSTR* arglist;
568 int32_t numArgs;
569
570 arglist = ::CommandLineToArgvW(buf, &numArgs);
571 if (arglist && numArgs > 0) {
572 nsString spec(arglist[0]);
573 aURISpecs.AppendElement(std::move(spec));
574 ::LocalFree(arglist);
575 }
576 }
577
578 int iconIdx = 0;
579 hres = pLink->GetIconLocation(buf, MAX_PATH, &iconIdx);
580 if (SUCCEEDED(hres)) {
581 nsDependentString spec(buf);
582 DeleteIconFromDisk(spec);
583 }
584 }
585 }
586
DeleteIconFromDisk(const nsAString & aPath)587 void JumpListBuilder::DeleteIconFromDisk(const nsAString& aPath) {
588 MOZ_ASSERT(!NS_IsMainThread());
589
590 // Check that we aren't deleting some arbitrary file that is not an icon
591 if (StringTail(aPath, 4).LowerCaseEqualsASCII(".ico")) {
592 // Construct the parent path of the passed in path
593 nsCOMPtr<nsIFile> icoFile;
594 nsresult rv = NS_NewLocalFile(aPath, true, getter_AddRefs(icoFile));
595 if (NS_WARN_IF(NS_FAILED(rv))) {
596 return;
597 }
598
599 icoFile->Remove(false);
600 }
601 }
602
Observe(nsISupports * aSubject,const char * aTopic,const char16_t * aData)603 NS_IMETHODIMP JumpListBuilder::Observe(nsISupports* aSubject,
604 const char* aTopic,
605 const char16_t* aData) {
606 NS_ENSURE_ARG_POINTER(aTopic);
607 if (strcmp(aTopic, TOPIC_PROFILE_BEFORE_CHANGE) == 0) {
608 nsCOMPtr<nsIObserverService> observerService =
609 do_GetService("@mozilla.org/observer-service;1");
610 if (observerService) {
611 observerService->RemoveObserver(this, TOPIC_PROFILE_BEFORE_CHANGE);
612 }
613 mIOThread->Shutdown();
614 // Clear out mJumpListMgr, as MSCOM services won't be available soon.
615 ReentrantMonitorAutoEnter lock(mMonitor);
616 mJumpListMgr = nullptr;
617 } else if (strcmp(aTopic, "nsPref:changed") == 0 &&
618 nsDependentString(aData).EqualsASCII(kPrefTaskbarEnabled)) {
619 bool enabled = Preferences::GetBool(kPrefTaskbarEnabled, true);
620 if (!enabled) {
621 nsCOMPtr<nsIRunnable> event =
622 new mozilla::widget::AsyncDeleteAllFaviconsFromDisk();
623 mIOThread->Dispatch(event, NS_DISPATCH_NORMAL);
624 }
625 } else if (strcmp(aTopic, TOPIC_CLEAR_PRIVATE_DATA) == 0) {
626 // Delete JumpListCache icons from Disk, if any.
627 nsCOMPtr<nsIRunnable> event =
628 new mozilla::widget::AsyncDeleteAllFaviconsFromDisk(false);
629 mIOThread->Dispatch(event, NS_DISPATCH_NORMAL);
630 }
631 return NS_OK;
632 }
633
634 } // namespace widget
635 } // namespace mozilla
636