зеркало из https://github.com/mozilla/gecko-dev.git
Backed out 2 changesets (bug 1827651) as requested by developer CLOSED TREE
Backed out changeset e6a9e84f78d9 (bug 1827651) Backed out changeset c6020af95de1 (bug 1827651)
This commit is contained in:
Родитель
9e27b342a4
Коммит
1dc5980f77
|
@ -11,14 +11,3 @@ endif
|
|||
endif
|
||||
|
||||
include $(topsrcdir)/config/rules.mk
|
||||
|
||||
ifeq (cocoa,$(MOZ_WIDGET_TOOLKIT))
|
||||
XPCSHELLTESTDIR = $(topobjdir)/_tests/xpcshell/toolkit/mozapps/update/tests/data/
|
||||
|
||||
libs::
|
||||
$(NSINSTALL) -D $(DIST)/bin/callback_app.app
|
||||
rsync -a -C $(srcdir)/macbuild/Contents $(DIST)/bin/callback_app.app
|
||||
$(NSINSTALL) -D $(DIST)/bin/callback_app.app/Contents/MacOS
|
||||
$(NSINSTALL) $(XPCSHELLTESTDIR)/TestAUSHelper $(DIST)/bin/callback_app.app/Contents/MacOS
|
||||
cp -R $(DIST)/bin/callback_app.app $(XPCSHELLTESTDIR)
|
||||
endif
|
||||
|
|
|
@ -81,37 +81,6 @@ static bool CheckMsg(const NS_tchar* path, const char* expected) {
|
|||
return isMatch;
|
||||
}
|
||||
|
||||
static bool BuildLogFilePath(NS_tchar* aLogFilePath, size_t aBufferSize,
|
||||
NS_tchar** aArgv) {
|
||||
#ifdef XP_MACOSX
|
||||
// Our tests on macOS require absolute paths to log files, as relative paths
|
||||
// will be interpreted as relative to the root of the file system, i.e. '/',
|
||||
// which is a read-only location where our tests can't write to.
|
||||
//
|
||||
// This finds the absolute path to `callback_app.app`, which will usually be:
|
||||
// <abs-path>/dir.app/Contents/MacOS/
|
||||
//
|
||||
NS_tchar* callbackAppBundle = NS_tstrstr(aArgv[0], "callback_app.app");
|
||||
if (!callbackAppBundle) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// This appends the log file name to the same absolute path as
|
||||
// `callback_app.app`, which is what our tests expect.
|
||||
if (!NS_tvsnprintf(aLogFilePath, aBufferSize / sizeof(aLogFilePath[0]),
|
||||
NS_T("%.*s%s"), callbackAppBundle - aArgv[0], aArgv[0],
|
||||
aArgv[2])) {
|
||||
return false;
|
||||
}
|
||||
#else
|
||||
if (!NS_tvsnprintf(aLogFilePath, aBufferSize / sizeof(aLogFilePath[0]),
|
||||
NS_T("%s"), aArgv[2])) {
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
int NS_main(int argc, NS_tchar** argv) {
|
||||
if (argc == 2) {
|
||||
if (!NS_tstrcmp(argv[1], NS_T("post-update-async")) ||
|
||||
|
@ -360,7 +329,9 @@ int NS_main(int argc, NS_tchar** argv) {
|
|||
::umask(umask);
|
||||
|
||||
NS_tchar logFilePath[MAXPATHLEN];
|
||||
if (!BuildLogFilePath(logFilePath, sizeof(logFilePath), argv)) {
|
||||
if (!NS_tvsnprintf(logFilePath,
|
||||
sizeof(logFilePath) / sizeof(logFilePath[0]), NS_T("%s"),
|
||||
argv[2])) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
@ -503,7 +474,9 @@ int NS_main(int argc, NS_tchar** argv) {
|
|||
{
|
||||
// Command line argument test helper section
|
||||
NS_tchar logFilePath[MAXPATHLEN];
|
||||
if (!BuildLogFilePath(logFilePath, sizeof(logFilePath), argv)) {
|
||||
if (!NS_tvsnprintf(logFilePath,
|
||||
sizeof(logFilePath) / sizeof(logFilePath[0]), NS_T("%s"),
|
||||
argv[2])) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
|
|
@ -315,13 +315,10 @@ function moveRealUpdater() {
|
|||
return (async function () {
|
||||
try {
|
||||
// Move away the real updater
|
||||
let updaterBackup = getGREBinDir();
|
||||
let updater = getGREBinDir();
|
||||
let greBinDir = getGREBinDir();
|
||||
let updater = greBinDir.clone();
|
||||
updater.append(FILE_UPDATER_BIN);
|
||||
if (AppConstants.platform == "macosx") {
|
||||
updaterBackup = updaterBackup.parent.parent.parent;
|
||||
}
|
||||
updater.moveTo(updaterBackup, FILE_UPDATER_BIN_BAK);
|
||||
updater.moveTo(greBinDir, FILE_UPDATER_BIN_BAK);
|
||||
|
||||
let greDir = getGREDir();
|
||||
let updateSettingsIni = greDir.clone();
|
||||
|
@ -409,9 +406,6 @@ function restoreUpdaterBackup() {
|
|||
let greBinDir = getGREBinDir();
|
||||
let updater = greBinDir.clone();
|
||||
let updaterBackup = greBinDir.clone();
|
||||
if (AppConstants.platform == "macosx") {
|
||||
updaterBackup = updaterBackup.parent.parent.parent;
|
||||
}
|
||||
updater.append(FILE_UPDATER_BIN);
|
||||
updaterBackup.append(FILE_UPDATER_BIN_BAK);
|
||||
if (updaterBackup.exists()) {
|
||||
|
|
|
@ -77,7 +77,6 @@ const FILE_BACKUP_UPDATE_ELEVATED_LOG = "backup-update-elevated.log";
|
|||
const FILE_BACKUP_UPDATE_LOG = "backup-update.log";
|
||||
const FILE_CHANNEL_PREFS =
|
||||
AppConstants.platform == "macosx" ? "ChannelPrefs" : "channel-prefs.js";
|
||||
const FILE_INFO_PLIST = "Info.plist";
|
||||
const FILE_LAST_UPDATE_ELEVATED_LOG = "last-update-elevated.log";
|
||||
const FILE_LAST_UPDATE_LOG = "last-update.log";
|
||||
const FILE_PRECOMPLETE = "precomplete";
|
||||
|
@ -100,9 +99,6 @@ const UPDATE_SETTINGS_CONTENTS =
|
|||
"[Settings]\nACCEPTED_MAR_CHANNEL_IDS=xpcshell-test\n";
|
||||
const PRECOMPLETE_CONTENTS = 'rmdir "nonexistent_dir/"\n';
|
||||
|
||||
const DIR_APP_INFO_PLIST_FILE_CONTENTS =
|
||||
'<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"><plist version="1.0"><dict><key>CFBundleDevelopmentRegion</key><string>English</string><key>CFBundleDisplayName</key><string>dir</string><key>CFBundleExecutable</key><string>firefox</string><key>CFBundleIdentifier</key><string>org.mozilla.firefox</string><key>CFBundleInfoDictionaryVersion</key><string>6.0</string><key>CFBundleName</key><string>dir</string><key>CFBundlePackageType</key><string>APPL</string><key>CFBundleSignature</key><string>????</string><key>CFBundleVersion</key><string>1.0</string></dict></plist>';
|
||||
|
||||
const PR_RDWR = 0x04;
|
||||
const PR_CREATE_FILE = 0x08;
|
||||
const PR_TRUNCATE = 0x20;
|
||||
|
|
|
@ -45,13 +45,9 @@ const Cm = Components.manager;
|
|||
/* global MOZ_VERIFY_MAR_SIGNATURE, IS_AUTHENTICODE_CHECK_ENABLED */
|
||||
load("../data/xpcshellConstantsPP.js");
|
||||
|
||||
// Note: DIR_CONTENTS, DIR_MACOS and DIR_RESOURCES only differ on macOS. They
|
||||
// default to "" on all other platforms.
|
||||
const DIR_CONTENTS = AppConstants.platform == "macosx" ? "Contents/" : "";
|
||||
const DIR_MACOS =
|
||||
AppConstants.platform == "macosx" ? DIR_CONTENTS + "MacOS/" : "";
|
||||
const DIR_MACOS = AppConstants.platform == "macosx" ? "Contents/MacOS/" : "";
|
||||
const DIR_RESOURCES =
|
||||
AppConstants.platform == "macosx" ? DIR_CONTENTS + "Resources/" : "";
|
||||
AppConstants.platform == "macosx" ? "Contents/Resources/" : "";
|
||||
const TEST_FILE_SUFFIX = AppConstants.platform == "macosx" ? "_mac" : "";
|
||||
const FILE_COMPLETE_MAR = "complete" + TEST_FILE_SUFFIX + ".mar";
|
||||
const FILE_PARTIAL_MAR = "partial" + TEST_FILE_SUFFIX + ".mar";
|
||||
|
@ -79,19 +75,13 @@ const APP_BIN_SUFFIX =
|
|||
AppConstants.platform == "linux" ? "-bin" : mozinfo.bin_suffix;
|
||||
const FILE_APP_BIN = AppConstants.MOZ_APP_NAME + APP_BIN_SUFFIX;
|
||||
const FILE_COMPLETE_EXE = "complete.exe";
|
||||
const FILE_HELPER_BIN =
|
||||
AppConstants.platform == "macosx"
|
||||
? "callback_app.app/Contents/MacOS/TestAUSHelper"
|
||||
: "TestAUSHelper" + mozinfo.bin_suffix;
|
||||
const FILE_HELPER_APP =
|
||||
AppConstants.platform == "macosx" ? "callback_app.app" : FILE_HELPER_BIN;
|
||||
const FILE_HELPER_BIN = "TestAUSHelper" + mozinfo.bin_suffix;
|
||||
const FILE_MAINTENANCE_SERVICE_BIN = "maintenanceservice.exe";
|
||||
const FILE_MAINTENANCE_SERVICE_INSTALLER_BIN =
|
||||
"maintenanceservice_installer.exe";
|
||||
const FILE_OLD_VERSION_MAR = "old_version.mar";
|
||||
const FILE_PARTIAL_EXE = "partial.exe";
|
||||
const FILE_UPDATER_BIN =
|
||||
"updater" + (AppConstants.platform == "macosx" ? ".app" : mozinfo.bin_suffix);
|
||||
const FILE_UPDATER_BIN = "updater" + mozinfo.bin_suffix;
|
||||
|
||||
const PERFORMING_STAGED_UPDATE = "Performing a staged update";
|
||||
const CALL_QUIT = "calling QuitProgressUI";
|
||||
|
@ -155,21 +145,8 @@ var gPIDPersistProcess;
|
|||
|
||||
// Variables are used instead of contants so tests can override these values if
|
||||
// necessary.
|
||||
var gCallbackBinFile = "callback_app" + mozinfo.bin_suffix;
|
||||
var gCallbackArgs = ["./", "callback.log", "Test Arg 2", "Test Arg 3"];
|
||||
var gCallbackApp = (() => {
|
||||
if (AppConstants.platform == "macosx") {
|
||||
return "callback_app.app";
|
||||
}
|
||||
return "callback_app" + mozinfo.bin_suffix;
|
||||
})();
|
||||
|
||||
var gCallbackBinFile = (() => {
|
||||
if (AppConstants.platform == "macosx") {
|
||||
return FILE_HELPER_BIN;
|
||||
}
|
||||
return "callback_app" + mozinfo.bin_suffix;
|
||||
})();
|
||||
|
||||
var gPostUpdateBinFile = "postup_app" + mozinfo.bin_suffix;
|
||||
|
||||
var gTimeoutRuns = 0;
|
||||
|
@ -250,43 +227,7 @@ var gTestFilesCommonMac = [
|
|||
description: "Should never change",
|
||||
fileName: FILE_UPDATE_SETTINGS_FRAMEWORK,
|
||||
relPathDir:
|
||||
DIR_MACOS + "updater.app/Contents/Frameworks/UpdateSettings.framework/",
|
||||
originalContents: null,
|
||||
compareContents: null,
|
||||
originalFile: null,
|
||||
compareFile: null,
|
||||
originalPerms: null,
|
||||
comparePerms: null,
|
||||
existingFile: true,
|
||||
},
|
||||
{
|
||||
description: "Should never change",
|
||||
fileName: FILE_INFO_PLIST,
|
||||
relPathDir: DIR_CONTENTS,
|
||||
originalContents: DIR_APP_INFO_PLIST_FILE_CONTENTS,
|
||||
compareContents: DIR_APP_INFO_PLIST_FILE_CONTENTS,
|
||||
originalFile: null,
|
||||
compareFile: null,
|
||||
originalPerms: null,
|
||||
comparePerms: null,
|
||||
existingFile: true,
|
||||
},
|
||||
{
|
||||
description: "Should never change",
|
||||
fileName: FILE_INFO_PLIST,
|
||||
relPathDir: DIR_MACOS + "updater.app/Contents/",
|
||||
originalContents: null,
|
||||
compareContents: null,
|
||||
originalFile: null,
|
||||
compareFile: null,
|
||||
originalPerms: null,
|
||||
comparePerms: null,
|
||||
existingFile: true,
|
||||
},
|
||||
{
|
||||
description: "Should never change",
|
||||
fileName: FILE_INFO_PLIST,
|
||||
relPathDir: DIR_MACOS + "callback_app.app/Contents/",
|
||||
"Contents/MacOS/updater.app/Contents/Frameworks/UpdateSettings.framework/",
|
||||
originalContents: null,
|
||||
compareContents: null,
|
||||
originalFile: null,
|
||||
|
@ -1058,10 +999,10 @@ function setupTestCommon(aAppUpdateAutoEnabled = false, aAllowBits = false) {
|
|||
);
|
||||
syncManager.resetLock();
|
||||
|
||||
// Remove the updates directory on Windows and macOS which is located
|
||||
// Remove the updates directory on Windows and Mac OS X which is located
|
||||
// outside of the application directory after the call to adjustGeneralPaths
|
||||
// has set it up. Since the test hasn't run yet, the directory shouldn't
|
||||
// exist and failure to remove the directory should be non-fatal for the test.
|
||||
// has set it up. Since the test hasn't ran yet and the directory shouldn't
|
||||
// exist this is non-fatal for the test.
|
||||
if (AppConstants.platform == "win" || AppConstants.platform == "macosx") {
|
||||
let updatesDir = getMockUpdRootD();
|
||||
if (updatesDir.exists()) {
|
||||
|
@ -1560,7 +1501,7 @@ function getApplyDirPath() {
|
|||
* update will be applied.
|
||||
*
|
||||
* The files for the update are located two directories below the apply to
|
||||
* directory since macOS sets the last modified time for the root directory
|
||||
* directory since Mac OS X sets the last modified time for the root directory
|
||||
* to the current time and if the update changes any files in the root directory
|
||||
* then it wouldn't be possible to test (bug 600098).
|
||||
*
|
||||
|
@ -1968,21 +1909,6 @@ function removeUpdateInProgressLockFile(aDir) {
|
|||
Assert.ok(!file.exists(), MSG_SHOULD_NOT_EXIST + getMsgPath(file.path));
|
||||
}
|
||||
|
||||
function stripQuarantineBitFromPath(aPath) {
|
||||
if (AppConstants.platform != "macosx") {
|
||||
do_throw("macOS-only function called by a different platform!");
|
||||
}
|
||||
|
||||
let args = ["-dr", "com.apple.quarantine", aPath];
|
||||
let stripQuarantineBitProcess = Cc[
|
||||
"@mozilla.org/process/util;1"
|
||||
].createInstance(Ci.nsIProcess);
|
||||
let xattrBin = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsIFile);
|
||||
xattrBin.initWithPath("/usr/bin/xattr");
|
||||
stripQuarantineBitProcess.init(xattrBin);
|
||||
stripQuarantineBitProcess.run(true, args, args.length);
|
||||
}
|
||||
|
||||
/**
|
||||
* Copies the test updater to the GRE binary directory and returns the nsIFile
|
||||
* for the copied test updater.
|
||||
|
@ -1990,15 +1916,15 @@ function stripQuarantineBitFromPath(aPath) {
|
|||
* @return nsIFIle for the copied test updater.
|
||||
*/
|
||||
function copyTestUpdaterToBinDir() {
|
||||
let testUpdater = getTestDirFile(FILE_UPDATER_BIN);
|
||||
let updaterLeafName =
|
||||
AppConstants.platform == "macosx" ? "updater.app" : FILE_UPDATER_BIN;
|
||||
let testUpdater = getTestDirFile(updaterLeafName);
|
||||
let updater = getGREBinDir();
|
||||
updater.append(FILE_UPDATER_BIN);
|
||||
updater.append(updaterLeafName);
|
||||
if (!updater.exists()) {
|
||||
testUpdater.copyToFollowingLinks(updater.parent, FILE_UPDATER_BIN);
|
||||
testUpdater.copyToFollowingLinks(updater.parent, updaterLeafName);
|
||||
}
|
||||
|
||||
if (AppConstants.platform == "macosx") {
|
||||
stripQuarantineBitFromPath(updater.path);
|
||||
updater.append("Contents");
|
||||
updater.append("MacOS");
|
||||
updater.append("org.mozilla.updater");
|
||||
|
@ -2125,8 +2051,8 @@ function runUpdate(
|
|||
|
||||
let svcOriginalLog;
|
||||
if (gIsServiceTest) {
|
||||
copyFileToTestAppDir(FILE_MAINTENANCE_SERVICE_BIN, DIR_MACOS);
|
||||
copyFileToTestAppDir(FILE_MAINTENANCE_SERVICE_INSTALLER_BIN, DIR_MACOS);
|
||||
copyFileToTestAppDir(FILE_MAINTENANCE_SERVICE_BIN, false);
|
||||
copyFileToTestAppDir(FILE_MAINTENANCE_SERVICE_INSTALLER_BIN, false);
|
||||
if (aCheckSvcLog) {
|
||||
svcOriginalLog = readServiceLogFile();
|
||||
}
|
||||
|
@ -2141,7 +2067,6 @@ function runUpdate(
|
|||
if (!gUpdateBin) {
|
||||
gUpdateBin = copyTestUpdaterToBinDir();
|
||||
}
|
||||
|
||||
Assert.ok(
|
||||
gUpdateBin.exists(),
|
||||
MSG_SHOULD_EXIST + getMsgPath(gUpdateBin.path)
|
||||
|
@ -2152,7 +2077,7 @@ function runUpdate(
|
|||
let applyToDirPath = aApplyToDirPath || getApplyDirFile().path;
|
||||
let stageDirPath = aApplyToDirPath || getStageDirFile().path;
|
||||
|
||||
let callbackApp = getApplyDirFile(DIR_MACOS + gCallbackApp);
|
||||
let callbackApp = getApplyDirFile(DIR_RESOURCES + gCallbackBinFile);
|
||||
Assert.ok(
|
||||
callbackApp.exists(),
|
||||
MSG_SHOULD_EXIST + ", path: " + callbackApp.path
|
||||
|
@ -2623,19 +2548,19 @@ function setupAppFiles({ requiresOmnijar = false } = {}) {
|
|||
// Required files for the application or the test that aren't listed in the
|
||||
// dependentlibs.list file.
|
||||
let appFiles = [
|
||||
{ relPath: FILE_APP_BIN, inDir: DIR_MACOS },
|
||||
{ relPath: FILE_APPLICATION_INI, inDir: DIR_RESOURCES },
|
||||
{ relPath: "dependentlibs.list", inDir: DIR_RESOURCES },
|
||||
{ relPath: FILE_APP_BIN, inGreDir: false },
|
||||
{ relPath: FILE_APPLICATION_INI, inGreDir: true },
|
||||
{ relPath: "dependentlibs.list", inGreDir: true },
|
||||
];
|
||||
|
||||
if (requiresOmnijar) {
|
||||
appFiles.push({ relPath: AppConstants.OMNIJAR_NAME, inDir: DIR_RESOURCES });
|
||||
appFiles.push({ relPath: AppConstants.OMNIJAR_NAME, inGreDir: true });
|
||||
|
||||
if (AppConstants.MOZ_BUILD_APP == "browser") {
|
||||
// Only Firefox uses an app-specific omnijar.
|
||||
appFiles.push({
|
||||
relPath: "browser/" + AppConstants.OMNIJAR_NAME,
|
||||
inDir: DIR_RESOURCES,
|
||||
inGreDir: true,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -2644,8 +2569,8 @@ function setupAppFiles({ requiresOmnijar = false } = {}) {
|
|||
// symlinked or copied.
|
||||
if (AppConstants.platform == "linux") {
|
||||
appFiles.push(
|
||||
{ relPath: "icons/updater.png", inDir: DIR_RESOURCES },
|
||||
{ relPath: "libsoftokn3.so", inDir: DIR_RESOURCES }
|
||||
{ relPath: "icons/updater.png", inGreDir: true },
|
||||
{ relPath: "libsoftokn3.so", inGreDir: true }
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -2663,13 +2588,13 @@ function setupAppFiles({ requiresOmnijar = false } = {}) {
|
|||
let line = {};
|
||||
do {
|
||||
hasMore = fis.readLine(line);
|
||||
appFiles.push({ relPath: line.value, inDir: DIR_MACOS });
|
||||
appFiles.push({ relPath: line.value, inGreDir: false });
|
||||
} while (hasMore);
|
||||
|
||||
fis.close();
|
||||
|
||||
appFiles.forEach(function CMAF_FLN_FE(aAppFile) {
|
||||
copyFileToTestAppDir(aAppFile.relPath, aAppFile.inDir);
|
||||
copyFileToTestAppDir(aAppFile.relPath, aAppFile.inGreDir);
|
||||
});
|
||||
|
||||
copyTestUpdaterToBinDir();
|
||||
|
@ -2687,39 +2612,18 @@ function setupAppFiles({ requiresOmnijar = false } = {}) {
|
|||
* @param aFileRelPath
|
||||
* The relative path to the source and the destination of the file to
|
||||
* copy.
|
||||
* @param aDir
|
||||
* The relative subdirectory within the .app bundle on macOS. This is
|
||||
* ignored on all other platforms.
|
||||
* @param aInGreDir
|
||||
* Whether the file is located in the GRE directory which is
|
||||
* <bundle>/Contents/Resources on Mac OS X and is the installation
|
||||
* directory on all other platforms. If false the file must be in the
|
||||
* GRE Binary directory which is <bundle>/Contents/MacOS on Mac OS X
|
||||
* and is the installation directory on on all other platforms.
|
||||
*/
|
||||
function copyFileToTestAppDir(aFileRelPath, aDir) {
|
||||
let srcFile;
|
||||
let destFile;
|
||||
|
||||
function copyFileToTestAppDir(aFileRelPath, aInGreDir) {
|
||||
// gGREDirOrig and gGREBinDirOrig must always be cloned when changing its
|
||||
// properties
|
||||
if (AppConstants.platform == "macosx") {
|
||||
switch (aDir) {
|
||||
case DIR_RESOURCES:
|
||||
srcFile = gGREDirOrig.clone();
|
||||
destFile = getGREDir();
|
||||
break;
|
||||
case DIR_MACOS:
|
||||
srcFile = gGREBinDirOrig.clone();
|
||||
destFile = getGREBinDir();
|
||||
break;
|
||||
case DIR_CONTENTS:
|
||||
srcFile = gGREBinDirOrig.parent.clone();
|
||||
destFile = getGREBinDir().parent;
|
||||
break;
|
||||
default:
|
||||
debugDump("invalid path given. Path: " + aDir);
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
srcFile = gGREDirOrig.clone();
|
||||
destFile = getGREDir();
|
||||
}
|
||||
|
||||
let srcFile = aInGreDir ? gGREDirOrig.clone() : gGREBinDirOrig.clone();
|
||||
let destFile = aInGreDir ? getGREDir() : getGREBinDir();
|
||||
let fileRelPath = aFileRelPath;
|
||||
let pathParts = fileRelPath.split("/");
|
||||
for (let i = 0; i < pathParts.length; i++) {
|
||||
|
@ -2736,6 +2640,10 @@ function copyFileToTestAppDir(aFileRelPath, aDir) {
|
|||
".app exists. Path: " +
|
||||
srcFile.path
|
||||
);
|
||||
// gGREDirOrig and gGREBinDirOrig must always be cloned when changing its
|
||||
// properties
|
||||
srcFile = aInGreDir ? gGREDirOrig.clone() : gGREBinDirOrig.clone();
|
||||
destFile = aInGreDir ? getGREDir() : getGREBinDir();
|
||||
for (let i = 0; i < pathParts.length; i++) {
|
||||
if (pathParts[i]) {
|
||||
srcFile.append(
|
||||
|
@ -3272,14 +3180,11 @@ async function setupUpdaterTest(
|
|||
let mar = getTestDirFile(aMarFile);
|
||||
mar.copyToFollowingLinks(updatesPatchDir, FILE_UPDATE_MAR);
|
||||
|
||||
let helperApp = getTestDirFile(FILE_HELPER_APP);
|
||||
let helperBin = getTestDirFile(FILE_HELPER_BIN);
|
||||
helperApp.permissions = PERMS_DIRECTORY;
|
||||
helperBin.permissions = PERMS_DIRECTORY;
|
||||
let afterApplyBinDir = getApplyDirFile(DIR_MACOS);
|
||||
|
||||
let afterApplyBinDir = getApplyDirFile(DIR_RESOURCES);
|
||||
helperBin.copyToFollowingLinks(afterApplyBinDir, gCallbackBinFile);
|
||||
helperBin.copyToFollowingLinks(afterApplyBinDir, gPostUpdateBinFile);
|
||||
helperApp.copyToFollowingLinks(afterApplyBinDir, gCallbackApp);
|
||||
|
||||
// On macOS, some test files (like the Update Settings file) may be within the
|
||||
// updater app bundle, so make sure it is in place now in case we want to
|
||||
|
@ -3288,10 +3193,6 @@ async function setupUpdaterTest(
|
|||
gUpdateBin = copyTestUpdaterToBinDir();
|
||||
}
|
||||
|
||||
if (AppConstants.platform == "macosx") {
|
||||
stripQuarantineBitFromPath(afterApplyBinDir.parent.parent.path);
|
||||
}
|
||||
|
||||
gTestFiles.forEach(function SUT_TF_FE(aTestFile) {
|
||||
debugDump("start - setup test file: " + aTestFile.fileName);
|
||||
if (aTestFile.originalFile || aTestFile.originalContents) {
|
||||
|
@ -3465,7 +3366,11 @@ function createUpdaterINI(
|
|||
}
|
||||
|
||||
let exeRelPathMac =
|
||||
"ExeRelPath=" + aExeRelPathPrefix + DIR_MACOS + gPostUpdateBinFile + "\n";
|
||||
"ExeRelPath=" +
|
||||
aExeRelPathPrefix +
|
||||
DIR_RESOURCES +
|
||||
gPostUpdateBinFile +
|
||||
"\n";
|
||||
let exeRelPathWin =
|
||||
"ExeRelPath=" + aExeRelPathPrefix + gPostUpdateBinFile + "\n";
|
||||
let updaterIniContents =
|
||||
|
@ -4139,10 +4044,9 @@ function checkFilesAfterUpdateCommon(aStageDirExists, aToBeDeletedDirExists) {
|
|||
* parameter passed to the callback executable (in the apply directory).
|
||||
*/
|
||||
function checkCallbackLog(
|
||||
appLaunchLog = getApplyDirFile(DIR_MACOS + gCallbackArgs[1])
|
||||
appLaunchLog = getApplyDirFile(DIR_RESOURCES + gCallbackArgs[1])
|
||||
) {
|
||||
if (!appLaunchLog.exists()) {
|
||||
debugDump("Callback log does not exist yet. Path: " + appLaunchLog.path);
|
||||
// Uses do_timeout instead of do_execute_soon to lessen log spew.
|
||||
do_timeout(FILE_IN_USE_TIMEOUT_MS, checkCallbackLog);
|
||||
return;
|
||||
|
@ -4203,7 +4107,7 @@ function checkCallbackLog(
|
|||
* The string to append to the post update test helper binary path.
|
||||
*/
|
||||
function getPostUpdateFile(aSuffix) {
|
||||
return getApplyDirFile(DIR_MACOS + gPostUpdateBinFile + aSuffix);
|
||||
return getApplyDirFile(DIR_RESOURCES + gPostUpdateBinFile + aSuffix);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -1,24 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
<string>English</string>
|
||||
<key>CFBundleDisplayName</key>
|
||||
<string>callback_app</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>TestAUSHelper</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>org.mozilla.TestAUSHelper</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundleName</key>
|
||||
<string>callback_app</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>APPL</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>1.0</string>
|
||||
</dict>
|
||||
</plist>
|
|
@ -11,7 +11,7 @@ async function run_test() {
|
|||
gTestFiles = gTestFilesCompleteSuccess;
|
||||
gTestDirs = gTestDirsCompleteSuccess;
|
||||
await setupUpdaterTest(FILE_COMPLETE_MAR, false);
|
||||
await runHelperFileInUse(DIR_MACOS + gCallbackBinFile, false);
|
||||
await runHelperFileInUse(DIR_RESOURCES + gCallbackBinFile, false);
|
||||
runUpdate(STATE_SUCCEEDED, false, 0, true);
|
||||
await waitForHelperExit();
|
||||
await checkPostUpdateAppLog();
|
||||
|
|
|
@ -16,9 +16,9 @@ async function run_test() {
|
|||
|
||||
// Our callback is `TestAUSHelper check-umask <umask from before updating>`.
|
||||
// Including the umask from before updating as an argument allows to re-use
|
||||
// the callback log checking code below. The argument is also used as the log
|
||||
// the callback log checking code below. The argument is also used as the log
|
||||
// file name, so we prefix it with "umask" so that it doesn't clash with
|
||||
// numericfile and directory names in the update data. In particular, "2"
|
||||
// numericfile and directory names in the update data. In particular, "2"
|
||||
// clashes with an existing directory name in the update data, leading to
|
||||
// failing tests.
|
||||
let umask = Services.sysinfo.getProperty("umask");
|
||||
|
@ -36,7 +36,7 @@ async function run_test() {
|
|||
await checkUpdateManager(STATE_NONE, false, STATE_SUCCEEDED, 0, 1);
|
||||
|
||||
// This compares the callback arguments given, including the umask before
|
||||
// updating, to the umask set when the app callback is launched. They should
|
||||
// updating, to the umask set when the app callback is launched. They should
|
||||
// be the same.
|
||||
checkCallbackLog();
|
||||
checkCallbackLog(getApplyDirFile(DIR_RESOURCES + "callback_app.log"));
|
||||
}
|
||||
|
|
|
@ -30,89 +30,128 @@ class MacAutoreleasePool {
|
|||
NSAutoreleasePool* mPool;
|
||||
};
|
||||
|
||||
/**
|
||||
* Helper to launch macOS tasks via NSTask and wait for the launched task to
|
||||
* terminate.
|
||||
#if defined(__x86_64__)
|
||||
/*
|
||||
* Returns true if the process is running under Rosetta translation. Returns
|
||||
* false if running natively or if an error was encountered. We use the
|
||||
* `sysctl.proc_translated` sysctl which is documented by Apple to be used
|
||||
* for this purpose.
|
||||
*/
|
||||
static void LaunchTask(NSString* aPath, NSArray* aArguments) {
|
||||
MacAutoreleasePool pool;
|
||||
|
||||
NSTask* task = [[NSTask alloc] init];
|
||||
[task setExecutableURL:[NSURL fileURLWithPath:aPath]];
|
||||
if (aArguments) {
|
||||
[task setArguments:aArguments];
|
||||
}
|
||||
[task launchAndReturnError:nil];
|
||||
[task waitUntilExit];
|
||||
[task release];
|
||||
}
|
||||
|
||||
static void RegisterAppWithLaunchServices(NSString* aBundlePath) {
|
||||
MacAutoreleasePool pool;
|
||||
|
||||
@try {
|
||||
OSStatus status =
|
||||
LSRegisterURL((CFURLRef)[NSURL fileURLWithPath:aBundlePath], YES);
|
||||
if (status != noErr) {
|
||||
NSLog(@"We failed to register the app in the Launch Services database, "
|
||||
@"which may lead to a failure to launch the app. Launch path: %@",
|
||||
aBundlePath);
|
||||
bool IsProcessRosettaTranslated() {
|
||||
int ret = 0;
|
||||
size_t size = sizeof(ret);
|
||||
if (sysctlbyname("sysctl.proc_translated", &ret, &size, NULL, 0) == -1) {
|
||||
if (errno != ENOENT) {
|
||||
fprintf(stderr, "Failed to check for translation environment\n");
|
||||
}
|
||||
} @catch (NSException* e) {
|
||||
NSLog(@"%@: %@", e.name, e.reason);
|
||||
return false;
|
||||
}
|
||||
return (ret == 1);
|
||||
}
|
||||
|
||||
static void StripQuarantineBit(NSString* aBundlePath) {
|
||||
MacAutoreleasePool pool;
|
||||
// Returns true if the binary at |executablePath| can be executed natively
|
||||
// on an arm64 Mac. Returns false otherwise or if an error occurred.
|
||||
bool IsBinaryArmExecutable(const char* executablePath) {
|
||||
bool isArmExecutable = false;
|
||||
|
||||
NSArray* arguments = @[ @"-dr", @"com.apple.quarantine", aBundlePath ];
|
||||
LaunchTask(@"/usr/bin/xattr", arguments);
|
||||
CFURLRef url = ::CFURLCreateFromFileSystemRepresentation(
|
||||
kCFAllocatorDefault, (const UInt8*)executablePath, strlen(executablePath),
|
||||
false);
|
||||
if (!url) {
|
||||
return false;
|
||||
}
|
||||
|
||||
CFArrayRef archs = ::CFBundleCopyExecutableArchitecturesForURL(url);
|
||||
if (!archs) {
|
||||
CFRelease(url);
|
||||
return false;
|
||||
}
|
||||
|
||||
CFIndex archCount = ::CFArrayGetCount(archs);
|
||||
for (CFIndex i = 0; i < archCount; i++) {
|
||||
CFNumberRef currentArch =
|
||||
static_cast<CFNumberRef>(::CFArrayGetValueAtIndex(archs, i));
|
||||
int currentArchInt = 0;
|
||||
if (!::CFNumberGetValue(currentArch, kCFNumberIntType, ¤tArchInt)) {
|
||||
continue;
|
||||
}
|
||||
if (currentArchInt == kCFBundleExecutableArchitectureARM64) {
|
||||
isArmExecutable = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
CFRelease(url);
|
||||
CFRelease(archs);
|
||||
|
||||
return isArmExecutable;
|
||||
}
|
||||
|
||||
void LaunchMacApp(int argc, const char** argv) {
|
||||
// Returns true if the executable provided in |executablePath| should be
|
||||
// launched with a preference for arm64. After updating from an x64 version
|
||||
// running under Rosetta, if the update is to a universal binary with arm64
|
||||
// support we want to switch to arm64 execution mode. Returns true if those
|
||||
// conditions are met and the arch(1) utility at |archPath| is executable.
|
||||
// It should be safe to always launch with arch and fallback to x64, but we
|
||||
// limit its use to the only scenario it is necessary to minimize risk.
|
||||
bool ShouldPreferArmLaunch(const char* archPath, const char* executablePath) {
|
||||
// If not running under Rosetta, we are not on arm64 hardware.
|
||||
if (!IsProcessRosettaTranslated()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Ensure the arch(1) utility is present and executable.
|
||||
NSFileManager* fileMgr = [NSFileManager defaultManager];
|
||||
NSString* archPathString = [NSString stringWithUTF8String:archPath];
|
||||
if (![fileMgr isExecutableFileAtPath:archPathString]) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Ensure the binary can be run natively on arm64.
|
||||
return IsBinaryArmExecutable(executablePath);
|
||||
}
|
||||
#endif // __x86_64__
|
||||
|
||||
void LaunchChild(int argc, const char** argv) {
|
||||
MacAutoreleasePool pool;
|
||||
|
||||
@try {
|
||||
NSString* launchPath = [NSString stringWithUTF8String:argv[0]];
|
||||
NSMutableArray* arguments = [NSMutableArray arrayWithCapacity:argc - 1];
|
||||
bool preferArmLaunch = false;
|
||||
|
||||
#if defined(__x86_64__)
|
||||
// When running under Rosetta, child processes inherit the architecture
|
||||
// preference of their parent and therefore universal binaries launched
|
||||
// by an emulated x64 process will launch in x64 mode. If we are running
|
||||
// under Rosetta, launch the child process with a preference for arm64 so
|
||||
// that we will switch to arm64 execution if we have just updated from
|
||||
// x64 to a universal build. This includes if we were already a universal
|
||||
// build and the user is intentionally running under Rosetta.
|
||||
preferArmLaunch = ShouldPreferArmLaunch(ARCH_PATH, argv[0]);
|
||||
#endif // __x86_64__
|
||||
|
||||
NSString* launchPath;
|
||||
NSMutableArray* arguments;
|
||||
|
||||
if (preferArmLaunch) {
|
||||
launchPath = [NSString stringWithUTF8String:ARCH_PATH];
|
||||
|
||||
// Size the arguments array to include all the arguments
|
||||
// in |argv| plus two arguments to pass to the arch(1) utility.
|
||||
arguments = [NSMutableArray arrayWithCapacity:argc + 2];
|
||||
[arguments addObject:[NSString stringWithUTF8String:"-arm64"]];
|
||||
[arguments addObject:[NSString stringWithUTF8String:"-x86_64"]];
|
||||
|
||||
// Add the first argument from |argv|. The rest are added below.
|
||||
[arguments addObject:[NSString stringWithUTF8String:argv[0]]];
|
||||
} else {
|
||||
launchPath = [NSString stringWithUTF8String:argv[0]];
|
||||
arguments = [NSMutableArray arrayWithCapacity:argc - 1];
|
||||
}
|
||||
|
||||
for (int i = 1; i < argc; i++) {
|
||||
[arguments addObject:[NSString stringWithUTF8String:argv[i]]];
|
||||
}
|
||||
if (![launchPath hasSuffix:@".app"]) {
|
||||
// We only support launching applications inside .app bundles.
|
||||
NSLog(@"The updater attempted to launch an app that was not a .app "
|
||||
@"bundle. Please verify launch path: %@",
|
||||
launchPath);
|
||||
return;
|
||||
}
|
||||
|
||||
StripQuarantineBit(launchPath);
|
||||
RegisterAppWithLaunchServices(launchPath);
|
||||
|
||||
// We use NSWorkspace to register the application into the
|
||||
// `TALAppsToRelaunchAtLogin` list and allow for macOS session resume.
|
||||
// This API only works with `.app`s.
|
||||
__block dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
|
||||
NSWorkspaceOpenConfiguration* config =
|
||||
[NSWorkspaceOpenConfiguration configuration];
|
||||
[config setArguments:arguments];
|
||||
[config setCreatesNewApplicationInstance:YES];
|
||||
[config setEnvironment:[[NSProcessInfo processInfo] environment]];
|
||||
|
||||
[[NSWorkspace sharedWorkspace]
|
||||
openApplicationAtURL:[NSURL fileURLWithPath:launchPath]
|
||||
configuration:config
|
||||
completionHandler:^(NSRunningApplication* aChild, NSError* aError) {
|
||||
if (aError) {
|
||||
NSLog(@"launchchild_osx: Failed to run application. Error: %@",
|
||||
aError);
|
||||
}
|
||||
dispatch_semaphore_signal(semaphore);
|
||||
}];
|
||||
|
||||
// We use a semaphore to wait for the application to launch.
|
||||
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
|
||||
[NSTask launchedTaskWithLaunchPath:launchPath arguments:arguments];
|
||||
} @catch (NSException* e) {
|
||||
NSLog(@"%@: %@", e.name, e.reason);
|
||||
}
|
||||
|
@ -241,10 +280,11 @@ void CleanupElevatedMacUpdate(bool aFailureOccurred) {
|
|||
error:nil];
|
||||
[manager removeItemAtPath:@"/Library/LaunchDaemons/org.mozilla.updater.plist"
|
||||
error:nil];
|
||||
|
||||
const char* launchctlArgs[] = {"/bin/launchctl", "remove",
|
||||
"org.mozilla.updater"};
|
||||
// The following call will terminate the current process due to the "remove"
|
||||
// argument.
|
||||
LaunchTask(@"/bin/launchctl", @[ @"remove", @"org.mozilla.updater" ]);
|
||||
// argument in launchctlArgs.
|
||||
LaunchChild(3, launchctlArgs);
|
||||
}
|
||||
|
||||
// Note: Caller is responsible for freeing aArgv.
|
||||
|
@ -470,6 +510,31 @@ void SetGroupOwnershipAndPermissions(const char* aAppBundle) {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper to launch macOS tasks via NSTask.
|
||||
*/
|
||||
static void LaunchTask(NSString* aPath, NSArray* aArguments) {
|
||||
NSTask* task = [[NSTask alloc] init];
|
||||
[task setExecutableURL:[NSURL fileURLWithPath:aPath]];
|
||||
if (aArguments) {
|
||||
[task setArguments:aArguments];
|
||||
}
|
||||
[task launchAndReturnError:nil];
|
||||
[task release];
|
||||
}
|
||||
|
||||
static void RegisterAppWithLaunchServices(NSString* aBundlePath) {
|
||||
NSArray* arguments = @[ @"-f", aBundlePath ];
|
||||
LaunchTask(@"/System/Library/Frameworks/CoreServices.framework/Frameworks/"
|
||||
@"LaunchServices.framework/Support/lsregister",
|
||||
arguments);
|
||||
}
|
||||
|
||||
static void StripQuarantineBit(NSString* aBundlePath) {
|
||||
NSArray* arguments = @[ @"-d", @"com.apple.quarantine", aBundlePath ];
|
||||
LaunchTask(@"/usr/bin/xattr", arguments);
|
||||
}
|
||||
|
||||
bool PerformInstallationFromDMG(int argc, char** argv) {
|
||||
MacAutoreleasePool pool;
|
||||
if (argc < 4) {
|
||||
|
@ -480,8 +545,8 @@ bool PerformInstallationFromDMG(int argc, char** argv) {
|
|||
if ([[NSFileManager defaultManager] copyItemAtPath:bundlePath
|
||||
toPath:destPath
|
||||
error:nil]) {
|
||||
StripQuarantineBit(destPath);
|
||||
RegisterAppWithLaunchServices(destPath);
|
||||
StripQuarantineBit(destPath);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
|
|
@ -27,7 +27,6 @@ ifeq (cocoa,$(MOZ_WIDGET_TOOLKIT))
|
|||
# Copy for xpcshell tests
|
||||
$(NSINSTALL) -D $(XPCSHELLTESTDIR)/data/updater-xpcshell.app
|
||||
rsync -a -C --exclude '*.in' $(srcdir)/../macbuild/Contents $(XPCSHELLTESTDIR)/data/updater-xpcshell.app
|
||||
$(NSINSTALL) $(DIST)/bin/Info.plist $(XPCSHELLTESTDIR)/data/updater-xpcshell.app/Contents
|
||||
$(call py_action,preprocessor updater-xpcshell.app/Contents/Resources/English.lproj/InfoPlist.strings,-Fsubstitution --output-encoding utf-16 -DAPP_NAME='$(MOZ_APP_DISPLAYNAME)' $(srcdir)/../macbuild/Contents/Resources/English.lproj/InfoPlist.strings.in -o $(XPCSHELLTESTDIR)/data/updater-xpcshell.app/Contents/Resources/English.lproj/InfoPlist.strings)
|
||||
$(NSINSTALL) -D $(XPCSHELLTESTDIR)/data/updater-xpcshell.app/Contents/MacOS
|
||||
$(NSINSTALL) $(FINAL_TARGET)/updater-xpcshell $(XPCSHELLTESTDIR)/data/updater-xpcshell.app/Contents/MacOS
|
||||
|
|
|
@ -74,7 +74,7 @@
|
|||
void CleanupElevatedMacUpdate(bool aFailureOccurred);
|
||||
bool IsOwnedByGroupAdmin(const char* aAppBundle);
|
||||
bool IsRecursivelyWritable(const char* aPath);
|
||||
void LaunchMacApp(int argc, const char** argv);
|
||||
void LaunchChild(int argc, const char** argv);
|
||||
void LaunchMacPostProcess(const char* aAppBundle);
|
||||
bool ObtainUpdaterArguments(int* aArgc, char*** aArgv,
|
||||
MARChannelStringTable* aMARStrings);
|
||||
|
@ -2160,7 +2160,7 @@ static void LaunchCallbackApp(const NS_tchar* workingDir, int argc,
|
|||
#if defined(USE_EXECV)
|
||||
execv(argv[0], argv);
|
||||
#elif defined(XP_MACOSX)
|
||||
LaunchMacApp(argc, (const char**)argv);
|
||||
LaunchChild(argc, (const char**)argv);
|
||||
#elif defined(XP_WIN)
|
||||
// Do not allow the callback to run when running an update through the
|
||||
// service as session 0. The unelevated updater.exe will do the launching.
|
||||
|
|
|
@ -7,19 +7,9 @@
|
|||
#define MacLaunchHelper_h_
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include <unistd.h>
|
||||
|
||||
#ifdef __OBJC__
|
||||
# include <Foundation/Foundation.h>
|
||||
|
||||
namespace mozilla::MacLaunchHelper {
|
||||
|
||||
void LaunchMacAppWithBundle(NSString* aBundlePath,
|
||||
NSArray* aArguments = nullptr);
|
||||
|
||||
} // namespace mozilla::MacLaunchHelper
|
||||
#endif // __OBJC__
|
||||
|
||||
extern "C" {
|
||||
/**
|
||||
* Passing an aPid parameter to LaunchChildMac will wait for the launched
|
||||
|
@ -27,7 +17,6 @@ extern "C" {
|
|||
* pid of the terminated process to confirm that it executed successfully.
|
||||
*/
|
||||
void LaunchChildMac(int aArgc, char** aArgv, pid_t* aPid = 0);
|
||||
void LaunchMacApp(int aArgc, char** aArgv);
|
||||
bool LaunchElevatedUpdate(int aArgc, char** aArgv, pid_t* aPid = 0);
|
||||
bool InstallPrivilegedHelper();
|
||||
void AbortElevatedUpdate();
|
||||
|
|
|
@ -6,7 +6,6 @@
|
|||
#include "MacLaunchHelper.h"
|
||||
|
||||
#include "MacAutoreleasePool.h"
|
||||
#include "MacUtils.h"
|
||||
#include "mozilla/UniquePtr.h"
|
||||
|
||||
#include <Cocoa/Cocoa.h>
|
||||
|
@ -17,79 +16,6 @@
|
|||
#include <stdio.h>
|
||||
|
||||
using namespace mozilla;
|
||||
using namespace mozilla::MacUtils;
|
||||
using namespace mozilla::MacLaunchHelper;
|
||||
|
||||
static void RegisterAppWithLaunchServices(NSString* aBundlePath) {
|
||||
MacAutoreleasePool pool;
|
||||
|
||||
@try {
|
||||
OSStatus status =
|
||||
LSRegisterURL((CFURLRef)[NSURL fileURLWithPath:aBundlePath], YES);
|
||||
if (status != noErr) {
|
||||
NSLog(@"We failed to register the app in the Launch Services database, "
|
||||
@"which may lead to a failure to launch the app. Launch path: %@",
|
||||
aBundlePath);
|
||||
}
|
||||
} @catch (NSException* e) {
|
||||
NSLog(@"%@: %@", e.name, e.reason);
|
||||
}
|
||||
}
|
||||
|
||||
static void StripQuarantineBit(NSString* aBundlePath) {
|
||||
MacAutoreleasePool pool;
|
||||
|
||||
NSArray* arguments = @[ @"-dr", @"com.apple.quarantine", aBundlePath ];
|
||||
LaunchTask(@"/usr/bin/xattr", arguments);
|
||||
}
|
||||
|
||||
namespace mozilla::MacLaunchHelper {
|
||||
|
||||
void LaunchMacAppWithBundle(NSString* aBundlePath, NSArray* aArguments) {
|
||||
MacAutoreleasePool pool;
|
||||
|
||||
@try {
|
||||
NSString* launchPath = aBundlePath;
|
||||
if (![launchPath hasSuffix:@".app"]) {
|
||||
// We only support launching applications inside .app bundles.
|
||||
NSLog(@"An attempt was made to launch an app that was not in a .app "
|
||||
@"bundle. Please verify launch path: %@",
|
||||
launchPath);
|
||||
return;
|
||||
}
|
||||
|
||||
StripQuarantineBit(launchPath);
|
||||
RegisterAppWithLaunchServices(launchPath);
|
||||
|
||||
// We use NSWorkspace to register the application into the
|
||||
// `TALAppsToRelaunchAtLogin` list and allow for macOS session resume.
|
||||
// This API only works with `.app`s.
|
||||
__block dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
|
||||
NSWorkspaceOpenConfiguration* config =
|
||||
[NSWorkspaceOpenConfiguration configuration];
|
||||
[config setArguments:aArguments];
|
||||
[config setCreatesNewApplicationInstance:YES];
|
||||
[config setEnvironment:[[NSProcessInfo processInfo] environment]];
|
||||
|
||||
[[NSWorkspace sharedWorkspace]
|
||||
openApplicationAtURL:[NSURL fileURLWithPath:launchPath]
|
||||
configuration:config
|
||||
completionHandler:^(NSRunningApplication* aChild, NSError* aError) {
|
||||
if (aError) {
|
||||
NSLog(@"LaunchMacApp: Failed to run application. Error: %@",
|
||||
aError);
|
||||
}
|
||||
dispatch_semaphore_signal(semaphore);
|
||||
}];
|
||||
|
||||
// We use a semaphore to wait for the application to launch.
|
||||
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
|
||||
} @catch (NSException* e) {
|
||||
NSLog(@"%@: %@", e.name, e.reason);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace mozilla::MacLaunchHelper
|
||||
|
||||
void LaunchChildMac(int aArgc, char** aArgv, pid_t* aPid) {
|
||||
MacAutoreleasePool pool;
|
||||
|
@ -100,32 +26,20 @@ void LaunchChildMac(int aArgc, char** aArgv, pid_t* aPid) {
|
|||
for (int i = 1; i < aArgc; i++) {
|
||||
[arguments addObject:[NSString stringWithUTF8String:aArgv[i]]];
|
||||
}
|
||||
NSTask* task = [[NSTask alloc] init];
|
||||
[task setExecutableURL:[NSURL fileURLWithPath:launchPath]];
|
||||
[task setArguments:arguments];
|
||||
NSError* error = nil;
|
||||
[task launchAndReturnError:&error];
|
||||
if (!error && aPid) {
|
||||
*aPid = [task processIdentifier];
|
||||
[task waitUntilExit];
|
||||
NSTask* child = [NSTask launchedTaskWithLaunchPath:launchPath
|
||||
arguments:arguments];
|
||||
if (aPid) {
|
||||
*aPid = [child processIdentifier];
|
||||
// We used to use waitpid to wait for the process to terminate. This is
|
||||
// incompatible with NSTask and we wait for the process to exit here
|
||||
// instead.
|
||||
[child waitUntilExit];
|
||||
}
|
||||
[task release];
|
||||
} @catch (NSException* e) {
|
||||
NSLog(@"%@: %@", e.name, e.reason);
|
||||
}
|
||||
}
|
||||
|
||||
void LaunchMacApp(int aArgc, char** aArgv) {
|
||||
MacAutoreleasePool pool;
|
||||
|
||||
NSString* launchPath = [NSString stringWithUTF8String:aArgv[0]];
|
||||
NSMutableArray* arguments = [NSMutableArray arrayWithCapacity:aArgc - 1];
|
||||
for (int i = 1; i < aArgc; i++) {
|
||||
[arguments addObject:[NSString stringWithUTF8String:aArgv[i]]];
|
||||
}
|
||||
LaunchMacAppWithBundle(launchPath, arguments);
|
||||
}
|
||||
|
||||
bool InstallPrivilegedHelper() {
|
||||
AuthorizationRef authRef = NULL;
|
||||
OSStatus status = AuthorizationCreate(
|
||||
|
@ -156,9 +70,8 @@ bool InstallPrivilegedHelper() {
|
|||
CFErrorRef cfError;
|
||||
// This does all the work of verifying the helper tool against the
|
||||
// application and vice-versa. Once verification has passed, the embedded
|
||||
// launchd.plist is extracted and placed in /Library/LaunchDaemons and
|
||||
// then loaded. The executable is placed in
|
||||
// /Library/PrivilegedHelperTools.
|
||||
// launchd.plist is extracted and placed in /Library/LaunchDaemons and then
|
||||
// loaded. The executable is placed in /Library/PrivilegedHelperTools.
|
||||
result = (BOOL)SMJobBless(kSMDomainSystemLaunchd,
|
||||
(CFStringRef) @"org.mozilla.updater", authRef,
|
||||
&cfError);
|
||||
|
|
|
@ -13,9 +13,8 @@
|
|||
#include <sys/mount.h>
|
||||
#include <sys/param.h>
|
||||
|
||||
#include "MacLaunchHelper.h"
|
||||
#include "MacRunFromDmgUtils.h"
|
||||
#include "MacUtils.h"
|
||||
#include "MacLaunchHelper.h"
|
||||
|
||||
#include "mozilla/ErrorResult.h"
|
||||
#include "mozilla/glean/GleanMetrics.h"
|
||||
|
@ -38,9 +37,6 @@
|
|||
// https://developer.apple.com/documentation/iokit
|
||||
// https://developer.apple.com/library/archive/documentation/DeviceDrivers/Conceptual/IOKitFundamentals/
|
||||
|
||||
using namespace mozilla::MacUtils;
|
||||
using namespace mozilla::MacLaunchHelper;
|
||||
|
||||
namespace mozilla::MacRunFromDmgUtils {
|
||||
|
||||
/**
|
||||
|
@ -263,6 +259,35 @@ static void ShowInstallFailedDialog() {
|
|||
NS_OBJC_END_TRY_IGNORE_BLOCK;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper to launch macOS tasks via NSTask.
|
||||
*/
|
||||
static void LaunchTask(NSString* aPath, NSArray* aArguments) {
|
||||
NSTask* task = [[NSTask alloc] init];
|
||||
[task setExecutableURL:[NSURL fileURLWithPath:aPath]];
|
||||
if (aArguments) {
|
||||
[task setArguments:aArguments];
|
||||
}
|
||||
[task launchAndReturnError:nil];
|
||||
[task release];
|
||||
}
|
||||
|
||||
static void LaunchInstalledApp(NSString* aBundlePath) {
|
||||
LaunchTask([[NSBundle bundleWithPath:aBundlePath] executablePath], nil);
|
||||
}
|
||||
|
||||
static void RegisterAppWithLaunchServices(NSString* aBundlePath) {
|
||||
NSArray* arguments = @[ @"-f", aBundlePath ];
|
||||
LaunchTask(@"/System/Library/Frameworks/CoreServices.framework/Frameworks/"
|
||||
@"LaunchServices.framework/Support/lsregister",
|
||||
arguments);
|
||||
}
|
||||
|
||||
static void StripQuarantineBit(NSString* aBundlePath) {
|
||||
NSArray* arguments = @[ @"-d", @"com.apple.quarantine", aBundlePath ];
|
||||
LaunchTask(@"/usr/bin/xattr", arguments);
|
||||
}
|
||||
|
||||
#ifdef MOZ_UPDATER
|
||||
bool LaunchElevatedDmgInstall(NSString* aBundlePath, NSArray* aArguments) {
|
||||
NSTask* task = [[NSTask alloc] init];
|
||||
|
@ -289,6 +314,8 @@ static bool InstallFromPath(NSString* aBundlePath, NSString* aDestPath) {
|
|||
bool installSuccessful = false;
|
||||
NSFileManager* fileManager = [NSFileManager defaultManager];
|
||||
if ([fileManager copyItemAtPath:aBundlePath toPath:aDestPath error:nil]) {
|
||||
RegisterAppWithLaunchServices(aDestPath);
|
||||
StripQuarantineBit(aDestPath);
|
||||
installSuccessful = true;
|
||||
}
|
||||
|
||||
|
@ -457,7 +484,8 @@ bool MaybeInstallAndRelaunch() {
|
|||
// a more sophisticated user intentionally running from .dmg.
|
||||
if ([fileManager fileExistsAtPath:destPath]) {
|
||||
if (AskUserIfWeShouldLaunchExistingInstall()) {
|
||||
LaunchMacAppWithBundle(destPath);
|
||||
StripQuarantineBit(destPath);
|
||||
LaunchInstalledApp(destPath);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
@ -472,7 +500,7 @@ bool MaybeInstallAndRelaunch() {
|
|||
return false;
|
||||
}
|
||||
|
||||
LaunchMacAppWithBundle(destPath);
|
||||
LaunchInstalledApp(destPath);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -1,17 +0,0 @@
|
|||
/* -*- Mode: C++; tab-width: 2; 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/. */
|
||||
|
||||
#ifndef MacUtils_h_
|
||||
#define MacUtils_h_
|
||||
|
||||
#include <Cocoa/Cocoa.h>
|
||||
|
||||
namespace mozilla::MacUtils {
|
||||
|
||||
void LaunchTask(NSString* aPath, NSArray* aArguments);
|
||||
|
||||
} // namespace mozilla::MacUtils
|
||||
|
||||
#endif
|
|
@ -1,32 +0,0 @@
|
|||
/* -*- Mode: C++; tab-width: 4; 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 "MacAutoreleasePool.h"
|
||||
#include "MacUtils.h"
|
||||
|
||||
namespace mozilla::MacUtils {
|
||||
|
||||
/**
|
||||
* Helper to launch macOS tasks via NSTask and wait for the launched task to
|
||||
* terminate.
|
||||
*/
|
||||
void LaunchTask(NSString* aPath, NSArray* aArguments) {
|
||||
MacAutoreleasePool pool;
|
||||
|
||||
@try {
|
||||
NSTask* task = [[NSTask alloc] init];
|
||||
[task setExecutableURL:[NSURL fileURLWithPath:aPath]];
|
||||
if (aArguments) {
|
||||
[task setArguments:aArguments];
|
||||
}
|
||||
[task launchAndReturnError:nil];
|
||||
[task waitUntilExit];
|
||||
[task release];
|
||||
} @catch (NSException* e) {
|
||||
NSLog(@"%@: %@", e.name, e.reason);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace mozilla::MacUtils
|
|
@ -76,14 +76,12 @@ if CONFIG["MOZ_WIDGET_TOOLKIT"] == "windows":
|
|||
elif CONFIG["MOZ_WIDGET_TOOLKIT"] == "cocoa":
|
||||
EXPORTS.mozilla += [
|
||||
"MacRunFromDmgUtils.h",
|
||||
"MacUtils.h",
|
||||
]
|
||||
UNIFIED_SOURCES += [
|
||||
"MacApplicationDelegate.mm",
|
||||
"MacAutoreleasePool.mm",
|
||||
"MacLaunchHelper.mm",
|
||||
"MacRunFromDmgUtils.mm",
|
||||
"MacUtils.mm",
|
||||
"nsCommandLineServiceMac.mm",
|
||||
"nsNativeAppSupportCocoa.mm",
|
||||
"updaterfileutils_osx.mm",
|
||||
|
|
|
@ -2530,7 +2530,7 @@ nsresult LaunchChild(bool aBlankCommandLine, bool aTryExec) {
|
|||
# if defined(XP_MACOSX)
|
||||
InitializeMacApp();
|
||||
CommandLineServiceMac::SetupMacCommandLine(gRestartArgc, gRestartArgv, true);
|
||||
LaunchMacApp(gRestartArgc, gRestartArgv);
|
||||
LaunchChildMac(gRestartArgc, gRestartArgv);
|
||||
# else
|
||||
nsCOMPtr<nsIFile> lf;
|
||||
nsresult rv = XRE_GetBinaryPath(getter_AddRefs(lf));
|
||||
|
|
|
@ -42,42 +42,13 @@ void AddToCommandLine(const char* inArgText) {
|
|||
// memory.
|
||||
void SetupMacCommandLine(int& argc, char**& argv, bool forRestart) {
|
||||
sArgs = static_cast<char**>(malloc(kArgsGrowSize * sizeof(char*)));
|
||||
if (!sArgs) {
|
||||
return;
|
||||
}
|
||||
if (!sArgs) return;
|
||||
sArgsAllocated = kArgsGrowSize;
|
||||
sArgs[0] = nullptr;
|
||||
sArgsUsed = 0;
|
||||
|
||||
NSString* path = [NSString stringWithUTF8String:argv[0]];
|
||||
// We adjust the path to point to the .app bundle, rather than the executable
|
||||
// itself, to allow for the use of the NSWorkspace API for launching and
|
||||
// relaunching the application. We intentionally exclude the
|
||||
// org.mozilla.updater binary because we are experiencing NSCocoaErrors of
|
||||
// type `kLSUnknownErr` when trying to launch the updater.app with the
|
||||
// NSWorkspace API, at least on macOS 10.15. The updater is launched using
|
||||
// NSTask instead. We do not experience these NSCocoaErrors on more modern
|
||||
// versions of macOS and we may be able to switch to the NSWorkspace API once
|
||||
// we no longer support the older versions of macOS where these errors occur.
|
||||
// See bug 1911178.
|
||||
if (![path hasSuffix:@"org.mozilla.updater"] && ![path hasSuffix:@".app"]) {
|
||||
// Ensure that the path in the first argument points to the .app bundle.
|
||||
// This strips three last path components, for example:
|
||||
//
|
||||
// Firefox.app/Contents/MacOS/firefox -> Firefox.app
|
||||
//
|
||||
path = [[[path stringByDeletingLastPathComponent]
|
||||
stringByDeletingLastPathComponent] stringByDeletingLastPathComponent];
|
||||
}
|
||||
if (![path hasSuffix:@"org.mozilla.updater"] && ![path hasSuffix:@".app"]) {
|
||||
// We were unable to obtain the path to the .app bundle and are unable to
|
||||
// build a valid command line.
|
||||
return;
|
||||
}
|
||||
AddToCommandLine(path.UTF8String);
|
||||
|
||||
// Copy the rest of the args, stripping anything we don't want.
|
||||
for (int arg = 1; arg < argc; arg++) {
|
||||
// Copy args, stripping anything we don't want.
|
||||
for (int arg = 0; arg < argc; arg++) {
|
||||
char* flag = argv[arg];
|
||||
// Don't pass on the psn (Process Serial Number) flag from the OS, or
|
||||
// the "-foreground" flag since it will be set below if necessary.
|
||||
|
|
|
@ -362,9 +362,7 @@ static void ApplyUpdate(nsIFile* greDir, nsIFile* updateDir, nsIFile* appDir,
|
|||
|
||||
// appFilePath and workingDirPath are only used when the application will be
|
||||
// restarted.
|
||||
#ifndef XP_MACOSX
|
||||
nsAutoCString appFilePath;
|
||||
#endif
|
||||
nsAutoCString workingDirPath;
|
||||
if (restart) {
|
||||
// Get the path to the current working directory.
|
||||
|
@ -374,9 +372,7 @@ static void ApplyUpdate(nsIFile* greDir, nsIFile* updateDir, nsIFile* appDir,
|
|||
}
|
||||
|
||||
// Get the application file path used by the updater to restart the
|
||||
// application after the update has finished. Note that macOS uses the
|
||||
// path to the application bundle, i.e. installDirPath, to relaunch the
|
||||
// application.
|
||||
// application after the update has finished.
|
||||
nsCOMPtr<nsIFile> appFile;
|
||||
XRE_GetBinaryPath(getter_AddRefs(appFile));
|
||||
if (!appFile) {
|
||||
|
@ -390,7 +386,7 @@ static void ApplyUpdate(nsIFile* greDir, nsIFile* updateDir, nsIFile* appDir,
|
|||
return;
|
||||
}
|
||||
CopyUTF16toUTF8(appFilePathW, appFilePath);
|
||||
#elif !defined(XP_MACOSX)
|
||||
#else
|
||||
rv = appFile->GetNativePath(appFilePath);
|
||||
if (NS_FAILED(rv)) {
|
||||
return;
|
||||
|
@ -529,11 +525,7 @@ static void ApplyUpdate(nsIFile* greDir, nsIFile* updateDir, nsIFile* appDir,
|
|||
argv[4] = (char*)pid.get();
|
||||
if (restart && appArgc) {
|
||||
argv[5] = (char*)workingDirPath.get();
|
||||
#if defined(XP_MACOSX)
|
||||
argv[6] = (char*)installDirPath.get();
|
||||
#else
|
||||
argv[6] = (char*)appFilePath.get();
|
||||
#endif
|
||||
for (int i = 1; i < appArgc; ++i) {
|
||||
argv[6 + i] = appArgv[i];
|
||||
}
|
||||
|
|
Загрузка…
Ссылка в новой задаче