Backed out changeset 2450366cf7ca (bug 1891629) for causing win msix mochitest failures
[gecko.git] / widget / windows / LegacyJumpListBuilder.cpp
blobfbfe10f64b1265153d0ca665442817a5fc6e7051
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 "LegacyJumpListBuilder.h"
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"
27 #include <shellapi.h>
28 #include "WinUtils.h"
30 using mozilla::dom::Promise;
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
36 namespace mozilla {
37 namespace widget {
39 // defined in WinTaskbar.cpp
40 extern const wchar_t* gMozillaJumpListIDGeneric;
42 Atomic<bool> LegacyJumpListBuilder::sBuildingList(false);
43 const char kPrefTaskbarEnabled[] = "browser.taskbar.lists.enabled";
45 NS_IMPL_ISUPPORTS(LegacyJumpListBuilder, nsILegacyJumpListBuilder, nsIObserver)
46 #define TOPIC_PROFILE_BEFORE_CHANGE "profile-before-change"
47 #define TOPIC_CLEAR_PRIVATE_DATA "clear-private-data"
49 namespace detail {
51 class DoneCommitListBuildCallback final : public nsIRunnable {
52 NS_DECL_THREADSAFE_ISUPPORTS
54 public:
55 DoneCommitListBuildCallback(nsILegacyJumpListCommittedCallback* aCallback,
56 LegacyJumpListBuilder* aBuilder)
57 : mCallback(aCallback), mBuilder(aBuilder), mResult(false) {}
59 NS_IMETHOD Run() override {
60 MOZ_ASSERT(NS_IsMainThread());
61 if (mCallback) {
62 Unused << mCallback->Done(mResult);
64 // Ensure we are releasing on the main thread.
65 Destroy();
66 return NS_OK;
69 void SetResult(bool aResult) { mResult = aResult; }
71 private:
72 ~DoneCommitListBuildCallback() {
73 // Destructor does not always call on the main thread.
74 MOZ_ASSERT(!mCallback);
75 MOZ_ASSERT(!mBuilder);
78 void Destroy() {
79 MOZ_ASSERT(NS_IsMainThread());
80 mCallback = nullptr;
81 mBuilder = nullptr;
84 // These two references MUST be released on the main thread.
85 RefPtr<nsILegacyJumpListCommittedCallback> mCallback;
86 RefPtr<LegacyJumpListBuilder> mBuilder;
87 bool mResult;
90 NS_IMPL_ISUPPORTS(DoneCommitListBuildCallback, nsIRunnable);
92 } // namespace detail
94 LegacyJumpListBuilder::LegacyJumpListBuilder()
95 : mMaxItems(0),
96 mHasCommit(false),
97 mMonitor("LegacyJumpListBuilderMonitor") {
98 MOZ_ASSERT(NS_IsMainThread());
100 // Instantiate mJumpListMgr in the multithreaded apartment so that proxied
101 // calls on that object do not need to interact with the main thread's message
102 // pump.
103 mscom::EnsureMTA([&]() {
104 RefPtr<ICustomDestinationList> jumpListMgr;
105 HRESULT hr = ::CoCreateInstance(
106 CLSID_DestinationList, nullptr, CLSCTX_INPROC_SERVER,
107 IID_ICustomDestinationList, getter_AddRefs(jumpListMgr));
108 if (FAILED(hr)) {
109 return;
112 ReentrantMonitorAutoEnter lock(mMonitor);
113 // Since we are accessing mJumpListMgr across different threads
114 // (ie, different apartments), mJumpListMgr must be an agile reference.
115 mJumpListMgr = mscom::AgileReference(jumpListMgr);
118 if (!mJumpListMgr) {
119 return;
122 // Make a lazy thread for any IO
123 mIOThread = new LazyIdleThread(DEFAULT_THREAD_TIMEOUT_MS, "Jump List",
124 LazyIdleThread::ManualShutdown);
125 Preferences::AddStrongObserver(this, kPrefTaskbarEnabled);
127 nsCOMPtr<nsIObserverService> observerService =
128 do_GetService("@mozilla.org/observer-service;1");
129 if (observerService) {
130 observerService->AddObserver(this, TOPIC_PROFILE_BEFORE_CHANGE, false);
131 observerService->AddObserver(this, TOPIC_CLEAR_PRIVATE_DATA, false);
135 LegacyJumpListBuilder::~LegacyJumpListBuilder() {
136 Preferences::RemoveObserver(this, kPrefTaskbarEnabled);
139 NS_IMETHODIMP LegacyJumpListBuilder::SetAppUserModelID(
140 const nsAString& aAppUserModelId) {
141 ReentrantMonitorAutoEnter lock(mMonitor);
142 if (!mJumpListMgr) return NS_ERROR_NOT_AVAILABLE;
144 RefPtr<ICustomDestinationList> jumpListMgr = mJumpListMgr.Resolve();
145 if (!jumpListMgr) {
146 return NS_ERROR_NOT_AVAILABLE;
149 mAppUserModelId.Assign(aAppUserModelId);
150 // MSIX packages explicitly do not support setting the appid from within
151 // the app, as it is set in the package manifest instead.
152 if (!mozilla::widget::WinUtils::HasPackageIdentity()) {
153 jumpListMgr->SetAppID(mAppUserModelId.get());
156 return NS_OK;
159 NS_IMETHODIMP LegacyJumpListBuilder::GetAvailable(int16_t* aAvailable) {
160 *aAvailable = false;
162 ReentrantMonitorAutoEnter lock(mMonitor);
163 if (mJumpListMgr) *aAvailable = true;
165 return NS_OK;
168 NS_IMETHODIMP LegacyJumpListBuilder::GetIsListCommitted(bool* aCommit) {
169 *aCommit = mHasCommit;
171 return NS_OK;
174 NS_IMETHODIMP LegacyJumpListBuilder::GetMaxListItems(int16_t* aMaxItems) {
175 ReentrantMonitorAutoEnter lock(mMonitor);
176 if (!mJumpListMgr) return NS_ERROR_NOT_AVAILABLE;
178 *aMaxItems = 0;
180 if (sBuildingList) {
181 *aMaxItems = mMaxItems;
182 return NS_OK;
185 RefPtr<ICustomDestinationList> jumpListMgr = mJumpListMgr.Resolve();
186 if (!jumpListMgr) {
187 return NS_ERROR_UNEXPECTED;
190 IObjectArray* objArray;
191 if (SUCCEEDED(jumpListMgr->BeginList(&mMaxItems, IID_PPV_ARGS(&objArray)))) {
192 *aMaxItems = mMaxItems;
194 if (objArray) objArray->Release();
196 jumpListMgr->AbortList();
199 return NS_OK;
202 NS_IMETHODIMP LegacyJumpListBuilder::InitListBuild(JSContext* aCx,
203 Promise** aPromise) {
204 ReentrantMonitorAutoEnter lock(mMonitor);
205 if (!mJumpListMgr) {
206 return NS_ERROR_NOT_AVAILABLE;
209 nsIGlobalObject* globalObject = xpc::CurrentNativeGlobal(aCx);
210 if (NS_WARN_IF(!globalObject)) {
211 return NS_ERROR_FAILURE;
214 ErrorResult result;
215 RefPtr<Promise> promise = Promise::Create(globalObject, result);
216 if (NS_WARN_IF(result.Failed())) {
217 return result.StealNSResult();
220 nsCOMPtr<nsIRunnable> runnable =
221 NewRunnableMethod<StoreCopyPassByRRef<RefPtr<Promise>>>(
222 "InitListBuild", this, &LegacyJumpListBuilder::DoInitListBuild,
223 promise);
224 nsresult rv = mIOThread->Dispatch(runnable, NS_DISPATCH_NORMAL);
225 if (NS_WARN_IF(NS_FAILED(rv))) {
226 return rv;
229 promise.forget(aPromise);
230 return NS_OK;
233 void LegacyJumpListBuilder::DoInitListBuild(RefPtr<Promise>&& aPromise) {
234 // Since we're invoking COM interfaces to talk to the shell on a background
235 // thread, we need to be running inside a multithreaded apartment.
236 mscom::MTARegion mta;
237 MOZ_ASSERT(mta.IsValid());
239 ReentrantMonitorAutoEnter lock(mMonitor);
240 MOZ_ASSERT(mJumpListMgr);
242 if (sBuildingList) {
243 AbortListBuild();
246 HRESULT hr = E_UNEXPECTED;
247 auto errorHandler = MakeScopeExit([&aPromise, &hr]() {
248 if (SUCCEEDED(hr)) {
249 return;
252 NS_DispatchToMainThread(NS_NewRunnableFunction(
253 "InitListBuildReject", [promise = std::move(aPromise)]() {
254 promise->MaybeReject(NS_ERROR_FAILURE);
255 }));
258 RefPtr<ICustomDestinationList> jumpListMgr = mJumpListMgr.Resolve();
259 if (!jumpListMgr) {
260 return;
263 nsTArray<nsString> urisToRemove;
264 RefPtr<IObjectArray> objArray;
265 hr = jumpListMgr->BeginList(
266 &mMaxItems,
267 IID_PPV_ARGS(static_cast<IObjectArray**>(getter_AddRefs(objArray))));
268 if (FAILED(hr)) {
269 return;
272 // The returned objArray of removed items are for manually removed items.
273 // This does not return items which are removed because they were previously
274 // part of the jump list but are no longer part of the jump list.
275 sBuildingList = true;
276 RemoveIconCacheAndGetJumplistShortcutURIs(objArray, urisToRemove);
278 NS_DispatchToMainThread(NS_NewRunnableFunction(
279 "InitListBuildResolve", [urisToRemove = std::move(urisToRemove),
280 promise = std::move(aPromise)]() {
281 promise->MaybeResolve(urisToRemove);
282 }));
285 // Ensures that we have no old ICO files left in the jump list cache
286 nsresult LegacyJumpListBuilder::RemoveIconCacheForAllItems() {
287 // Construct the path of our jump list cache
288 nsCOMPtr<nsIFile> jumpListCacheDir;
289 nsresult rv =
290 NS_GetSpecialDirectory("ProfLDS", getter_AddRefs(jumpListCacheDir));
291 NS_ENSURE_SUCCESS(rv, rv);
292 rv = jumpListCacheDir->AppendNative(
293 nsDependentCString(mozilla::widget::FaviconHelper::kJumpListCacheDir));
294 NS_ENSURE_SUCCESS(rv, rv);
296 nsCOMPtr<nsIDirectoryEnumerator> entries;
297 rv = jumpListCacheDir->GetDirectoryEntries(getter_AddRefs(entries));
298 NS_ENSURE_SUCCESS(rv, rv);
300 // Loop through each directory entry and remove all ICO files found
301 do {
302 nsCOMPtr<nsIFile> currFile;
303 if (NS_FAILED(entries->GetNextFile(getter_AddRefs(currFile))) || !currFile)
304 break;
306 nsAutoString path;
307 if (NS_FAILED(currFile->GetPath(path))) continue;
309 if (StringTail(path, 4).LowerCaseEqualsASCII(".ico")) {
310 // Check if the cached ICO file exists
311 bool exists;
312 if (NS_FAILED(currFile->Exists(&exists)) || !exists) continue;
314 // We found an ICO file that exists, so we should remove it
315 currFile->Remove(false);
317 } while (true);
319 return NS_OK;
322 NS_IMETHODIMP LegacyJumpListBuilder::AddListToBuild(int16_t aCatType,
323 nsIArray* items,
324 const nsAString& catName,
325 bool* _retval) {
326 nsresult rv;
328 *_retval = false;
330 ReentrantMonitorAutoEnter lock(mMonitor);
331 if (!mJumpListMgr) return NS_ERROR_NOT_AVAILABLE;
333 RefPtr<ICustomDestinationList> jumpListMgr = mJumpListMgr.Resolve();
334 if (!jumpListMgr) {
335 return NS_ERROR_UNEXPECTED;
338 switch (aCatType) {
339 case nsILegacyJumpListBuilder::JUMPLIST_CATEGORY_TASKS: {
340 NS_ENSURE_ARG_POINTER(items);
342 HRESULT hr;
343 RefPtr<IObjectCollection> collection;
344 hr = CoCreateInstance(CLSID_EnumerableObjectCollection, nullptr,
345 CLSCTX_INPROC_SERVER, IID_IObjectCollection,
346 getter_AddRefs(collection));
347 if (FAILED(hr)) return NS_ERROR_UNEXPECTED;
349 // Build the list
350 uint32_t length;
351 items->GetLength(&length);
352 for (uint32_t i = 0; i < length; ++i) {
353 nsCOMPtr<nsILegacyJumpListItem> item = do_QueryElementAt(items, i);
354 if (!item) continue;
355 // Check for separators
356 if (IsSeparator(item)) {
357 RefPtr<IShellLinkW> link;
358 rv = LegacyJumpListSeparator::GetSeparator(link);
359 if (NS_FAILED(rv)) return rv;
360 collection->AddObject(link);
361 continue;
363 // These should all be ShellLinks
364 RefPtr<IShellLinkW> link;
365 rv = LegacyJumpListShortcut::GetShellLink(item, link, mIOThread);
366 if (NS_FAILED(rv)) return rv;
367 collection->AddObject(link);
370 // We need IObjectArray to submit
371 RefPtr<IObjectArray> pArray;
372 hr = collection->QueryInterface(IID_IObjectArray, getter_AddRefs(pArray));
373 if (FAILED(hr)) return NS_ERROR_UNEXPECTED;
375 // Add the tasks
376 hr = jumpListMgr->AddUserTasks(pArray);
377 if (SUCCEEDED(hr)) *_retval = true;
378 return NS_OK;
379 } break;
380 case nsILegacyJumpListBuilder::JUMPLIST_CATEGORY_RECENT: {
381 if (SUCCEEDED(jumpListMgr->AppendKnownCategory(KDC_RECENT)))
382 *_retval = true;
383 return NS_OK;
384 } break;
385 case nsILegacyJumpListBuilder::JUMPLIST_CATEGORY_FREQUENT: {
386 if (SUCCEEDED(jumpListMgr->AppendKnownCategory(KDC_FREQUENT)))
387 *_retval = true;
388 return NS_OK;
389 } break;
390 case nsILegacyJumpListBuilder::JUMPLIST_CATEGORY_CUSTOMLIST: {
391 NS_ENSURE_ARG_POINTER(items);
393 if (catName.IsEmpty()) return NS_ERROR_INVALID_ARG;
395 HRESULT hr;
396 RefPtr<IObjectCollection> collection;
397 hr = CoCreateInstance(CLSID_EnumerableObjectCollection, nullptr,
398 CLSCTX_INPROC_SERVER, IID_IObjectCollection,
399 getter_AddRefs(collection));
400 if (FAILED(hr)) return NS_ERROR_UNEXPECTED;
402 uint32_t length;
403 items->GetLength(&length);
404 for (uint32_t i = 0; i < length; ++i) {
405 nsCOMPtr<nsILegacyJumpListItem> item = do_QueryElementAt(items, i);
406 if (!item) continue;
407 int16_t type;
408 if (NS_FAILED(item->GetType(&type))) continue;
409 switch (type) {
410 case nsILegacyJumpListItem::JUMPLIST_ITEM_SEPARATOR: {
411 RefPtr<IShellLinkW> shellItem;
412 rv = LegacyJumpListSeparator::GetSeparator(shellItem);
413 if (NS_FAILED(rv)) return rv;
414 collection->AddObject(shellItem);
415 } break;
416 case nsILegacyJumpListItem::JUMPLIST_ITEM_LINK: {
417 RefPtr<IShellItem2> shellItem;
418 rv = LegacyJumpListLink::GetShellItem(item, shellItem);
419 if (NS_FAILED(rv)) return rv;
420 collection->AddObject(shellItem);
421 } break;
422 case nsILegacyJumpListItem::JUMPLIST_ITEM_SHORTCUT: {
423 RefPtr<IShellLinkW> shellItem;
424 rv = LegacyJumpListShortcut::GetShellLink(item, shellItem,
425 mIOThread);
426 if (NS_FAILED(rv)) return rv;
427 collection->AddObject(shellItem);
428 } break;
432 // We need IObjectArray to submit
433 RefPtr<IObjectArray> pArray;
434 hr = collection->QueryInterface(IID_IObjectArray, (LPVOID*)&pArray);
435 if (FAILED(hr)) return NS_ERROR_UNEXPECTED;
437 // Add the tasks
438 hr = jumpListMgr->AppendCategory(
439 reinterpret_cast<const wchar_t*>(catName.BeginReading()), pArray);
440 if (SUCCEEDED(hr)) *_retval = true;
442 // Get rid of the old icons
443 nsCOMPtr<nsIRunnable> event =
444 new mozilla::widget::AsyncDeleteAllFaviconsFromDisk(true);
445 mIOThread->Dispatch(event, NS_DISPATCH_NORMAL);
447 return NS_OK;
448 } break;
450 return NS_OK;
453 NS_IMETHODIMP LegacyJumpListBuilder::AbortListBuild() {
454 ReentrantMonitorAutoEnter lock(mMonitor);
455 if (!mJumpListMgr) return NS_ERROR_NOT_AVAILABLE;
457 RefPtr<ICustomDestinationList> jumpListMgr = mJumpListMgr.Resolve();
458 if (!jumpListMgr) {
459 return NS_ERROR_UNEXPECTED;
462 jumpListMgr->AbortList();
463 sBuildingList = false;
465 return NS_OK;
468 NS_IMETHODIMP LegacyJumpListBuilder::CommitListBuild(
469 nsILegacyJumpListCommittedCallback* aCallback) {
470 ReentrantMonitorAutoEnter lock(mMonitor);
471 if (!mJumpListMgr) return NS_ERROR_NOT_AVAILABLE;
473 // Also holds a strong reference to this to prevent use-after-free.
474 RefPtr<detail::DoneCommitListBuildCallback> callback =
475 new detail::DoneCommitListBuildCallback(aCallback, this);
477 // The builder has a strong reference in the callback already, so we do not
478 // need to do it for this runnable again.
479 RefPtr<nsIRunnable> event =
480 NewNonOwningRunnableMethod<RefPtr<detail::DoneCommitListBuildCallback>>(
481 "LegacyJumpListBuilder::DoCommitListBuild", this,
482 &LegacyJumpListBuilder::DoCommitListBuild, std::move(callback));
483 Unused << mIOThread->Dispatch(event, NS_DISPATCH_NORMAL);
485 return NS_OK;
488 void LegacyJumpListBuilder::DoCommitListBuild(
489 RefPtr<detail::DoneCommitListBuildCallback> aCallback) {
490 // Since we're invoking COM interfaces to talk to the shell on a background
491 // thread, we need to be running inside a multithreaded apartment.
492 mscom::MTARegion mta;
493 MOZ_ASSERT(mta.IsValid());
495 ReentrantMonitorAutoEnter lock(mMonitor);
496 MOZ_ASSERT(mJumpListMgr);
497 MOZ_ASSERT(aCallback);
499 HRESULT hr = E_UNEXPECTED;
500 auto onExit = MakeScopeExit([&hr, &aCallback]() {
501 // XXX We might want some specific error data here.
502 aCallback->SetResult(SUCCEEDED(hr));
503 Unused << NS_DispatchToMainThread(aCallback);
506 RefPtr<ICustomDestinationList> jumpListMgr = mJumpListMgr.Resolve();
507 if (!jumpListMgr) {
508 return;
511 hr = jumpListMgr->CommitList();
512 sBuildingList = false;
514 if (SUCCEEDED(hr)) {
515 mHasCommit = true;
519 NS_IMETHODIMP LegacyJumpListBuilder::DeleteActiveList(bool* _retval) {
520 *_retval = false;
522 ReentrantMonitorAutoEnter lock(mMonitor);
523 if (!mJumpListMgr) return NS_ERROR_NOT_AVAILABLE;
525 if (sBuildingList) {
526 AbortListBuild();
529 RefPtr<ICustomDestinationList> jumpListMgr = mJumpListMgr.Resolve();
530 if (!jumpListMgr) {
531 return NS_ERROR_UNEXPECTED;
534 if (SUCCEEDED(jumpListMgr->DeleteList(mAppUserModelId.get()))) {
535 *_retval = true;
538 return NS_OK;
541 /* internal */
543 bool LegacyJumpListBuilder::IsSeparator(nsCOMPtr<nsILegacyJumpListItem>& item) {
544 int16_t type;
545 item->GetType(&type);
546 if (NS_FAILED(item->GetType(&type))) return false;
548 if (type == nsILegacyJumpListItem::JUMPLIST_ITEM_SEPARATOR) return true;
549 return false;
552 // RemoveIconCacheAndGetJumplistShortcutURIs - does multiple things to
553 // avoid unnecessary extra XPCOM incantations. For each object in the input
554 // array, if it's an IShellLinkW, this deletes the cached icon and adds the
555 // url param to a list of URLs to be removed from the places database.
556 void LegacyJumpListBuilder::RemoveIconCacheAndGetJumplistShortcutURIs(
557 IObjectArray* aObjArray, nsTArray<nsString>& aURISpecs) {
558 MOZ_ASSERT(!NS_IsMainThread());
560 // Early return here just in case some versions of Windows don't populate this
561 if (!aObjArray) {
562 return;
565 uint32_t count = 0;
566 aObjArray->GetCount(&count);
568 for (uint32_t idx = 0; idx < count; idx++) {
569 RefPtr<IShellLinkW> pLink;
571 if (FAILED(aObjArray->GetAt(idx, IID_IShellLinkW,
572 static_cast<void**>(getter_AddRefs(pLink))))) {
573 continue;
576 wchar_t buf[MAX_PATH];
577 HRESULT hres = pLink->GetArguments(buf, MAX_PATH);
578 if (SUCCEEDED(hres)) {
579 LPWSTR* arglist;
580 int32_t numArgs;
582 arglist = ::CommandLineToArgvW(buf, &numArgs);
583 if (arglist && numArgs > 0) {
584 nsString spec(arglist[0]);
585 aURISpecs.AppendElement(std::move(spec));
586 ::LocalFree(arglist);
590 int iconIdx = 0;
591 hres = pLink->GetIconLocation(buf, MAX_PATH, &iconIdx);
592 if (SUCCEEDED(hres)) {
593 nsDependentString spec(buf);
594 DeleteIconFromDisk(spec);
599 void LegacyJumpListBuilder::DeleteIconFromDisk(const nsAString& aPath) {
600 MOZ_ASSERT(!NS_IsMainThread());
602 // Check that we aren't deleting some arbitrary file that is not an icon
603 if (StringTail(aPath, 4).LowerCaseEqualsASCII(".ico")) {
604 // Construct the parent path of the passed in path
605 nsCOMPtr<nsIFile> icoFile;
606 nsresult rv = NS_NewLocalFile(aPath, true, getter_AddRefs(icoFile));
607 if (NS_WARN_IF(NS_FAILED(rv))) {
608 return;
611 icoFile->Remove(false);
615 NS_IMETHODIMP LegacyJumpListBuilder::Observe(nsISupports* aSubject,
616 const char* aTopic,
617 const char16_t* aData) {
618 NS_ENSURE_ARG_POINTER(aTopic);
619 if (strcmp(aTopic, TOPIC_PROFILE_BEFORE_CHANGE) == 0) {
620 nsCOMPtr<nsIObserverService> observerService =
621 do_GetService("@mozilla.org/observer-service;1");
622 if (observerService) {
623 observerService->RemoveObserver(this, TOPIC_PROFILE_BEFORE_CHANGE);
625 mIOThread->Shutdown();
626 // Clear out mJumpListMgr, as MSCOM services won't be available soon.
627 ReentrantMonitorAutoEnter lock(mMonitor);
628 mJumpListMgr = nullptr;
629 } else if (strcmp(aTopic, "nsPref:changed") == 0 &&
630 nsDependentString(aData).EqualsASCII(kPrefTaskbarEnabled)) {
631 bool enabled = Preferences::GetBool(kPrefTaskbarEnabled, true);
632 if (!enabled) {
633 nsCOMPtr<nsIRunnable> event =
634 new mozilla::widget::AsyncDeleteAllFaviconsFromDisk();
635 mIOThread->Dispatch(event, NS_DISPATCH_NORMAL);
637 } else if (strcmp(aTopic, TOPIC_CLEAR_PRIVATE_DATA) == 0) {
638 // Delete JumpListCache icons from Disk, if any.
639 nsCOMPtr<nsIRunnable> event =
640 new mozilla::widget::AsyncDeleteAllFaviconsFromDisk(false);
641 mIOThread->Dispatch(event, NS_DISPATCH_NORMAL);
643 return NS_OK;
646 } // namespace widget
647 } // namespace mozilla