Bug 1861709 replace AudioCallbackDriver::ThreadRunning() assertions that mean to...
[gecko.git] / widget / windows / JumpListBuilder.cpp
blob133bcd25867bc088b8f2592e1d51833e4e152169
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 "JumpListBuilder.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> JumpListBuilder::sBuildingList(false);
43 const char kPrefTaskbarEnabled[] = "browser.taskbar.lists.enabled";
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"
49 namespace detail {
51 class DoneCommitListBuildCallback final : public nsIRunnable {
52 NS_DECL_THREADSAFE_ISUPPORTS
54 public:
55 DoneCommitListBuildCallback(nsIJumpListCommittedCallback* aCallback,
56 JumpListBuilder* 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<nsIJumpListCommittedCallback> mCallback;
86 RefPtr<JumpListBuilder> mBuilder;
87 bool mResult;
90 NS_IMPL_ISUPPORTS(DoneCommitListBuildCallback, nsIRunnable);
92 } // namespace detail
94 JumpListBuilder::JumpListBuilder()
95 : mMaxItems(0), mHasCommit(false), mMonitor("JumpListBuilderMonitor") {
96 MOZ_ASSERT(NS_IsMainThread());
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;
110 ReentrantMonitorAutoEnter lock(mMonitor);
111 // Since we are accessing mJumpListMgr across different threads
112 // (ie, different apartments), mJumpListMgr must be an agile reference.
113 mJumpListMgr = jumpListMgr;
116 if (!mJumpListMgr) {
117 return;
120 // Make a lazy thread for any IO
121 mIOThread = new LazyIdleThread(DEFAULT_THREAD_TIMEOUT_MS, "Jump List",
122 LazyIdleThread::ManualShutdown);
123 Preferences::AddStrongObserver(this, kPrefTaskbarEnabled);
125 nsCOMPtr<nsIObserverService> observerService =
126 do_GetService("@mozilla.org/observer-service;1");
127 if (observerService) {
128 observerService->AddObserver(this, TOPIC_PROFILE_BEFORE_CHANGE, false);
129 observerService->AddObserver(this, TOPIC_CLEAR_PRIVATE_DATA, false);
132 RefPtr<ICustomDestinationList> jumpListMgr = mJumpListMgr;
133 if (!jumpListMgr) {
134 return;
138 JumpListBuilder::~JumpListBuilder() {
139 Preferences::RemoveObserver(this, kPrefTaskbarEnabled);
142 NS_IMETHODIMP JumpListBuilder::SetAppUserModelID(
143 const nsAString& aAppUserModelId) {
144 ReentrantMonitorAutoEnter lock(mMonitor);
145 if (!mJumpListMgr) return NS_ERROR_NOT_AVAILABLE;
147 RefPtr<ICustomDestinationList> jumpListMgr = mJumpListMgr;
148 if (!jumpListMgr) {
149 return NS_ERROR_NOT_AVAILABLE;
152 mAppUserModelId.Assign(aAppUserModelId);
153 // MSIX packages explicitly do not support setting the appid from within
154 // the app, as it is set in the package manifest instead.
155 if (!mozilla::widget::WinUtils::HasPackageIdentity()) {
156 jumpListMgr->SetAppID(mAppUserModelId.get());
159 return NS_OK;
162 NS_IMETHODIMP JumpListBuilder::GetAvailable(int16_t* aAvailable) {
163 *aAvailable = false;
165 ReentrantMonitorAutoEnter lock(mMonitor);
166 if (mJumpListMgr) *aAvailable = true;
168 return NS_OK;
171 NS_IMETHODIMP JumpListBuilder::GetIsListCommitted(bool* aCommit) {
172 *aCommit = mHasCommit;
174 return NS_OK;
177 NS_IMETHODIMP JumpListBuilder::GetMaxListItems(int16_t* aMaxItems) {
178 ReentrantMonitorAutoEnter lock(mMonitor);
179 if (!mJumpListMgr) return NS_ERROR_NOT_AVAILABLE;
181 *aMaxItems = 0;
183 if (sBuildingList) {
184 *aMaxItems = mMaxItems;
185 return NS_OK;
188 RefPtr<ICustomDestinationList> jumpListMgr = mJumpListMgr;
189 if (!jumpListMgr) {
190 return NS_ERROR_UNEXPECTED;
193 IObjectArray* objArray;
194 if (SUCCEEDED(jumpListMgr->BeginList(&mMaxItems, IID_PPV_ARGS(&objArray)))) {
195 *aMaxItems = mMaxItems;
197 if (objArray) objArray->Release();
199 jumpListMgr->AbortList();
202 return NS_OK;
205 NS_IMETHODIMP JumpListBuilder::InitListBuild(JSContext* aCx,
206 Promise** aPromise) {
207 ReentrantMonitorAutoEnter lock(mMonitor);
208 if (!mJumpListMgr) {
209 return NS_ERROR_NOT_AVAILABLE;
212 nsIGlobalObject* globalObject = xpc::CurrentNativeGlobal(aCx);
213 if (NS_WARN_IF(!globalObject)) {
214 return NS_ERROR_FAILURE;
217 ErrorResult result;
218 RefPtr<Promise> promise = Promise::Create(globalObject, result);
219 if (NS_WARN_IF(result.Failed())) {
220 return result.StealNSResult();
223 nsCOMPtr<nsIRunnable> runnable =
224 NewRunnableMethod<StoreCopyPassByRRef<RefPtr<Promise>>>(
225 "InitListBuild", this, &JumpListBuilder::DoInitListBuild, promise);
226 nsresult rv = mIOThread->Dispatch(runnable, NS_DISPATCH_NORMAL);
227 if (NS_WARN_IF(NS_FAILED(rv))) {
228 return rv;
231 promise.forget(aPromise);
232 return NS_OK;
235 void JumpListBuilder::DoInitListBuild(RefPtr<Promise>&& aPromise) {
236 // Since we're invoking COM interfaces to talk to the shell on a background
237 // thread, we need to be running inside a multithreaded apartment.
238 mscom::MTARegion mta;
239 MOZ_ASSERT(mta.IsValid());
241 ReentrantMonitorAutoEnter lock(mMonitor);
242 MOZ_ASSERT(mJumpListMgr);
244 if (sBuildingList) {
245 AbortListBuild();
248 HRESULT hr = E_UNEXPECTED;
249 auto errorHandler = MakeScopeExit([&aPromise, &hr]() {
250 if (SUCCEEDED(hr)) {
251 return;
254 NS_DispatchToMainThread(NS_NewRunnableFunction(
255 "InitListBuildReject", [promise = std::move(aPromise)]() {
256 promise->MaybeReject(NS_ERROR_FAILURE);
257 }));
260 RefPtr<ICustomDestinationList> jumpListMgr = mJumpListMgr;
261 if (!jumpListMgr) {
262 return;
265 nsTArray<nsString> urisToRemove;
266 RefPtr<IObjectArray> objArray;
267 hr = jumpListMgr->BeginList(
268 &mMaxItems,
269 IID_PPV_ARGS(static_cast<IObjectArray**>(getter_AddRefs(objArray))));
270 if (FAILED(hr)) {
271 return;
274 // The returned objArray of removed items are for manually removed items.
275 // This does not return items which are removed because they were previously
276 // part of the jump list but are no longer part of the jump list.
277 sBuildingList = true;
278 RemoveIconCacheAndGetJumplistShortcutURIs(objArray, urisToRemove);
280 NS_DispatchToMainThread(NS_NewRunnableFunction(
281 "InitListBuildResolve", [urisToRemove = std::move(urisToRemove),
282 promise = std::move(aPromise)]() {
283 promise->MaybeResolve(urisToRemove);
284 }));
287 // Ensures that we have no old ICO files left in the jump list cache
288 nsresult JumpListBuilder::RemoveIconCacheForAllItems() {
289 // Construct the path of our jump list cache
290 nsCOMPtr<nsIFile> jumpListCacheDir;
291 nsresult rv =
292 NS_GetSpecialDirectory("ProfLDS", getter_AddRefs(jumpListCacheDir));
293 NS_ENSURE_SUCCESS(rv, rv);
294 rv = jumpListCacheDir->AppendNative(
295 nsDependentCString(mozilla::widget::FaviconHelper::kJumpListCacheDir));
296 NS_ENSURE_SUCCESS(rv, rv);
298 nsCOMPtr<nsIDirectoryEnumerator> entries;
299 rv = jumpListCacheDir->GetDirectoryEntries(getter_AddRefs(entries));
300 NS_ENSURE_SUCCESS(rv, rv);
302 // Loop through each directory entry and remove all ICO files found
303 do {
304 nsCOMPtr<nsIFile> currFile;
305 if (NS_FAILED(entries->GetNextFile(getter_AddRefs(currFile))) || !currFile)
306 break;
308 nsAutoString path;
309 if (NS_FAILED(currFile->GetPath(path))) continue;
311 if (StringTail(path, 4).LowerCaseEqualsASCII(".ico")) {
312 // Check if the cached ICO file exists
313 bool exists;
314 if (NS_FAILED(currFile->Exists(&exists)) || !exists) continue;
316 // We found an ICO file that exists, so we should remove it
317 currFile->Remove(false);
319 } while (true);
321 return NS_OK;
324 NS_IMETHODIMP JumpListBuilder::AddListToBuild(int16_t aCatType, nsIArray* items,
325 const nsAString& catName,
326 bool* _retval) {
327 nsresult rv;
329 *_retval = false;
331 ReentrantMonitorAutoEnter lock(mMonitor);
332 if (!mJumpListMgr) return NS_ERROR_NOT_AVAILABLE;
334 RefPtr<ICustomDestinationList> jumpListMgr = mJumpListMgr;
335 if (!jumpListMgr) {
336 return NS_ERROR_UNEXPECTED;
339 switch (aCatType) {
340 case nsIJumpListBuilder::JUMPLIST_CATEGORY_TASKS: {
341 NS_ENSURE_ARG_POINTER(items);
343 HRESULT hr;
344 RefPtr<IObjectCollection> collection;
345 hr = CoCreateInstance(CLSID_EnumerableObjectCollection, nullptr,
346 CLSCTX_INPROC_SERVER, IID_IObjectCollection,
347 getter_AddRefs(collection));
348 if (FAILED(hr)) return NS_ERROR_UNEXPECTED;
350 // Build the list
351 uint32_t length;
352 items->GetLength(&length);
353 for (uint32_t i = 0; i < length; ++i) {
354 nsCOMPtr<nsIJumpListItem> item = do_QueryElementAt(items, i);
355 if (!item) continue;
356 // Check for separators
357 if (IsSeparator(item)) {
358 RefPtr<IShellLinkW> link;
359 rv = JumpListSeparator::GetSeparator(link);
360 if (NS_FAILED(rv)) return rv;
361 collection->AddObject(link);
362 continue;
364 // These should all be ShellLinks
365 RefPtr<IShellLinkW> link;
366 rv = JumpListShortcut::GetShellLink(item, link, mIOThread);
367 if (NS_FAILED(rv)) return rv;
368 collection->AddObject(link);
371 // We need IObjectArray to submit
372 RefPtr<IObjectArray> pArray;
373 hr = collection->QueryInterface(IID_IObjectArray, getter_AddRefs(pArray));
374 if (FAILED(hr)) return NS_ERROR_UNEXPECTED;
376 // Add the tasks
377 hr = jumpListMgr->AddUserTasks(pArray);
378 if (SUCCEEDED(hr)) *_retval = true;
379 return NS_OK;
380 } break;
381 case nsIJumpListBuilder::JUMPLIST_CATEGORY_RECENT: {
382 if (SUCCEEDED(jumpListMgr->AppendKnownCategory(KDC_RECENT)))
383 *_retval = true;
384 return NS_OK;
385 } break;
386 case nsIJumpListBuilder::JUMPLIST_CATEGORY_FREQUENT: {
387 if (SUCCEEDED(jumpListMgr->AppendKnownCategory(KDC_FREQUENT)))
388 *_retval = true;
389 return NS_OK;
390 } break;
391 case nsIJumpListBuilder::JUMPLIST_CATEGORY_CUSTOMLIST: {
392 NS_ENSURE_ARG_POINTER(items);
394 if (catName.IsEmpty()) return NS_ERROR_INVALID_ARG;
396 HRESULT hr;
397 RefPtr<IObjectCollection> collection;
398 hr = CoCreateInstance(CLSID_EnumerableObjectCollection, nullptr,
399 CLSCTX_INPROC_SERVER, IID_IObjectCollection,
400 getter_AddRefs(collection));
401 if (FAILED(hr)) return NS_ERROR_UNEXPECTED;
403 uint32_t length;
404 items->GetLength(&length);
405 for (uint32_t i = 0; i < length; ++i) {
406 nsCOMPtr<nsIJumpListItem> item = do_QueryElementAt(items, i);
407 if (!item) continue;
408 int16_t type;
409 if (NS_FAILED(item->GetType(&type))) continue;
410 switch (type) {
411 case nsIJumpListItem::JUMPLIST_ITEM_SEPARATOR: {
412 RefPtr<IShellLinkW> shellItem;
413 rv = JumpListSeparator::GetSeparator(shellItem);
414 if (NS_FAILED(rv)) return rv;
415 collection->AddObject(shellItem);
416 } break;
417 case nsIJumpListItem::JUMPLIST_ITEM_LINK: {
418 RefPtr<IShellItem2> shellItem;
419 rv = JumpListLink::GetShellItem(item, shellItem);
420 if (NS_FAILED(rv)) return rv;
421 collection->AddObject(shellItem);
422 } break;
423 case nsIJumpListItem::JUMPLIST_ITEM_SHORTCUT: {
424 RefPtr<IShellLinkW> shellItem;
425 rv = JumpListShortcut::GetShellLink(item, shellItem, 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 JumpListBuilder::AbortListBuild() {
454 ReentrantMonitorAutoEnter lock(mMonitor);
455 if (!mJumpListMgr) return NS_ERROR_NOT_AVAILABLE;
457 RefPtr<ICustomDestinationList> jumpListMgr = mJumpListMgr;
458 if (!jumpListMgr) {
459 return NS_ERROR_UNEXPECTED;
462 jumpListMgr->AbortList();
463 sBuildingList = false;
465 return NS_OK;
468 NS_IMETHODIMP JumpListBuilder::CommitListBuild(
469 nsIJumpListCommittedCallback* 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 "JumpListBuilder::DoCommitListBuild", this,
482 &JumpListBuilder::DoCommitListBuild, std::move(callback));
483 Unused << mIOThread->Dispatch(event, NS_DISPATCH_NORMAL);
485 return NS_OK;
488 void JumpListBuilder::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;
507 if (!jumpListMgr) {
508 return;
511 hr = jumpListMgr->CommitList();
512 sBuildingList = false;
514 if (SUCCEEDED(hr)) {
515 mHasCommit = true;
519 NS_IMETHODIMP JumpListBuilder::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;
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 JumpListBuilder::IsSeparator(nsCOMPtr<nsIJumpListItem>& item) {
544 int16_t type;
545 item->GetType(&type);
546 if (NS_FAILED(item->GetType(&type))) return false;
548 if (type == nsIJumpListItem::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 JumpListBuilder::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 JumpListBuilder::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 JumpListBuilder::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