mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-30 13:45:27 +00:00
2362f4b5ee
When Firefox downloads an update, it previously kept the update around to apply it on the next restart. This patch changes this so that the updater program is launched in the background as soon as the update has finished downloading in order to stage the updated version of the application by copying the existing installation directory to a temporary location and applying the update on top of it, and replace the existing installation directory with the staged directory on the next restart. Because the replacing step is typically very fast, this patch eliminates the wait for the update to be applied on restart, making it unnecessary to show a progress dialog when restarting. --HG-- rename : toolkit/mozapps/update/test/chrome/test_0092_finishedBackground.xul => toolkit/mozapps/update/test/chrome/test_0093_stagedBackground.xul rename : toolkit/mozapps/update/test/unit/test_0110_general.js => toolkit/mozapps/update/test/unit/test_0113_general.js rename : toolkit/mozapps/update/test/unit/test_0111_general.js => toolkit/mozapps/update/test/unit/test_0114_general.js rename : toolkit/mozapps/update/test/unit/test_0112_general.js => toolkit/mozapps/update/test/unit/test_0115_general.js rename : toolkit/mozapps/update/test/unit/test_0170_fileLocked_xp_win_complete.js => toolkit/mozapps/update/test/unit/test_0172_fileLocked_xp_win_complete.js rename : toolkit/mozapps/update/test/unit/test_0171_fileLocked_xp_win_partial.js => toolkit/mozapps/update/test/unit/test_0173_fileLocked_xp_win_partial.js rename : toolkit/mozapps/update/test/unit/test_0110_general.js => toolkit/mozapps/update/test_svc/unit/test_0113_general_svc.js rename : toolkit/mozapps/update/test/unit/test_0111_general.js => toolkit/mozapps/update/test_svc/unit/test_0114_general_svc.js rename : toolkit/mozapps/update/test/unit/test_0112_general.js => toolkit/mozapps/update/test_svc/unit/test_0115_general_svc.js rename : toolkit/mozapps/update/test/unit/test_0170_fileLocked_xp_win_complete.js => toolkit/mozapps/update/test_svc/unit/test_0172_fileLocked_xp_win_complete_svc.js rename : toolkit/mozapps/update/test/unit/test_0171_fileLocked_xp_win_partial.js => toolkit/mozapps/update/test_svc/unit/test_0173_fileLocked_xp_win_partial_svc.js
301 lines
7.2 KiB
C++
301 lines
7.2 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 "nsUTF8Utils.h"
|
|
|
|
#include <shellapi.h>
|
|
|
|
// Needed for CreateEnvironmentBlock
|
|
#include <userenv.h>
|
|
#pragma comment(lib, "userenv.lib")
|
|
|
|
/**
|
|
* Get the length that the string will take and takes into account the
|
|
* additional length if the string needs to be quoted and if characters need to
|
|
* be escaped.
|
|
*/
|
|
static int ArgStrLen(const PRUnichar *s)
|
|
{
|
|
int backslashes = 0;
|
|
int i = wcslen(s);
|
|
BOOL hasDoubleQuote = wcschr(s, L'"') != NULL;
|
|
// Only add doublequotes if the string contains a space or a tab
|
|
BOOL addDoubleQuotes = wcspbrk(s, L" \t") != NULL;
|
|
|
|
if (addDoubleQuotes) {
|
|
i += 2; // initial and final duoblequote
|
|
}
|
|
|
|
if (hasDoubleQuote) {
|
|
while (*s) {
|
|
if (*s == '\\') {
|
|
++backslashes;
|
|
} else {
|
|
if (*s == '"') {
|
|
// Escape the doublequote and all backslashes preceding the doublequote
|
|
i += backslashes + 1;
|
|
}
|
|
|
|
backslashes = 0;
|
|
}
|
|
|
|
++s;
|
|
}
|
|
}
|
|
|
|
return i;
|
|
}
|
|
|
|
/**
|
|
* Copy string "s" to string "d", quoting the argument as appropriate and
|
|
* escaping doublequotes along with any backslashes that immediately precede
|
|
* doublequotes.
|
|
* The CRT parses this to retrieve the original argc/argv that we meant,
|
|
* see STDARGV.C in the MSVC CRT sources.
|
|
*
|
|
* @return the end of the string
|
|
*/
|
|
static PRUnichar* ArgToString(PRUnichar *d, const PRUnichar *s)
|
|
{
|
|
int backslashes = 0;
|
|
BOOL hasDoubleQuote = wcschr(s, L'"') != NULL;
|
|
// Only add doublequotes if the string contains a space or a tab
|
|
BOOL addDoubleQuotes = wcspbrk(s, L" \t") != NULL;
|
|
|
|
if (addDoubleQuotes) {
|
|
*d = '"'; // initial doublequote
|
|
++d;
|
|
}
|
|
|
|
if (hasDoubleQuote) {
|
|
int i;
|
|
while (*s) {
|
|
if (*s == '\\') {
|
|
++backslashes;
|
|
} else {
|
|
if (*s == '"') {
|
|
// Escape the doublequote and all backslashes preceding the doublequote
|
|
for (i = 0; i <= backslashes; ++i) {
|
|
*d = '\\';
|
|
++d;
|
|
}
|
|
}
|
|
|
|
backslashes = 0;
|
|
}
|
|
|
|
*d = *s;
|
|
++d; ++s;
|
|
}
|
|
} else {
|
|
wcscpy(d, s);
|
|
d += wcslen(s);
|
|
}
|
|
|
|
if (addDoubleQuotes) {
|
|
*d = '"'; // final doublequote
|
|
++d;
|
|
}
|
|
|
|
return d;
|
|
}
|
|
|
|
/**
|
|
* Creates a command line from a list of arguments. The returned
|
|
* string is allocated with "malloc" and should be "free"d.
|
|
*
|
|
* argv is UTF8
|
|
*/
|
|
PRUnichar*
|
|
MakeCommandLine(int argc, PRUnichar **argv)
|
|
{
|
|
int i;
|
|
int len = 0;
|
|
|
|
// The + 1 of the last argument handles the allocation for null termination
|
|
for (i = 0; i < argc; ++i)
|
|
len += ArgStrLen(argv[i]) + 1;
|
|
|
|
// Protect against callers that pass 0 arguments
|
|
if (len == 0)
|
|
len = 1;
|
|
|
|
PRUnichar *s = (PRUnichar*) malloc(len * sizeof(PRUnichar));
|
|
if (!s)
|
|
return NULL;
|
|
|
|
PRUnichar *c = s;
|
|
for (i = 0; i < argc; ++i) {
|
|
c = ArgToString(c, argv[i]);
|
|
if (i + 1 != argc) {
|
|
*c = ' ';
|
|
++c;
|
|
}
|
|
}
|
|
|
|
*c = '\0';
|
|
|
|
return s;
|
|
}
|
|
|
|
/**
|
|
* Convert UTF8 to UTF16 without using the normal XPCOM goop, which we
|
|
* can't link to updater.exe.
|
|
*/
|
|
static PRUnichar*
|
|
AllocConvertUTF8toUTF16(const char *arg)
|
|
{
|
|
// UTF16 can't be longer in units than UTF8
|
|
int len = strlen(arg);
|
|
PRUnichar *s = new PRUnichar[(len + 1) * sizeof(PRUnichar)];
|
|
if (!s)
|
|
return NULL;
|
|
|
|
ConvertUTF8toUTF16 convert(s);
|
|
convert.write(arg, len);
|
|
convert.write_terminator();
|
|
return s;
|
|
}
|
|
|
|
static void
|
|
FreeAllocStrings(int argc, PRUnichar **argv)
|
|
{
|
|
while (argc) {
|
|
--argc;
|
|
delete [] argv[argc];
|
|
}
|
|
|
|
delete [] argv;
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
* 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 PRUnichar *exePath,
|
|
int argc, PRUnichar **argv,
|
|
HANDLE userToken = NULL,
|
|
HANDLE *hProcess = NULL);
|
|
|
|
BOOL
|
|
WinLaunchChild(const PRUnichar *exePath,
|
|
int argc, char **argv,
|
|
HANDLE userToken,
|
|
HANDLE *hProcess)
|
|
{
|
|
PRUnichar** argvConverted = new PRUnichar*[argc];
|
|
if (!argvConverted)
|
|
return FALSE;
|
|
|
|
for (int i = 0; i < argc; ++i) {
|
|
argvConverted[i] = AllocConvertUTF8toUTF16(argv[i]);
|
|
if (!argvConverted[i]) {
|
|
FreeAllocStrings(i, argvConverted);
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
BOOL ok = WinLaunchChild(exePath, argc, argvConverted, userToken, hProcess);
|
|
FreeAllocStrings(argc, argvConverted);
|
|
return ok;
|
|
}
|
|
|
|
BOOL
|
|
WinLaunchChild(const PRUnichar *exePath,
|
|
int argc,
|
|
PRUnichar **argv,
|
|
HANDLE userToken,
|
|
HANDLE *hProcess)
|
|
{
|
|
PRUnichar *cl;
|
|
BOOL ok;
|
|
|
|
cl = 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 == NULL) {
|
|
ok = CreateProcessW(exePath,
|
|
cl,
|
|
NULL, // no special security attributes
|
|
NULL, // no special thread attributes
|
|
FALSE, // don't inherit filehandles
|
|
0, // creation flags
|
|
NULL, // inherit my environment
|
|
NULL, // 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 = NULL;
|
|
if (!CreateEnvironmentBlock(&environmentBlock, userToken, TRUE)) {
|
|
environmentBlock = NULL;
|
|
}
|
|
|
|
ok = CreateProcessAsUserW(userToken,
|
|
exePath,
|
|
cl,
|
|
NULL, // no special security attributes
|
|
NULL, // no special thread attributes
|
|
FALSE, // don't inherit filehandles
|
|
0, // creation flags
|
|
environmentBlock,
|
|
NULL, // 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 = NULL;
|
|
FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
|
|
FORMAT_MESSAGE_FROM_SYSTEM |
|
|
FORMAT_MESSAGE_IGNORE_INSERTS,
|
|
NULL,
|
|
GetLastError(),
|
|
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
|
|
(LPTSTR) &lpMsgBuf,
|
|
0,
|
|
NULL);
|
|
wprintf(L"Error restarting: %s\n", lpMsgBuf ? lpMsgBuf : L"(null)");
|
|
if (lpMsgBuf)
|
|
LocalFree(lpMsgBuf);
|
|
}
|
|
|
|
free(cl);
|
|
|
|
return ok;
|
|
}
|