diff --git a/xpcom/io/nsLocalFileWin.cpp b/xpcom/io/nsLocalFileWin.cpp index aa908d6eab5d..a03f7160e653 100644 --- a/xpcom/io/nsLocalFileWin.cpp +++ b/xpcom/io/nsLocalFileWin.cpp @@ -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 winMediator( + do_GetService(NS_WINDOWMEDIATOR_CONTRACTID, &rv)); + if (NS_FAILED(rv)) { + return nullptr; + } + + nsCOMPtr navWin; + rv = winMediator->GetMostRecentWindow(MOZ_UTF16("navigator:browser"), + getter_AddRefs(navWin)); + if (NS_FAILED(rv) || !navWin) { + return nullptr; + } + + nsPIDOMWindowOuter* win = nsPIDOMWindowOuter::From(navWin); + nsCOMPtr widget = widget::WidgetUtils::DOMWindowToWidget(win); + if (!widget) { + return nullptr; + } + + return reinterpret_cast(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 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 runnable = - new AsyncLocalFileWinOperation(AsyncLocalFileWinOperation::RevealOp, - mResolvedPath); + nsCOMPtr 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 tm = do_GetService(NS_THREADMANAGER_CONTRACTID); - nsCOMPtr 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 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; }