Mirror of roytam1's UXP fork just in case Moonchild and Tobin decide to go after him
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

4415 lines
125 KiB

/* -*- 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/. */
#include "mozilla/dom/ContentParent.h"
#include "mozilla/dom/ContentChild.h"
#include "mozilla/ipc/GeckoChildProcessHost.h"
#include "mozilla/ArrayUtils.h"
#include "mozilla/Attributes.h"
#include "mozilla/FilePreferences.h"
#include "mozilla/ChaosMode.h"
#include "mozilla/IOInterposer.h"
#include "mozilla/Likely.h"
#include "mozilla/MemoryChecking.h"
#include "mozilla/Poison.h"
#include "mozilla/Preferences.h"
#include "mozilla/ScopeExit.h"
#include "mozilla/Services.h"
#include "mozilla/ServoBindings.h"
#include "mozilla/Telemetry.h"
#include "nsAppRunner.h"
#include "mozilla/AppData.h"
#if defined(MOZ_UPDATER) && !defined(MOZ_WIDGET_ANDROID)
#include "nsUpdateDriver.h"
#endif
#include "ProfileReset.h"
#ifdef MOZ_INSTRUMENT_EVENT_LOOP
#include "EventTracer.h"
#endif
#ifdef XP_MACOSX
#include "nsVersionComparator.h"
#include "MacLaunchHelper.h"
#include "MacApplicationDelegate.h"
#include "MacAutoreleasePool.h"
// these are needed for sysctl
#include <sys/types.h>
#include <sys/sysctl.h>
#endif
#include "prmem.h"
#include "prnetdb.h"
#include "prprf.h"
#include "prproces.h"
#include "prenv.h"
#include "prtime.h"
#include "nsIAppShellService.h"
#include "nsIAppStartup.h"
#include "nsIAppStartupNotifier.h"
#include "nsIMutableArray.h"
#include "nsICategoryManager.h"
#include "nsIChromeRegistry.h"
#include "nsICommandLineRunner.h"
#include "nsIComponentManager.h"
#include "nsIComponentRegistrar.h"
#include "nsIConsoleService.h"
#include "nsIContentHandler.h"
#include "nsIDialogParamBlock.h"
#include "nsIDOMWindow.h"
#include "mozilla/ModuleUtils.h"
#include "nsIIOService2.h"
#include "nsIObserverService.h"
#include "nsINativeAppSupport.h"
#include "nsIPlatformInfo.h"
#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 "nsIURI.h"
#include "nsIURL.h"
#include "nsIWindowCreator.h"
#include "nsIWindowMediator.h"
#include "nsIWindowWatcher.h"
#include "nsIXULAppInfo.h"
#include "nsIXULRuntime.h"
#include "nsPIDOMWindow.h"
#include "nsIBaseWindow.h"
#include "nsIWidget.h"
#include "nsIDocShell.h"
#include "nsAppShellCID.h"
#include "mozilla/scache/StartupCache.h"
#include "gfxPrefs.h"
#include "mozilla/Unused.h"
#ifdef XP_WIN
#include "nsIWinAppHelper.h"
#include <windows.h>
#include <intrin.h>
#include <math.h>
#include "cairo/cairo-features.h"
#include "mozilla/WindowsVersion.h"
#include "mozilla/mscom/MainThreadRuntime.h"
#include "mozilla/widget/AudioSession.h"
#ifndef PROCESS_DEP_ENABLE
#define PROCESS_DEP_ENABLE 0x1
#endif
#endif
#ifdef ACCESSIBILITY
#include "nsAccessibilityService.h"
#if defined(XP_WIN)
#include "mozilla/a11y/Compatibility.h"
#endif
#endif
#include "nsCRT.h"
#include "nsCOMPtr.h"
#include "nsDirectoryServiceDefs.h"
#include "nsDirectoryServiceUtils.h"
#include "nsEmbedCID.h"
#include "nsNetUtil.h"
#include "nsReadableUtils.h"
#include "nsXPCOM.h"
#include "nsXPCOMCIDInternal.h"
#include "nsXPIDLString.h"
#include "nsPrintfCString.h"
#include "nsVersionComparator.h"
#include "nsAppDirectoryServiceDefs.h"
#include "nsXULAppAPI.h"
#include "nsXREDirProvider.h"
#include "nsToolkitCompsCID.h"
#include "nsINIParser.h"
#include "mozilla/Omnijar.h"
#include "mozilla/StartupTimeline.h"
#include "mozilla/LateWriteChecks.h"
#include <stdlib.h>
#include <locale.h>
#ifdef XP_UNIX
#include <sys/stat.h>
#include <unistd.h>
#include <pwd.h>
#endif
#ifdef XP_WIN
#include <process.h>
#include <shlobj.h>
#include "nsThreadUtils.h"
#include <comdef.h>
#include <wbemidl.h>
#include "WinUtils.h"
#endif
#ifdef XP_MACOSX
#include "nsILocalFileMac.h"
#include "nsCommandLineServiceMac.h"
#endif
// for X remote support
#ifdef MOZ_ENABLE_XREMOTE
#include "XRemoteClient.h"
#include "nsIRemoteService.h"
#include "nsProfileLock.h"
#include "SpecialSystemDirectory.h"
#include <sched.h>
// Time to wait for the remoting service to start
#define MOZ_XREMOTE_START_TIMEOUT_SEC 5
#endif
#if defined(DEBUG) && defined(XP_WIN32)
#include <malloc.h>
#endif
#if defined (XP_MACOSX)
#include <Carbon/Carbon.h>
#endif
#ifdef DEBUG
#include "mozilla/Logging.h"
#endif
#include "base/command_line.h"
#include "GTestRunner.h"
#ifdef MOZ_WIDGET_ANDROID
#include "GeneratedJNIWrappers.h"
#endif
extern uint32_t gRestartMode;
extern void InstallSignalHandlers(const char *ProgramName);
#define FILE_COMPATIBILITY_INFO NS_LITERAL_CSTRING("compatibility.ini")
#define FILE_INVALIDATE_CACHES NS_LITERAL_CSTRING(".purgecaches")
int gArgc;
char **gArgv;
static const char gToolkitVersion[] = NS_STRINGIFY(GRE_MILESTONE);
static const char gToolkitBuildID[] = NS_STRINGIFY(MOZ_BUILDID);
static nsIProfileLock* gProfileLock;
int gRestartArgc;
char **gRestartArgv;
bool gIsGtest = false;
nsString gAbsoluteArgv0Path;
#if defined(MOZ_WIDGET_GTK)
#include <glib.h>
#if defined(DEBUG) || defined(NS_BUILD_REFCNT_LOGGING)
#define CLEANUP_MEMORY 1
#define PANGO_ENABLE_BACKEND
#include <pango/pangofc-fontmap.h>
#endif
#include <gtk/gtk.h>
#ifdef MOZ_X11
#include <gdk/gdkx.h>
#endif /* MOZ_X11 */
#include "nsGTKToolkit.h"
#include <fontconfig/fontconfig.h>
#endif
#include "BinaryPath.h"
#ifndef MOZ_BUILDID
// See comment in Makefile.in why we want to avoid including buildid.h.
// Still include it when MOZ_BUILDID is not set, which can happen with some
// build backends.
#include "buildid.h"
#endif
#ifdef MOZ_LINKER
extern "C" MFBT_API bool IsSignalHandlingBroken();
#endif
#ifdef LIBFUZZER
#include "LibFuzzerRunner.h"
namespace mozilla {
LibFuzzerRunner* libFuzzerRunner = 0;
} // namespace mozilla
extern "C" MOZ_EXPORT void XRE_LibFuzzerSetMain(int argc, char** argv, LibFuzzerMain main) {
mozilla::libFuzzerRunner->setParams(argc, argv, main);
}
#endif
namespace mozilla {
int (*RunGTest)() = 0;
} // namespace mozilla
using namespace mozilla;
using mozilla::Unused;
using mozilla::scache::StartupCache;
using mozilla::dom::ContentParent;
using mozilla::dom::ContentChild;
// Save literal putenv string to environment variable.
static void
SaveToEnv(const char *putenv)
{
char *expr = strdup(putenv);
if (expr)
PR_SetEnv(expr);
// We intentionally leak |expr| here since it is required by PR_SetEnv.
MOZ_LSAN_INTENTIONALLY_LEAK_OBJECT(expr);
}
// Tests that an environment variable exists and has a value
static bool
EnvHasValue(const char *name)
{
const char *val = PR_GetEnv(name);
return (val && *val);
}
// Save the given word to the specified environment variable.
static void
SaveWordToEnv(const char *name, const nsACString & word)
{
char *expr = PR_smprintf("%s=%s", name, PromiseFlatCString(word).get());
if (expr)
PR_SetEnv(expr);
// We intentionally leak |expr| here since it is required by PR_SetEnv.
}
// Save the path of the given file to the specified environment variable.
static void
SaveFileToEnv(const char *name, nsIFile *file)
{
#ifdef XP_WIN
nsAutoString path;
file->GetPath(path);
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
static 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
strimatch(const char* lowerstr, const char* mixedstr)
{
while(*lowerstr) {
if (!*mixedstr) return false; // mixedstr is shorter
if (tolower(*mixedstr) != *lowerstr) return false; // no match
++lowerstr;
++mixedstr;
}
if (*mixedstr) return false; // lowerstr is shorter
return true;
}
static bool gIsExpectedExit = false;
void MozExpectedExit() {
gIsExpectedExit = true;
}
/**
* Runs atexit() to catch unexpected exit from 3rd party libraries like the
* Intel graphics driver calling exit in an error condition. When they
* call exit() to report an error we won't shutdown correctly and wont catch
* the issue with our crash reporter.
*/
static void UnexpectedExit() {
if (!gIsExpectedExit) {
gIsExpectedExit = true; // Don't risk re-entrency issues when crashing.
MOZ_CRASH("Exit called by third party code.");
}
}
/**
* Output a string to the user. This method is really only meant to be used to
* output last-ditch error messages designed for developers NOT END USERS.
*
* @param isError
* Pass true to indicate severe errors.
* @param fmt
* printf-style format string followed by arguments.
*/
static void Output(bool isError, const char *fmt, ... )
{
va_list ap;
va_start(ap, fmt);
#if defined(XP_WIN) && !MOZ_WINCONSOLE
char *msg = PR_vsmprintf(fmt, ap);
if (msg)
{
UINT flags = MB_OK;
if (isError)
flags |= MB_ICONERROR;
else
flags |= MB_ICONINFORMATION;
wchar_t wide_msg[1024];
MultiByteToWideChar(CP_ACP,
0,
msg,
-1,
wide_msg,
sizeof(wide_msg) / sizeof(wchar_t));
MessageBoxW(nullptr, wide_msg, L"XULRunner", flags);
PR_smprintf_free(msg);
}
#else
vfprintf(stderr, fmt, ap);
#endif
va_end(ap);
}
enum RemoteResult {
REMOTE_NOT_FOUND = 0,
REMOTE_FOUND = 1,
REMOTE_ARG_BAD = 2
};
enum ArgResult {
ARG_NONE = 0,
ARG_FOUND = 1,
ARG_BAD = 2 // you wanted a param, but there isn't one
};
static void RemoveArg(char **argv)
{
do {
*argv = *(argv + 1);
++argv;
} while (*argv);
--gArgc;
}
/**
* Check for a commandline flag. If the flag takes a parameter, the
* parameter is returned in aParam. Flags may be in the form -arg or
* --arg (or /arg on win32).
*
* @param aArg the parameter to check. Must be lowercase.
* @param aCheckOSInt if true returns ARG_BAD if the osint argument is present
* when aArg is also present.
* @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 aRemArg if true, the argument is removed from the gArgv array.
*/
static ArgResult
CheckArg(const char* aArg, bool aCheckOSInt = false, const char **aParam = nullptr, bool aRemArg = true)
{
MOZ_ASSERT(gArgv, "gArgv must be initialized before CheckArg()");
char **curarg = gArgv + 1; // skip argv[0]
ArgResult ar = ARG_NONE;
while (*curarg) {
char *arg = curarg[0];
if (arg[0] == '-'
#if defined(XP_WIN)
|| *arg == '/'
#endif
) {
++arg;
if (*arg == '-')
++arg;
if (strimatch(aArg, arg)) {
if (aRemArg)
RemoveArg(curarg);
else
++curarg;
if (!aParam) {
ar = ARG_FOUND;
break;
}
if (*curarg) {
if (**curarg == '-'
#if defined(XP_WIN)
|| **curarg == '/'
#endif
)
return ARG_BAD;
*aParam = *curarg;
if (aRemArg)
RemoveArg(curarg);
ar = ARG_FOUND;
break;
}
return ARG_BAD;
}
}
++curarg;
}
if (aCheckOSInt && ar == ARG_FOUND) {
ArgResult arOSInt = CheckArg("osint");
if (arOSInt == ARG_FOUND) {
ar = ARG_BAD;
PR_fprintf(PR_STDERR, "Error: argument --osint is invalid\n");
}
}
return ar;
}
#if defined(XP_WIN)
/**
* Check for a commandline flag from the windows shell and remove it from the
* argv used when restarting. Flags MUST be in the form -arg.
*
* @param aArg the parameter to check. Must be lowercase.
*/
static ArgResult
CheckArgShell(const char* aArg)
{
char **curarg = gRestartArgv + 1; // skip argv[0]
while (*curarg) {
char *arg = curarg[0];
if (arg[0] == '-') {
++arg;
if (strimatch(aArg, arg)) {
do {
*curarg = *(curarg + 1);
++curarg;
} while (*curarg);
--gRestartArgc;
return ARG_FOUND;
}
}
++curarg;
}
return ARG_NONE;
}
/**
* Enabled Native App Support to process DDE messages when the app needs to
* restart and the app has been launched by the Windows shell to open an url.
* When aWait is false this will process the DDE events manually. This prevents
* Windows from displaying an error message due to the DDE message not being
* acknowledged.
*/
static void
ProcessDDE(nsINativeAppSupport* aNative, bool aWait)
{
// When the app is launched by the windows shell the windows shell
// expects the app to be available for DDE messages and if it isn't
// windows displays an error dialog. To prevent the error the DDE server
// is enabled and pending events are processed when the app needs to
// restart after it was launched by the shell with the requestpending
// argument. The requestpending pending argument is removed to
// differentiate it from being launched when an app restart is not
// required.
ArgResult ar;
ar = CheckArgShell("requestpending");
if (ar == ARG_FOUND) {
aNative->Enable(); // enable win32 DDE responses
if (aWait) {
nsIThread *thread = NS_GetCurrentThread();
// This is just a guesstimate based on testing different values.
// If count is 8 or less windows will display an error dialog.
int32_t count = 20;
while(--count >= 0) {
NS_ProcessNextEvent(thread);
PR_Sleep(PR_MillisecondsToInterval(1));
}
}
}
}
#endif
/**
* Determines if there is support for showing the profile manager
*
* @return true in all environments
*/
static bool
CanShowProfileManager()
{
return true;
}
bool gSafeMode = false;
/**
* The nsXULAppInfo object implements nsIFactory so that it can be its own
* singleton.
*/
class nsXULAppInfo : public nsIXULAppInfo,
public nsIObserver,
#ifdef XP_WIN
public nsIWinAppHelper,
#endif
public nsIXULRuntime
{
public:
constexpr nsXULAppInfo() {}
NS_DECL_ISUPPORTS_INHERITED
NS_DECL_NSIPLATFORMINFO
NS_DECL_NSIXULAPPINFO
NS_DECL_NSIXULRUNTIME
NS_DECL_NSIOBSERVER
#ifdef XP_WIN
NS_DECL_NSIWINAPPHELPER
#endif
};
NS_INTERFACE_MAP_BEGIN(nsXULAppInfo)
NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIXULRuntime)
NS_INTERFACE_MAP_ENTRY(nsIXULRuntime)
NS_INTERFACE_MAP_ENTRY(nsIObserver)
#ifdef XP_WIN
NS_INTERFACE_MAP_ENTRY(nsIWinAppHelper)
#endif
NS_INTERFACE_MAP_ENTRY(nsIPlatformInfo)
NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIXULAppInfo, gAppData ||
XRE_IsContentProcess())
NS_INTERFACE_MAP_END
NS_IMETHODIMP_(MozExternalRefCountType)
nsXULAppInfo::AddRef()
{
return 1;
}
NS_IMETHODIMP_(MozExternalRefCountType)
nsXULAppInfo::Release()
{
return 1;
}
NS_IMETHODIMP
nsXULAppInfo::GetVendor(nsACString& aResult)
{
if (XRE_IsContentProcess()) {
ContentChild* cc = ContentChild::GetSingleton();
aResult = cc->GetAppInfo().vendor;
return NS_OK;
}
aResult.Assign(gAppData->vendor);
return NS_OK;
}
NS_IMETHODIMP
nsXULAppInfo::GetName(nsACString& aResult)
{
if (XRE_IsContentProcess()) {
ContentChild* cc = ContentChild::GetSingleton();
aResult = cc->GetAppInfo().name;
return NS_OK;
}
aResult.Assign(gAppData->name);
return NS_OK;
}
NS_IMETHODIMP
nsXULAppInfo::GetID(nsACString& aResult)
{
if (XRE_IsContentProcess()) {
ContentChild* cc = ContentChild::GetSingleton();
aResult = cc->GetAppInfo().ID;
return NS_OK;
}
aResult.Assign(gAppData->ID);
return NS_OK;
}
NS_IMETHODIMP
nsXULAppInfo::GetVersion(nsACString& aResult)
{
if (XRE_IsContentProcess()) {
ContentChild* cc = ContentChild::GetSingleton();
aResult = cc->GetAppInfo().version;
return NS_OK;
}
aResult.Assign(gAppData->version);
return NS_OK;
}
NS_IMETHODIMP
nsXULAppInfo::GetPlatformVersion(nsACString& aResult)
{
aResult.Assign(gToolkitVersion);
return NS_OK;
}
NS_IMETHODIMP
nsXULAppInfo::GetAppBuildID(nsACString& aResult)
{
if (XRE_IsContentProcess()) {
ContentChild* cc = ContentChild::GetSingleton();
aResult = cc->GetAppInfo().buildID;
return NS_OK;
}
aResult.Assign(gAppData->buildID);
return NS_OK;
}
NS_IMETHODIMP
nsXULAppInfo::GetPlatformBuildID(nsACString& aResult)
{
aResult.Assign(gToolkitBuildID);
return NS_OK;
}
NS_IMETHODIMP
nsXULAppInfo::GetUAName(nsACString& aResult)
{
if (XRE_IsContentProcess()) {
ContentChild* cc = ContentChild::GetSingleton();
aResult = cc->GetAppInfo().UAName;
return NS_OK;
}
aResult.Assign(gAppData->UAName);
return NS_OK;
}
NS_IMETHODIMP
nsXULAppInfo::GetLogConsoleErrors(bool *aResult)
{
*aResult = gLogConsoleErrors;
return NS_OK;
}
NS_IMETHODIMP
nsXULAppInfo::SetLogConsoleErrors(bool aValue)
{
gLogConsoleErrors = aValue;
return NS_OK;
}
NS_IMETHODIMP
nsXULAppInfo::GetInSafeMode(bool *aResult)
{
*aResult = gSafeMode;
return NS_OK;
}
NS_IMETHODIMP
nsXULAppInfo::GetOS(nsACString& aResult)
{
aResult.AssignLiteral(OS_TARGET);
return NS_OK;
}
NS_IMETHODIMP
nsXULAppInfo::GetXPCOMABI(nsACString& aResult)
{
#ifdef TARGET_XPCOM_ABI
aResult.AssignLiteral(TARGET_XPCOM_ABI);
return NS_OK;
#else
return NS_ERROR_NOT_AVAILABLE;
#endif
}
NS_IMETHODIMP
nsXULAppInfo::GetWidgetToolkit(nsACString& aResult)
{
aResult.AssignLiteral(MOZ_WIDGET_TOOLKIT);
return NS_OK;
}
// Ensure that the GeckoProcessType enum, defined in xpcom/build/nsXULAppAPI.h,
// is synchronized with the const unsigned longs defined in
// xpcom/system/nsIXULRuntime.idl.
#define SYNC_ENUMS(a,b) \
static_assert(nsIXULRuntime::PROCESS_TYPE_ ## a == \
static_cast<int>(GeckoProcessType_ ## b), \
"GeckoProcessType in nsXULAppAPI.h not synchronized with nsIXULRuntime.idl");
SYNC_ENUMS(DEFAULT, Default)
SYNC_ENUMS(PLUGIN, Plugin)
SYNC_ENUMS(CONTENT, Content)
SYNC_ENUMS(IPDLUNITTEST, IPDLUnitTest)
SYNC_ENUMS(GMPLUGIN, GMPlugin)
SYNC_ENUMS(GPU, GPU)
// .. and ensure that that is all of them:
static_assert(GeckoProcessType_GPU + 1 == GeckoProcessType_End,
"Did not find the final GeckoProcessType");
NS_IMETHODIMP
nsXULAppInfo::GetProcessType(uint32_t* aResult)
{
NS_ENSURE_ARG_POINTER(aResult);
*aResult = XRE_GetProcessType();
return NS_OK;
}
NS_IMETHODIMP
nsXULAppInfo::GetProcessID(uint32_t* aResult)
{
#ifdef XP_WIN
*aResult = GetCurrentProcessId();
#else
*aResult = getpid();
#endif
return NS_OK;
}
NS_IMETHODIMP
nsXULAppInfo::GetUniqueProcessID(uint64_t* aResult)
{
if (XRE_IsContentProcess()) {
ContentChild* cc = ContentChild::GetSingleton();
*aResult = cc->GetID();
} else {
*aResult = 0;
}
return NS_OK;
}
static bool gBrowserTabsRemoteAutostart = false;
static uint64_t gBrowserTabsRemoteStatus = 0;
static bool gBrowserTabsRemoteAutostartInitialized = false;
static bool gMultiprocessBlockPolicyInitialized = false;
static uint32_t gMultiprocessBlockPolicy = 0;
NS_IMETHODIMP
nsXULAppInfo::Observe(nsISupports *aSubject, const char *aTopic, const char16_t *aData) {
if (!nsCRT::strcmp(aTopic, "getE10SBlocked")) {
nsCOMPtr<nsISupportsPRUint64> ret = do_QueryInterface(aSubject);
if (!ret)
return NS_ERROR_FAILURE;
ret->SetData(gBrowserTabsRemoteStatus);
return NS_OK;
}
return NS_ERROR_FAILURE;
}
NS_IMETHODIMP
nsXULAppInfo::GetBrowserTabsRemoteAutostart(bool* aResult)
{
*aResult = BrowserTabsRemoteAutostart();
return NS_OK;
}
NS_IMETHODIMP
nsXULAppInfo::GetMultiprocessBlockPolicy(uint32_t* aResult)
{
*aResult = MultiprocessBlockPolicy();
return NS_OK;
}
NS_IMETHODIMP
nsXULAppInfo::GetAccessibilityEnabled(bool* aResult)
{
#ifdef ACCESSIBILITY
*aResult = GetAccService() != nullptr;
#else
*aResult = false;
#endif
return NS_OK;
}
NS_IMETHODIMP
nsXULAppInfo::GetIs64Bit(bool* aResult)
{
#ifdef HAVE_64BIT_BUILD
*aResult = true;
#else
*aResult = false;
#endif
return NS_OK;
}
NS_IMETHODIMP
nsXULAppInfo::EnsureContentProcess()
{
if (!XRE_IsParentProcess())
return NS_ERROR_NOT_AVAILABLE;
RefPtr<ContentParent> unused = ContentParent::GetNewOrUsedBrowserProcess();
return NS_OK;
}
NS_IMETHODIMP
nsXULAppInfo::InvalidateCachesOnRestart()
{
nsCOMPtr<nsIFile> file;
nsresult rv = NS_GetSpecialDirectory(NS_APP_PROFILE_DIR_STARTUP,
getter_AddRefs(file));
if (NS_FAILED(rv))
return rv;
if (!file)
return NS_ERROR_NOT_AVAILABLE;
file->AppendNative(FILE_COMPATIBILITY_INFO);
nsINIParser parser;
rv = parser.Init(file);
if (NS_FAILED(rv)) {
// This fails if compatibility.ini is not there, so we'll
// flush the caches on the next restart anyways.
return NS_OK;
}
nsAutoCString buf;
rv = parser.GetString("Compatibility", "InvalidateCaches", buf);
if (NS_FAILED(rv)) {
PRFileDesc *fd;
rv = file->OpenNSPRFileDesc(PR_RDWR | PR_APPEND, 0600, &fd);
if (NS_FAILED(rv)) {
NS_ERROR("could not create output stream");
return NS_ERROR_NOT_AVAILABLE;
}
static const char kInvalidationHeader[] = NS_LINEBREAK "InvalidateCaches=1" NS_LINEBREAK;
PR_Write(fd, kInvalidationHeader, sizeof(kInvalidationHeader) - 1);
PR_Close(fd);
}
return NS_OK;
}
NS_IMETHODIMP
nsXULAppInfo::GetReplacedLockTime(PRTime *aReplacedLockTime)
{
if (!gProfileLock)
return NS_ERROR_NOT_AVAILABLE;
gProfileLock->GetReplacedLockTime(aReplacedLockTime);
return NS_OK;
}
NS_IMETHODIMP
nsXULAppInfo::GetLastRunCrashID(nsAString &aLastRunCrashID)
{
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
nsXULAppInfo::GetIsReleaseOrBeta(bool* aResult)
{
#ifdef RELEASE_OR_BETA
*aResult = true;
#else
*aResult = false;
#endif
return NS_OK;
}
NS_IMETHODIMP
nsXULAppInfo::GetIsOfficialBranding(bool* aResult)
{
#ifdef MOZ_OFFICIAL_BRANDING
*aResult = true;
#else
*aResult = false;
#endif
return NS_OK;
}
NS_IMETHODIMP
nsXULAppInfo::GetDefaultUpdateChannel(nsACString& aResult)
{
aResult.AssignLiteral(NS_STRINGIFY(MOZ_UPDATE_CHANNEL));
return NS_OK;
}
NS_IMETHODIMP
nsXULAppInfo::GetDistributionID(nsACString& aResult)
{
aResult.AssignLiteral(MOZ_DISTRIBUTION_ID);
return NS_OK;
}
NS_IMETHODIMP
nsXULAppInfo::GetIsOfficial(bool* aResult)
{
#ifdef MC_OFFICIAL
*aResult = true;
#else
*aResult = false;
#endif
return NS_OK;
}
NS_IMETHODIMP
nsXULAppInfo::GetWindowsDLLBlocklistStatus(bool* aResult)
{
#if defined(XP_WIN)
*aResult = gAppData->flags & NS_XRE_DLL_BLOCKLIST_ENABLED;
#else
*aResult = false;
#endif
return NS_OK;
}
#ifdef XP_WIN
// Matches the enum in WinNT.h for the Vista SDK but renamed so that we can
// safely build with the Vista SDK and without it.
typedef enum
{
VistaTokenElevationTypeDefault = 1,
VistaTokenElevationTypeFull,
VistaTokenElevationTypeLimited
} VISTA_TOKEN_ELEVATION_TYPE;
// avoid collision with TokeElevationType enum in WinNT.h
// of the Vista SDK
#define VistaTokenElevationType static_cast< TOKEN_INFORMATION_CLASS >( 18 )
NS_IMETHODIMP
nsXULAppInfo::GetUserCanElevate(bool *aUserCanElevate)
{
HANDLE hToken;
VISTA_TOKEN_ELEVATION_TYPE elevationType;
DWORD dwSize;
if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hToken) ||
!GetTokenInformation(hToken, VistaTokenElevationType, &elevationType,
sizeof(elevationType), &dwSize)) {
*aUserCanElevate = false;
}
else {
// The possible values returned for elevationType and their meanings are:
// TokenElevationTypeDefault: The token does not have a linked token
// (e.g. UAC disabled or a standard user, so they can't be elevated)
// TokenElevationTypeFull: The token is linked to an elevated token
// (e.g. UAC is enabled and the user is already elevated so they can't
// be elevated again)
// TokenElevationTypeLimited: The token is linked to a limited token
// (e.g. UAC is enabled and the user is not elevated, so they can be
// elevated)
*aUserCanElevate = (elevationType == VistaTokenElevationTypeLimited);
}
if (hToken)
CloseHandle(hToken);
return NS_OK;
}
#endif
static const nsXULAppInfo kAppInfo;
static nsresult AppInfoConstructor(nsISupports* aOuter,
REFNSIID aIID, void **aResult)
{
NS_ENSURE_NO_AGGREGATION(aOuter);
return const_cast<nsXULAppInfo*>(&kAppInfo)->
QueryInterface(aIID, aResult);
}
bool gLogConsoleErrors = false;
#define NS_ENSURE_TRUE_LOG(x, ret) \
PR_BEGIN_MACRO \
if (MOZ_UNLIKELY(!(x))) { \
NS_WARNING("NS_ENSURE_TRUE(" #x ") failed"); \
gLogConsoleErrors = true; \
return ret; \
} \
PR_END_MACRO
#define NS_ENSURE_SUCCESS_LOG(res, ret) \
NS_ENSURE_TRUE_LOG(NS_SUCCEEDED(res), ret)
/**
* Because we're starting/stopping XPCOM several times in different scenarios,
* this class is a stack-based critter that makes sure that XPCOM is shut down
* during early returns.
*/
class ScopedXPCOMStartup
{
public:
ScopedXPCOMStartup() :
mServiceManager(nullptr) { }
~ScopedXPCOMStartup();
nsresult Initialize();
nsresult SetWindowCreator(nsINativeAppSupport* native);
static nsresult CreateAppSupport(nsISupports* aOuter, REFNSIID aIID, void** aResult);
private:
nsIServiceManager* mServiceManager;
static nsINativeAppSupport* gNativeAppSupport;
};
ScopedXPCOMStartup::~ScopedXPCOMStartup()
{
NS_IF_RELEASE(gNativeAppSupport);
if (mServiceManager) {
#ifdef XP_MACOSX
// On OS X, we need a pool to catch cocoa objects that are autoreleased
// during teardown.
mozilla::MacAutoreleasePool pool;
#endif
nsCOMPtr<nsIAppStartup> appStartup (do_GetService(NS_APPSTARTUP_CONTRACTID));
if (appStartup)
appStartup->DestroyHiddenWindow();
gDirServiceProvider->DoShutdown();
PROFILER_MARKER("Shutdown early");
WriteConsoleLog();
NS_ShutdownXPCOM(mServiceManager);
mServiceManager = nullptr;
}
}
// {95d89e3e-a169-41a3-8e56-719978e15b12}
#define APPINFO_CID \
{ 0x95d89e3e, 0xa169, 0x41a3, { 0x8e, 0x56, 0x71, 0x99, 0x78, 0xe1, 0x5b, 0x12 } }
// {0C4A446C-EE82-41f2-8D04-D366D2C7A7D4}
static const nsCID kNativeAppSupportCID =
{ 0xc4a446c, 0xee82, 0x41f2, { 0x8d, 0x4, 0xd3, 0x66, 0xd2, 0xc7, 0xa7, 0xd4 } };
// {5F5E59CE-27BC-47eb-9D1F-B09CA9049836}
static const nsCID kProfileServiceCID =
{ 0x5f5e59ce, 0x27bc, 0x47eb, { 0x9d, 0x1f, 0xb0, 0x9c, 0xa9, 0x4, 0x98, 0x36 } };
static already_AddRefed<nsIFactory>
ProfileServiceFactoryConstructor(const mozilla::Module& module, const mozilla::Module::CIDEntry& entry)
{
nsCOMPtr<nsIFactory> factory;
NS_NewToolkitProfileFactory(getter_AddRefs(factory));
return factory.forget();
}
NS_DEFINE_NAMED_CID(APPINFO_CID);
static const mozilla::Module::CIDEntry kXRECIDs[] = {
{ &kAPPINFO_CID, false, nullptr, AppInfoConstructor },
{ &kProfileServiceCID, false, ProfileServiceFactoryConstructor, nullptr },
{ &kNativeAppSupportCID, false, nullptr, ScopedXPCOMStartup::CreateAppSupport },
{ nullptr }
};
static const mozilla::Module::ContractIDEntry kXREContracts[] = {
{ XULAPPINFO_SERVICE_CONTRACTID, &kAPPINFO_CID },
{ XULRUNTIME_SERVICE_CONTRACTID, &kAPPINFO_CID },
{ NS_PROFILESERVICE_CONTRACTID, &kProfileServiceCID },
{ NS_NATIVEAPPSUPPORT_CONTRACTID, &kNativeAppSupportCID },
{ nullptr }
};
static const mozilla::Module kXREModule = {
mozilla::Module::kVersion,
kXRECIDs,
kXREContracts
};
NSMODULE_DEFN(Apprunner) = &kXREModule;
nsresult
ScopedXPCOMStartup::Initialize()
{
NS_ASSERTION(gDirServiceProvider, "Should not get here!");
nsresult rv;
rv = NS_InitXPCOM2(&mServiceManager, gDirServiceProvider->GetAppDir(),
gDirServiceProvider);
if (NS_FAILED(rv)) {
NS_ERROR("Couldn't start xpcom!");
mServiceManager = nullptr;
}
else {
#ifdef DEBUG
nsCOMPtr<nsIComponentRegistrar> reg =
do_QueryInterface(mServiceManager);
NS_ASSERTION(reg, "Service Manager doesn't QI to Registrar.");
#endif
}
return rv;
}
/**
* This is a little factory class that serves as a singleton-service-factory
* for the nativeappsupport object.
*/
class nsSingletonFactory final : public nsIFactory
{
public:
NS_DECL_ISUPPORTS
NS_DECL_NSIFACTORY
explicit nsSingletonFactory(nsISupports* aSingleton);
private:
~nsSingletonFactory() { }
nsCOMPtr<nsISupports> mSingleton;
};
nsSingletonFactory::nsSingletonFactory(nsISupports* aSingleton)
: mSingleton(aSingleton)
{
NS_ASSERTION(mSingleton, "Singleton was null!");
}
NS_IMPL_ISUPPORTS(nsSingletonFactory, nsIFactory)
NS_IMETHODIMP
nsSingletonFactory::CreateInstance(nsISupports* aOuter,
const nsIID& aIID,
void* *aResult)
{
NS_ENSURE_NO_AGGREGATION(aOuter);
return mSingleton->QueryInterface(aIID, aResult);
}
NS_IMETHODIMP
nsSingletonFactory::LockFactory(bool)
{
return NS_OK;
}
/**
* Set our windowcreator on the WindowWatcher service.
*/
nsresult
ScopedXPCOMStartup::SetWindowCreator(nsINativeAppSupport* native)
{
nsresult rv;
NS_IF_ADDREF(gNativeAppSupport = native);
// Inform the chrome registry about OS accessibility
nsCOMPtr<nsIToolkitChromeRegistry> cr =
mozilla::services::GetToolkitChromeRegistryService();
if (cr)
cr->CheckForOSAccessibility();
nsCOMPtr<nsIWindowCreator> creator (do_GetService(NS_APPSTARTUP_CONTRACTID));
if (!creator) return NS_ERROR_UNEXPECTED;
nsCOMPtr<nsIWindowWatcher> wwatch
(do_GetService(NS_WINDOWWATCHER_CONTRACTID, &rv));
NS_ENSURE_SUCCESS(rv, rv);
return wwatch->SetWindowCreator(creator);
}
/* static */ nsresult
ScopedXPCOMStartup::CreateAppSupport(nsISupports* aOuter, REFNSIID aIID, void** aResult)
{
if (aOuter)
return NS_ERROR_NO_AGGREGATION;
if (!gNativeAppSupport)
return NS_ERROR_NOT_INITIALIZED;
return gNativeAppSupport->QueryInterface(aIID, aResult);
}
nsINativeAppSupport* ScopedXPCOMStartup::gNativeAppSupport;
static void DumpArbitraryHelp()
{
nsresult rv;
ScopedLogging log;
{
ScopedXPCOMStartup xpcom;
xpcom.Initialize();
nsCOMPtr<nsICommandLineRunner> cmdline
(do_CreateInstance("@mozilla.org/toolkit/command-line;1"));
if (!cmdline)
return;
nsCString text;
rv = cmdline->GetHelpText(text);
if (NS_SUCCEEDED(rv))
printf("%s", text.get());
}
}
// English text needs to go into a dtd file.
// But when this is called we have no components etc. These strings must either be
// here, or in a native resource file.
static void
DumpHelp()
{
printf("Usage: %s [ options ... ] [URL]\n"
" where options include:\n\n", gArgv[0]);
#ifdef MOZ_X11
printf("X11 options\n"
" --display=DISPLAY X display to use.\n"
" --sync Make X calls synchronous.\n");
#endif
#ifdef XP_UNIX
printf(" --g-fatal-warnings Make all warnings fatal.\n"
"\n%s options\n", gAppData->name);
#endif
printf(" -h or --help Print this message.\n"
" -v or --version Print %s version.\n"
" -P <profile> Start with <profile>.\n"
" --profile <path> Start with profile at <path>.\n"
#ifdef MC_BASILISK
" --migration Start with migration wizard.\n"
#endif
" --ProfileManager Start with ProfileManager.\n"
" --no-remote Do not accept or send remote commands;\n"
" implies --new-instance.\n"
" --new-instance Open new instance, not a new window\n"
" in running instance.\n"
" --UILocale <locale> Start with <locale> resources as UI Locale.\n"
" --safe-mode Disables extensions and themes for this session.\n", (const char*) gAppData->name);
#if defined(XP_WIN)
printf(" --console Start %s with a debugging console.\n", (const char*) gAppData->name);
#endif
// this works, but only after the components have registered. so if you drop in a new command line handler, --help
// won't not until the second run.
// out of the bug, because we ship a component.reg file, it works correctly.
DumpArbitraryHelp();
}
#if defined(DEBUG) && defined(XP_WIN)
#ifdef DEBUG_warren
#define _CRTDBG_MAP_ALLOC
#endif
// Set a CRT ReportHook function to capture and format MSCRT
// warnings, errors and assertions.
// See http://msdn.microsoft.com/en-US/library/74kabxyx(v=VS.80).aspx
#include <stdio.h>
#include <crtdbg.h>
#include "mozilla/mozalloc_abort.h"
static int MSCRTReportHook( int aReportType, char *aMessage, int *oReturnValue)
{
*oReturnValue = 0; // continue execution
// Do not use fprintf or other functions which may allocate
// memory from the heap which may be corrupted. Instead,
// use fputs to output the leading portion of the message
// and use mozalloc_abort to emit the remainder of the
// message.
switch(aReportType) {
case 0:
fputs("\nWARNING: CRT WARNING", stderr);
fputs(aMessage, stderr);
fputs("\n", stderr);
break;
case 1:
fputs("\n###!!! ABORT: CRT ERROR ", stderr);
mozalloc_abort(aMessage);
break;
case 2:
fputs("\n###!!! ABORT: CRT ASSERT ", stderr);
mozalloc_abort(aMessage);
break;
}
// do not invoke the debugger
return 1;
}
#endif
static inline void
DumpVersion()
{
if (gAppData->vendor)
printf("%s ", gAppData->vendor);
printf("%s %s", gAppData->name, gAppData->version);
if (gAppData->copyright)
printf(", %s", gAppData->copyright);
printf("\n");
}
#ifdef MOZ_ENABLE_XREMOTE
static RemoteResult
ParseRemoteCommandLine(nsCString& program,
const char** profile,
const char** username)
{
ArgResult ar;
ar = CheckArg("p", false, profile, false);
if (ar == ARG_BAD) {
// Leave it to the normal command line handling to handle this situation.
return REMOTE_NOT_FOUND;
}
const char *temp = nullptr;
ar = CheckArg("a", true, &temp);
if (ar == ARG_BAD) {
PR_fprintf(PR_STDERR, "Error: argument -a requires an application name\n");
return REMOTE_ARG_BAD;
} else if (ar == ARG_FOUND) {
program.Assign(temp);
}
ar = CheckArg("u", true, username);
if (ar == ARG_BAD) {
PR_fprintf(PR_STDERR, "Error: argument -u requires a username\n");
return REMOTE_ARG_BAD;
}
return REMOTE_FOUND;
}
static RemoteResult
StartRemoteClient(const char* aDesktopStartupID,
nsCString& program,
const char* profile,
const char* username)
{
XRemoteClient client;
nsresult rv = client.Init();
if (NS_FAILED(rv))
return REMOTE_NOT_FOUND;
nsXPIDLCString response;
bool success = false;
rv = client.SendCommandLine(program.get(), username, profile,
gArgc, gArgv, aDesktopStartupID,
getter_Copies(response), &success);
// did the command fail?
if (!success)
return REMOTE_NOT_FOUND;
// The "command not parseable" error is returned when the
// nsICommandLineHandler throws a NS_ERROR_ABORT.
if (response.EqualsLiteral("500 command not parseable"))
return REMOTE_ARG_BAD;
if (NS_FAILED(rv))
return REMOTE_NOT_FOUND;
return REMOTE_FOUND;
}
#endif // MOZ_ENABLE_XREMOTE
void
XRE_InitOmnijar(nsIFile* greOmni, nsIFile* appOmni)
{
mozilla::Omnijar::Init(greOmni, appOmni);
}
nsresult
XRE_GetBinaryPath(const char* argv0, nsIFile* *aResult)
{
return mozilla::BinaryPath::GetFile(argv0, aResult);
}
#ifdef XP_WIN
#include "nsWindowsRestart.cpp"
#include <shellapi.h>
typedef BOOL (WINAPI* SetProcessDEPPolicyFunc)(DWORD dwFlags);
#endif
// If aBlankCommandLine is true, then the application will be launched with a
// blank command line instead of being launched with the same command line that
// it was initially started with.
static nsresult LaunchChild(nsINativeAppSupport* aNative,
bool aBlankCommandLine = false)
{
aNative->Quit(); // release DDE mutex, if we're holding it
// Restart this process by exec'ing it into the current process
// if supported by the platform. Otherwise, use NSPR.
if (aBlankCommandLine) {
gRestartArgc = 1;
gRestartArgv[gRestartArgc] = nullptr;
}
SaveToEnv("MOZ_LAUNCHED_CHILD=1");
#if defined(MOZ_WIDGET_ANDROID)
java::GeckoAppShell::ScheduleRestart();
#else
#if defined(XP_MACOSX)
CommandLineServiceMac::SetupMacCommandLine(gRestartArgc, gRestartArgv, true);
LaunchChildMac(gRestartArgc, gRestartArgv);
#else
nsCOMPtr<nsIFile> lf;
nsresult rv = XRE_GetBinaryPath(gArgv[0], getter_AddRefs(lf));
if (NS_FAILED(rv))
return rv;
#if defined(XP_WIN)
nsAutoString exePath;
rv = lf->GetPath(exePath);
if (NS_FAILED(rv))
return rv;
if (!WinLaunchChild(exePath.get(), gRestartArgc, gRestartArgv))
return NS_ERROR_FAILURE;
#else
nsAutoCString exePath;
rv = lf->GetNativePath(exePath);
if (NS_FAILED(rv))
return rv;
#if defined(XP_UNIX)
if (execv(exePath.get(), gRestartArgv) == -1)
return NS_ERROR_FAILURE;
#else
PRProcess* process = PR_CreateProcess(exePath.get(), gRestartArgv,
nullptr, nullptr);
if (!process) return NS_ERROR_FAILURE;
int32_t exitCode;
PRStatus failed = PR_WaitProcess(process, &exitCode);
if (failed || exitCode)
return NS_ERROR_FAILURE;
#endif // XP_UNIX
#endif // WP_WIN
#endif // WP_MACOSX
#endif // MOZ_WIDGET_ANDROID
return NS_ERROR_LAUNCHED_CHILD_PROCESS;
}
static const char kProfileProperties[] =
"chrome://mozapps/locale/profile/profileSelection.properties";
namespace {
/**
* This class, instead of a raw nsresult, should be the return type of any
* function called by SelectProfile that initializes XPCOM.
*/
class ReturnAbortOnError
{
public:
MOZ_IMPLICIT ReturnAbortOnError(nsresult aRv)
{
mRv = ConvertRv(aRv);
}
operator nsresult()
{
return mRv;
}
private:
inline nsresult
ConvertRv(nsresult aRv)
{
if (NS_SUCCEEDED(aRv) || aRv == NS_ERROR_LAUNCHED_CHILD_PROCESS) {
return aRv;
}
return NS_ERROR_ABORT;
}
nsresult mRv;
};
} // namespace
static ReturnAbortOnError
ProfileLockedDialog(nsIFile* aProfileDir, nsIFile* aProfileLocalDir,
nsIProfileUnlocker* aUnlocker,
nsINativeAppSupport* aNative, nsIProfileLock* *aResult)
{
nsresult rv;
ScopedXPCOMStartup xpcom;
rv = xpcom.Initialize();
NS_ENSURE_SUCCESS(rv, rv);
mozilla::Telemetry::WriteFailedProfileLock(aProfileDir);
rv = xpcom.SetWindowCreator(aNative);
NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
{ //extra scoping is needed so we release these components before xpcom shutdown
nsCOMPtr<nsIStringBundleService> sbs =
mozilla::services::GetStringBundleService();
NS_ENSURE_TRUE(sbs, NS_ERROR_FAILURE);
nsCOMPtr<nsIStringBundle> sb;
sbs->CreateBundle(kProfileProperties, getter_AddRefs(sb));
NS_ENSURE_TRUE_LOG(sbs, NS_ERROR_FAILURE);
NS_ConvertUTF8toUTF16 appName(gAppData->name);
const char16_t* params[] = {appName.get(), appName.get()};
nsXPIDLString killMessage;
#ifndef XP_MACOSX
sb->FormatStringFromName(aUnlocker ? u"restartMessageUnlocker"
: u"restartMessageNoUnlocker",
params, 2, getter_Copies(killMessage));
#else
sb->FormatStringFromName(aUnlocker ? u"restartMessageUnlockerMac"
: u"restartMessageNoUnlockerMac",
params, 2, getter_Copies(killMessage));
#endif
nsXPIDLString killTitle;
sb->FormatStringFromName(u"restartTitle",
params, 1, getter_Copies(killTitle));
if (!killMessage || !killTitle)
return NS_ERROR_FAILURE;
nsCOMPtr<nsIPromptService> ps
(do_GetService(NS_PROMPTSERVICE_CONTRACTID));
NS_ENSURE_TRUE(ps, NS_ERROR_FAILURE);
if (aUnlocker) {
int32_t button;
#ifdef MOZ_WIDGET_ANDROID
java::GeckoAppShell::KillAnyZombies();
button = 0;
#else
const uint32_t flags =
(nsIPromptService::BUTTON_TITLE_IS_STRING *
nsIPromptService::BUTTON_POS_0) +
(nsIPromptService::BUTTON_TITLE_CANCEL *
nsIPromptService::BUTTON_POS_1);
bool checkState = false;
rv = ps->ConfirmEx(nullptr, killTitle, killMessage, flags,
killTitle, nullptr, nullptr, nullptr,
&checkState, &button);
NS_ENSURE_SUCCESS_LOG(rv, rv);
#endif
if (button == 0) {
rv = aUnlocker->Unlock(nsIProfileUnlocker::FORCE_QUIT);
if (NS_FAILED(rv)) {
return rv;
}
SaveFileToEnv("XRE_PROFILE_PATH", aProfileDir);
SaveFileToEnv("XRE_PROFILE_LOCAL_PATH", aProfileLocalDir);
return LaunchChild(aNative);
}
} else {
#ifdef MOZ_WIDGET_ANDROID
if (java::GeckoAppShell::UnlockProfile()) {
return NS_LockProfilePath(aProfileDir, aProfileLocalDir,
nullptr, aResult);
}
#else
rv = ps->Alert(nullptr, killTitle, killMessage);
NS_ENSURE_SUCCESS_LOG(rv, rv);
#endif
}
return NS_ERROR_ABORT;
}
}
static nsresult
ProfileMissingDialog(nsINativeAppSupport* aNative)
{
nsresult rv;
ScopedXPCOMStartup xpcom;
rv = xpcom.Initialize();
NS_ENSURE_SUCCESS(rv, rv);
rv = xpcom.SetWindowCreator(aNative);
NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
{ //extra scoping is needed so we release these components before xpcom shutdown
nsCOMPtr<nsIStringBundleService> sbs =
mozilla::services::GetStringBundleService();
NS_ENSURE_TRUE(sbs, NS_ERROR_FAILURE);
nsCOMPtr<nsIStringBundle> sb;
sbs->CreateBundle(kProfileProperties, getter_AddRefs(sb));
NS_ENSURE_TRUE_LOG(sbs, NS_ERROR_FAILURE);
NS_ConvertUTF8toUTF16 appName(gAppData->name);
const char16_t* params[] = {appName.get(), appName.get()};
nsXPIDLString missingMessage;
// profileMissing
sb->FormatStringFromName(u"profileMissing", params, 2, getter_Copies(missingMessage));
nsXPIDLString missingTitle;
sb->FormatStringFromName(u"profileMissingTitle",
params, 1, getter_Copies(missingTitle));
if (missingMessage && missingTitle) {
nsCOMPtr<nsIPromptService> ps
(do_GetService(NS_PROMPTSERVICE_CONTRACTID));
NS_ENSURE_TRUE(ps, NS_ERROR_FAILURE);
ps->Alert(nullptr, missingTitle, missingMessage);
}
return NS_ERROR_ABORT;
}
}
static nsresult
ProfileLockedDialog(nsIToolkitProfile* aProfile, nsIProfileUnlocker* aUnlocker,
nsINativeAppSupport* aNative, nsIProfileLock* *aResult)
{
nsCOMPtr<nsIFile> profileDir;
nsresult rv = aProfile->GetRootDir(getter_AddRefs(profileDir));
if (NS_FAILED(rv)) return rv;
bool exists;
profileDir->Exists(&exists);
if (!exists) {
return ProfileMissingDialog(aNative);
}
nsCOMPtr<nsIFile> profileLocalDir;
rv = aProfile->GetLocalDir(getter_AddRefs(profileLocalDir));
if (NS_FAILED(rv)) return rv;
return ProfileLockedDialog(profileDir, profileLocalDir, aUnlocker, aNative,
aResult);
}
static const char kProfileManagerURL[] =
"chrome://mozapps/content/profile/profileSelection.xul";
static ReturnAbortOnError
ShowProfileManager(nsIToolkitProfileService* aProfileSvc,
nsINativeAppSupport* aNative)
{
if (!CanShowProfileManager()) {
return NS_ERROR_NOT_IMPLEMENTED;
}
nsresult rv;
nsCOMPtr<nsIFile> profD, profLD;
char16_t* profileNamePtr;
nsAutoCString profileName;
{
ScopedXPCOMStartup xpcom;
rv = xpcom.Initialize();
NS_ENSURE_SUCCESS(rv, rv);
// Initialize the graphics prefs, some of the paths need them before
// any other graphics is initialized (e.g., showing the profile chooser.)
gfxPrefs::GetSingleton();
rv = xpcom.SetWindowCreator(aNative);
NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
#ifdef XP_MACOSX
CommandLineServiceMac::SetupMacCommandLine(gRestartArgc, gRestartArgv, true);
#endif
#ifdef XP_WIN
// we don't have to wait here because profile manager window will pump
// and DDE message will be handled
ProcessDDE(aNative, false);
#endif
{ //extra scoping is needed so we release these components before xpcom shutdown
nsCOMPtr<nsIWindowWatcher> windowWatcher
(do_GetService(NS_WINDOWWATCHER_CONTRACTID));
nsCOMPtr<nsIDialogParamBlock> ioParamBlock
(do_CreateInstance(NS_DIALOGPARAMBLOCK_CONTRACTID));
nsCOMPtr<nsIMutableArray> dlgArray (do_CreateInstance(NS_ARRAY_CONTRACTID));
NS_ENSURE_TRUE(windowWatcher && ioParamBlock && dlgArray, NS_ERROR_FAILURE);
ioParamBlock->SetObjects(dlgArray);
nsCOMPtr<nsIAppStartup> appStartup
(do_GetService(NS_APPSTARTUP_CONTRACTID));
NS_ENSURE_TRUE(appStartup, NS_ERROR_FAILURE);
nsCOMPtr<mozIDOMWindowProxy> newWindow;
rv = windowWatcher->OpenWindow(nullptr,
kProfileManagerURL,
"_blank",
"centerscreen,chrome,modal,titlebar",
ioParamBlock,
getter_AddRefs(newWindow));
NS_ENSURE_SUCCESS_LOG(rv, rv);
aProfileSvc->Flush();
int32_t dialogConfirmed;
rv = ioParamBlock->GetInt(0, &dialogConfirmed);
if (NS_FAILED(rv) || dialogConfirmed == 0) return NS_ERROR_ABORT;
nsCOMPtr<nsIProfileLock> lock;
rv = dlgArray->QueryElementAt(0, NS_GET_IID(nsIProfileLock),
getter_AddRefs(lock));
NS_ENSURE_SUCCESS_LOG(rv, rv);
rv = lock->GetDirectory(getter_AddRefs(profD));
NS_ENSURE_SUCCESS(rv, rv);
rv = lock->GetLocalDirectory(getter_AddRefs(profLD));
NS_ENSURE_SUCCESS(rv, rv);
rv = ioParamBlock->GetString(0, &profileNamePtr);
NS_ENSURE_SUCCESS(rv, rv);
CopyUTF16toUTF8(profileNamePtr, profileName);
free(profileNamePtr);
lock->Unlock();
}
}
SaveFileToEnv("XRE_PROFILE_PATH", profD);
SaveFileToEnv("XRE_PROFILE_LOCAL_PATH", profLD);
SaveWordToEnv("XRE_PROFILE_NAME", profileName);
bool offline = false;
aProfileSvc->GetStartOffline(&offline);
if (offline) {
SaveToEnv("XRE_START_OFFLINE=1");
}
return LaunchChild(aNative);
}
/**
* Get the currently running profile using its root directory.
*
* @param aProfileSvc The profile service
* @param aCurrentProfileRoot The root directory of the current profile.
* @param aProfile Out-param that returns the profile object.
* @return an error if aCurrentProfileRoot is not found
*/
static nsresult
GetCurrentProfile(nsIToolkitProfileService* aProfileSvc,
nsIFile* aCurrentProfileRoot,
nsIToolkitProfile** aProfile)
{
NS_ENSURE_ARG_POINTER(aProfileSvc);
NS_ENSURE_ARG_POINTER(aProfile);
nsCOMPtr<nsISimpleEnumerator> profiles;
nsresult rv = aProfileSvc->GetProfiles(getter_AddRefs(profiles));
if (NS_FAILED(rv))
return rv;
bool foundMatchingProfile = false;
nsCOMPtr<nsISupports> supports;
rv = profiles->GetNext(getter_AddRefs(supports));
while (NS_SUCCEEDED(rv)) {
nsCOMPtr<nsIToolkitProfile> profile = do_QueryInterface(supports);
nsCOMPtr<nsIFile> profileRoot;
profile->GetRootDir(getter_AddRefs(profileRoot));
profileRoot->Equals(aCurrentProfileRoot, &foundMatchingProfile);
if (foundMatchingProfile) {
profile.forget(aProfile);
return NS_OK;
}
rv = profiles->GetNext(getter_AddRefs(supports));
}
return rv;
}
static bool gDoMigration = false;
static bool gDoProfileReset = false;
static nsAutoCString gResetOldProfileName;
// 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
// 6) display the profile-manager UI
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", true);
if (ar == ARG_BAD) {
PR_fprintf(PR_STDERR, "Error: argument --offline is invalid when argument --osint is specified\n");
return NS_ERROR_FAILURE;
}
if (ar || EnvHasValue("XRE_START_OFFLINE"))
*aStartOffline = true;
if (EnvHasValue("MOZ_RESET_PROFILE_RESTART")) {
gDoProfileReset = true;
gDoMigration = true;
SaveToEnv("MOZ_RESET_PROFILE_RESTART=");
}
// reset-profile and migration args need to be checked before any profiles are chosen below.
ar = CheckArg("reset-profile", true);
if (ar == ARG_BAD) {
PR_fprintf(PR_STDERR, "Error: argument --reset-profile is invalid when argument --osint is specified\n");
return NS_ERROR_FAILURE;
} else if (ar == ARG_FOUND) {
gDoProfileReset = true;
}
ar = CheckArg("migration", true);
if (ar == ARG_BAD) {
PR_fprintf(PR_STDERR, "Error: argument --migration is invalid when argument --osint is specified\n");
return NS_ERROR_FAILURE;
} else 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", false, &dummy);
CheckArg("profile", false, &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", true, &arg);
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", true, &arg);
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"));
nsAutoCString pathStr;
prefsJSFile->GetNativePath(pathStr);
PR_fprintf(PR_STDERR, "Success: created profile '%s' at '%s'\n", arg, pathStr.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", false, &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;
}
if (CanShowProfileManager()) {
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);
}
if (CanShowProfileManager()) {
return ShowProfileManager(aProfileSvc, aNative);
}
}
ar = CheckArg("profilemanager", true);
if (ar == ARG_BAD) {
PR_fprintf(PR_STDERR, "Error: argument --profilemanager is invalid when argument --osint is specified\n");
return NS_ERROR_FAILURE;
} else if (ar == ARG_FOUND && CanShowProfileManager()) {
return ShowProfileManager(aProfileSvc, aNative);
}
// Dev edition leftovers:
// If the only existing profile is the dev-edition-profile,
// 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;
}
}
if (!count) {
gDoMigration = true;
gDoProfileReset = false;
// create a default profile
nsCOMPtr<nsIToolkitProfile> profile;
nsresult rv = aProfileSvc->CreateProfile(nullptr, // choose a default dir for us
NS_LITERAL_CSTRING("default"),
getter_AddRefs(profile));
if (NS_SUCCEEDED(rv)) {
aProfileSvc->SetDefaultProfile(profile);
aProfileSvc->Flush();
rv = profile->Lock(nullptr, aResult);
if (NS_SUCCEEDED(rv)) {
if (aProfileName)
aProfileName->AssignLiteral("default");
return NS_OK;
}
}
}
bool useDefault = true;
if (count > 1 && CanShowProfileManager()) {
aProfileSvc->GetStartWithLastProfile(&useDefault);
}
if (useDefault) {
nsCOMPtr<nsIToolkitProfile> profile;
// GetSelectedProfile will auto-select the only profile if there's just one
aProfileSvc->GetSelectedProfile(getter_AddRefs(profile));
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;
}
}
// 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);
}
}
if (!CanShowProfileManager()) {
return NS_ERROR_FAILURE;
}
return ShowProfileManager(aProfileSvc, aNative);
}
/**
* 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.
*/
static bool
CheckCompatibility(nsIFile* aProfileDir, const nsCString& aVersion,
const nsCString& aOSABI, nsIFile* aXULRunnerDir,
nsIFile* aAppDir, nsIFile* aFlagFile,
bool* aCachesOK)
{
*aCachesOK = false;
nsCOMPtr<nsIFile> file;
aProfileDir->Clone(getter_AddRefs(file));
if (!file)
return false;
file->AppendNative(FILE_COMPATIBILITY_INFO);
nsINIParser parser;
nsresult rv = parser.Init(file);
if (NS_FAILED(rv))
return false;
nsAutoCString buf;
rv = parser.GetString("Compatibility", "LastVersion", buf);
if (NS_FAILED(rv) || !aVersion.Equals(buf))
return false;
rv = parser.GetString("Compatibility", "LastOSABI", buf);
if (NS_FAILED(rv) || !aOSABI.Equals(buf))
return false;
rv = parser.GetString("Compatibility", "LastPlatformDir", buf);
if (NS_FAILED(rv))
return false;
nsCOMPtr<nsIFile> lf;
rv = NS_NewNativeLocalFile(buf, false,
getter_AddRefs(lf));
if (NS_FAILED(rv))
return false;
bool eq;
rv = lf->Equals(aXULRunnerDir, &eq);
if (NS_FAILED(rv) || !eq)
return false;
if (aAppDir) {
rv = parser.GetString("Compatibility", "LastAppDir", buf);
if (NS_FAILED(rv))
return false;
rv = NS_NewNativeLocalFile(buf, false,
getter_AddRefs(lf));
if (NS_FAILED(rv))
return false;
rv = lf->Equals(aAppDir, &eq);
if (NS_FAILED(rv) || !eq)
return false;
}
// If we see this flag, caches are invalid.
rv = parser.GetString("Compatibility", "InvalidateCaches", buf);
*aCachesOK = (NS_FAILED(rv) || !buf.EqualsLiteral("1"));
bool purgeCaches = false;
if (aFlagFile) {
aFlagFile->Exists(&purgeCaches);
}
*aCachesOK = !purgeCaches && *aCachesOK;
return true;
}
static void BuildVersion(nsCString &aBuf)
{
aBuf.Assign(gAppData->version);
aBuf.Append('_');
aBuf.Append(gAppData->buildID);
aBuf.Append('/');
aBuf.Append(gToolkitBuildID);
}
static void
WriteVersion(nsIFile* aProfileDir, const nsCString& aVersion,
const nsCString& aOSABI, nsIFile* aXULRunnerDir,
nsIFile* aAppDir, bool invalidateCache)
{
nsCOMPtr<nsIFile> file;
aProfileDir->Clone(getter_AddRefs(file));
if (!file)
return;
file->AppendNative(FILE_COMPATIBILITY_INFO);
nsAutoCString platformDir;
aXULRunnerDir->GetNativePath(platformDir);
nsAutoCString appDir;
if (aAppDir)
aAppDir->GetNativePath(appDir);
PRFileDesc *fd;
nsresult rv =
file->OpenNSPRFileDesc(PR_WRONLY | PR_CREATE_FILE | PR_TRUNCATE, 0600, &fd);
if (NS_FAILED(rv)) {
NS_ERROR("could not create output stream");
return;
}
static const char kHeader[] = "[Compatibility]" NS_LINEBREAK
"LastVersion=";
PR_Write(fd, kHeader, sizeof(kHeader) - 1);
PR_Write(fd, aVersion.get(), aVersion.Length());
static const char kOSABIHeader[] = NS_LINEBREAK "LastOSABI=";
PR_Write(fd, kOSABIHeader, sizeof(kOSABIHeader) - 1);
PR_Write(fd, aOSABI.get(), aOSABI.Length());
static const char kPlatformDirHeader[] = NS_LINEBREAK "LastPlatformDir=";
PR_Write(fd, kPlatformDirHeader, sizeof(kPlatformDirHeader) - 1);
PR_Write(fd, platformDir.get(), platformDir.Length());
static const char kAppDirHeader[] = NS_LINEBREAK "LastAppDir=";
if (aAppDir) {
PR_Write(fd, kAppDirHeader, sizeof(kAppDirHeader) - 1);
PR_Write(fd, appDir.get(), appDir.Length());
}
static const char kInvalidationHeader[] = NS_LINEBREAK "InvalidateCaches=1";
if (invalidateCache)
PR_Write(fd, kInvalidationHeader, sizeof(kInvalidationHeader) - 1);
static const char kNL[] = NS_LINEBREAK;
PR_Write(fd, kNL, sizeof(kNL) - 1);
PR_Close(fd);
}
/**
* Returns true if the startup cache file was successfully removed.
* Returns false if file->Clone fails at any point (OOM) or if unable
* to remove the startup cache file. Note in particular the return value
* is unaffected by a failure to remove extensions.ini
*/
static bool
RemoveComponentRegistries(nsIFile* aProfileDir, nsIFile* aLocalProfileDir,
bool aRemoveEMFiles)
{
nsCOMPtr<nsIFile> file;
aProfileDir->Clone(getter_AddRefs(file));
if (!file)
return false;
if (aRemoveEMFiles) {
file->SetNativeLeafName(NS_LITERAL_CSTRING("extensions.ini"));
file->Remove(false);
}
aLocalProfileDir->Clone(getter_AddRefs(file));
if (!file)
return false;
#if defined(XP_UNIX)
#define PLATFORM_FASL_SUFFIX ".mfasl"
#elif defined(XP_WIN)
#define PLATFORM_FASL_SUFFIX ".mfl"
#endif
file->AppendNative(NS_LITERAL_CSTRING("XUL" PLATFORM_FASL_SUFFIX));
file->Remove(false);
file->SetNativeLeafName(NS_LITERAL_CSTRING("XPC" PLATFORM_FASL_SUFFIX));
file->Remove(false);
file->SetNativeLeafName(NS_LITERAL_CSTRING("startupCache"));
nsresult rv = file->Remove(true);
return NS_SUCCEEDED(rv) || rv == NS_ERROR_FILE_TARGET_DOES_NOT_EXIST;
}
// To support application initiated restart via nsIAppStartup.quit, we
// need to save various environment variables, and then restore them
// before re-launching the application.
static struct SavedVar {
const char *name;
char *value;
} gSavedVars[] = {
{"XUL_APP_FILE", nullptr}
};
static void SaveStateForAppInitiatedRestart()
{
for (size_t i = 0; i < ArrayLength(gSavedVars); ++i) {
const char *s = PR_GetEnv(gSavedVars[i].name);
if (s)
gSavedVars[i].value = PR_smprintf("%s=%s", gSavedVars[i].name, s);
}
}
static void RestoreStateForAppInitiatedRestart()
{
for (size_t i = 0; i < ArrayLength(gSavedVars); ++i) {
if (gSavedVars[i].value)
PR_SetEnv(gSavedVars[i].value);
}
}
const nsXREAppData* gAppData = nullptr;
#ifdef MOZ_WIDGET_GTK
static void MOZ_gdk_display_close(GdkDisplay *display)
{
#if CLEANUP_MEMORY
// XXX wallpaper for bug 417163: don't close the Display if we're using the
// Qt theme because we crash (in Qt code) when using jemalloc.
bool skip_display_close = false;
GtkSettings* settings =
gtk_settings_get_for_screen(gdk_display_get_default_screen(display));
gchar *theme_name;
g_object_get(settings, "gtk-theme-name", &theme_name, nullptr);
if (theme_name) {
skip_display_close = strcmp(theme_name, "Qt") == 0;
if (skip_display_close)
NS_WARNING("wallpaper bug 417163 for Qt theme");
g_free(theme_name);
}
#if (MOZ_WIDGET_GTK == 3)
// A workaround for https://bugzilla.gnome.org/show_bug.cgi?id=703257
if (gtk_check_version(3,9,8) != NULL)
skip_display_close = true;
#endif
// Get a (new) Pango context that holds a reference to the fontmap that
// GTK has been using. gdk_pango_context_get() must be called while GTK
// has a default display.
PangoContext *pangoContext = gdk_pango_context_get();
bool buggyCairoShutdown = cairo_version() < CAIRO_VERSION_ENCODE(1, 4, 0);
if (!buggyCairoShutdown) {
// We should shut down GDK before we shut down libraries it depends on
// like Pango and cairo. But if cairo shutdown is buggy, we should
// shut down cairo first otherwise it may crash because of dangling
// references to Display objects (see bug 469831).
if (!skip_display_close)
gdk_display_close(display);
}
// Clean up PangoCairo's default fontmap.
// This pango_fc_font_map_shutdown call (and the associated code to
// get the font map) really shouldn't be needed anymore, except that
// it's needed to avoid having cairo_debug_reset_static_data fatally
// assert if we've leaked other things that hold on to the fontmap,
// which is something that currently happens in mochitest-plugins.
// Even if it didn't happen in mochitest-plugins, we probably want to
// avoid the crash-on-leak problem since it makes it harder to use
// many of our leak tools to debug leaks.
// This doesn't take a reference.
PangoFontMap *fontmap = pango_context_get_font_map(pangoContext);
// Do some shutdown of the fontmap, which releases the fonts, clearing a
// bunch of circular references from the fontmap through the fonts back to
// itself. The shutdown that this does is much less than what's done by
// the fontmap's finalize, though.
if (PANGO_IS_FC_FONT_MAP(fontmap))
pango_fc_font_map_shutdown(PANGO_FC_FONT_MAP(fontmap));
g_object_unref(pangoContext);
// Tell PangoCairo to release its default fontmap.
pango_cairo_font_map_set_default(nullptr);
// cairo_debug_reset_static_data() is prototyped through cairo.h included
// by gtk.h.