Bug 1279240 - move path parsing of commandline handlers for mimetypes/protocols to nsILocalFileWin, r=froydnj
MozReview-Commit-ID: 4CENm3iqGUH
--- a/uriloader/exthandler/win/nsMIMEInfoWin.cpp
+++ b/uriloader/exthandler/win/nsMIMEInfoWin.cpp
@@ -1,29 +1,28 @@
/* -*- Mode: C++; tab-width: 3; indent-tabs-mode: nil; c-basic-offset: 2 -*-
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "nsArrayEnumerator.h"
#include "nsCOMArray.h"
-#include "nsIFile.h"
+#include "nsLocalFile.h"
#include "nsMIMEInfoWin.h"
#include "nsNetUtil.h"
#include <windows.h>
#include <shellapi.h>
#include "nsAutoPtr.h"
#include "nsIMutableArray.h"
#include "nsTArray.h"
#include "shlobj.h"
#include "windows.h"
#include "nsIWindowsRegKey.h"
#include "nsIProcess.h"
-#include "nsOSHelperAppService.h"
#include "nsUnicharUtils.h"
#include "nsITextToSubURI.h"
#include "nsVariant.h"
#include "mozilla/UniquePtrExtensions.h"
#define RUNDLL32_EXE L"\\rundll32.exe"
@@ -343,17 +342,17 @@ bool nsMIMEInfoWin::GetAppsVerbCommandHa
if (NS_FAILED(rv))
return false;
nsAutoString appFilesystemCommand;
if (NS_SUCCEEDED(appKey->ReadStringValue(EmptyString(),
appFilesystemCommand))) {
// Expand environment vars, clean up any misc.
- if (!nsOSHelperAppService::CleanupCmdHandlerPath(appFilesystemCommand))
+ if (!nsLocalFile::CleanupCmdHandlerPath(appFilesystemCommand))
return false;
applicationPath = appFilesystemCommand;
return true;
}
return false;
}
@@ -488,17 +487,17 @@ bool nsMIMEInfoWin::GetProgIDVerbCommand
nsIWindowsRegKey::ACCESS_QUERY_VALUE);
if (NS_FAILED(rv))
return false;
nsAutoString appFilesystemCommand;
if (NS_SUCCEEDED(appKey->ReadStringValue(EmptyString(), appFilesystemCommand))) {
// Expand environment vars, clean up any misc.
- if (!nsOSHelperAppService::CleanupCmdHandlerPath(appFilesystemCommand))
+ if (!nsLocalFile::CleanupCmdHandlerPath(appFilesystemCommand))
return false;
applicationPath = appFilesystemCommand;
return true;
}
return false;
}
--- a/uriloader/exthandler/win/nsOSHelperAppService.cpp
+++ b/uriloader/exthandler/win/nsOSHelperAppService.cpp
@@ -8,21 +8,21 @@
#include "nsOSHelperAppService.h"
#include "nsISupports.h"
#include "nsString.h"
#include "nsXPIDLString.h"
#include "nsIURL.h"
#include "nsIMIMEInfo.h"
#include "nsMIMEInfoWin.h"
#include "nsMimeTypes.h"
-#include "nsILocalFileWin.h"
#include "nsIProcess.h"
#include "plstr.h"
#include "nsAutoPtr.h"
#include "nsNativeCharsetUtils.h"
+#include "nsLocalFile.h"
#include "nsIWindowsRegKey.h"
#include "mozilla/UniquePtrExtensions.h"
#include "mozilla/WindowsVersion.h"
// shellapi.h is needed to build with WIN32_LEAN_AND_MEAN
#include <shellapi.h>
#include <shlwapi.h>
@@ -281,124 +281,16 @@ nsOSHelperAppService::typeFromExtEquals(
nsAutoString type;
rv = regKey->ReadStringValue(NS_LITERAL_STRING("Content Type"), type);
if (NS_SUCCEEDED(rv))
eq = type.EqualsASCII(aType);
return eq;
}
-// Strip a handler command string of its quotes and parameters.
-static void CleanupHandlerPath(nsString& aPath)
-{
- // Example command strings passed into this routine:
-
- // 1) C:\Program Files\Company\some.exe -foo -bar
- // 2) C:\Program Files\Company\some.dll
- // 3) C:\Windows\some.dll,-foo -bar
- // 4) C:\Windows\some.cpl,-foo -bar
-
- int32_t lastCommaPos = aPath.RFindChar(',');
- if (lastCommaPos != kNotFound)
- aPath.Truncate(lastCommaPos);
-
- aPath.Append(' ');
-
- // case insensitive
- uint32_t index = aPath.Find(".exe ", true);
- if (index == kNotFound)
- index = aPath.Find(".dll ", true);
- if (index == kNotFound)
- index = aPath.Find(".cpl ", true);
-
- if (index != kNotFound)
- aPath.Truncate(index + 4);
- aPath.Trim(" ", true, true);
-}
-
-// Strip the windows host process bootstrap executable rundll32.exe
-// from a handler's command string if it exists.
-static void StripRundll32(nsString& aCommandString)
-{
- // Example rundll formats:
- // C:\Windows\System32\rundll32.exe "path to dll"
- // rundll32.exe "path to dll"
- // C:\Windows\System32\rundll32.exe "path to dll", var var
- // rundll32.exe "path to dll", var var
-
- NS_NAMED_LITERAL_STRING(rundllSegment, "rundll32.exe ");
- NS_NAMED_LITERAL_STRING(rundllSegmentShort, "rundll32 ");
-
- // case insensitive
- int32_t strLen = rundllSegment.Length();
- int32_t index = aCommandString.Find(rundllSegment, true);
- if (index == kNotFound) {
- strLen = rundllSegmentShort.Length();
- index = aCommandString.Find(rundllSegmentShort, true);
- }
-
- if (index != kNotFound) {
- uint32_t rundllSegmentLength = index + strLen;
- aCommandString.Cut(0, rundllSegmentLength);
- }
-}
-
-// Returns the fully qualified path to an application handler based on
-// a parameterized command string. Note this routine should not be used
-// to launch the associated application as it strips parameters and
-// rundll.exe from the string. Designed for retrieving display information
-// on a particular handler.
-/* static */ bool nsOSHelperAppService::CleanupCmdHandlerPath(nsAString& aCommandHandler)
-{
- nsAutoString handlerCommand(aCommandHandler);
-
- // Straight command path:
- //
- // %SystemRoot%\system32\NOTEPAD.EXE var
- // "C:\Program Files\iTunes\iTunes.exe" var var
- // C:\Program Files\iTunes\iTunes.exe var var
- //
- // Example rundll handlers:
- //
- // rundll32.exe "%ProgramFiles%\Win...ery\PhotoViewer.dll", var var
- // rundll32.exe "%ProgramFiles%\Windows Photo Gallery\PhotoViewer.dll"
- // C:\Windows\System32\rundll32.exe "path to dll", var var
- // %SystemRoot%\System32\rundll32.exe "%ProgramFiles%\Win...ery\Photo
- // Viewer.dll", var var
-
- // Expand environment variables so we have full path strings.
- uint32_t bufLength = ::ExpandEnvironmentStringsW(handlerCommand.get(),
- L"", 0);
- if (bufLength == 0) // Error
- return false;
-
- auto destination = mozilla::MakeUniqueFallible<wchar_t[]>(bufLength);
- if (!destination)
- return false;
- if (!::ExpandEnvironmentStringsW(handlerCommand.get(), destination.get(),
- bufLength))
- return false;
-
- handlerCommand.Assign(destination.get());
-
- // Remove quotes around paths
- handlerCommand.StripChars("\"");
-
- // Strip windows host process bootstrap so we can get to the actual
- // handler.
- StripRundll32(handlerCommand);
-
- // Trim any command parameters so that we have a native path we can
- // initialize a local file with.
- CleanupHandlerPath(handlerCommand);
-
- aCommandHandler.Assign(handlerCommand);
- return true;
-}
-
// The "real" name of a given helper app (as specified by the path to the
// executable file held in various registry keys) is stored n the VERSIONINFO
// block in the file's resources. We need to find the path to the executable
// and then retrieve the "FileDescription" field value from the file.
nsresult
nsOSHelperAppService::GetDefaultAppInfo(const nsAString& aAppInfo,
nsAString& aDefaultDescription,
nsIFile** aDefaultApplication)
@@ -477,38 +369,28 @@ nsOSHelperAppService::GetDefaultAppInfo(
nsIWindowsRegKey::ACCESS_QUERY_VALUE);
NS_ENSURE_SUCCESS(rv, rv);
rv = chkKey->ReadStringValue(EmptyString(), handlerCommand);
NS_ENSURE_SUCCESS(rv, rv);
}
}
}
- if (!CleanupCmdHandlerPath(handlerCommand))
- return NS_ERROR_FAILURE;
-
// XXX FIXME: If this fails, the UI will display the full command
// string.
// There are some rare cases this can happen - ["url.dll" -foo]
// for example won't resolve correctly to the system dir. The
// subsequent launch of the helper app will work though.
- nsCOMPtr<nsIFile> lf;
- NS_NewLocalFile(handlerCommand, true, getter_AddRefs(lf));
- if (!lf)
- return NS_ERROR_FILE_NOT_FOUND;
+ nsCOMPtr<nsILocalFileWin> lf = new nsLocalFile();
+ rv = lf->InitWithCommandLine(handlerCommand);
+ NS_ENSURE_SUCCESS(rv, rv);
- nsILocalFileWin* lfw = nullptr;
- CallQueryInterface(lf, &lfw);
-
- if (lfw) {
- // The "FileDescription" field contains the actual name of the application.
- lfw->GetVersionInfoField("FileDescription", aDefaultDescription);
- // QI addref'ed for us.
- *aDefaultApplication = lfw;
- }
+ // The "FileDescription" field contains the actual name of the application.
+ lf->GetVersionInfoField("FileDescription", aDefaultDescription);
+ lf.forget(aDefaultApplication);
return NS_OK;
}
already_AddRefed<nsMIMEInfoWin> nsOSHelperAppService::GetByExtension(const nsAFlatString& aFileExt, const char *aTypeHint)
{
if (aFileExt.IsEmpty())
return nullptr;
--- a/uriloader/exthandler/win/nsOSHelperAppService.h
+++ b/uriloader/exthandler/win/nsOSHelperAppService.h
@@ -41,19 +41,16 @@ public:
bool *found,
nsIHandlerInfo **_retval);
/** Get the string value of a registry value and store it in result.
* @return true on success, false on failure
*/
static bool GetValueString(HKEY hKey, const char16_t* pValueName, nsAString& result);
- // Removes registry command handler parameters, quotes, and expands environment strings.
- static bool CleanupCmdHandlerPath(nsAString& aCommandHandler);
-
protected:
nsresult GetDefaultAppInfo(const nsAString& aTypeName, nsAString& aDefaultDescription, nsIFile** aDefaultApplication);
// Lookup a mime info by extension, using an optional type hint
already_AddRefed<nsMIMEInfoWin> GetByExtension(const nsAFlatString& aFileExt, const char *aTypeHint = nullptr);
nsresult FindOSMimeInfoForType(const char * aMimeContentType, nsIURI * aURI, char ** aFileExtension, nsIMIMEInfo ** aMIMEInfo);
static nsresult GetMIMEInfoFromRegistry(const nsAFlatString& fileType, nsIMIMEInfo *pInfo);
/// Looks up the type for the extension aExt and compares it to aType
--- a/xpcom/io/nsILocalFileWin.idl
+++ b/xpcom/io/nsILocalFileWin.idl
@@ -10,16 +10,26 @@
struct PRFileDesc;
%}
[ptr] native PRFileDescStar(PRFileDesc);
[scriptable, builtinclass, uuid(e7a3a954-384b-4aeb-a5f7-55626b0de9be)]
interface nsILocalFileWin : nsILocalFile
{
+ /**
+ * initWithCommandLine
+ *
+ * Initialize this object based on the main app path of a commandline
+ * handler.
+ *
+ * @param aCommandLine
+ * the commandline to parse an app path out of.
+ */
+ void initWithCommandLine(in AString aCommandLine);
/**
* getVersionInfoValue
*
* Retrieve a metadata field from the file's VERSIONINFO block.
* Throws NS_ERROR_FAILURE if no value is found, or the value is empty.
*
* @param aField The field to look up.
*
--- a/xpcom/io/nsLocalFileWin.cpp
+++ b/xpcom/io/nsLocalFileWin.cpp
@@ -1,16 +1,17 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "mozilla/ArrayUtils.h"
#include "mozilla/DebugOnly.h"
+#include "mozilla/UniquePtrExtensions.h"
#include "mozilla/WindowsVersion.h"
#include "nsCOMPtr.h"
#include "nsAutoPtr.h"
#include "nsMemory.h"
#include "GeckoProfiler.h"
#include "nsLocalFile.h"
@@ -1186,16 +1187,138 @@ nsLocalFile::InitWithPath(const nsAStrin
if (mWorkingPath.Last() == L'\\') {
mWorkingPath.Truncate(mWorkingPath.Length() - 1);
}
return NS_OK;
}
+// Strip a handler command string of its quotes and parameters.
+static void
+CleanupHandlerPath(nsString& aPath)
+{
+ // Example command strings passed into this routine:
+
+ // 1) C:\Program Files\Company\some.exe -foo -bar
+ // 2) C:\Program Files\Company\some.dll
+ // 3) C:\Windows\some.dll,-foo -bar
+ // 4) C:\Windows\some.cpl,-foo -bar
+
+ int32_t lastCommaPos = aPath.RFindChar(',');
+ if (lastCommaPos != kNotFound)
+ aPath.Truncate(lastCommaPos);
+
+ aPath.Append(' ');
+
+ // case insensitive
+ uint32_t index = aPath.Find(".exe ", true);
+ if (index == kNotFound)
+ index = aPath.Find(".dll ", true);
+ if (index == kNotFound)
+ index = aPath.Find(".cpl ", true);
+
+ if (index != kNotFound)
+ aPath.Truncate(index + 4);
+ aPath.Trim(" ", true, true);
+}
+
+// Strip the windows host process bootstrap executable rundll32.exe
+// from a handler's command string if it exists.
+static void
+StripRundll32(nsString& aCommandString)
+{
+ // Example rundll formats:
+ // C:\Windows\System32\rundll32.exe "path to dll"
+ // rundll32.exe "path to dll"
+ // C:\Windows\System32\rundll32.exe "path to dll", var var
+ // rundll32.exe "path to dll", var var
+
+ NS_NAMED_LITERAL_STRING(rundllSegment, "rundll32.exe ");
+ NS_NAMED_LITERAL_STRING(rundllSegmentShort, "rundll32 ");
+
+ // case insensitive
+ int32_t strLen = rundllSegment.Length();
+ int32_t index = aCommandString.Find(rundllSegment, true);
+ if (index == kNotFound) {
+ strLen = rundllSegmentShort.Length();
+ index = aCommandString.Find(rundllSegmentShort, true);
+ }
+
+ if (index != kNotFound) {
+ uint32_t rundllSegmentLength = index + strLen;
+ aCommandString.Cut(0, rundllSegmentLength);
+ }
+}
+
+// Returns the fully qualified path to an application handler based on
+// a parameterized command string. Note this routine should not be used
+// to launch the associated application as it strips parameters and
+// rundll.exe from the string. Designed for retrieving display information
+// on a particular handler.
+/* static */ bool
+nsLocalFile::CleanupCmdHandlerPath(nsAString& aCommandHandler)
+{
+ nsAutoString handlerCommand(aCommandHandler);
+
+ // Straight command path:
+ //
+ // %SystemRoot%\system32\NOTEPAD.EXE var
+ // "C:\Program Files\iTunes\iTunes.exe" var var
+ // C:\Program Files\iTunes\iTunes.exe var var
+ //
+ // Example rundll handlers:
+ //
+ // rundll32.exe "%ProgramFiles%\Win...ery\PhotoViewer.dll", var var
+ // rundll32.exe "%ProgramFiles%\Windows Photo Gallery\PhotoViewer.dll"
+ // C:\Windows\System32\rundll32.exe "path to dll", var var
+ // %SystemRoot%\System32\rundll32.exe "%ProgramFiles%\Win...ery\Photo
+ // Viewer.dll", var var
+
+ // Expand environment variables so we have full path strings.
+ uint32_t bufLength = ::ExpandEnvironmentStringsW(handlerCommand.get(),
+ L"", 0);
+ if (bufLength == 0) // Error
+ return false;
+
+ auto destination = mozilla::MakeUniqueFallible<wchar_t[]>(bufLength);
+ if (!destination)
+ return false;
+ if (!::ExpandEnvironmentStringsW(handlerCommand.get(), destination.get(),
+ bufLength))
+ return false;
+
+ handlerCommand.Assign(destination.get());
+
+ // Remove quotes around paths
+ handlerCommand.StripChars("\"");
+
+ // Strip windows host process bootstrap so we can get to the actual
+ // handler.
+ StripRundll32(handlerCommand);
+
+ // Trim any command parameters so that we have a native path we can
+ // initialize a local file with.
+ CleanupHandlerPath(handlerCommand);
+
+ aCommandHandler.Assign(handlerCommand);
+ return true;
+}
+
+
+NS_IMETHODIMP
+nsLocalFile::InitWithCommandLine(const nsAString& aCommandLine)
+{
+ nsAutoString commandLine(aCommandLine);
+ if (!CleanupCmdHandlerPath(commandLine)) {
+ return NS_ERROR_FILE_UNRECOGNIZED_PATH;
+ }
+ return InitWithPath(commandLine);
+}
+
NS_IMETHODIMP
nsLocalFile::OpenNSPRFileDesc(int32_t aFlags, int32_t aMode,
PRFileDesc** aResult)
{
nsresult rv = OpenNSPRFileDescMaybeShareDelete(aFlags, aMode, false, aResult);
if (NS_FAILED(rv)) {
return rv;
}
--- a/xpcom/io/nsLocalFileWin.h
+++ b/xpcom/io/nsLocalFileWin.h
@@ -52,16 +52,19 @@ public:
// nsIHashable interface
NS_DECL_NSIHASHABLE
public:
static void GlobalInit();
static void GlobalShutdown();
+ // Removes registry command handler parameters, quotes, and expands environment strings.
+ static bool CleanupCmdHandlerPath(nsAString& aCommandHandler);
+
private:
// CopyMove and CopySingleFile constants for |options| parameter:
enum CopyFileOption {
FollowSymlinks = 1u << 0,
Move = 1u << 1,
SkipNtfsAclReset = 1u << 2,
Rename = 1u << 3
};
new file mode 100644
--- /dev/null
+++ b/xpcom/tests/unit/test_windows_cmdline_file.js
@@ -0,0 +1,21 @@
+let { classes: Cc, interfaces: Ci, results: Cr, utils: Cu } = Components;
+Cu.import("resource://gre/modules/Services.jsm");
+
+let executableFile = Services.dirsvc.get("CurProcD", Ci.nsIFile);
+executableFile.append("xpcshell.exe");
+function run_test() {
+ let quote = '"'; // Windows' cmd processor doesn't actually use single quotes.
+ for (let suffix of ["", " -osint", ` --blah "%PROGRAMFILES%"`]) {
+ let cmdline = quote + executableFile.path + quote + suffix;
+ do_print(`Testing with ${cmdline}`);
+ let f = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsILocalFileWin);
+ f.initWithCommandLine(cmdline);
+ Assert.equal(f.path, executableFile.path, "Should be able to recover executable path");
+ }
+
+ let f = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsILocalFileWin);
+ f.initWithCommandLine("%ComSpec% -c echo 'hi'");
+ let cmd = Services.dirsvc.get("SysD", Ci.nsIFile);
+ cmd.append("cmd.exe");
+ Assert.equal(f.path, cmd.path, "Should be able to replace env vars.");
+}
--- a/xpcom/tests/unit/xpcshell.ini
+++ b/xpcom/tests/unit/xpcshell.ini
@@ -62,16 +62,19 @@ skip-if = os == "android"
fail-if = os == "android"
[test_systemInfo.js]
# Bug 902081: test fails consistently on Android 2.2, passes on 4.0
skip-if = os == "android"
[test_versioncomparator.js]
[test_comp_no_aslr.js]
skip-if = os != "win"
[test_windows_shortcut.js]
+skip-if = os != "win"
+[test_windows_cmdline_file.js]
+skip-if = os != "win"
[test_bug745466.js]
skip-if = os == "win"
# Bug 676998: test fails consistently on Android
fail-if = os == "android"
[test_file_renameTo.js]
[test_notxpcom_scriptable.js]
[test_windows_registry.js]
skip-if = os != "win"