author | Dave Townsend <dtownsend@oxymoronical.com> |
Mon, 14 Jan 2019 17:27:34 +0000 | |
changeset 453758 | 394b490d3e2deb8c31c71214320ebfc16a8db008 |
parent 453757 | 1b5c466a1c669657e323b52592edd468015e42e6 |
child 453759 | 8844751cb1698c69c2fead027fb31e791bc0ab00 |
push id | 35372 |
push user | cbrindusan@mozilla.com |
push date | Mon, 14 Jan 2019 21:49:33 +0000 |
treeherder | mozilla-central@50b3268954b1 [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | froydnj |
bugs | 1518587 |
milestone | 66.0a1 |
first release with | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
--- a/browser/components/migration/tests/marionette/test_refresh_firefox.py +++ b/browser/components/migration/tests/marionette/test_refresh_firefox.py @@ -536,17 +536,16 @@ class TestFirefoxRefresh(MarionetteTestC prefsToKeep.push("datareporting.policy.dataSubmissionPolicyBypassNotification"); let prefObj = {}; for (let pref of prefsToKeep) { prefObj[pref] = global.Preferences.get(pref); } env.set("MOZ_MARIONETTE_PREF_STATE_ACROSS_RESTARTS", JSON.stringify(prefObj)); env.set("MOZ_RESET_PROFILE_RESTART", "1"); env.set("XRE_PROFILE_PATH", arguments[0]); - env.set("XRE_PROFILE_NAME", profileName); """, script_args=(self.marionette.instance.profile.profile, profileName,)) profileLeafName = os.path.basename(os.path.normpath( self.marionette.instance.profile.profile)) # Now restart the browser to get it reset: self.marionette.restart(clean=False, in_app=True) self.setUpScriptData()
--- a/toolkit/profile/moz.build +++ b/toolkit/profile/moz.build @@ -1,16 +1,18 @@ # -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- # vim: set filetype=python: # 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/. MOCHITEST_CHROME_MANIFESTS += ['test/chrome.ini'] +XPCSHELL_TESTS_MANIFESTS += ['xpcshell/xpcshell.ini'] + if CONFIG['ENABLE_TESTS']: DIRS += ['gtest'] XPIDL_SOURCES += [ 'nsIProfileMigrator.idl', 'nsIProfileUnlocker.idl', 'nsIToolkitProfile.idl', 'nsIToolkitProfileService.idl',
--- a/toolkit/profile/nsIToolkitProfileService.idl +++ b/toolkit/profile/nsIToolkitProfileService.idl @@ -5,17 +5,17 @@ #include "nsISupports.idl" interface nsISimpleEnumerator; interface nsIFile; interface nsIToolkitProfile; interface nsIProfileLock; -[scriptable, uuid(1947899b-f369-48fa-89da-f7c37bb1e6bc)] +[scriptable, builtinclass, uuid(1947899b-f369-48fa-89da-f7c37bb1e6bc)] interface nsIToolkitProfileService : nsISupports { attribute boolean startWithLastProfile; readonly attribute nsISimpleEnumerator /*nsIToolkitProfile*/ profiles; /** * The currently selected profile (the one used or about to be used by the @@ -33,16 +33,38 @@ interface nsIToolkitProfileService : nsI * default profile (which it creates if it doesn't exist), unless a special * empty file named "ignore-dev-edition-profile" is present next to * profiles.ini. In that case Developer Edition behaves the same as any * other build of Firefox. */ attribute nsIToolkitProfile defaultProfile; /** + * Selects or creates a profile to use based on the profiles database, any + * environment variables and any command line arguments. Will not create + * a profile if aIsResetting is true. The profile is selected based on this + * order of preference: + * * Environment variables (set when restarting the application). + * * --profile command line argument. + * * --createprofile command line argument (this also causes the app to exit). + * * -p command line argument. + * * A new profile created if this is the first run of the application. + * * The default profile. + * aRootDir and aLocalDir are set to the data and local directories for the + * profile data. If a profile from the database was selected it will be + * returned in aProfile. + * This returns true if a new profile was created. + * This method is primarily for testing. It can be called only once. + */ + bool selectStartupProfile(in Array<ACString> aArgv, + in boolean aIsResetting, + out nsIFile aRootDir, out nsIFile aLocalDir, + out nsIToolkitProfile aProfile); + + /** * Get a profile by name. This is mainly for use by the -P * commandline flag. * * @param aName The profile name to find. */ nsIToolkitProfile getProfileByName(in AUTF8String aName); /**
--- a/toolkit/profile/nsToolkitProfileService.cpp +++ b/toolkit/profile/nsToolkitProfileService.cpp @@ -5,31 +5,28 @@ #include "mozilla/ArrayUtils.h" #include "mozilla/UniquePtr.h" #include <stdio.h> #include <stdlib.h> #include <prprf.h> #include <prtime.h> -#include "nsProfileLock.h" #ifdef XP_WIN #include <windows.h> #include <shlobj.h> #endif #ifdef XP_UNIX #include <unistd.h> #endif -#include "nsIToolkitProfileService.h" -#include "nsIToolkitProfile.h" -#include "nsIFactory.h" +#include "nsToolkitProfileService.h" +#include "CmdLineAndEnvUtils.h" #include "nsIFile.h" -#include "nsSimpleEnumerator.h" #ifdef XP_MACOSX #include <CoreFoundation/CoreFoundation.h> #include "nsILocalFileMac.h" #endif #include "nsAppDirectoryServiceDefs.h" #include "nsNetCID.h" @@ -43,111 +40,17 @@ #include "nsString.h" #include "nsReadableUtils.h" #include "nsNativeCharsetUtils.h" #include "mozilla/Attributes.h" #include "mozilla/Sprintf.h" using namespace mozilla; -class nsToolkitProfile final : public nsIToolkitProfile { - public: - NS_DECL_ISUPPORTS - NS_DECL_NSITOOLKITPROFILE - - friend class nsToolkitProfileService; - RefPtr<nsToolkitProfile> mNext; - nsToolkitProfile* mPrev; - - private: - ~nsToolkitProfile() {} - - nsToolkitProfile(const nsACString& aName, nsIFile* aRootDir, - nsIFile* aLocalDir, nsToolkitProfile* aPrev); - - nsresult RemoveInternal(bool aRemoveFiles, bool aInBackground); - - friend class nsToolkitProfileLock; - - nsCString mName; - nsCOMPtr<nsIFile> mRootDir; - nsCOMPtr<nsIFile> mLocalDir; - nsIProfileLock* mLock; -}; - -class nsToolkitProfileLock final : public nsIProfileLock { - public: - NS_DECL_ISUPPORTS - NS_DECL_NSIPROFILELOCK - - nsresult Init(nsToolkitProfile* aProfile, nsIProfileUnlocker** aUnlocker); - nsresult Init(nsIFile* aDirectory, nsIFile* aLocalDirectory, - nsIProfileUnlocker** aUnlocker); - - nsToolkitProfileLock() {} - - private: - ~nsToolkitProfileLock(); - - RefPtr<nsToolkitProfile> mProfile; - nsCOMPtr<nsIFile> mDirectory; - nsCOMPtr<nsIFile> mLocalDirectory; - - nsProfileLock mLock; -}; - -class nsToolkitProfileFactory final : public nsIFactory { - ~nsToolkitProfileFactory() {} - - public: - NS_DECL_ISUPPORTS - NS_DECL_NSIFACTORY -}; - -class nsToolkitProfileService final : public nsIToolkitProfileService { - public: - NS_DECL_ISUPPORTS - NS_DECL_NSITOOLKITPROFILESERVICE - - private: - friend class nsToolkitProfile; - friend class nsToolkitProfileFactory; - friend nsresult NS_NewToolkitProfileService(nsIToolkitProfileService**); - - nsToolkitProfileService() : mStartWithLast(true) { gService = this; } - ~nsToolkitProfileService() { gService = nullptr; } - - nsresult Init(); - - nsresult CreateTimesInternal(nsIFile* profileDir); - - RefPtr<nsToolkitProfile> mFirst; - nsCOMPtr<nsIToolkitProfile> mChosen; - nsCOMPtr<nsIToolkitProfile> mDefault; - nsCOMPtr<nsIFile> mAppData; - nsCOMPtr<nsIFile> mTempData; - nsCOMPtr<nsIFile> mListFile; - bool mStartWithLast; - - static nsToolkitProfileService* gService; - - class ProfileEnumerator final : public nsSimpleEnumerator { - public: - NS_DECL_NSISIMPLEENUMERATOR - - const nsID& DefaultInterface() override { - return NS_GET_IID(nsIToolkitProfile); - } - - explicit ProfileEnumerator(nsToolkitProfile* first) { mCurrent = first; } - - private: - RefPtr<nsToolkitProfile> mCurrent; - }; -}; +#define DEV_EDITION_NAME "dev-edition-default" nsToolkitProfile::nsToolkitProfile(const nsACString& aName, nsIFile* aRootDir, nsIFile* aLocalDir, nsToolkitProfile* aPrev) : mPrev(aPrev), mName(aName), mRootDir(aRootDir), mLocalDir(aLocalDir), mLock(nullptr) { @@ -355,16 +258,23 @@ nsToolkitProfileLock::~nsToolkitProfileL Unlock(); } } nsToolkitProfileService* nsToolkitProfileService::gService = nullptr; NS_IMPL_ISUPPORTS(nsToolkitProfileService, nsIToolkitProfileService) +nsToolkitProfileService::nsToolkitProfileService() + : mStartupProfileSelected(false), mStartWithLast(true), mIsFirstRun(true) { + gService = this; +} + +nsToolkitProfileService::~nsToolkitProfileService() { gService = nullptr; } + nsresult nsToolkitProfileService::Init() { NS_ASSERTION(gDirServiceProvider, "No dirserviceprovider!"); nsresult rv; rv = nsXREDirProvider::GetUserAppDataDirectory(getter_AddRefs(mAppData)); NS_ENSURE_SUCCESS(rv, rv); rv = nsXREDirProvider::GetUserLocalDataDirectory(getter_AddRefs(mTempData)); @@ -409,18 +319,20 @@ nsresult nsToolkitProfileService::Init() NS_LITERAL_CSTRING("ignore-dev-edition-profile")); if (NS_FAILED(rv)) return rv; bool shouldIgnoreSeparateProfile; rv = ignoreSeparateProfile->Exists(&shouldIgnoreSeparateProfile); if (NS_FAILED(rv)) return rv; #endif + nsCOMPtr<nsIToolkitProfile> autoSelectProfile; + + unsigned int nonDevEditionProfiles = 0; unsigned int c = 0; - bool foundAuroraDefault = false; for (c = 0; true; ++c) { nsAutoCString profileID("Profile"); profileID.AppendInt(c); rv = parser.GetString(profileID.get(), "IsRelative", buffer); if (NS_FAILED(rv)) break; bool isRelative = buffer.EqualsLiteral("1"); @@ -463,51 +375,54 @@ nsresult nsToolkitProfileService::Init() localDir = rootDir; } currentProfile = new nsToolkitProfile(name, rootDir, localDir, currentProfile); NS_ENSURE_TRUE(currentProfile, NS_ERROR_OUT_OF_MEMORY); rv = parser.GetString(profileID.get(), "Default", buffer); - if (NS_SUCCEEDED(rv) && buffer.EqualsLiteral("1") && !foundAuroraDefault) { - mChosen = currentProfile; - this->SetDefaultProfile(currentProfile); + if (NS_SUCCEEDED(rv) && buffer.EqualsLiteral("1")) { + mDefault = currentProfile; } + + if (name.EqualsLiteral(DEV_EDITION_NAME)) { #ifdef MOZ_DEV_EDITION - // Use the dev-edition-default profile if this is an Aurora build and - // ignore-dev-edition-profile is not present. - if (name.EqualsLiteral("dev-edition-default") && - !shouldIgnoreSeparateProfile) { - mChosen = currentProfile; - foundAuroraDefault = true; + // Use the dev-edition-default profile if this is an Aurora build and + // ignore-dev-edition-profile is not present. + if (!shouldIgnoreSeparateProfile) { + mChosen = currentProfile; + } +#endif + } else { + nonDevEditionProfiles++; + autoSelectProfile = currentProfile; } -#endif } + // If there is only one non-dev-edition profile then mark it as the default. + if (!mDefault && nonDevEditionProfiles == 1) { + mDefault = autoSelectProfile; + } + + // Normally having no non-dev-edition builds suggests this is the first run. + mIsFirstRun = nonDevEditionProfiles == 0; + #ifdef MOZ_DEV_EDITION - if (!foundAuroraDefault && !shouldIgnoreSeparateProfile) { - // If a single profile exists, it may not be already marked as default. - // Do it now to avoid problems when we create the dev-edition-default - // profile. - if (!mChosen && mFirst && !mFirst->mNext) this->SetDefaultProfile(mFirst); - - // Create a default profile for aurora, if none was found. - nsCOMPtr<nsIToolkitProfile> profile; - rv = CreateProfile(nullptr, NS_LITERAL_CSTRING("dev-edition-default"), - getter_AddRefs(profile)); - if (NS_FAILED(rv)) return rv; - mChosen = profile; - rv = Flush(); - if (NS_FAILED(rv)) return rv; + if (!shouldIgnoreSeparateProfile) { + // Except when using the separate dev-edition profile, in which case not + // finding it means this is a first run. + mIsFirstRun = !mChosen; + } else { + mChosen = mDefault; } +#else + mChosen = mDefault; #endif - if (!mChosen && mFirst && !mFirst->mNext) // only one profile - mChosen = mFirst; return NS_OK; } NS_IMETHODIMP nsToolkitProfileService::SetStartWithLastProfile(bool aValue) { if (mStartWithLast != aValue) { mStartWithLast = aValue; } @@ -575,31 +490,315 @@ nsToolkitProfileService::GetDefaultProfi NS_IMETHODIMP nsToolkitProfileService::SetDefaultProfile(nsIToolkitProfile* aProfile) { if (mDefault != aProfile) { mDefault = aProfile; } return NS_OK; } +/** + * An implementation of SelectStartupProfile callable from JavaScript via XPCOM. + * See nsIToolkitProfileService.idl. + */ +NS_IMETHODIMP +nsToolkitProfileService::SelectStartupProfile( + const nsTArray<nsCString>& aArgv, bool aIsResetting, nsIFile** aRootDir, + nsIFile** aLocalDir, nsIToolkitProfile** aProfile, bool* aDidCreate) { + int argc = aArgv.Length(); + // Our command line handling expects argv to be null-terminated so construct + // an appropriate array. + auto argv = MakeUnique<char*[]>(argc + 1); + // Also, our command line handling removes things from the array without + // freeing them so keep track of what we've created separately. + auto allocated = MakeUnique<UniqueFreePtr<char>[]>(argc); + + for (int i = 0; i < argc; i++) { + allocated[i].reset(ToNewCString(aArgv[i])); + argv[i] = allocated[i].get(); + } + argv[argc] = nullptr; + + nsresult rv = SelectStartupProfile(&argc, argv.get(), aIsResetting, aRootDir, + aLocalDir, aProfile, aDidCreate); + + return rv; +} + +/** + * Selects or creates a profile to use based on the profiles database, any + * environment variables and any command line arguments. Will not create + * a profile if aIsResetting is true. The profile is selected based on this + * order of preference: + * * Environment variables (set when restarting the application). + * * --profile command line argument. + * * --createprofile command line argument (this also causes the app to exit). + * * -p command line argument. + * * A new profile created if this is the first run of the application. + * * The default profile. + * aRootDir and aLocalDir are set to the data and local directories for the + * profile data. If a profile from the database was selected it will be + * returned in aProfile. + * aDidCreate will be set to true if a new profile was created. + * This function should be called once at startup and will fail if called again. + * aArgv should be an array of aArgc + 1 strings, the last element being null. + * Both aArgv and aArgc will be mutated. + */ +nsresult nsToolkitProfileService::SelectStartupProfile( + int* aArgc, char* aArgv[], bool aIsResetting, nsIFile** aRootDir, + nsIFile** aLocalDir, nsIToolkitProfile** aProfile, bool* aDidCreate) { + if (mStartupProfileSelected) { + return NS_ERROR_ALREADY_INITIALIZED; + } + + mStartupProfileSelected = true; + *aDidCreate = false; + + nsresult rv; + const char* arg; + nsCOMPtr<nsIToolkitProfile> profile; + + // Use the profile specified in the environment variables (generally from an + // app initiated restart). + nsCOMPtr<nsIFile> lf = GetFileFromEnv("XRE_PROFILE_PATH"); + if (lf) { + nsCOMPtr<nsIFile> localDir = GetFileFromEnv("XRE_PROFILE_LOCAL_PATH"); + if (!localDir) { + localDir = lf; + } + + // Clear out flags that we handled (or should have handled!) last startup. + const char* dummy; + CheckArg(*aArgc, aArgv, "p", &dummy); + CheckArg(*aArgc, aArgv, "profile", &dummy); + CheckArg(*aArgc, aArgv, "profilemanager"); + + GetProfileByDir(lf, localDir, aProfile); + lf.forget(aRootDir); + localDir.forget(aLocalDir); + return NS_OK; + } + + // Check the -profile command line argument. It accepts a single argument that + // gives the path to use for the profile. + ArgResult ar = CheckArg(*aArgc, aArgv, "profile", &arg, + CheckArgFlag::CheckOSInt | CheckArgFlag::RemoveArg); + if (ar == ARG_BAD) { + PR_fprintf(PR_STDERR, "Error: argument --profile requires a path\n"); + return NS_ERROR_FAILURE; + } + if (ar) { + nsCOMPtr<nsIFile> lf; + rv = XRE_GetFileFromPath(arg, getter_AddRefs(lf)); + NS_ENSURE_SUCCESS(rv, rv); + + // Make sure that the profile path exists and it's a directory. + bool exists; + rv = lf->Exists(&exists); + NS_ENSURE_SUCCESS(rv, rv); + if (!exists) { + rv = lf->Create(nsIFile::DIRECTORY_TYPE, 0700); + NS_ENSURE_SUCCESS(rv, rv); + } else { + bool isDir; + rv = lf->IsDirectory(&isDir); + NS_ENSURE_SUCCESS(rv, rv); + if (!isDir) { + PR_fprintf( + PR_STDERR, + "Error: argument --profile requires a path to a directory\n"); + return NS_ERROR_FAILURE; + } + } + + // If a profile path is specified directly on the command line, then + // assume that the temp directory is the same as the given directory. + GetProfileByDir(lf, lf, aProfile); + NS_ADDREF(*aRootDir = lf); + lf.forget(aLocalDir); + return NS_OK; + } + + // Check the -createprofile command line argument. It accepts a single + // argument that is either the name for the new profile or the name followed + // by the path to use. + ar = CheckArg(*aArgc, aArgv, "createprofile", &arg, + CheckArgFlag::CheckOSInt | CheckArgFlag::RemoveArg); + if (ar == ARG_BAD) { + PR_fprintf(PR_STDERR, + "Error: argument --createprofile requires a profile name\n"); + return NS_ERROR_FAILURE; + } + if (ar) { + const char* delim = strchr(arg, ' '); + if (delim) { + nsCOMPtr<nsIFile> lf; + rv = NS_NewNativeLocalFile(nsDependentCString(delim + 1), true, + getter_AddRefs(lf)); + if (NS_FAILED(rv)) { + PR_fprintf(PR_STDERR, "Error: profile path not valid.\n"); + return rv; + } + + // As with --profile, assume that the given path will be used for the + // main profile directory. + rv = CreateProfile(lf, nsDependentCSubstring(arg, delim), + getter_AddRefs(profile)); + } else { + rv = CreateProfile(nullptr, nsDependentCString(arg), + getter_AddRefs(profile)); + } + // Some pathological arguments can make it this far + if (NS_FAILED(rv)) { + PR_fprintf(PR_STDERR, "Error creating profile.\n"); + return rv; + } + rv = NS_ERROR_ABORT; + Flush(); + + return rv; + } + + // Check the -p command line argument. It either accepts a profile name and + // uses that named profile or without a name it opens the profile manager. + ar = CheckArg(*aArgc, aArgv, "p", &arg); + if (ar == ARG_BAD) { + ar = CheckArg(*aArgc, aArgv, "osint"); + if (ar == ARG_FOUND) { + PR_fprintf( + PR_STDERR, + "Error: argument -p is invalid when argument --osint is specified\n"); + return NS_ERROR_FAILURE; + } + + return NS_ERROR_SHOW_PROFILE_MANAGER; + } + if (ar) { + ar = CheckArg(*aArgc, aArgv, "osint"); + if (ar == ARG_FOUND) { + PR_fprintf( + PR_STDERR, + "Error: argument -p is invalid when argument --osint is specified\n"); + return NS_ERROR_FAILURE; + } + + rv = GetProfileByName(nsDependentCString(arg), getter_AddRefs(profile)); + if (NS_SUCCEEDED(rv)) { + profile->GetRootDir(aRootDir); + profile->GetLocalDir(aLocalDir); + profile.forget(aProfile); + return NS_OK; + } + + return NS_ERROR_SHOW_PROFILE_MANAGER; + } + + ar = CheckArg(*aArgc, aArgv, "profilemanager", (const char**)nullptr, + CheckArgFlag::CheckOSInt | CheckArgFlag::RemoveArg); + if (ar == ARG_BAD) { + PR_fprintf(PR_STDERR, + "Error: argument --profilemanager is invalid when argument " + "--osint is specified\n"); + return NS_ERROR_FAILURE; + } + if (ar == ARG_FOUND) { + return NS_ERROR_SHOW_PROFILE_MANAGER; + } + + // If this is a first run then create a new profile. + if (mIsFirstRun) { + if (aIsResetting) { + // We don't want to create a fresh profile when we're attempting a + // profile reset so just bail out here, the calling code will handle it. + *aProfile = nullptr; + return NS_OK; + } + + // create a default profile + nsresult rv = CreateProfile(nullptr, // choose a default dir for us +#ifdef MOZ_DEV_EDITION + NS_LITERAL_CSTRING(DEV_EDITION_NAME), +#else + NS_LITERAL_CSTRING("default"), +#endif + getter_AddRefs(mChosen)); + if (NS_SUCCEEDED(rv)) { +#ifndef MOZ_DEV_EDITION + SetDefaultProfile(mChosen); +#endif + Flush(); + + mChosen->GetRootDir(aRootDir); + mChosen->GetLocalDir(aLocalDir); + NS_ADDREF(*aProfile = mChosen); + + *aDidCreate = true; + return NS_OK; + } + } + + // There are multiple profiles available. + if (!mStartWithLast) { + return NS_ERROR_SHOW_PROFILE_MANAGER; + } + + // GetSelectedProfile will auto-select the only profile if there's just one + GetSelectedProfile(getter_AddRefs(profile)); + + // None of the profiles was marked as default (generally only happens if the + // user modifies profiles.ini manually). Let the user choose. + if (!profile) { + return NS_ERROR_SHOW_PROFILE_MANAGER; + } + + // Use the selected profile. + profile->GetRootDir(aRootDir); + profile->GetLocalDir(aLocalDir); + profile.forget(aProfile); + + return NS_OK; +} + NS_IMETHODIMP nsToolkitProfileService::GetProfileByName(const nsACString& aName, nsIToolkitProfile** aResult) { nsToolkitProfile* curP = mFirst; while (curP) { if (curP->mName.Equals(aName)) { NS_ADDREF(*aResult = curP); return NS_OK; } curP = curP->mNext; } return NS_ERROR_FAILURE; } +/** + * Finds a profile from the database that uses the given root and local + * directories. + */ +void nsToolkitProfileService::GetProfileByDir(nsIFile* aRootDir, + nsIFile* aLocalDir, + nsIToolkitProfile** aResult) { + nsToolkitProfile* curP = mFirst; + while (curP) { + bool equal; + nsresult rv = curP->mRootDir->Equals(aRootDir, &equal); + if (NS_SUCCEEDED(rv) && equal) { + rv = curP->mLocalDir->Equals(aLocalDir, &equal); + if (NS_SUCCEEDED(rv) && equal) { + NS_ADDREF(*aResult = curP); + return; + } + } + curP = curP->mNext; + } +} + nsresult NS_LockProfilePath(nsIFile* aPath, nsIFile* aTempPath, nsIProfileUnlocker** aUnlocker, nsIProfileLock** aResult) { RefPtr<nsToolkitProfileLock> lock = new nsToolkitProfileLock(); if (!lock) return NS_ERROR_OUT_OF_MEMORY; nsresult rv = lock->Init(aPath, aTempPath, aUnlocker); if (NS_FAILED(rv)) return rv;
new file mode 100644 --- /dev/null +++ b/toolkit/profile/nsToolkitProfileService.h @@ -0,0 +1,121 @@ + +/* -*- 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/. */ + +#ifndef nsToolkitProfileService_h +#define nsToolkitProfileService_h + +#include "nsIToolkitProfileService.h" +#include "nsIToolkitProfile.h" +#include "nsIFactory.h" +#include "nsSimpleEnumerator.h" +#include "nsProfileLock.h" + +class nsToolkitProfile final : public nsIToolkitProfile { + public: + NS_DECL_ISUPPORTS + NS_DECL_NSITOOLKITPROFILE + + friend class nsToolkitProfileService; + RefPtr<nsToolkitProfile> mNext; + nsToolkitProfile* mPrev; + + private: + ~nsToolkitProfile() = default; + + nsToolkitProfile(const nsACString& aName, nsIFile* aRootDir, + nsIFile* aLocalDir, nsToolkitProfile* aPrev); + + nsresult RemoveInternal(bool aRemoveFiles, bool aInBackground); + + friend class nsToolkitProfileLock; + + nsCString mName; + nsCOMPtr<nsIFile> mRootDir; + nsCOMPtr<nsIFile> mLocalDir; + nsIProfileLock* mLock; +}; + +class nsToolkitProfileLock final : public nsIProfileLock { + public: + NS_DECL_ISUPPORTS + NS_DECL_NSIPROFILELOCK + + nsresult Init(nsToolkitProfile* aProfile, nsIProfileUnlocker** aUnlocker); + nsresult Init(nsIFile* aDirectory, nsIFile* aLocalDirectory, + nsIProfileUnlocker** aUnlocker); + + nsToolkitProfileLock() = default; + + private: + ~nsToolkitProfileLock(); + + RefPtr<nsToolkitProfile> mProfile; + nsCOMPtr<nsIFile> mDirectory; + nsCOMPtr<nsIFile> mLocalDirectory; + + nsProfileLock mLock; +}; + +class nsToolkitProfileFactory final : public nsIFactory { + ~nsToolkitProfileFactory() = default; + + public: + NS_DECL_ISUPPORTS + NS_DECL_NSIFACTORY +}; + +class nsToolkitProfileService final : public nsIToolkitProfileService { + public: + NS_DECL_ISUPPORTS + NS_DECL_NSITOOLKITPROFILESERVICE + + nsresult SelectStartupProfile(int* aArgc, char* aArgv[], bool aIsResetting, + nsIFile** aRootDir, nsIFile** aLocalDir, + nsIToolkitProfile** aProfile, bool* aDidCreate); + + private: + friend class nsToolkitProfile; + friend class nsToolkitProfileFactory; + friend nsresult NS_NewToolkitProfileService(nsIToolkitProfileService**); + + nsToolkitProfileService(); + ~nsToolkitProfileService(); + + nsresult Init(); + + nsresult CreateTimesInternal(nsIFile* profileDir); + void GetProfileByDir(nsIFile* aRootDir, nsIFile* aLocalDir, + nsIToolkitProfile** aResult); + + bool mStartupProfileSelected; + RefPtr<nsToolkitProfile> mFirst; + nsCOMPtr<nsIToolkitProfile> mChosen; + nsCOMPtr<nsIToolkitProfile> mDefault; + nsCOMPtr<nsIFile> mAppData; + nsCOMPtr<nsIFile> mTempData; + nsCOMPtr<nsIFile> mListFile; + bool mStartWithLast; + bool mIsFirstRun; + + static nsToolkitProfileService* gService; + + class ProfileEnumerator final : public nsSimpleEnumerator { + public: + NS_DECL_NSISIMPLEENUMERATOR + + const nsID& DefaultInterface() override { + return NS_GET_IID(nsIToolkitProfile); + } + + explicit ProfileEnumerator(nsToolkitProfile* first) { mCurrent = first; } + + private: + RefPtr<nsToolkitProfile> mCurrent; + }; +}; + +#endif
new file mode 100644 --- /dev/null +++ b/toolkit/profile/xpcshell/.eslintrc.js @@ -0,0 +1,7 @@ +"use strict"; + +module.exports = { + "extends": [ + "plugin:mozilla/xpcshell-test" + ] +};
new file mode 100644 --- /dev/null +++ b/toolkit/profile/xpcshell/head.js @@ -0,0 +1,221 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm", {}); +const { NetUtil } = ChromeUtils.import("resource://gre/modules/NetUtil.jsm", {}); +const { FileUtils } = ChromeUtils.import("resource://gre/modules/FileUtils.jsm", {}); +const { OS } = ChromeUtils.import("resource://gre/modules/osfile.jsm", {}); +const { AppConstants } = ChromeUtils.import("resource://gre/modules/AppConstants.jsm", {}); + +const NS_ERROR_START_PROFILE_MANAGER = 0x805800c9; + +let gProfD = do_get_profile(); +let gDataHome = gProfD.clone(); +gDataHome.append("data"); +gDataHome.createUnique(Ci.nsIFile.DIRECTORY_TYPE, 0o755); +let gDataHomeLocal = gProfD.clone(); +gDataHomeLocal.append("local"); +gDataHomeLocal.createUnique(Ci.nsIFile.DIRECTORY_TYPE, 0o755); + +let xreDirProvider = Cc["@mozilla.org/xre/directory-provider;1"]. + getService(Ci.nsIXREDirProvider); +xreDirProvider.setUserDataDirectory(gDataHome, false); +xreDirProvider.setUserDataDirectory(gDataHomeLocal, true); + +function getProfileService() { + return Cc["@mozilla.org/toolkit/profile-service;1"]. + getService(Ci.nsIToolkitProfileService); +} + +let PROFILE_DEFAULT = "default"; +if (AppConstants.MOZ_DEV_EDITION) { + PROFILE_DEFAULT = "dev-edition-default"; +} + +/** + * Creates a random profile path for use. + */ +function makeRandomProfileDir(name) { + let file = gDataHome.clone(); + file.append(name); + file.createUnique(Ci.nsIFile.DIRECTORY_TYPE, 0o755); + return file; +} + +/** + * A wrapper around nsIToolkitProfileService.selectStartupProfile to make it + * a bit nicer to use from JS. + */ +function selectStartupProfile(args = [], isResetting = false) { + let rootDir = {}; + let localDir = {}; + let profile = {}; + let didCreate = getProfileService().selectStartupProfile(["xpcshell", ...args], isResetting, + rootDir, localDir, profile); + + if (profile.value) { + Assert.ok(rootDir.value.equals(profile.value.rootDir), "Should have matched the root dir."); + Assert.ok(localDir.value.equals(profile.value.localDir), "Should have matched the local dir."); + } + + return { + rootDir: rootDir.value, + localDir: localDir.value, + profile: profile.value, + didCreate, + }; +} + +function testStartsProfileManager(args = [], isResetting = false) { + try { + selectStartupProfile(args, isResetting); + Assert.ok(false, "Should have started the profile manager"); + } catch (e) { + Assert.equal(e.result, NS_ERROR_START_PROFILE_MANAGER, "Should have started the profile manager"); + } +} + +function safeGet(ini, section, key) { + try { + return ini.getString(section, key); + } catch (e) { + return null; + } +} + +/** + * Writes a profiles.ini based on the passed profile data. + * profileData should contain two properties, options and profiles. + * options contains a single property, startWithLastProfile. + * profiles is an array of profiles each containing name, path and default + * properties. + */ +function writeProfilesIni(profileData) { + let target = gDataHome.clone(); + target.append("profiles.ini"); + + let factory = Cc["@mozilla.org/xpcom/ini-parser-factory;1"]. + getService(Ci.nsIINIParserFactory); + let ini = factory.createINIParser().QueryInterface(Ci.nsIINIParserWriter); + + const { options = {}, profiles = [] } = profileData; + + let { startWithLastProfile = true } = options; + ini.setString("General", "StartWithLastProfile", startWithLastProfile ? "1" : "0"); + + for (let i = 0; i < profiles.length; i++) { + let profile = profiles[i]; + let section = `Profile${i}`; + + ini.setString(section, "Name", profile.name); + ini.setString(section, "IsRelative", 1); + ini.setString(section, "Path", profile.path); + + if (profile.default) { + ini.setString(section, "Default", "1"); + } + } + + ini.writeFile(target); +} + +/** + * Reads the existing profiles.ini into the same structure as that accepted by + * writeProfilesIni above. The profiles property is sorted according to name + * because the order is irrelevant and it makes testing easier if we can make + * that assumption. + */ +function readProfilesIni() { + let target = gDataHome.clone(); + target.append("profiles.ini"); + + let profileData = { + options: {}, + profiles: [], + }; + + if (!target.exists()) { + return profileData; + } + + let factory = Cc["@mozilla.org/xpcom/ini-parser-factory;1"]. + getService(Ci.nsIINIParserFactory); + let ini = factory.createINIParser(target); + + profileData.options.startWithLastProfile = safeGet(ini, "General", "StartWithLastProfile") == "1"; + + for (let i = 0; true; i++) { + let section = `Profile${i}`; + + let isRelative = safeGet(ini, section, "IsRelative"); + if (isRelative === null) { + break; + } + Assert.equal(isRelative, "1", "Paths should always be relative in these tests."); + + let profile = { + name: safeGet(ini, section, "Name"), + path: safeGet(ini, section, "Path"), + }; + + try { + profile.default = ini.getString(section, "Default") == "1"; + Assert.ok(profile.default, "The Default value is only written when true."); + } catch (e) { + profile.default = false; + } + + profileData.profiles.push(profile); + } + + profileData.profiles.sort((a, b) => a.name.localeCompare(b.name)); + + return profileData; +} + +/** + * Checks that the profile service seems to have the right data in it compared + * to profile and install data structured as in the above functions. + */ +function checkProfileService(profileData = readProfilesIni()) { + let service = getProfileService(); + + let serviceProfiles = Array.from(service.profiles); + + Assert.equal(serviceProfiles.length, profileData.profiles.length, "Should be the same number of profiles."); + + // Sort to make matching easy. + serviceProfiles.sort((a, b) => a.name.localeCompare(b.name)); + profileData.profiles.sort((a, b) => a.name.localeCompare(b.name)); + + let defaultProfile = null; + + for (let i = 0; i < serviceProfiles.length; i++) { + let serviceProfile = serviceProfiles[i]; + let expectedProfile = profileData.profiles[i]; + + Assert.equal(serviceProfile.name, expectedProfile.name, "Should have the same name."); + + let expectedPath = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsIFile); + expectedPath.setRelativeDescriptor(gDataHome, expectedProfile.path); + Assert.equal(serviceProfile.rootDir.path, expectedPath.path, "Should have the same path."); + + if (AppConstants.MOZ_DEV_EDITION) { + if (expectedProfile.name == PROFILE_DEFAULT) { + defaultProfile = serviceProfile; + } + } else if (expectedProfile.default) { + defaultProfile = serviceProfile; + } + } + + let selectedProfile = null; + try { + selectedProfile = service.selectedProfile; + } catch (e) { + // GetSelectedProfile throws when there are no profiles. + } + + Assert.equal(selectedProfile, defaultProfile, "Should have seen the right profile selected."); +}
new file mode 100644 --- /dev/null +++ b/toolkit/profile/xpcshell/test_create_default.js @@ -0,0 +1,15 @@ +/* + * Tests that from an empty database a default profile is created. + */ + +add_task(async () => { + let service = getProfileService(); + + let { profile, didCreate } = selectStartupProfile(); + checkProfileService(); + + Assert.ok(didCreate, "Should have created a new profile."); + Assert.equal(service.profileCount, 1, "Should be only one profile."); + Assert.equal(profile, service.selectedProfile, "Should now be the selected profile."); + Assert.equal(profile.name, PROFILE_DEFAULT, "Should have created a new profile with the right name."); +});
new file mode 100644 --- /dev/null +++ b/toolkit/profile/xpcshell/test_profile_reset.js @@ -0,0 +1,14 @@ +/* + * Tests that from an empty database profile reset doesn't create a new profile. + */ + +add_task(async () => { + let service = getProfileService(); + + let { profile, didCreate } = selectStartupProfile([], true); + checkProfileService(); + + Assert.ok(!didCreate, "Should not have created a new profile."); + Assert.ok(!profile, "Should not be a returned profile."); + Assert.equal(service.profileCount, 0, "Still should be no profiles."); +});
new file mode 100644 --- /dev/null +++ b/toolkit/profile/xpcshell/test_select_default.js @@ -0,0 +1,46 @@ +/* + * Tests that from a database of profiles the default profile is selected. + */ + +add_task(async () => { + let profileData = { + options: { + startWithLastProfile: true, + }, + profiles: [{ + name: "Profile1", + path: "Path1", + }, { + name: "Profile3", + path: "Path3", + }], + }; + + if (AppConstants.MOZ_DEV_EDITION) { + profileData.profiles.push({ + name: "default", + path: "Path2", + default: true, + }, { + name: PROFILE_DEFAULT, + path: "Path4", + }); + } else { + profileData.profiles.push({ + name: PROFILE_DEFAULT, + path: "Path2", + default: true, + }); + } + + writeProfilesIni(profileData); + + let service = getProfileService(); + checkProfileService(profileData); + + let { profile, didCreate } = selectStartupProfile(); + + Assert.ok(!didCreate, "Should not have created a new profile."); + Assert.equal(profile, service.selectedProfile, "Should have returned the selected profile."); + Assert.equal(profile.name, PROFILE_DEFAULT, "Should have selected the right profile"); +});
new file mode 100644 --- /dev/null +++ b/toolkit/profile/xpcshell/test_select_environment.js @@ -0,0 +1,39 @@ +/* + * Tests that the environment variables are used to select a profile. + */ + +add_task(async () => { + let dir = makeRandomProfileDir("foo"); + + let profileData = { + options: { + startWithLastProfile: true, + }, + profiles: [{ + name: "Profile1", + path: dir.leafName, + }, { + name: "Profile2", + path: "Path2", + default: true, + }, { + name: "Profile3", + path: "Path3", + }], + }; + + writeProfilesIni(profileData); + checkProfileService(profileData); + + let env = Cc["@mozilla.org/process/environment;1"]. + getService(Ci.nsIEnvironment); + env.set("XRE_PROFILE_PATH", dir.path); + env.set("XRE_PROFILE_LOCAL_PATH", dir.path); + + let { rootDir, localDir, profile, didCreate } = selectStartupProfile(); + + Assert.ok(!didCreate, "Should not have created a new profile."); + Assert.ok(rootDir.equals(dir), "Should have selected the right root dir."); + Assert.ok(localDir.equals(dir), "Should have selected the right local dir."); + Assert.ok(!profile, "No named profile matches this."); +});
new file mode 100644 --- /dev/null +++ b/toolkit/profile/xpcshell/test_select_environment_named.js @@ -0,0 +1,42 @@ +/* + * Tests that the environment variables are used to select a profile. + */ + +add_task(async () => { + let root = makeRandomProfileDir("foo"); + let local = gDataHomeLocal.clone(); + local.append("foo"); + + let profileData = { + options: { + startWithLastProfile: true, + }, + profiles: [{ + name: "Profile1", + path: root.leafName, + }, { + name: "Profile2", + path: "Path2", + default: true, + }, { + name: "Profile3", + path: "Path3", + }], + }; + + writeProfilesIni(profileData); + checkProfileService(profileData); + + let env = Cc["@mozilla.org/process/environment;1"]. + getService(Ci.nsIEnvironment); + env.set("XRE_PROFILE_PATH", root.path); + env.set("XRE_PROFILE_LOCAL_PATH", local.path); + + let { rootDir, localDir, profile, didCreate } = selectStartupProfile(["-P", "Profile3"]); + + Assert.ok(!didCreate, "Should not have created a new profile."); + Assert.ok(rootDir.equals(root), "Should have selected the right root dir."); + Assert.ok(localDir.equals(local), "Should have selected the right local dir."); + Assert.ok(profile, "A named profile matches this."); + Assert.equal(profile.name, "Profile1", "The right profile was matched."); +});
new file mode 100644 --- /dev/null +++ b/toolkit/profile/xpcshell/test_select_missing.js @@ -0,0 +1,27 @@ +/* + * Tests that when choosing an unknown profile the profile manager is shown. + */ + +add_task(async () => { + let profileData = { + options: { + startWithLastProfile: true, + }, + profiles: [{ + name: "Profile1", + path: "Path1", + }, { + name: "Profile2", + path: "Path2", + default: true, + }, { + name: "Profile3", + path: "Path3", + }], + }; + + writeProfilesIni(profileData); + checkProfileService(profileData); + + testStartsProfileManager(["-P", "foo"]); +});
new file mode 100644 --- /dev/null +++ b/toolkit/profile/xpcshell/test_select_named.js @@ -0,0 +1,31 @@ +/* + * Tests that from a database of profiles the correct profile is selected. + */ + +add_task(async () => { + let profileData = { + options: { + startWithLastProfile: true, + }, + profiles: [{ + name: "Profile1", + path: "Path1", + }, { + name: "Profile2", + path: "Path2", + default: true, + }, { + name: "Profile3", + path: "Path3", + }], + }; + + writeProfilesIni(profileData); + + checkProfileService(profileData); + + let { profile, didCreate } = selectStartupProfile(["-P", "Profile1"]); + + Assert.ok(!didCreate, "Should not have created a new profile."); + Assert.equal(profile.name, "Profile1", "Should have chosen the right profile"); +});
new file mode 100644 --- /dev/null +++ b/toolkit/profile/xpcshell/test_select_noname.js @@ -0,0 +1,28 @@ +/* + * Tests that when passing the -P command line argument and not passing a + * profile name the profile manager is opened. + */ + +add_task(async () => { + let profileData = { + options: { + startWithLastProfile: true, + }, + profiles: [{ + name: "Profile1", + path: "Path1", + }, { + name: "Profile2", + path: "Path2", + default: true, + }, { + name: "Profile3", + path: "Path3", + }], + }; + + writeProfilesIni(profileData); + checkProfileService(profileData); + + testStartsProfileManager(["-P"]); +});
new file mode 100644 --- /dev/null +++ b/toolkit/profile/xpcshell/test_select_profilemanager.js @@ -0,0 +1,27 @@ +/* + * Tests that when requested the profile manager is shown. + */ + +add_task(async () => { + let profileData = { + options: { + startWithLastProfile: true, + }, + profiles: [{ + name: "Profile1", + path: "Path1", + }, { + name: "Profile2", + path: "Path2", + default: true, + }, { + name: "Profile3", + path: "Path3", + }], + }; + + writeProfilesIni(profileData); + checkProfileService(profileData); + + testStartsProfileManager(["-profilemanager"]); +});
new file mode 100644 --- /dev/null +++ b/toolkit/profile/xpcshell/xpcshell.ini @@ -0,0 +1,13 @@ +[DEFAULT] +head = head.js +skip-if = toolkit == 'android' + +[test_select_default.js] +[test_select_profilemanager.js] +[test_select_named.js] +[test_select_missing.js] +[test_select_noname.js] +[test_create_default.js] +[test_select_environment.js] +[test_select_environment_named.js] +[test_profile_reset.js]
new file mode 100644 --- /dev/null +++ b/toolkit/xre/CmdLineAndEnvUtils.cpp @@ -0,0 +1,41 @@ +/* -*- Mode: C++; tab-width: 8; 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 "prenv.h" + +namespace mozilla { + +// Load the path of a file saved with SaveFileToEnv +already_AddRefed<nsIFile> GetFileFromEnv(const char* name) { + nsresult rv; + nsCOMPtr<nsIFile> file; + +#ifdef XP_WIN + WCHAR path[_MAX_PATH]; + if (!GetEnvironmentVariableW(NS_ConvertASCIItoUTF16(name).get(), path, + _MAX_PATH)) + return nullptr; + + rv = NS_NewLocalFile(nsDependentString(path), true, getter_AddRefs(file)); + if (NS_FAILED(rv)) return nullptr; + + return file.forget(); +#else + const char* arg = PR_GetEnv(name); + if (!arg || !*arg) { + return nullptr; + } + + rv = NS_NewNativeLocalFile(nsDependentCString(arg), true, + getter_AddRefs(file)); + if (NS_FAILED(rv)) { + return nullptr; + } + + return file.forget(); +#endif +} + +} // namespace mozilla
--- a/toolkit/xre/CmdLineAndEnvUtils.h +++ b/toolkit/xre/CmdLineAndEnvUtils.h @@ -27,16 +27,21 @@ #endif // defined(XP_WIN) #include "mozilla/MemoryChecking.h" #include "mozilla/TypedEnumBits.h" #include <ctype.h> #include <stdint.h> +#ifndef NS_NO_XPCOM +#include "nsIFile.h" +#include "mozilla/AlreadyAddRefed.h" +#endif + // Undo X11/X.h's definition of None #undef None namespace mozilla { enum ArgResult { ARG_NONE = 0, ARG_FOUND = 1, @@ -118,17 +123,18 @@ MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(Ch * @param aArgv The original argv. * @param aArg the parameter to check. Must be lowercase. * @param aParam if non-null, the -arg <data> will be stored in this pointer. * This is *not* allocated, but rather a pointer to the argv data. * @param aFlags Flags @see CheckArgFlag */ template <typename CharT> inline ArgResult CheckArg(int& aArgc, CharT** aArgv, const CharT* aArg, - const CharT** aParam, CheckArgFlag aFlags) { + const CharT** aParam = nullptr, + CheckArgFlag aFlags = CheckArgFlag::RemoveArg) { MOZ_ASSERT(aArgv && aArg); CharT** curarg = aArgv + 1; // skip argv[0] ArgResult ar = ARG_NONE; while (*curarg) { CharT* arg = curarg[0]; @@ -423,11 +429,15 @@ inline bool EnvHasValue(const char* aVar // This is the same as the NSPR implementation const char* val = getenv(aVarName); return val && *val; #else #error "Not implemented for this configuration" #endif } +#ifndef NS_NO_XPCOM +already_AddRefed<nsIFile> GetFileFromEnv(const char* name); +#endif + } // namespace mozilla #endif // mozilla_CmdLineAndEnvUtils_h
--- a/toolkit/xre/ProfileReset.cpp +++ b/toolkit/xre/ProfileReset.cpp @@ -30,26 +30,29 @@ extern const XREAppData* gAppData; static const char kProfileProperties[] = "chrome://mozapps/locale/profile/profileSelection.properties"; /** * Creates a new profile with a timestamp in the name to use for profile reset. */ nsresult CreateResetProfile(nsIToolkitProfileService* aProfileSvc, - const nsACString& aOldProfileName, + nsIToolkitProfile* aOldProfile, nsIToolkitProfile** aNewProfile) { MOZ_ASSERT(aProfileSvc, "NULL profile service"); + nsAutoCString oldProfileName; + aOldProfile->GetName(oldProfileName); + nsCOMPtr<nsIToolkitProfile> newProfile; // Make the new profile the old profile (or "default-") + the time in seconds // since epoch for uniqueness. nsAutoCString newProfileName; - if (!aOldProfileName.IsEmpty()) { - newProfileName.Assign(aOldProfileName); + if (!oldProfileName.IsEmpty()) { + newProfileName.Assign(oldProfileName); newProfileName.Append("-"); } else { newProfileName.AssignLiteral("default-"); } newProfileName.Append(nsPrintfCString("%" PRId64, PR_Now() / 1000)); nsresult rv = aProfileSvc->CreateProfile(nullptr, // choose a default dir for us newProfileName, getter_AddRefs(newProfile));
--- a/toolkit/xre/ProfileReset.h +++ b/toolkit/xre/ProfileReset.h @@ -7,17 +7,17 @@ #include "nsIFile.h" #include "nsThreadUtils.h" static bool gProfileResetCleanupCompleted = false; static const char kResetProgressURL[] = "chrome://global/content/resetProfileProgress.xul"; nsresult CreateResetProfile(nsIToolkitProfileService* aProfileSvc, - const nsACString& aOldProfileName, + nsIToolkitProfile* aOldProfile, nsIToolkitProfile** aNewProfile); nsresult ProfileResetCleanup(nsIToolkitProfile* aOldProfile); class ProfileResetCleanupResultTask : public mozilla::Runnable { public: ProfileResetCleanupResultTask() : mozilla::Runnable("ProfileResetCleanupResultTask"),
--- a/toolkit/xre/moz.build +++ b/toolkit/xre/moz.build @@ -101,16 +101,17 @@ if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'andr UNIFIED_SOURCES += [ 'nsAndroidStartup.cpp', ] UNIFIED_SOURCES += [ '/toolkit/mozapps/update/common/commonupdatedir.cpp', 'AutoSQLiteLifetime.cpp', 'Bootstrap.cpp', + 'CmdLineAndEnvUtils.cpp', 'CreateAppData.cpp', 'nsAppStartupNotifier.cpp', 'nsConsoleWriter.cpp', 'nsEmbeddingModule.cpp', 'nsNativeAppSupportBase.cpp', 'nsSigHandlers.cpp', 'nsXREDirProvider.cpp', ]
--- a/toolkit/xre/nsAppRunner.cpp +++ b/toolkit/xre/nsAppRunner.cpp @@ -75,17 +75,17 @@ #include "nsIProcess.h" #include "nsIProfileUnlocker.h" #include "nsIPromptService.h" #include "nsIServiceManager.h" #include "nsIStringBundle.h" #include "nsISupportsPrimitives.h" #include "nsIToolkitChromeRegistry.h" #include "nsIToolkitProfile.h" -#include "nsIToolkitProfileService.h" +#include "nsToolkitProfileService.h" #include "nsIURI.h" #include "nsIURL.h" #include "nsIWindowCreator.h" #include "nsIWindowMediator.h" #include "nsIWindowWatcher.h" #include "nsIXULAppInfo.h" #include "nsIXULRuntime.h" #include "nsPIDOMWindow.h" @@ -335,53 +335,16 @@ static void SaveFileToEnv(const char* na SetEnvironmentVariableW(NS_ConvertASCIItoUTF16(name).get(), path.get()); #else nsAutoCString path; file->GetNativePath(path); SaveWordToEnv(name, path); #endif } -// Load the path of a file saved with SaveFileToEnv -#ifndef MOZ_ASAN_REPORTER -static -#endif - already_AddRefed<nsIFile> - GetFileFromEnv(const char* name) { - nsresult rv; - nsCOMPtr<nsIFile> file; - -#ifdef XP_WIN - WCHAR path[_MAX_PATH]; - if (!GetEnvironmentVariableW(NS_ConvertASCIItoUTF16(name).get(), path, - _MAX_PATH)) - return nullptr; - - rv = NS_NewLocalFile(nsDependentString(path), true, getter_AddRefs(file)); - if (NS_FAILED(rv)) return nullptr; - - return file.forget(); -#else - const char* arg = PR_GetEnv(name); - if (!arg || !*arg) return nullptr; - - rv = NS_NewNativeLocalFile(nsDependentCString(arg), true, - getter_AddRefs(file)); - if (NS_FAILED(rv)) return nullptr; - - return file.forget(); -#endif -} - -// Save the path of the given word to the specified environment variable -// provided the environment variable does not have a value. -static void SaveWordToEnvIfUnset(const char* name, const nsACString& word) { - if (!EnvHasValue(name)) SaveWordToEnv(name, word); -} - // Save the path of the given file to the specified environment variable // provided the environment variable does not have a value. static void SaveFileToEnvIfUnset(const char* name, nsIFile* file) { if (!EnvHasValue(name)) SaveFileToEnv(name, file); } static bool gIsExpectedExit = false; @@ -2017,17 +1980,16 @@ static ReturnAbortOnError ShowProfileMan free(profileNamePtr); lock->Unlock(); } } SaveFileToEnv("XRE_PROFILE_PATH", profD); SaveFileToEnv("XRE_PROFILE_LOCAL_PATH", profLD); - SaveWordToEnv("XRE_PROFILE_NAME", profileName); if (offline) { SaveToEnv("XRE_START_OFFLINE=1"); } if (gRestartedByOS) { // Re-add this argument when actually starting the application. char** newArgv = (char**)realloc(gRestartArgv, sizeof(char*) * (gRestartArgc + 2)); @@ -2072,17 +2034,17 @@ static nsresult GetCurrentProfile(nsIToo } rv = profiles->GetNext(getter_AddRefs(supports)); } return rv; } static bool gDoMigration = false; static bool gDoProfileReset = false; -static nsAutoCString gResetOldProfileName; +static nsCOMPtr<nsIToolkitProfile> gResetOldProfile; // Pick a profile. We need to end up with a profile lock. // // 1) check for --profile <path> // 2) check for -P <name> // 3) check for --ProfileManager // 4) use the default profile, if there is one // 5) if there are *no* profiles, set up profile-migration @@ -2090,17 +2052,16 @@ static nsAutoCString gResetOldProfileNam static nsresult SelectProfile(nsIProfileLock** aResult, nsIToolkitProfileService* aProfileSvc, nsINativeAppSupport* aNative, bool* aStartOffline, nsACString* aProfileName) { StartupTimeline::Record(StartupTimeline::SELECT_PROFILE); nsresult rv; ArgResult ar; - const char* arg; *aResult = nullptr; *aStartOffline = false; ar = CheckArg("offline", nullptr, CheckArgFlag::CheckOSInt | CheckArgFlag::RemoveArg); if (ar == ARG_BAD) { PR_fprintf(PR_STDERR, "Error: argument --offline is invalid when argument --osint is " @@ -2144,359 +2105,121 @@ static nsresult SelectProfile(nsIProfile "Error: argument --migration is invalid when argument --osint " "is specified\n"); return NS_ERROR_FAILURE; } if (ar == ARG_FOUND) { gDoMigration = true; } - nsCOMPtr<nsIFile> lf = GetFileFromEnv("XRE_PROFILE_PATH"); - if (lf) { - nsCOMPtr<nsIFile> localDir = GetFileFromEnv("XRE_PROFILE_LOCAL_PATH"); - if (!localDir) { - localDir = lf; - } - - arg = PR_GetEnv("XRE_PROFILE_NAME"); - if (arg && *arg && aProfileName) { - aProfileName->Assign(nsDependentCString(arg)); - if (gDoProfileReset) { - gResetOldProfileName.Assign(*aProfileName); - } - } - - // Clear out flags that we handled (or should have handled!) last startup. - const char* dummy; - CheckArg("p", &dummy); - CheckArg("profile", &dummy); - CheckArg("profilemanager"); - - if (gDoProfileReset) { - // If we're resetting a profile, create a new one and use it to startup. - nsCOMPtr<nsIToolkitProfile> newProfile; - rv = CreateResetProfile(aProfileSvc, gResetOldProfileName, - getter_AddRefs(newProfile)); - if (NS_SUCCEEDED(rv)) { - rv = newProfile->GetRootDir(getter_AddRefs(lf)); - NS_ENSURE_SUCCESS(rv, rv); - SaveFileToEnv("XRE_PROFILE_PATH", lf); - - rv = newProfile->GetLocalDir(getter_AddRefs(localDir)); - NS_ENSURE_SUCCESS(rv, rv); - SaveFileToEnv("XRE_PROFILE_LOCAL_PATH", localDir); - - rv = newProfile->GetName(*aProfileName); - if (NS_FAILED(rv)) aProfileName->Truncate(0); - SaveWordToEnv("XRE_PROFILE_NAME", *aProfileName); - } else { - NS_WARNING("Profile reset failed."); - gDoProfileReset = false; - } - } - - return NS_LockProfilePath(lf, localDir, nullptr, aResult); - } - - ar = CheckArg("profile", &arg, - CheckArgFlag::CheckOSInt | CheckArgFlag::RemoveArg); - if (ar == ARG_BAD) { - PR_fprintf(PR_STDERR, "Error: argument --profile requires a path\n"); - return NS_ERROR_FAILURE; - } - if (ar) { - if (gDoProfileReset) { - NS_WARNING( - "Profile reset is not supported in conjunction with --profile."); - gDoProfileReset = false; - } - - nsCOMPtr<nsIFile> lf; - rv = XRE_GetFileFromPath(arg, getter_AddRefs(lf)); - NS_ENSURE_SUCCESS(rv, rv); - - nsCOMPtr<nsIProfileUnlocker> unlocker; - - // Check if the profile path exists and it's a directory. - bool exists; - lf->Exists(&exists); - if (!exists) { - rv = lf->Create(nsIFile::DIRECTORY_TYPE, 0700); - NS_ENSURE_SUCCESS(rv, rv); - } - - // If a profile path is specified directory on the command line, then - // assume that the temp directory is the same as the given directory. - rv = NS_LockProfilePath(lf, lf, getter_AddRefs(unlocker), aResult); - if (NS_SUCCEEDED(rv)) return rv; - - return ProfileLockedDialog(lf, lf, unlocker, aNative, aResult); - } - - ar = CheckArg("createprofile", &arg, - CheckArgFlag::CheckOSInt | CheckArgFlag::RemoveArg); - if (ar == ARG_BAD) { - PR_fprintf(PR_STDERR, - "Error: argument --createprofile requires a profile name\n"); - return NS_ERROR_FAILURE; - } - if (ar) { - nsCOMPtr<nsIToolkitProfile> profile; - - const char* delim = strchr(arg, ' '); - if (delim) { - nsCOMPtr<nsIFile> lf; - rv = NS_NewNativeLocalFile(nsDependentCString(delim + 1), true, - getter_AddRefs(lf)); - if (NS_FAILED(rv)) { - PR_fprintf(PR_STDERR, "Error: profile path not valid.\n"); - return rv; - } - - // As with --profile, assume that the given path will be used for the - // main profile directory. - rv = aProfileSvc->CreateProfile(lf, nsDependentCSubstring(arg, delim), - getter_AddRefs(profile)); - } else { - rv = aProfileSvc->CreateProfile(nullptr, nsDependentCString(arg), - getter_AddRefs(profile)); - } - // Some pathological arguments can make it this far - if (NS_FAILED(rv)) { - PR_fprintf(PR_STDERR, "Error creating profile.\n"); - return rv; - } - rv = NS_ERROR_ABORT; - aProfileSvc->Flush(); - - // XXXben need to ensure prefs.js exists here so the tinderboxes will - // not go orange. - nsCOMPtr<nsIFile> prefsJSFile; - profile->GetRootDir(getter_AddRefs(prefsJSFile)); - prefsJSFile->AppendNative(NS_LITERAL_CSTRING("prefs.js")); - PR_fprintf(PR_STDERR, "Success: created profile '%s' at '%s'\n", arg, - prefsJSFile->HumanReadablePath().get()); - bool exists; - prefsJSFile->Exists(&exists); - if (!exists) { - // Ignore any errors; we're about to return NS_ERROR_ABORT anyway. - Unused << prefsJSFile->Create(nsIFile::NORMAL_FILE_TYPE, 0644); - } - // XXXdarin perhaps 0600 would be better? - - return rv; - } - - uint32_t count; - rv = aProfileSvc->GetProfileCount(&count); - NS_ENSURE_SUCCESS(rv, rv); - - ar = CheckArg("p", &arg); - if (ar == ARG_BAD) { - ar = CheckArg("osint"); - if (ar == ARG_FOUND) { - PR_fprintf( - PR_STDERR, - "Error: argument -p is invalid when argument --osint is specified\n"); - return NS_ERROR_FAILURE; - } - + nsCOMPtr<nsIFile> rootDir; + nsCOMPtr<nsIFile> localDir; + nsCOMPtr<nsIToolkitProfile> profile; + // Ask the profile manager to select the profile directories to use. + nsToolkitProfileService* service = + static_cast<nsToolkitProfileService*>(aProfileSvc); + bool didCreate = false; + rv = service->SelectStartupProfile( + &gArgc, gArgv, gDoProfileReset, getter_AddRefs(rootDir), + getter_AddRefs(localDir), getter_AddRefs(profile), &didCreate); + + if (rv == NS_ERROR_SHOW_PROFILE_MANAGER) { return ShowProfileManager(aProfileSvc, aNative); } - if (ar) { - ar = CheckArg("osint"); - if (ar == ARG_FOUND) { - PR_fprintf( - PR_STDERR, - "Error: argument -p is invalid when argument --osint is specified\n"); - return NS_ERROR_FAILURE; - } - nsCOMPtr<nsIToolkitProfile> profile; - rv = aProfileSvc->GetProfileByName(nsDependentCString(arg), - getter_AddRefs(profile)); - if (NS_SUCCEEDED(rv)) { - if (gDoProfileReset) { - { - // Check that the source profile is not in use by temporarily - // acquiring its lock. - nsIProfileLock* tempProfileLock; - nsCOMPtr<nsIProfileUnlocker> unlocker; - rv = profile->Lock(getter_AddRefs(unlocker), &tempProfileLock); - if (NS_FAILED(rv)) - return ProfileLockedDialog(profile, unlocker, aNative, - &tempProfileLock); - } - - nsresult gotName = profile->GetName(gResetOldProfileName); - if (NS_SUCCEEDED(gotName)) { - nsCOMPtr<nsIToolkitProfile> newProfile; - rv = CreateResetProfile(aProfileSvc, gResetOldProfileName, - getter_AddRefs(newProfile)); - if (NS_FAILED(rv)) { - NS_WARNING("Failed to create a profile to reset to."); - gDoProfileReset = false; - } else { - profile = newProfile; - } - } else { - NS_WARNING( - "Failed to get the name of the profile we're resetting, so " - "aborting reset."); - gResetOldProfileName.Truncate(0); - gDoProfileReset = false; - } - } - - nsCOMPtr<nsIProfileUnlocker> unlocker; - rv = profile->Lock(getter_AddRefs(unlocker), aResult); - if (NS_SUCCEEDED(rv)) { - if (aProfileName) aProfileName->Assign(nsDependentCString(arg)); - return NS_OK; - } - - return ProfileLockedDialog(profile, unlocker, aNative, aResult); - } - - return ShowProfileManager(aProfileSvc, aNative); - } - - ar = CheckArg("profilemanager", nullptr, - CheckArgFlag::CheckOSInt | CheckArgFlag::RemoveArg); - if (ar == ARG_BAD) { - PR_fprintf(PR_STDERR, - "Error: argument --profilemanager is invalid when argument " - "--osint is specified\n"); - return NS_ERROR_FAILURE; - } - if (ar == ARG_FOUND) { - return ShowProfileManager(aProfileSvc, aNative); - } - -#ifndef MOZ_DEV_EDITION - // If the only existing profile is the dev-edition-profile and this is not - // Developer Edition, then no valid profiles were found. - if (count == 1) { - nsCOMPtr<nsIToolkitProfile> deProfile; - // GetSelectedProfile will auto-select the only profile if there's just one - aProfileSvc->GetSelectedProfile(getter_AddRefs(deProfile)); - nsAutoCString profileName; - deProfile->GetName(profileName); - if (profileName.EqualsLiteral("dev-edition-default")) { - count = 0; - } - } -#endif - - if (!count) { + + NS_ENSURE_SUCCESS(rv, rv); + + if (didCreate) { // For a fresh install, we would like to let users decide // to do profile migration on their own later after using. + gDoProfileReset = false; gDoMigration = false; - gDoProfileReset = false; - - // create a default profile - nsCOMPtr<nsIToolkitProfile> profile; - nsresult rv = - aProfileSvc->CreateProfile(nullptr, // choose a default dir for us -#ifdef MOZ_DEV_EDITION - NS_LITERAL_CSTRING("dev-edition-default"), -#else - NS_LITERAL_CSTRING("default"), -#endif - getter_AddRefs(profile)); + } + + if (gDoProfileReset) { + if (!profile) { + NS_WARNING("Profile reset is only supported for named profiles."); + return NS_ERROR_ABORT; + } + + { + // Check that the source profile is not in use by temporarily + // acquiring its lock. + nsIProfileLock* tempProfileLock; + nsCOMPtr<nsIProfileUnlocker> unlocker; + rv = profile->Lock(getter_AddRefs(unlocker), &tempProfileLock); + if (NS_FAILED(rv)) { + return ProfileLockedDialog(profile, unlocker, aNative, + &tempProfileLock); + } + } + + // If we're resetting a profile, create a new one and use it to startup. + gResetOldProfile = profile; + rv = CreateResetProfile(aProfileSvc, gResetOldProfile, + getter_AddRefs(profile)); if (NS_SUCCEEDED(rv)) { -#ifndef MOZ_DEV_EDITION - aProfileSvc->SetDefaultProfile(profile); -#endif - aProfileSvc->Flush(); - rv = profile->Lock(nullptr, aResult); - if (NS_SUCCEEDED(rv)) { - if (aProfileName) -#ifdef MOZ_DEV_EDITION - aProfileName->AssignLiteral("dev-edition-default"); -#else - aProfileName->AssignLiteral("default"); -#endif - return NS_OK; + rv = profile->GetRootDir(getter_AddRefs(rootDir)); + NS_ENSURE_SUCCESS(rv, rv); + SaveFileToEnv("XRE_PROFILE_PATH", rootDir); + + rv = profile->GetLocalDir(getter_AddRefs(localDir)); + NS_ENSURE_SUCCESS(rv, rv); + SaveFileToEnv("XRE_PROFILE_LOCAL_PATH", localDir); + + rv = profile->GetName(*aProfileName); + if (NS_FAILED(rv)) { + aProfileName->Truncate(0); } + } else { + NS_WARNING("Profile reset failed."); + return NS_ERROR_ABORT; } } - bool useDefault = true; - if (count > 1) { - aProfileSvc->GetStartWithLastProfile(&useDefault); + // No profile could be found. This generally shouldn't happen, a new profile + // should be created in all cases except for profile reset which is covered + // above, but just in case... + if (!rootDir) { + return NS_ERROR_ABORT; } - if (useDefault) { - nsCOMPtr<nsIToolkitProfile> profile; - // GetSelectedProfile will auto-select the only profile if there's just one - aProfileSvc->GetSelectedProfile(getter_AddRefs(profile)); + // If you close Firefox and very quickly reopen it, the old Firefox may + // still be closing down. Rather than immediately showing the + // "Firefox is running but is not responding" message, we spend a few + // seconds retrying first. + + static const int kLockRetrySeconds = 5; + static const int kLockRetrySleepMS = 100; + + nsCOMPtr<nsIProfileUnlocker> unlocker; + const TimeStamp start = TimeStamp::Now(); + do { if (profile) { - // If we're resetting a profile, create a new one and use it to startup. - if (gDoProfileReset) { - { - // Check that the source profile is not in use by temporarily - // acquiring its lock. - nsIProfileLock* tempProfileLock; - nsCOMPtr<nsIProfileUnlocker> unlocker; - rv = profile->Lock(getter_AddRefs(unlocker), &tempProfileLock); - if (NS_FAILED(rv)) - return ProfileLockedDialog(profile, unlocker, aNative, - &tempProfileLock); - } - - nsresult gotName = profile->GetName(gResetOldProfileName); - if (NS_SUCCEEDED(gotName)) { - nsCOMPtr<nsIToolkitProfile> newProfile; - rv = CreateResetProfile(aProfileSvc, gResetOldProfileName, - getter_AddRefs(newProfile)); - if (NS_FAILED(rv)) { - NS_WARNING("Failed to create a profile to reset to."); - gDoProfileReset = false; - } else { - profile = newProfile; - } - } else { - NS_WARNING( - "Failed to get the name of the profile we're resetting, so " - "aborting reset."); - gResetOldProfileName.Truncate(0); - gDoProfileReset = false; + rv = profile->Lock(getter_AddRefs(unlocker), aResult); + } else { + rv = NS_LockProfilePath(rootDir, localDir, getter_AddRefs(unlocker), + aResult); + } + if (NS_SUCCEEDED(rv)) { + StartupTimeline::Record(StartupTimeline::AFTER_PROFILE_LOCKED); + // Try to grab the profile name. + if (aProfileName && profile) { + rv = profile->GetName(*aProfileName); + if (NS_FAILED(rv)) { + aProfileName->Truncate(0); } } - - // If you close Firefox and very quickly reopen it, the old Firefox may - // still be closing down. Rather than immediately showing the - // "Firefox is running but is not responding" message, we spend a few - // seconds retrying first. - - static const int kLockRetrySeconds = 5; - static const int kLockRetrySleepMS = 100; - - nsCOMPtr<nsIProfileUnlocker> unlocker; - const TimeStamp start = TimeStamp::Now(); - do { - rv = profile->Lock(getter_AddRefs(unlocker), aResult); - if (NS_SUCCEEDED(rv)) { - StartupTimeline::Record(StartupTimeline::AFTER_PROFILE_LOCKED); - // Try to grab the profile name. - if (aProfileName) { - rv = profile->GetName(*aProfileName); - if (NS_FAILED(rv)) aProfileName->Truncate(0); - } - return NS_OK; - } - PR_Sleep(kLockRetrySleepMS); - } while (TimeStamp::Now() - start < - TimeDuration::FromSeconds(kLockRetrySeconds)); - - return ProfileLockedDialog(profile, unlocker, aNative, aResult); + return NS_OK; } - } - - return ShowProfileManager(aProfileSvc, aNative); + PR_Sleep(kLockRetrySleepMS); + } while (TimeStamp::Now() - start < + TimeDuration::FromSeconds(kLockRetrySeconds)); + + return ProfileLockedDialog(rootDir, localDir, unlocker, aNative, aResult); } /** * Checks the compatibility.ini file to see if we have updated our application * or otherwise invalidated our caches. If the application has been updated, * we return false; otherwise, we return true. We also write the status * of the caches (valid/invalid) into the return param aCachesOK. The aCachesOK * is always invalid if the application has been updated. @@ -4351,67 +4074,58 @@ nsresult XREMain::XRE_mainRun() { if (buf[0] == '0' || buf[0] == 'f' || buf[0] == 'F') { gDoMigration = false; } } } } { - nsCOMPtr<nsIToolkitProfile> profileBeingReset; bool profileWasSelected = false; if (gDoProfileReset) { - if (gResetOldProfileName.IsEmpty()) { - NS_WARNING("Not resetting profile as the profile has no name."); - gDoProfileReset = false; - } else { - rv = mProfileSvc->GetProfileByName(gResetOldProfileName, - getter_AddRefs(profileBeingReset)); - if (NS_FAILED(rv)) { - gDoProfileReset = false; - return NS_ERROR_FAILURE; - } - - nsCOMPtr<nsIToolkitProfile> defaultProfile; - // This can fail if there is no default profile. - // That shouldn't stop reset from proceeding. - nsresult gotSelected = - mProfileSvc->GetSelectedProfile(getter_AddRefs(defaultProfile)); - if (NS_SUCCEEDED(gotSelected)) { - profileWasSelected = defaultProfile == profileBeingReset; - } + nsCOMPtr<nsIToolkitProfile> defaultProfile; + // This can fail if there is no default profile. + // That shouldn't stop reset from proceeding. + nsresult gotSelected = + mProfileSvc->GetSelectedProfile(getter_AddRefs(defaultProfile)); + if (NS_SUCCEEDED(gotSelected)) { + profileWasSelected = defaultProfile == gResetOldProfile; } } // Profile Migration if (mAppData->flags & NS_XRE_ENABLE_PROFILE_MIGRATOR && gDoMigration) { gDoMigration = false; nsCOMPtr<nsIProfileMigrator> pm( do_CreateInstance(NS_PROFILEMIGRATOR_CONTRACTID)); if (pm) { nsAutoCString aKey; + nsAutoCString aName; if (gDoProfileReset) { // Automatically migrate from the current application if we just // reset the profile. aKey = MOZ_APP_NAME; + gResetOldProfile->GetName(aName); } - pm->Migrate(&mDirProvider, aKey, gResetOldProfileName); + pm->Migrate(&mDirProvider, aKey, aName); } } if (gDoProfileReset) { - nsresult backupCreated = ProfileResetCleanup(profileBeingReset); + nsresult backupCreated = ProfileResetCleanup(gResetOldProfile); if (NS_FAILED(backupCreated)) NS_WARNING("Could not cleanup the profile that was reset"); nsCOMPtr<nsIToolkitProfile> newProfile; rv = GetCurrentProfile(mProfileSvc, mProfD, getter_AddRefs(newProfile)); if (NS_SUCCEEDED(rv)) { - newProfile->SetName(gResetOldProfileName); - mProfileName.Assign(gResetOldProfileName); + nsAutoCString name; + gResetOldProfile->GetName(name); + newProfile->SetName(name); + mProfileName.Assign(name); // Set the new profile as the default after we're done cleaning up the // old profile, iff that profile was already the default if (profileWasSelected) { rv = mProfileSvc->SetDefaultProfile(newProfile); if (NS_FAILED(rv)) NS_WARNING("Could not set current profile as the default"); } } else { @@ -4500,17 +4214,16 @@ nsresult XREMain::XRE_mainRun() { #endif SaveStateForAppInitiatedRestart(); // clear out any environment variables which may have been set // during the relaunch process now that we know we won't be relaunching. SaveToEnv("XRE_PROFILE_PATH="); SaveToEnv("XRE_PROFILE_LOCAL_PATH="); - SaveToEnv("XRE_PROFILE_NAME="); SaveToEnv("XRE_START_OFFLINE="); SaveToEnv("XUL_APP_FILE="); SaveToEnv("XRE_BINARY_PATH="); if (!mShuttingDown) { rv = appStartup->CreateHiddenWindow(); NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE); @@ -4795,17 +4508,16 @@ int XREMain::XRE_main(int argc, char* ar // Restart the app after XPCOM has been shut down cleanly. if (appInitiatedRestart) { RestoreStateForAppInitiatedRestart(); if (rv != NS_SUCCESS_RESTART_APP_NOT_SAME_PROFILE) { // Ensure that these environment variables are set: SaveFileToEnvIfUnset("XRE_PROFILE_PATH", mProfD); SaveFileToEnvIfUnset("XRE_PROFILE_LOCAL_PATH", mProfLD); - SaveWordToEnvIfUnset("XRE_PROFILE_NAME", mProfileName); } #ifdef MOZ_WIDGET_GTK if (!gfxPlatform::IsHeadless()) { MOZ_gdk_display_close(mGdkDisplay); } #endif
--- a/toolkit/xre/nsAppRunner.h +++ b/toolkit/xre/nsAppRunner.h @@ -121,13 +121,11 @@ const char* PlatformBuildID(); */ void SetupErrorHandling(const char* progname); #ifdef MOZ_ASAN_REPORTER extern "C" { void MOZ_EXPORT __sanitizer_set_report_path(const char* path); } void setASanReporterPath(nsIFile* aDir); - -already_AddRefed<nsIFile> GetFileFromEnv(const char* name); #endif #endif // nsAppRunner_h__
--- a/toolkit/xre/nsXREDirProvider.cpp +++ b/toolkit/xre/nsXREDirProvider.cpp @@ -14,16 +14,17 @@ #include "nsIDirectoryEnumerator.h" #include "nsIFile.h" #include "nsIObserver.h" #include "nsIObserverService.h" #include "nsISimpleEnumerator.h" #include "nsIToolkitChromeRegistry.h" #include "nsIToolkitProfileService.h" #include "nsIXULRuntime.h" +#include "commonupdatedir.h" #include "nsAppDirectoryServiceDefs.h" #include "nsDirectoryServiceDefs.h" #include "nsDirectoryServiceUtils.h" #include "nsXULAppAPI.h" #include "nsCategoryManagerUtils.h" #include "nsDependentString.h" @@ -42,17 +43,16 @@ #include "mozilla/Preferences.h" #include "mozilla/Telemetry.h" #include <stdlib.h> #ifdef XP_WIN #include <windows.h> #include <shlobj.h> -#include "commonupdatedir.h" #endif #ifdef XP_MACOSX #include "nsILocalFileMac.h" // for chflags() #include <sys/stat.h> #include <unistd.h> #endif #ifdef XP_UNIX @@ -93,29 +93,24 @@ static already_AddRefed<nsIFile> CreateP #endif nsXREDirProvider* gDirServiceProvider = nullptr; nsIFile* gDataDirHomeLocal = nullptr; nsIFile* gDataDirHome = nullptr; // These are required to allow nsXREDirProvider to be usable in xpcshell tests. // where gAppData is null. -static const char* GetAppProfile() { - if (gAppData) { - return gAppData->profile; - } - return nullptr; -} - +#if defined(XP_MACOSX) || defined(XP_WIN) static const char* GetAppName() { if (gAppData) { return gAppData->name; } return nullptr; } +#endif static const char* GetAppVendor() { if (gAppData) { return gAppData->vendor; } return nullptr; } @@ -1603,24 +1598,31 @@ nsresult nsXREDirProvider::AppendSysUser #error "Don't know how to get XRE system extension dev path on your platform" #endif return NS_OK; } nsresult nsXREDirProvider::AppendProfilePath(nsIFile* aFile, bool aLocal) { NS_ASSERTION(aFile, "Null pointer!"); + // If there is no XREAppData then there is no information to use to build + // the profile path so just do nothing. This should only happen in xpcshell + // tests. + if (!gAppData) { + return NS_OK; + } + nsAutoCString profile; nsAutoCString appName; nsAutoCString vendor; - if (GetAppProfile()) { - profile = GetAppProfile(); + if (gAppData->profile) { + profile = gAppData->profile; } else { - appName = GetAppName(); - vendor = GetAppVendor(); + appName = gAppData->name; + vendor = gAppData->vendor; } nsresult rv; #if defined(XP_MACOSX) if (!profile.IsEmpty()) { rv = AppendProfileString(aFile, profile.get()); } else {
--- a/xpcom/base/ErrorList.py +++ b/xpcom/base/ErrorList.py @@ -739,16 +739,17 @@ with modules["XPCONNECT"]: # any new errors here should have an associated entry added in xpc.msg # ======================================================================= # 19: NS_ERROR_MODULE_PROFILE # ======================================================================= with modules["PROFILE"]: errors["NS_ERROR_LAUNCHED_CHILD_PROCESS"] = FAILURE(200) + errors["NS_ERROR_SHOW_PROFILE_MANAGER"] = FAILURE(201) # ======================================================================= # 21: NS_ERROR_MODULE_SECURITY # ======================================================================= with modules["SECURITY"]: # Error code for CSP errors["NS_ERROR_CSP_FORM_ACTION_VIOLATION"] = FAILURE(98)