Bug 1425144 - Init Jump list on lazy idle thread r=aklotz

We're already committing jump lists on mIOThread, and I see
no errors or problems with initting on mIOThread either, so
I think this is okay. Had to restructure some things to
make using a Promise simple, and to avoid dispatching to
mIOThread from within mIOThread (since we can only dispatch
to a LazyIdleThread from the owner thread).

MozReview-Commit-ID: 1imBF8Jzmn6

--HG--
extra : rebase_source : 4f912e819cec898910e72d95311e3924714a3090
This commit is contained in:
Doug Thayer 2018-06-27 08:59:07 -07:00
parent c9cbf586a8
commit 56f41b91a9
5 changed files with 115 additions and 172 deletions

View file

@ -6,7 +6,6 @@
#include "nsISupports.idl"
interface nsIArray;
interface nsIMutableArray;
[scriptable, function, uuid(5131a62a-e99f-4631-9138-751f8aad1ae4)]
interface nsIJumpListCommittedCallback : nsISupports
@ -103,17 +102,17 @@ interface nsIJumpListBuilder : nsISupports
readonly attribute short maxListItems;
/**
* Initializes a jump list build and returns a list of items the user removed
* since the last time a jump list was committed. Removed items can become state
* after initListBuild is called, lists should be built in single-shot fasion.
* Initializes a jump list build and returns a promise with the list of
* items the user removed since the last time a jump list was committed.
* Removed items can become state after initListBuild is called, lists
* should be built in single-shot fasion.
*
* @param removedItems
* A list of items that were removed by the user since the last commit.
*
* @returns true if the operation completed successfully.
* @returns a promise with the list of items that were removed by the user
* since the last commit.
*/
boolean initListBuild(in nsIMutableArray removedItems);
[implicit_jscontext]
Promise initListBuild();
/**
* Adds a list and if required, a set of items for the list.
*

View file

@ -21,9 +21,13 @@
#include "mozilla/LazyIdleThread.h"
#include "nsIObserverService.h"
#include "mozilla/Unused.h"
#include "mozilla/dom/Promise.h"
#include <shellapi.h>
#include "WinUtils.h"
using mozilla::dom::Promise;
// The amount of time, in milliseconds, that our IO thread will stay alive after the last event it processes.
#define DEFAULT_THREAD_TIMEOUT_MS 30000
@ -177,91 +181,68 @@ NS_IMETHODIMP JumpListBuilder::GetMaxListItems(int16_t *aMaxItems)
return NS_OK;
}
NS_IMETHODIMP JumpListBuilder::InitListBuild(nsIMutableArray *removedItems, bool *_retval)
NS_IMETHODIMP JumpListBuilder::InitListBuild(JSContext* aCx,
Promise** aPromise)
{
NS_ENSURE_ARG_POINTER(removedItems);
*_retval = false;
ReentrantMonitorAutoEnter lock(mMonitor);
if (!mJumpListMgr)
if (!mJumpListMgr) {
return NS_ERROR_NOT_AVAILABLE;
if(sBuildingList)
AbortListBuild();
IObjectArray *objArray;
// The returned objArray of removed items are for manually removed items.
// This does not return items which are removed because they were previously
// part of the jump list but are no longer part of the jump list.
if (SUCCEEDED(mJumpListMgr->BeginList(&mMaxItems, IID_PPV_ARGS(&objArray)))) {
if (objArray) {
TransferIObjectArrayToIMutableArray(objArray, removedItems);
objArray->Release();
}
RemoveIconCacheForItems(removedItems);
sBuildingList = true;
*_retval = true;
return NS_OK;
}
nsIGlobalObject* globalObject =
xpc::NativeGlobal(JS::CurrentGlobalOrNull(aCx));
if (NS_WARN_IF(!globalObject)) {
return NS_ERROR_FAILURE;
}
ErrorResult result;
RefPtr<Promise> promise = Promise::Create(globalObject, result);
if (NS_WARN_IF(result.Failed())) {
return result.StealNSResult();
}
nsCOMPtr<nsIRunnable> runnable =
NewRunnableMethod<StoreCopyPassByRRef<RefPtr<Promise>>>("InitListBuild",
this,
&JumpListBuilder::DoInitListBuild,
promise);
nsresult rv = mIOThread->Dispatch(runnable, NS_DISPATCH_NORMAL);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
promise.forget(aPromise);
return NS_OK;
}
// Ensures that we don't have old ICO files that aren't in our jump lists
// anymore left over in the cache.
nsresult JumpListBuilder::RemoveIconCacheForItems(nsIMutableArray *items)
void JumpListBuilder::DoInitListBuild(RefPtr<Promise>&& aPromise)
{
NS_ENSURE_ARG_POINTER(items);
ReentrantMonitorAutoEnter lock(mMonitor);
MOZ_ASSERT(mJumpListMgr);
nsresult rv;
uint32_t length;
items->GetLength(&length);
for (uint32_t i = 0; i < length; ++i) {
if(sBuildingList) {
AbortListBuild();
}
//Obtain an IJumpListItem and get the type
nsCOMPtr<nsIJumpListItem> item = do_QueryElementAt(items, i);
if (!item) {
continue;
}
int16_t type;
if (NS_FAILED(item->GetType(&type))) {
continue;
}
nsTArray<nsString> urisToRemove;
RefPtr<IObjectArray> objArray;
HRESULT hr = mJumpListMgr->BeginList(&mMaxItems,
IID_PPV_ARGS(static_cast<IObjectArray**>
(getter_AddRefs(objArray))));
// The returned objArray of removed items are for manually removed items.
// This does not return items which are removed because they were previously
// part of the jump list but are no longer part of the jump list.
if (SUCCEEDED(hr) && objArray) {
sBuildingList = true;
RemoveIconCacheAndGetJumplistShortcutURIs(objArray, urisToRemove);
}
// If the item is a shortcut, remove its associated icon if any
if (type == nsIJumpListItem::JUMPLIST_ITEM_SHORTCUT) {
nsCOMPtr<nsIJumpListShortcut> shortcut = do_QueryInterface(item);
if (shortcut) {
nsCOMPtr<nsIURI> uri;
rv = shortcut->GetFaviconPageUri(getter_AddRefs(uri));
if (NS_SUCCEEDED(rv) && uri) {
// The local file path is stored inside the nsIURI
// Get the nsIURI spec which stores the local path for the icon to remove
nsAutoCString spec;
nsresult rv = uri->GetSpec(spec);
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIRunnable> event
= new mozilla::widget::AsyncDeleteIconFromDisk(NS_ConvertUTF8toUTF16(spec));
mIOThread->Dispatch(event, NS_DISPATCH_NORMAL);
// The shortcut was generated from an IShellLinkW so IShellLinkW can
// only tell us what the original icon is and not the URI.
// So this field was used only temporarily as the actual icon file
// path. It should be cleared.
shortcut->SetFaviconPageUri(nullptr);
}
}
}
} // end for
return NS_OK;
NS_DispatchToMainThread(NS_NewRunnableFunction("InitListBuildResolve",
[urisToRemove = std::move(urisToRemove),
promise = std::move(aPromise)]() {
promise->MaybeResolve(urisToRemove);
}));
}
// Ensures that we have no old ICO files left in the jump list cache
@ -542,51 +523,64 @@ bool JumpListBuilder::IsSeparator(nsCOMPtr<nsIJumpListItem>& item)
return false;
}
// TransferIObjectArrayToIMutableArray - used in converting removed items
// to our objects.
nsresult JumpListBuilder::TransferIObjectArrayToIMutableArray(IObjectArray *objArray, nsIMutableArray *removedItems)
// RemoveIconCacheAndGetJumplistShortcutURIs - does multiple things to
// avoid unnecessary extra XPCOM incantations. For each object in the input
// array, if it's an IShellLinkW, this deletes the cached icon and adds the
// url param to a list of URLs to be removed from the places database.
void JumpListBuilder::RemoveIconCacheAndGetJumplistShortcutURIs(IObjectArray *aObjArray,
nsTArray<nsString>& aURISpecs)
{
NS_ENSURE_ARG_POINTER(objArray);
NS_ENSURE_ARG_POINTER(removedItems);
nsresult rv;
MOZ_ASSERT(!NS_IsMainThread());
uint32_t count = 0;
objArray->GetCount(&count);
nsCOMPtr<nsIJumpListItem> item;
aObjArray->GetCount(&count);
for (uint32_t idx = 0; idx < count; idx++) {
IShellLinkW * pLink = nullptr;
IShellItem * pItem = nullptr;
RefPtr<IShellLinkW> pLink;
if (SUCCEEDED(objArray->GetAt(idx, IID_IShellLinkW, (LPVOID*)&pLink))) {
nsCOMPtr<nsIJumpListShortcut> shortcut =
do_CreateInstance(kJumpListShortcutCID, &rv);
if (NS_FAILED(rv))
return NS_ERROR_UNEXPECTED;
rv = JumpListShortcut::GetJumpListShortcut(pLink, shortcut);
item = do_QueryInterface(shortcut);
}
else if (SUCCEEDED(objArray->GetAt(idx, IID_IShellItem, (LPVOID*)&pItem))) {
nsCOMPtr<nsIJumpListLink> link =
do_CreateInstance(kJumpListLinkCID, &rv);
if (NS_FAILED(rv))
return NS_ERROR_UNEXPECTED;
rv = JumpListLink::GetJumpListLink(pItem, link);
item = do_QueryInterface(link);
if (FAILED(aObjArray->GetAt(idx, IID_IShellLinkW,
static_cast<void**>(getter_AddRefs(pLink))))) {
continue;
}
if (pLink)
pLink->Release();
if (pItem)
pItem->Release();
wchar_t buf[MAX_PATH];
HRESULT hres = pLink->GetArguments(buf, MAX_PATH);
if (SUCCEEDED(hres)) {
LPWSTR *arglist;
int32_t numArgs;
if (NS_SUCCEEDED(rv)) {
removedItems->AppendElement(item);
arglist = ::CommandLineToArgvW(buf, &numArgs);
if(arglist && numArgs > 0) {
nsString spec(arglist[0]);
aURISpecs.AppendElement(std::move(spec));
::LocalFree(arglist);
}
}
int iconIdx = 0;
hres = pLink->GetIconLocation(buf, MAX_PATH, &iconIdx);
if (SUCCEEDED(hres)) {
nsDependentString spec(buf);
DeleteIconFromDisk(spec);
}
}
return NS_OK;
}
void JumpListBuilder::DeleteIconFromDisk(const nsAString& aPath)
{
MOZ_ASSERT(!NS_IsMainThread());
// Check that we aren't deleting some arbitrary file that is not an icon
if (StringTail(aPath, 4).LowerCaseEqualsASCII(".ico")) {
// Construct the parent path of the passed in path
nsCOMPtr<nsIFile> icoFile;
nsresult rv = NS_NewLocalFile(aPath, true, getter_AddRefs(icoFile));
if (NS_WARN_IF(NS_FAILED(rv))) {
return;
}
icoFile->Remove(false);
}
}
NS_IMETHODIMP JumpListBuilder::Observe(nsISupports* aSubject,

View file

@ -21,6 +21,7 @@
#include "nsIJumpListItem.h"
#include "JumpListItem.h"
#include "nsIObserver.h"
#include "nsTArray.h"
#include "mozilla/Attributes.h"
#include "mozilla/ReentrantMonitor.h"
@ -37,7 +38,7 @@ class JumpListBuilder : public nsIJumpListBuilder,
virtual ~JumpListBuilder();
public:
NS_DECL_ISUPPORTS
NS_DECL_THREADSAFE_ISUPPORTS
NS_DECL_NSIJUMPLISTBUILDER
NS_DECL_NSIOBSERVER
@ -54,10 +55,11 @@ private:
ReentrantMonitor mMonitor;
bool IsSeparator(nsCOMPtr<nsIJumpListItem>& item);
nsresult TransferIObjectArrayToIMutableArray(IObjectArray *objArray, nsIMutableArray *removedItems);
nsresult RemoveIconCacheForItems(nsIMutableArray *removedItems);
void RemoveIconCacheAndGetJumplistShortcutURIs(IObjectArray *aObjArray, nsTArray<nsString>& aURISpecs);
void DeleteIconFromDisk(const nsAString& aPath);
nsresult RemoveIconCacheForAllItems();
void DoCommitListBuild(RefPtr<detail::DoneCommitListBuildCallback> aCallback);
void DoInitListBuild(RefPtr<dom::Promise>&& aPromise);
friend class WinTaskbar;
};

View file

@ -413,7 +413,6 @@ NS_IMPL_ISUPPORTS(myDownloadObserver, nsIDownloadObserver)
NS_IMPL_ISUPPORTS(AsyncFaviconDataReady, nsIFaviconDataCallback)
#endif
NS_IMPL_ISUPPORTS(AsyncEncodeAndWriteIcon, nsIRunnable)
NS_IMPL_ISUPPORTS(AsyncDeleteIconFromDisk, nsIRunnable)
NS_IMPL_ISUPPORTS(AsyncDeleteAllFaviconsFromDisk, nsIRunnable)
@ -1405,42 +1404,6 @@ AsyncEncodeAndWriteIcon::~AsyncEncodeAndWriteIcon()
{
}
AsyncDeleteIconFromDisk::AsyncDeleteIconFromDisk(const nsAString &aIconPath)
: mIconPath(aIconPath)
{
}
NS_IMETHODIMP AsyncDeleteIconFromDisk::Run()
{
// Construct the parent path of the passed in path
nsCOMPtr<nsIFile> icoFile = do_CreateInstance("@mozilla.org/file/local;1");
NS_ENSURE_TRUE(icoFile, NS_ERROR_FAILURE);
nsresult rv = icoFile->InitWithPath(mIconPath);
NS_ENSURE_SUCCESS(rv, rv);
// Check if the cached ICO file exists
bool exists;
rv = icoFile->Exists(&exists);
NS_ENSURE_SUCCESS(rv, rv);
// Check that we aren't deleting some arbitrary file that is not an icon
if (StringTail(mIconPath, 4).LowerCaseEqualsASCII(".ico")) {
// Check if the cached ICO file exists
bool exists;
if (NS_FAILED(icoFile->Exists(&exists)) || !exists)
return NS_ERROR_FAILURE;
// We found an ICO file that exists, so we should remove it
icoFile->Remove(false);
}
return NS_OK;
}
AsyncDeleteIconFromDisk::~AsyncDeleteIconFromDisk()
{
}
AsyncDeleteAllFaviconsFromDisk::
AsyncDeleteAllFaviconsFromDisk(bool aIgnoreRecent)
: mIgnoreRecent(aIgnoreRecent)

View file

@ -557,21 +557,6 @@ private:
uint32_t mHeight;
};
class AsyncDeleteIconFromDisk : public nsIRunnable
{
public:
NS_DECL_THREADSAFE_ISUPPORTS
NS_DECL_NSIRUNNABLE
explicit AsyncDeleteIconFromDisk(const nsAString &aIconPath);
private:
virtual ~AsyncDeleteIconFromDisk();
nsAutoString mIconPath;
};
class AsyncDeleteAllFaviconsFromDisk : public nsIRunnable
{
public: