fune/toolkit/xre/nsWindowsRestart.cpp
Henri Sivonen 3edc601325 Bug 1402247 - Use encoding_rs for XPCOM string encoding conversions. r=Nika,erahm,froydnj.
Correctness improvements:

 * UTF errors are handled safely per spec instead of dangerously truncating
   strings.

 * There are fewer converter implementations.

Performance improvements:

 * The old code did exact buffer length math, which meant doing UTF math twice
   on each input string (once for length calculation and another time for
   conversion). Exact length math is more complicated when handling errors
   properly, which the old code didn't do. The new code does UTF math on the
   string content only once (when converting) but risks allocating more than
   once. There are heuristics in place to lower the probability of
   reallocation in cases where the double math avoidance isn't enough of a
   saving to absorb an allocation and memcpy.

 * Previously, in UTF-16 <-> UTF-8 conversions, an ASCII prefix was optimized
   but a single non-ASCII code point pessimized the rest of the string. The
   new code tries to get back on the fast ASCII path.

 * UTF-16 to Latin1 conversion guarantees less about handling of out-of-range
   input to eliminate an operation from the inner loop on x86/x86_64.

 * When assigning to a pre-existing string, the new code tries to reuse the
   old buffer instead of first releasing the old buffer and then allocating a
   new one.

 * When reallocating from the new code, the memcpy covers only the data that
   is part of the logical length of the old string instead of memcpying the
   whole capacity. (For old callers old excess memcpy behavior is preserved
   due to bogus callers. See bug 1472113.)

 * UTF-8 strings in XPConnect that are in the Latin1 range are passed to
   SpiderMonkey as Latin1.

New features:

 * Conversion between UTF-8 and Latin1 is added in order to enable faster
   future interop between Rust code (or otherwise UTF-8-using code) and text
   node and SpiderMonkey code that uses Latin1.

MozReview-Commit-ID: JaJuExfILM9
2018-08-14 14:43:42 +03:00

178 lines
4.9 KiB
C++

/* 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/. */
// This file is not build directly. Instead, it is included in multiple
// shared objects.
#ifdef nsWindowsRestart_cpp
#error "nsWindowsRestart.cpp is not a header file, and must only be included once."
#else
#define nsWindowsRestart_cpp
#endif
#include "mozilla/CmdLineAndEnvUtils.h"
#include "nsUTF8Utils.h"
#include <shellapi.h>
// Needed for CreateEnvironmentBlock
#include <userenv.h>
#pragma comment(lib, "userenv.lib")
/**
* Convert UTF8 to UTF16 without using the normal XPCOM goop, which we
* can't link to updater.exe.
*/
static char16_t*
AllocConvertUTF8toUTF16(const char *arg)
{
// UTF16 can't be longer in units than UTF8
size_t len = strlen(arg);
char16_t *s = new char16_t[(len + 1) * sizeof(char16_t)];
if (!s)
return nullptr;
size_t dstLen = ::MultiByteToWideChar(
CP_UTF8, 0, arg, len, reinterpret_cast<wchar_t*>(s), len);
s[dstLen] = 0;
return s;
}
static void
FreeAllocStrings(int argc, wchar_t **argv)
{
while (argc) {
--argc;
delete [] argv[argc];
}
delete [] argv;
}
static wchar_t**
AllocConvertUTF8toUTF16Strings(int argc, char **argv)
{
wchar_t **argvConverted = new wchar_t*[argc];
if (!argvConverted)
return nullptr;
for (int i = 0; i < argc; ++i) {
argvConverted[i] = reinterpret_cast<wchar_t*>(AllocConvertUTF8toUTF16(argv[i]));
if (!argvConverted[i]) {
FreeAllocStrings(i, argvConverted);
return nullptr;
}
}
return argvConverted;
}
/**
* Launch a child process with the specified arguments.
* @note argv[0] is ignored
* @note The form of this function that takes char **argv expects UTF-8
*/
BOOL
WinLaunchChild(const wchar_t *exePath,
int argc, wchar_t **argv,
HANDLE userToken = nullptr,
HANDLE *hProcess = nullptr);
BOOL
WinLaunchChild(const wchar_t *exePath,
int argc, char **argv,
HANDLE userToken,
HANDLE *hProcess)
{
wchar_t **argvConverted = AllocConvertUTF8toUTF16Strings(argc, argv);
if (!argvConverted)
return FALSE;
BOOL ok = WinLaunchChild(exePath, argc, argvConverted, userToken, hProcess);
FreeAllocStrings(argc, argvConverted);
return ok;
}
BOOL
WinLaunchChild(const wchar_t *exePath,
int argc,
wchar_t **argv,
HANDLE userToken,
HANDLE *hProcess)
{
BOOL ok;
mozilla::UniquePtr<wchar_t[]> cl(mozilla::MakeCommandLine(argc, argv));
if (!cl) {
return FALSE;
}
STARTUPINFOW si = {0};
si.cb = sizeof(STARTUPINFOW);
si.lpDesktop = L"winsta0\\Default";
PROCESS_INFORMATION pi = {0};
if (userToken == nullptr) {
ok = CreateProcessW(exePath,
cl.get(),
nullptr, // no special security attributes
nullptr, // no special thread attributes
FALSE, // don't inherit filehandles
0, // creation flags
nullptr, // inherit my environment
nullptr, // use my current directory
&si,
&pi);
} else {
// Create an environment block for the process we're about to start using
// the user's token.
LPVOID environmentBlock = nullptr;
if (!CreateEnvironmentBlock(&environmentBlock, userToken, TRUE)) {
environmentBlock = nullptr;
}
ok = CreateProcessAsUserW(userToken,
exePath,
cl.get(),
nullptr, // no special security attributes
nullptr, // no special thread attributes
FALSE, // don't inherit filehandles
0, // creation flags
environmentBlock,
nullptr, // use my current directory
&si,
&pi);
if (environmentBlock) {
DestroyEnvironmentBlock(environmentBlock);
}
}
if (ok) {
if (hProcess) {
*hProcess = pi.hProcess; // the caller now owns the HANDLE
} else {
CloseHandle(pi.hProcess);
}
CloseHandle(pi.hThread);
} else {
LPVOID lpMsgBuf = nullptr;
FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_IGNORE_INSERTS,
nullptr,
GetLastError(),
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPTSTR) &lpMsgBuf,
0,
nullptr);
wprintf(L"Error restarting: %s\n", lpMsgBuf ? static_cast<const wchar_t*>(lpMsgBuf) : L"(null)");
if (lpMsgBuf)
LocalFree(lpMsgBuf);
}
return ok;
}