mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-25 22:01:30 +00:00
Bug 1258009
: Move nsLocalFile::Launch back to the main thread on Win32, but pass SEE_MASK_ASYNCOK and a parent HWND; r=jimm
This is as much a perf issue as it is a UX issue. We should be passing a HWND to ShellExecuteEx because it can show UI, and that UI should have a proper parent-child relationship with the Mozilla window. We should do that on the main thread because of the GUI stuff. OTOH, we want the ShellExecuteEx call to be a lightweight as possible, hence the SEE_MASK_ASYNCOK flag. MozReview-Commit-ID: 7VLkWTRWPoe --HG-- extra : rebase_source : ce16bc0c926a299d9b9103ad0697e3cd07b9157d
This commit is contained in:
parent
3c76d5adef
commit
4d218d003f
@ -53,6 +53,12 @@
|
||||
#include "nsThreadUtils.h"
|
||||
#include "nsXULAppAPI.h"
|
||||
|
||||
#include "nsIWindowMediator.h"
|
||||
#include "mozIDOMWindow.h"
|
||||
#include "nsPIDOMWindow.h"
|
||||
#include "nsIWidget.h"
|
||||
#include "mozilla/WidgetUtils.h"
|
||||
|
||||
using namespace mozilla;
|
||||
|
||||
#define CHECK_mWorkingPath() \
|
||||
@ -74,9 +80,36 @@ using namespace mozilla;
|
||||
#define DRIVE_REMOTE 4
|
||||
#endif
|
||||
|
||||
static HWND
|
||||
GetMostRecentNavigatorHWND()
|
||||
{
|
||||
nsresult rv;
|
||||
nsCOMPtr<nsIWindowMediator> winMediator(
|
||||
do_GetService(NS_WINDOWMEDIATOR_CONTRACTID, &rv));
|
||||
if (NS_FAILED(rv)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
nsCOMPtr<mozIDOMWindowProxy> navWin;
|
||||
rv = winMediator->GetMostRecentWindow(MOZ_UTF16("navigator:browser"),
|
||||
getter_AddRefs(navWin));
|
||||
if (NS_FAILED(rv) || !navWin) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
nsPIDOMWindowOuter* win = nsPIDOMWindowOuter::From(navWin);
|
||||
nsCOMPtr<nsIWidget> widget = widget::WidgetUtils::DOMWindowToWidget(win);
|
||||
if (!widget) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return reinterpret_cast<HWND>(widget->GetNativeData(NS_NATIVE_WINDOW));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* A runnable to dispatch back to the main thread when
|
||||
* AsyncLocalFileWinOperation completes.
|
||||
* AsyncRevealOperation completes.
|
||||
*/
|
||||
class AsyncLocalFileWinDone : public Runnable
|
||||
{
|
||||
@ -107,37 +140,28 @@ private:
|
||||
* A runnable to dispatch from the main thread when an async operation should
|
||||
* be performed.
|
||||
*/
|
||||
class AsyncLocalFileWinOperation : public Runnable
|
||||
class AsyncRevealOperation : public Runnable
|
||||
{
|
||||
public:
|
||||
enum FileOp { RevealOp, LaunchOp };
|
||||
|
||||
AsyncLocalFileWinOperation(AsyncLocalFileWinOperation::FileOp aOperation,
|
||||
const nsAString& aResolvedPath) :
|
||||
mOperation(aOperation),
|
||||
mResolvedPath(aResolvedPath)
|
||||
explicit AsyncRevealOperation(const nsAString& aResolvedPath)
|
||||
: mResolvedPath(aResolvedPath)
|
||||
{
|
||||
}
|
||||
|
||||
NS_IMETHOD Run()
|
||||
{
|
||||
MOZ_ASSERT(!NS_IsMainThread(),
|
||||
"AsyncLocalFileWinOperation should not be run on the main thread!");
|
||||
"AsyncRevealOperation should not be run on the main thread!");
|
||||
|
||||
CoInitialize(nullptr);
|
||||
switch (mOperation) {
|
||||
case RevealOp: {
|
||||
Reveal();
|
||||
}
|
||||
break;
|
||||
case LaunchOp: {
|
||||
Launch();
|
||||
}
|
||||
break;
|
||||
bool doCoUninitialize = SUCCEEDED(
|
||||
CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE));
|
||||
Reveal();
|
||||
if (doCoUninitialize) {
|
||||
CoUninitialize();
|
||||
}
|
||||
CoUninitialize();
|
||||
|
||||
// Send the result back to the main thread so that it can shutdown
|
||||
// Send the result back to the main thread so that this thread can be
|
||||
// cleanly shut down
|
||||
nsCOMPtr<nsIRunnable> resultrunnable = new AsyncLocalFileWinDone();
|
||||
NS_DispatchToMainThread(resultrunnable);
|
||||
return NS_OK;
|
||||
@ -206,82 +230,6 @@ private:
|
||||
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.hwnd = nullptr;
|
||||
seinfo.lpVerb = nullptr;
|
||||
seinfo.lpFile = mResolvedPath.get();
|
||||
seinfo.lpParameters = nullptr;
|
||||
seinfo.lpDirectory = nullptr;
|
||||
seinfo.nShow = SW_SHOWNORMAL;
|
||||
|
||||
// Use the directory of the file we're launching as the working
|
||||
// directory. That way if we have a self extracting EXE it won't
|
||||
// suggest to extract to the install directory.
|
||||
WCHAR workingDirectory[MAX_PATH + 1] = { L'\0' };
|
||||
wcsncpy(workingDirectory, mResolvedPath.get(), MAX_PATH);
|
||||
if (PathRemoveFileSpecW(workingDirectory)) {
|
||||
seinfo.lpDirectory = workingDirectory;
|
||||
} else {
|
||||
NS_WARNING("Could not set working directory for launched file.");
|
||||
}
|
||||
|
||||
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.AssignLiteral(MOZ_UTF16("shell32.dll,OpenAs_RunDLL "));
|
||||
shellArg.Append(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 operation that will be performed on the thread
|
||||
AsyncLocalFileWinOperation::FileOp mOperation;
|
||||
|
||||
// Stores the path to perform the operation on
|
||||
nsString mResolvedPath;
|
||||
};
|
||||
@ -3313,9 +3261,7 @@ nsLocalFile::Reveal()
|
||||
return rv;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIRunnable> runnable =
|
||||
new AsyncLocalFileWinOperation(AsyncLocalFileWinOperation::RevealOp,
|
||||
mResolvedPath);
|
||||
nsCOMPtr<nsIRunnable> runnable = new AsyncRevealOperation(mResolvedPath);
|
||||
|
||||
// After the dispatch, the result runnable will shut down the worker
|
||||
// thread, so we can let it go.
|
||||
@ -3335,21 +3281,74 @@ nsLocalFile::Launch()
|
||||
return rv;
|
||||
}
|
||||
|
||||
// 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;
|
||||
// use the app registry name to launch a shell execute....
|
||||
SHELLEXECUTEINFOW seinfo;
|
||||
memset(&seinfo, 0, sizeof(seinfo));
|
||||
seinfo.cbSize = sizeof(SHELLEXECUTEINFOW);
|
||||
seinfo.fMask = SEE_MASK_ASYNCOK;
|
||||
seinfo.hwnd = GetMostRecentNavigatorHWND();
|
||||
seinfo.lpVerb = nullptr;
|
||||
seinfo.lpFile = mResolvedPath.get();
|
||||
seinfo.lpParameters = nullptr;
|
||||
seinfo.lpDirectory = nullptr;
|
||||
seinfo.nShow = SW_SHOWNORMAL;
|
||||
|
||||
// Use the directory of the file we're launching as the working
|
||||
// directory. That way if we have a self extracting EXE it won't
|
||||
// suggest to extract to the install directory.
|
||||
WCHAR workingDirectory[MAX_PATH + 1] = { L'\0' };
|
||||
wcsncpy(workingDirectory, mResolvedPath.get(), MAX_PATH);
|
||||
if (PathRemoveFileSpecW(workingDirectory)) {
|
||||
seinfo.lpDirectory = workingDirectory;
|
||||
} else {
|
||||
NS_WARNING("Could not set working directory for launched file.");
|
||||
}
|
||||
|
||||
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);
|
||||
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.AssignLiteral(MOZ_UTF16("shell32.dll,OpenAs_RunDLL "));
|
||||
shellArg.Append(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;
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user