зеркало из https://github.com/mozilla/gecko-dev.git
Bug 632556 - Run nsIFile::Reveal/Launch asynchronously. r=bsmedberg
This commit is contained in:
Родитель
3bc1ae7585
Коммит
079540f7c6
|
@ -155,7 +155,8 @@ interface nsILocalFile : nsIFile
|
|||
*
|
||||
* Ask the operating system to open the folder which contains
|
||||
* this file or folder. This routine only works on platforms which
|
||||
* support the ability to open a folder...
|
||||
* support the ability to open a folder and is run async on Windows.
|
||||
* This routine must be called on the main.
|
||||
*/
|
||||
void reveal();
|
||||
|
||||
|
@ -164,7 +165,9 @@ interface nsILocalFile : nsIFile
|
|||
*
|
||||
* Ask the operating system to attempt to open the file.
|
||||
* this really just simulates "double clicking" the file on your platform.
|
||||
* This routine only works on platforms which support this functionality.
|
||||
* This routine only works on platforms which support this functionality
|
||||
* and is run async on Windows. This routine must be called on the
|
||||
* main thread.
|
||||
*/
|
||||
void launch();
|
||||
|
||||
|
|
|
@ -82,6 +82,8 @@
|
|||
#include "SpecialSystemDirectory.h"
|
||||
|
||||
#include "nsTraceRefcntImpl.h"
|
||||
#include "nsXPCOMCIDInternal.h"
|
||||
#include "nsThreadUtils.h"
|
||||
|
||||
using namespace mozilla;
|
||||
|
||||
|
@ -110,6 +112,199 @@ unsigned char *_mbsstr( const unsigned char *str,
|
|||
#define DRIVE_REMOTE 4
|
||||
#endif
|
||||
|
||||
/**
|
||||
* A runnable to dispatch back to the main thread when
|
||||
* AsyncLocalFileWinOperation completes.
|
||||
*/
|
||||
class AsyncLocalFileWinDone : public nsRunnable
|
||||
{
|
||||
public:
|
||||
AsyncLocalFileWinDone() :
|
||||
mWorkerThread(do_GetCurrentThread())
|
||||
{
|
||||
// Objects of this type must only be created on worker threads
|
||||
MOZ_ASSERT(!NS_IsMainThread());
|
||||
}
|
||||
|
||||
NS_IMETHOD Run() {
|
||||
// This event shuts down the worker thread and so must be main thread.
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
// If we don't destroy the thread when we're done with it, it will hang
|
||||
// around forever... and that is bad!
|
||||
mWorkerThread->Shutdown();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
private:
|
||||
nsCOMPtr<nsIThread> mWorkerThread;
|
||||
};
|
||||
|
||||
/**
|
||||
* A runnable to dispatch from the main thread when an async operation should
|
||||
* be performed.
|
||||
*/
|
||||
class AsyncLocalFileWinOperation : public nsRunnable
|
||||
{
|
||||
public:
|
||||
enum FileOp { RevealOp, LaunchOp };
|
||||
|
||||
AsyncLocalFileWinOperation(AsyncLocalFileWinOperation::FileOp aOperation,
|
||||
const nsAString &aResolvedPath) :
|
||||
mOperation(aOperation),
|
||||
mResolvedPath(aResolvedPath)
|
||||
{
|
||||
}
|
||||
|
||||
NS_IMETHOD Run() {
|
||||
NS_ASSERTION(!NS_IsMainThread(),
|
||||
"AsyncLocalFileWinOperation should not be run on the main thread!");
|
||||
|
||||
switch(mOperation) {
|
||||
case RevealOp: {
|
||||
Reveal();
|
||||
}
|
||||
break;
|
||||
case LaunchOp: {
|
||||
Launch();
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// Send the result back to the main thread so that it can shutdown
|
||||
nsCOMPtr<nsIRunnable> resultrunnable = new AsyncLocalFileWinDone();
|
||||
NS_DispatchToMainThread(resultrunnable);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
private:
|
||||
// Reveals the path in explorer.
|
||||
nsresult Reveal()
|
||||
{
|
||||
DWORD attributes = GetFileAttributesW(mResolvedPath.get());
|
||||
if (INVALID_FILE_ATTRIBUTES == attributes) {
|
||||
return NS_ERROR_FILE_INVALID_PATH;
|
||||
}
|
||||
|
||||
HRESULT hr;
|
||||
if (attributes & FILE_ATTRIBUTE_DIRECTORY) {
|
||||
// We have a directory so we should open the directory itself.
|
||||
ITEMIDLIST *dir = ILCreateFromPathW(mResolvedPath.get());
|
||||
if (!dir) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
const ITEMIDLIST* selection[] = { dir };
|
||||
UINT count = ArrayLength(selection);
|
||||
|
||||
//Perform the open of the directory.
|
||||
hr = SHOpenFolderAndSelectItems(dir, count, selection, 0);
|
||||
CoTaskMemFree(dir);
|
||||
} else {
|
||||
PRInt32 len = mResolvedPath.Length();
|
||||
// We don't currently handle UNC long paths of the form \\?\ anywhere so
|
||||
// this should be fine.
|
||||
if (len > MAX_PATH) {
|
||||
return NS_ERROR_FILE_INVALID_PATH;
|
||||
}
|
||||
WCHAR parentDirectoryPath[MAX_PATH + 1] = { 0 };
|
||||
wcsncpy(parentDirectoryPath, mResolvedPath.get(), MAX_PATH);
|
||||
PathRemoveFileSpecW(parentDirectoryPath);
|
||||
|
||||
// We have a file so we should open the parent directory.
|
||||
ITEMIDLIST *dir = ILCreateFromPathW(parentDirectoryPath);
|
||||
if (!dir) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
// Set the item in the directory to select to the file we want to reveal.
|
||||
ITEMIDLIST *item = ILCreateFromPathW(mResolvedPath.get());
|
||||
if (!item) {
|
||||
CoTaskMemFree(dir);
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
const ITEMIDLIST* selection[] = { item };
|
||||
UINT count = ArrayLength(selection);
|
||||
|
||||
//Perform the selection of the file.
|
||||
hr = SHOpenFolderAndSelectItems(dir, count, selection, 0);
|
||||
|
||||
CoTaskMemFree(dir);
|
||||
CoTaskMemFree(item);
|
||||
}
|
||||
|
||||
return SUCCEEDED(hr) ? NS_OK : NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
// Launches the default shell operation for the file path
|
||||
nsresult Launch()
|
||||
{
|
||||
// use the app registry name to launch a shell execute....
|
||||
SHELLEXECUTEINFOW seinfo;
|
||||
memset(&seinfo, 0, sizeof(seinfo));
|
||||
seinfo.cbSize = sizeof(SHELLEXECUTEINFOW);
|
||||
seinfo.fMask = NULL;
|
||||
seinfo.hwnd = NULL;
|
||||
seinfo.lpVerb = NULL;
|
||||
seinfo.lpFile = mResolvedPath.get();
|
||||
seinfo.lpParameters = NULL;
|
||||
seinfo.lpDirectory = NULL;
|
||||
seinfo.nShow = SW_SHOWNORMAL;
|
||||
|
||||
if (ShellExecuteExW(&seinfo))
|
||||
return NS_OK;
|
||||
DWORD r = GetLastError();
|
||||
// if the file has no association, we launch windows'
|
||||
// "what do you want to do" dialog
|
||||
if (r == SE_ERR_NOASSOC) {
|
||||
nsAutoString shellArg;
|
||||
shellArg.Assign(NS_LITERAL_STRING("shell32.dll,OpenAs_RunDLL ") +
|
||||
mResolvedPath);
|
||||
seinfo.lpFile = L"RUNDLL32.EXE";
|
||||
seinfo.lpParameters = shellArg.get();
|
||||
if (ShellExecuteExW(&seinfo))
|
||||
return NS_OK;
|
||||
r = GetLastError();
|
||||
}
|
||||
if (r < 32) {
|
||||
switch (r) {
|
||||
case 0:
|
||||
case SE_ERR_OOM:
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
case ERROR_FILE_NOT_FOUND:
|
||||
return NS_ERROR_FILE_NOT_FOUND;
|
||||
case ERROR_PATH_NOT_FOUND:
|
||||
return NS_ERROR_FILE_UNRECOGNIZED_PATH;
|
||||
case ERROR_BAD_FORMAT:
|
||||
return NS_ERROR_FILE_CORRUPTED;
|
||||
case SE_ERR_ACCESSDENIED:
|
||||
return NS_ERROR_FILE_ACCESS_DENIED;
|
||||
case SE_ERR_ASSOCINCOMPLETE:
|
||||
case SE_ERR_NOASSOC:
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
case SE_ERR_DDEBUSY:
|
||||
case SE_ERR_DDEFAIL:
|
||||
case SE_ERR_DDETIMEOUT:
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
case SE_ERR_DLLNOTFOUND:
|
||||
return NS_ERROR_FAILURE;
|
||||
case SE_ERR_SHARE:
|
||||
return NS_ERROR_FILE_IS_LOCKED;
|
||||
default:
|
||||
return NS_ERROR_FILE_EXECUTION_FAILED;
|
||||
}
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// Stores the path to perform the operation on
|
||||
nsString mResolvedPath;
|
||||
|
||||
// Stores the operation that will be performed on the thread
|
||||
AsyncLocalFileWinOperation::FileOp mOperation;
|
||||
};
|
||||
|
||||
class nsDriveEnumerator : public nsISimpleEnumerator
|
||||
{
|
||||
public:
|
||||
|
@ -2796,129 +2991,62 @@ nsLocalFile::SetFileAttributesWin(PRUint32 aAttribs)
|
|||
NS_IMETHODIMP
|
||||
nsLocalFile::Reveal()
|
||||
{
|
||||
// make sure mResolvedPath is set
|
||||
bool isDirectory = false;
|
||||
nsresult rv = IsDirectory(&isDirectory);
|
||||
if (NS_FAILED(rv) && rv != NS_ERROR_FILE_NOT_FOUND)
|
||||
return rv;
|
||||
// This API should be main thread only
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
HRESULT hr;
|
||||
if (isDirectory) {
|
||||
// We have a directory so we should open the directory itself.
|
||||
ITEMIDLIST *dir = ILCreateFromPathW(mResolvedPath.get());
|
||||
if (!dir) {
|
||||
return NS_ERROR_FAILURE;
|
||||
// make sure mResolvedPath is set
|
||||
nsresult rv = Resolve();
|
||||
if (NS_FAILED(rv) && rv != NS_ERROR_FILE_NOT_FOUND) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
const ITEMIDLIST* selection[] = { dir };
|
||||
UINT count = ArrayLength(selection);
|
||||
|
||||
//Perform the open of the directory.
|
||||
hr = SHOpenFolderAndSelectItems(dir, count, selection, 0);
|
||||
CoTaskMemFree(dir);
|
||||
}
|
||||
else {
|
||||
// Obtain the parent path of the item we are revealing.
|
||||
nsCOMPtr<nsIFile> parentDirectory;
|
||||
rv = GetParent(getter_AddRefs(parentDirectory));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
nsAutoString parentDirectoryPath;
|
||||
rv = parentDirectory->GetPath(parentDirectoryPath);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// We have a file so we should open the parent directory.
|
||||
ITEMIDLIST *dir = ILCreateFromPathW(parentDirectoryPath.get());
|
||||
if (!dir) {
|
||||
return NS_ERROR_FAILURE;
|
||||
// To create a new thread, get the thread manager
|
||||
nsCOMPtr<nsIThreadManager> tm = do_GetService(NS_THREADMANAGER_CONTRACTID);
|
||||
nsCOMPtr<nsIThread> mythread;
|
||||
rv = tm->NewThread(0, 0, getter_AddRefs(mythread));
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
// Set the item in the directory to select to the file we want to reveal.
|
||||
ITEMIDLIST *item = ILCreateFromPathW(mResolvedPath.get());
|
||||
if (!item) {
|
||||
CoTaskMemFree(dir);
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
nsCOMPtr<nsIRunnable> runnable =
|
||||
new AsyncLocalFileWinOperation(AsyncLocalFileWinOperation::RevealOp,
|
||||
mResolvedPath);
|
||||
|
||||
const ITEMIDLIST* selection[] = { item };
|
||||
UINT count = ArrayLength(selection);
|
||||
|
||||
//Perform the selection of the file.
|
||||
hr = SHOpenFolderAndSelectItems(dir, count, selection, 0);
|
||||
|
||||
CoTaskMemFree(dir);
|
||||
CoTaskMemFree(item);
|
||||
}
|
||||
|
||||
if (SUCCEEDED(hr)) {
|
||||
// After the dispatch, the result runnable will shut down the worker
|
||||
// thread, so we can let it go.
|
||||
mythread->Dispatch(runnable, NS_DISPATCH_NORMAL);
|
||||
return NS_OK;
|
||||
}
|
||||
else {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsLocalFile::Launch()
|
||||
{
|
||||
const nsString &path = mWorkingPath;
|
||||
// This API should be main thread only
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
// use the app registry name to launch a shell execute....
|
||||
SHELLEXECUTEINFOW seinfo;
|
||||
memset(&seinfo, 0, sizeof(seinfo));
|
||||
seinfo.cbSize = sizeof(SHELLEXECUTEINFOW);
|
||||
seinfo.fMask = NULL;
|
||||
seinfo.hwnd = NULL;
|
||||
seinfo.lpVerb = NULL;
|
||||
seinfo.lpFile = path.get();
|
||||
seinfo.lpParameters = NULL;
|
||||
seinfo.lpDirectory = NULL;
|
||||
seinfo.nShow = SW_SHOWNORMAL;
|
||||
// make sure mResolvedPath is set
|
||||
nsresult rv = Resolve();
|
||||
if (NS_FAILED(rv))
|
||||
return rv;
|
||||
|
||||
if (ShellExecuteExW(&seinfo))
|
||||
return NS_OK;
|
||||
DWORD r = GetLastError();
|
||||
// if the file has no association, we launch windows' "what do you want to do" dialog
|
||||
if (r == SE_ERR_NOASSOC) {
|
||||
nsAutoString shellArg;
|
||||
shellArg.Assign(NS_LITERAL_STRING("shell32.dll,OpenAs_RunDLL ") + path);
|
||||
seinfo.lpFile = L"RUNDLL32.EXE";
|
||||
seinfo.lpParameters = shellArg.get();
|
||||
if (ShellExecuteExW(&seinfo))
|
||||
return NS_OK;
|
||||
r = GetLastError();
|
||||
}
|
||||
if (r < 32) {
|
||||
switch (r) {
|
||||
case 0:
|
||||
case SE_ERR_OOM:
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
case ERROR_FILE_NOT_FOUND:
|
||||
return NS_ERROR_FILE_NOT_FOUND;
|
||||
case ERROR_PATH_NOT_FOUND:
|
||||
return NS_ERROR_FILE_UNRECOGNIZED_PATH;
|
||||
case ERROR_BAD_FORMAT:
|
||||
return NS_ERROR_FILE_CORRUPTED;
|
||||
case SE_ERR_ACCESSDENIED:
|
||||
return NS_ERROR_FILE_ACCESS_DENIED;
|
||||
case SE_ERR_ASSOCINCOMPLETE:
|
||||
case SE_ERR_NOASSOC:
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
case SE_ERR_DDEBUSY:
|
||||
case SE_ERR_DDEFAIL:
|
||||
case SE_ERR_DDETIMEOUT:
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
case SE_ERR_DLLNOTFOUND:
|
||||
return NS_ERROR_FAILURE;
|
||||
case SE_ERR_SHARE:
|
||||
return NS_ERROR_FILE_IS_LOCKED;
|
||||
default:
|
||||
return NS_ERROR_FILE_EXECUTION_FAILED;
|
||||
}
|
||||
// To create a new thread, get the thread manager
|
||||
nsCOMPtr<nsIThreadManager> tm = do_GetService(NS_THREADMANAGER_CONTRACTID);
|
||||
nsCOMPtr<nsIThread> mythread;
|
||||
rv = tm->NewThread(0, 0, getter_AddRefs(mythread));
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIRunnable> runnable =
|
||||
new AsyncLocalFileWinOperation(AsyncLocalFileWinOperation::LaunchOp,
|
||||
mResolvedPath);
|
||||
|
||||
// After the dispatch, the result runnable will shut down the worker
|
||||
// thread, so we can let it go.
|
||||
mythread->Dispatch(runnable, NS_DISPATCH_NORMAL);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
||||
nsresult
|
||||
NS_NewLocalFile(const nsAString &path, bool followLinks, nsILocalFile* *result)
|
||||
{
|
||||
|
|
Загрузка…
Ссылка в новой задаче