summaryrefslogtreecommitdiffstats
path: root/toolkit/xre
diff options
context:
space:
mode:
Diffstat (limited to 'toolkit/xre')
-rw-r--r--toolkit/xre/CreateAppData.cpp165
-rw-r--r--toolkit/xre/EventTracer.cpp267
-rw-r--r--toolkit/xre/EventTracer.h23
-rw-r--r--toolkit/xre/MacApplicationDelegate.h16
-rw-r--r--toolkit/xre/MacApplicationDelegate.mm396
-rw-r--r--toolkit/xre/MacAutoreleasePool.h31
-rw-r--r--toolkit/xre/MacAutoreleasePool.mm20
-rw-r--r--toolkit/xre/MacLaunchHelper.h23
-rw-r--r--toolkit/xre/MacLaunchHelper.mm137
-rw-r--r--toolkit/xre/Makefile.in21
-rw-r--r--toolkit/xre/MozMeegoAppService.h28
-rw-r--r--toolkit/xre/ProfileReset.cpp175
-rw-r--r--toolkit/xre/ProfileReset.h81
-rw-r--r--toolkit/xre/UIKitDirProvider.h13
-rw-r--r--toolkit/xre/UIKitDirProvider.mm19
-rw-r--r--toolkit/xre/glxtest.cpp349
-rw-r--r--toolkit/xre/moz.build185
-rw-r--r--toolkit/xre/nsAndroidStartup.cpp55
-rw-r--r--toolkit/xre/nsAppRunner.cpp5068
-rw-r--r--toolkit/xre/nsAppRunner.h138
-rw-r--r--toolkit/xre/nsCommandLineServiceMac.cpp102
-rw-r--r--toolkit/xre/nsCommandLineServiceMac.h20
-rw-r--r--toolkit/xre/nsConsoleWriter.cpp95
-rw-r--r--toolkit/xre/nsEmbedFunctions.cpp966
-rw-r--r--toolkit/xre/nsGDKErrorHandler.cpp100
-rw-r--r--toolkit/xre/nsGDKErrorHandler.h8
-rw-r--r--toolkit/xre/nsINativeAppSupport.idl104
-rw-r--r--toolkit/xre/nsIWinAppHelper.idl19
-rw-r--r--toolkit/xre/nsNativeAppSupportBase.cpp55
-rw-r--r--toolkit/xre/nsNativeAppSupportBase.h28
-rw-r--r--toolkit/xre/nsNativeAppSupportCocoa.mm198
-rw-r--r--toolkit/xre/nsNativeAppSupportDefault.cpp17
-rw-r--r--toolkit/xre/nsNativeAppSupportUnix.cpp708
-rw-r--r--toolkit/xre/nsNativeAppSupportWin.cpp1541
-rw-r--r--toolkit/xre/nsNativeAppSupportWin.h39
-rw-r--r--toolkit/xre/nsSigHandlers.cpp404
-rw-r--r--toolkit/xre/nsSigHandlers.h39
-rw-r--r--toolkit/xre/nsUpdateDriver.cpp1316
-rw-r--r--toolkit/xre/nsUpdateDriver.h107
-rw-r--r--toolkit/xre/nsWindowsRestart.cpp300
-rw-r--r--toolkit/xre/nsWindowsWMain.cpp122
-rw-r--r--toolkit/xre/nsX11ErrorHandler.cpp162
-rw-r--r--toolkit/xre/nsX11ErrorHandler.h17
-rw-r--r--toolkit/xre/nsXREDirProvider.cpp1905
-rw-r--r--toolkit/xre/nsXREDirProvider.h158
-rw-r--r--toolkit/xre/platform.ini17
-rw-r--r--toolkit/xre/test/.eslintrc.js7
-rw-r--r--toolkit/xre/test/browser.ini4
-rw-r--r--toolkit/xre/test/browser_checkdllblockliststate.js16
-rw-r--r--toolkit/xre/test/mochitest.ini3
-rw-r--r--toolkit/xre/test/test_fpuhandler.html38
-rw-r--r--toolkit/xre/test/win/Makefile.in14
-rw-r--r--toolkit/xre/test/win/TestDllInterceptor.cpp237
-rw-r--r--toolkit/xre/test/win/TestXREMakeCommandLineWin.cpp265
-rw-r--r--toolkit/xre/test/win/TestXREMakeCommandLineWin.ini94
-rw-r--r--toolkit/xre/test/win/moz.build30
-rw-r--r--toolkit/xre/updaterfileutils_osx.h13
-rw-r--r--toolkit/xre/updaterfileutils_osx.mm52
58 files changed, 16530 insertions, 0 deletions
diff --git a/toolkit/xre/CreateAppData.cpp b/toolkit/xre/CreateAppData.cpp
new file mode 100644
index 000000000..8c91ddc87
--- /dev/null
+++ b/toolkit/xre/CreateAppData.cpp
@@ -0,0 +1,165 @@
+/* -*- 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 "nsXULAppAPI.h"
+#include "nsINIParser.h"
+#include "nsIFile.h"
+#include "nsAutoPtr.h"
+#include "mozilla/AppData.h"
+
+using namespace mozilla;
+
+nsresult
+XRE_CreateAppData(nsIFile* aINIFile, nsXREAppData **aAppData)
+{
+ NS_ENSURE_ARG(aINIFile && aAppData);
+
+ nsAutoPtr<ScopedAppData> data(new ScopedAppData());
+ if (!data)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ nsresult rv = XRE_ParseAppData(aINIFile, data);
+ if (NS_FAILED(rv))
+ return rv;
+
+ if (!data->directory) {
+ nsCOMPtr<nsIFile> appDir;
+ rv = aINIFile->GetParent(getter_AddRefs(appDir));
+ if (NS_FAILED(rv))
+ return rv;
+
+ appDir.forget(&data->directory);
+ }
+
+ *aAppData = data.forget();
+ return NS_OK;
+}
+
+struct ReadString {
+ const char *section;
+ const char *key;
+ const char **buffer;
+};
+
+static void
+ReadStrings(nsINIParser &parser, const ReadString *reads)
+{
+ nsresult rv;
+ nsCString str;
+
+ while (reads->section) {
+ rv = parser.GetString(reads->section, reads->key, str);
+ if (NS_SUCCEEDED(rv)) {
+ SetAllocatedString(*reads->buffer, str);
+ }
+
+ ++reads;
+ }
+}
+
+struct ReadFlag {
+ const char *section;
+ const char *key;
+ uint32_t flag;
+};
+
+static void
+ReadFlags(nsINIParser &parser, const ReadFlag *reads, uint32_t *buffer)
+{
+ nsresult rv;
+ char buf[6]; // large enough to hold "false"
+
+ while (reads->section) {
+ rv = parser.GetString(reads->section, reads->key, buf, sizeof(buf));
+ if (NS_SUCCEEDED(rv) || rv == NS_ERROR_LOSS_OF_SIGNIFICANT_DATA) {
+ if (buf[0] == '1' || buf[0] == 't' || buf[0] == 'T') {
+ *buffer |= reads->flag;
+ }
+ if (buf[0] == '0' || buf[0] == 'f' || buf[0] == 'F') {
+ *buffer &= ~reads->flag;
+ }
+ }
+
+ ++reads;
+ }
+}
+
+nsresult
+XRE_ParseAppData(nsIFile* aINIFile, nsXREAppData *aAppData)
+{
+ NS_ENSURE_ARG(aINIFile && aAppData);
+
+ nsresult rv;
+
+ nsINIParser parser;
+ rv = parser.Init(aINIFile);
+ if (NS_FAILED(rv))
+ return rv;
+
+ nsCString str;
+
+ ReadString strings[] = {
+ { "App", "Vendor", &aAppData->vendor },
+ { "App", "Name", &aAppData->name },
+ { "App", "RemotingName", &aAppData->remotingName },
+ { "App", "Version", &aAppData->version },
+ { "App", "BuildID", &aAppData->buildID },
+ { "App", "ID", &aAppData->ID },
+ { "App", "Copyright", &aAppData->copyright },
+ { "App", "Profile", &aAppData->profile },
+ { nullptr }
+ };
+ ReadStrings(parser, strings);
+
+ ReadFlag flags[] = {
+ { "XRE", "EnableProfileMigrator", NS_XRE_ENABLE_PROFILE_MIGRATOR },
+ { nullptr }
+ };
+ ReadFlags(parser, flags, &aAppData->flags);
+
+ if (aAppData->size > offsetof(nsXREAppData, xreDirectory)) {
+ ReadString strings2[] = {
+ { "Gecko", "MinVersion", &aAppData->minVersion },
+ { "Gecko", "MaxVersion", &aAppData->maxVersion },
+ { nullptr }
+ };
+ ReadStrings(parser, strings2);
+ }
+
+ if (aAppData->size > offsetof(nsXREAppData, crashReporterURL)) {
+ ReadString strings3[] = {
+ { "Crash Reporter", "ServerURL", &aAppData->crashReporterURL },
+ { nullptr }
+ };
+ ReadStrings(parser, strings3);
+ ReadFlag flags2[] = {
+ { "Crash Reporter", "Enabled", NS_XRE_ENABLE_CRASH_REPORTER },
+ { nullptr }
+ };
+ ReadFlags(parser, flags2, &aAppData->flags);
+ }
+
+ if (aAppData->size > offsetof(nsXREAppData, UAName)) {
+ ReadString strings4[] = {
+ { "App", "UAName", &aAppData->UAName },
+ { nullptr }
+ };
+ ReadStrings(parser, strings4);
+ }
+
+ return NS_OK;
+}
+
+void
+XRE_FreeAppData(nsXREAppData *aAppData)
+{
+ if (!aAppData) {
+ NS_ERROR("Invalid arg");
+ return;
+ }
+
+ ScopedAppData* sad = static_cast<ScopedAppData*>(aAppData);
+ delete sad;
+}
diff --git a/toolkit/xre/EventTracer.cpp b/toolkit/xre/EventTracer.cpp
new file mode 100644
index 000000000..cb0d88524
--- /dev/null
+++ b/toolkit/xre/EventTracer.cpp
@@ -0,0 +1,267 @@
+/* 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/. */
+
+/*
+ * Event loop instrumentation. This code attempts to measure the
+ * latency of the UI-thread event loop by firing native events at it from
+ * a background thread, and measuring how long it takes for them
+ * to be serviced. The measurement interval (kMeasureInterval, below)
+ * is also used as the upper bound of acceptable response time.
+ * When an event takes longer than that interval to be serviced,
+ * a sample will be written to the log.
+ *
+ * Usage:
+ *
+ * Set MOZ_INSTRUMENT_EVENT_LOOP=1 in the environment to enable
+ * this instrumentation. Currently only the UI process is instrumented.
+ *
+ * Set MOZ_INSTRUMENT_EVENT_LOOP_OUTPUT in the environment to a
+ * file path to contain the log output, the default is to log to stdout.
+ *
+ * Set MOZ_INSTRUMENT_EVENT_LOOP_THRESHOLD in the environment to an
+ * integer number of milliseconds to change the threshold for reporting.
+ * The default is 20 milliseconds. Unresponsive periods shorter than this
+ * threshold will not be reported.
+ *
+ * Set MOZ_INSTRUMENT_EVENT_LOOP_INTERVAL in the environment to an
+ * integer number of milliseconds to change the maximum sampling frequency.
+ * This variable controls how often events will be sent to the main
+ * thread's event loop to sample responsiveness. The sampler will not
+ * send events twice within LOOP_INTERVAL milliseconds.
+ * The default is 10 milliseconds.
+ *
+ * All logged output lines start with MOZ_EVENT_TRACE. All timestamps
+ * output are milliseconds since the epoch (PRTime / 1000).
+ *
+ * On startup, a line of the form:
+ * MOZ_EVENT_TRACE start <timestamp>
+ * will be output.
+ *
+ * On shutdown, a line of the form:
+ * MOZ_EVENT_TRACE stop <timestamp>
+ * will be output.
+ *
+ * When an event servicing time exceeds the threshold, a line of the form:
+ * MOZ_EVENT_TRACE sample <timestamp> <duration>
+ * will be output, where <duration> is the number of milliseconds that
+ * it took for the event to be serviced. Duration may contain a fractional
+ * component.
+ */
+
+#include "GeckoProfiler.h"
+
+#include "EventTracer.h"
+
+#include <stdio.h>
+
+#include "mozilla/Preferences.h"
+#include "mozilla/TimeStamp.h"
+#include "mozilla/WidgetTraceEvent.h"
+#include "nsDebug.h"
+#include <limits.h>
+#include <prenv.h>
+#include <prinrval.h>
+#include <prthread.h>
+#include <prtime.h>
+
+#ifdef MOZ_WIDGET_GONK
+#include "nsThreadUtils.h"
+#include "nsIObserverService.h"
+#include "mozilla/Services.h"
+#endif
+
+using mozilla::TimeDuration;
+using mozilla::TimeStamp;
+using mozilla::FireAndWaitForTracerEvent;
+
+namespace {
+
+PRThread* sTracerThread = nullptr;
+bool sExit = false;
+
+struct TracerStartClosure {
+ bool mLogTracing;
+ int32_t mThresholdInterval;
+};
+
+#ifdef MOZ_WIDGET_GONK
+class EventLoopLagDispatcher : public Runnable
+{
+ public:
+ explicit EventLoopLagDispatcher(int aLag)
+ : mLag(aLag) {}
+
+ NS_IMETHOD Run() override
+ {
+ nsCOMPtr<nsIObserverService> obsService =
+ mozilla::services::GetObserverService();
+ if (!obsService) {
+ return NS_ERROR_FAILURE;
+ }
+
+ nsAutoString value;
+ value.AppendInt(mLag);
+ return obsService->NotifyObservers(nullptr, "event-loop-lag", value.get());
+ }
+
+ private:
+ int mLag;
+};
+#endif
+
+/*
+ * The tracer thread fires events at the native event loop roughly
+ * every kMeasureInterval. It will sleep to attempt not to send them
+ * more quickly, but if the response time is longer than kMeasureInterval
+ * it will not send another event until the previous response is received.
+ *
+ * The output defaults to stdout, but can be redirected to a file by
+ * settting the environment variable MOZ_INSTRUMENT_EVENT_LOOP_OUTPUT
+ * to the name of a file to use.
+ */
+void TracerThread(void *arg)
+{
+ PR_SetCurrentThreadName("Event Tracer");
+
+ TracerStartClosure* threadArgs = static_cast<TracerStartClosure*>(arg);
+
+ // These are the defaults. They can be overridden by environment vars.
+ // This should be set to the maximum latency we'd like to allow
+ // for responsiveness.
+ int32_t thresholdInterval = threadArgs->mThresholdInterval;
+ PRIntervalTime threshold = PR_MillisecondsToInterval(thresholdInterval);
+ // This is the sampling interval.
+ PRIntervalTime interval = PR_MillisecondsToInterval(thresholdInterval / 2);
+
+ sExit = false;
+ FILE* log = nullptr;
+ char* envfile = PR_GetEnv("MOZ_INSTRUMENT_EVENT_LOOP_OUTPUT");
+ if (envfile) {
+ log = fopen(envfile, "w");
+ }
+ if (log == nullptr)
+ log = stdout;
+
+ char* thresholdenv = PR_GetEnv("MOZ_INSTRUMENT_EVENT_LOOP_THRESHOLD");
+ if (thresholdenv && *thresholdenv) {
+ int val = atoi(thresholdenv);
+ if (val != 0 && val != INT_MAX && val != INT_MIN) {
+ threshold = PR_MillisecondsToInterval(val);
+ }
+ }
+
+ char* intervalenv = PR_GetEnv("MOZ_INSTRUMENT_EVENT_LOOP_INTERVAL");
+ if (intervalenv && *intervalenv) {
+ int val = atoi(intervalenv);
+ if (val != 0 && val != INT_MAX && val != INT_MIN) {
+ interval = PR_MillisecondsToInterval(val);
+ }
+ }
+
+ if (threadArgs->mLogTracing) {
+ long long now = PR_Now() / PR_USEC_PER_MSEC;
+ fprintf(log, "MOZ_EVENT_TRACE start %llu\n", now);
+ }
+
+ while (!sExit) {
+ TimeStamp start(TimeStamp::Now());
+ profiler_responsiveness(start);
+ PRIntervalTime next_sleep = interval;
+
+ //TODO: only wait up to a maximum of interval; return
+ // early if that threshold is exceeded and dump a stack trace
+ // or do something else useful.
+ if (FireAndWaitForTracerEvent()) {
+ TimeDuration duration = TimeStamp::Now() - start;
+ // Only report samples that exceed our measurement threshold.
+ long long now = PR_Now() / PR_USEC_PER_MSEC;
+ if (threadArgs->mLogTracing && duration.ToMilliseconds() > threshold) {
+ fprintf(log, "MOZ_EVENT_TRACE sample %llu %lf\n",
+ now,
+ duration.ToMilliseconds());
+#ifdef MOZ_WIDGET_GONK
+ NS_DispatchToMainThread(
+ new EventLoopLagDispatcher(int(duration.ToSecondsSigDigits() * 1000)));
+#endif
+ }
+
+ if (next_sleep > duration.ToMilliseconds()) {
+ next_sleep -= int(duration.ToMilliseconds());
+ }
+ else {
+ // Don't sleep at all if this event took longer than the measure
+ // interval to deliver.
+ next_sleep = 0;
+ }
+ }
+
+ if (next_sleep != 0 && !sExit) {
+ PR_Sleep(next_sleep);
+ }
+ }
+
+ if (threadArgs->mLogTracing) {
+ long long now = PR_Now() / PR_USEC_PER_MSEC;
+ fprintf(log, "MOZ_EVENT_TRACE stop %llu\n", now);
+ }
+
+ if (log != stdout)
+ fclose(log);
+
+ delete threadArgs;
+}
+
+} // namespace
+
+namespace mozilla {
+
+bool InitEventTracing(bool aLog)
+{
+ if (sTracerThread)
+ return true;
+
+ // Initialize the widget backend.
+ if (!InitWidgetTracing())
+ return false;
+
+ // The tracer thread owns the object and will delete it.
+ TracerStartClosure* args = new TracerStartClosure();
+ args->mLogTracing = aLog;
+
+ // Pass the default threshold interval.
+ int32_t thresholdInterval = 20;
+ Preferences::GetInt("devtools.eventlooplag.threshold", &thresholdInterval);
+ args->mThresholdInterval = thresholdInterval;
+
+ // Create a thread that will fire events back at the
+ // main thread to measure responsiveness.
+ MOZ_ASSERT(!sTracerThread, "Event tracing already initialized!");
+ sTracerThread = PR_CreateThread(PR_USER_THREAD,
+ TracerThread,
+ args,
+ PR_PRIORITY_NORMAL,
+ PR_GLOBAL_THREAD,
+ PR_JOINABLE_THREAD,
+ 0);
+ return sTracerThread != nullptr;
+}
+
+void ShutdownEventTracing()
+{
+ if (!sTracerThread)
+ return;
+
+ sExit = true;
+ // Ensure that the tracer thread doesn't hang.
+ SignalTracerThread();
+
+ if (sTracerThread)
+ PR_JoinThread(sTracerThread);
+ sTracerThread = nullptr;
+
+ // Allow the widget backend to clean up.
+ CleanUpWidgetTracing();
+}
+
+} // namespace mozilla
diff --git a/toolkit/xre/EventTracer.h b/toolkit/xre/EventTracer.h
new file mode 100644
index 000000000..886b624e0
--- /dev/null
+++ b/toolkit/xre/EventTracer.h
@@ -0,0 +1,23 @@
+/* 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 XRE_EVENTTRACER_H_
+#define XRE_EVENTTRACER_H_
+
+namespace mozilla {
+
+// Create a thread that will fire events back at the
+// main thread to measure responsiveness. Return true
+// if the thread was created successfully.
+// aLog If the tracing results should be printed to
+// the console.
+bool InitEventTracing(bool aLog);
+
+// Signal the background thread to stop, and join it.
+// Must be called from the same thread that called InitEventTracing.
+void ShutdownEventTracing();
+
+} // namespace mozilla
+
+#endif /* XRE_EVENTTRACER_H_ */
diff --git a/toolkit/xre/MacApplicationDelegate.h b/toolkit/xre/MacApplicationDelegate.h
new file mode 100644
index 000000000..74f9a93ed
--- /dev/null
+++ b/toolkit/xre/MacApplicationDelegate.h
@@ -0,0 +1,16 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// This file defines the interface between Cocoa-specific Obj-C++ and generic C++,
+// so it itself cannot have any Obj-C bits in it.
+
+#ifndef MacApplicationDelegate_h_
+#define MacApplicationDelegate_h_
+
+void EnsureUseCocoaDockAPI(void);
+void SetupMacApplicationDelegate(void);
+void ProcessPendingGetURLAppleEvents(void);
+
+#endif
diff --git a/toolkit/xre/MacApplicationDelegate.mm b/toolkit/xre/MacApplicationDelegate.mm
new file mode 100644
index 000000000..2b295aa7d
--- /dev/null
+++ b/toolkit/xre/MacApplicationDelegate.mm
@@ -0,0 +1,396 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// NSApplication delegate for Mac OS X Cocoa API.
+
+// As of 10.4 Tiger, the system can send six kinds of Apple Events to an application;
+// a well-behaved XUL app should have some kind of handling for all of them.
+//
+// See http://developer.apple.com/documentation/Cocoa/Conceptual/ScriptableCocoaApplications/SApps_handle_AEs/chapter_11_section_3.html for details.
+
+#import <Cocoa/Cocoa.h>
+#import <Carbon/Carbon.h>
+
+#include "nsCOMPtr.h"
+#include "nsINativeAppSupport.h"
+#include "nsAppRunner.h"
+#include "nsAppShell.h"
+#include "nsComponentManagerUtils.h"
+#include "nsIServiceManager.h"
+#include "nsServiceManagerUtils.h"
+#include "nsIAppStartup.h"
+#include "nsIObserverService.h"
+#include "nsISupportsPrimitives.h"
+#include "nsObjCExceptions.h"
+#include "nsIFile.h"
+#include "nsDirectoryServiceDefs.h"
+#include "nsICommandLineRunner.h"
+#include "nsIMacDockSupport.h"
+#include "nsIStandaloneNativeMenu.h"
+#include "nsILocalFileMac.h"
+#include "nsString.h"
+#include "nsCommandLineServiceMac.h"
+
+class AutoAutoreleasePool {
+public:
+ AutoAutoreleasePool()
+ {
+ mLocalPool = [[NSAutoreleasePool alloc] init];
+ }
+ ~AutoAutoreleasePool()
+ {
+ [mLocalPool release];
+ }
+private:
+ NSAutoreleasePool *mLocalPool;
+};
+
+@interface MacApplicationDelegate : NSObject<NSApplicationDelegate>
+{
+}
+
+@end
+
+static bool sProcessedGetURLEvent = false;
+
+// Methods that can be called from non-Objective-C code.
+
+// This is needed, on relaunch, to force the OS to use the "Cocoa Dock API"
+// instead of the "Carbon Dock API". For more info see bmo bug 377166.
+void
+EnsureUseCocoaDockAPI()
+{
+ NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
+
+ [GeckoNSApplication sharedApplication];
+
+ NS_OBJC_END_TRY_ABORT_BLOCK;
+}
+
+void
+SetupMacApplicationDelegate()
+{
+ NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
+
+ // this is called during startup, outside an event loop, and therefore
+ // needs an autorelease pool to avoid cocoa object leakage (bug 559075)
+ AutoAutoreleasePool pool;
+
+ // Ensure that ProcessPendingGetURLAppleEvents() doesn't regress bug 377166.
+ [GeckoNSApplication sharedApplication];
+
+ // This call makes it so that application:openFile: doesn't get bogus calls
+ // from Cocoa doing its own parsing of the argument string. And yes, we need
+ // to use a string with a boolean value in it. That's just how it works.
+ [[NSUserDefaults standardUserDefaults] setObject:@"NO"
+ forKey:@"NSTreatUnknownArgumentsAsOpen"];
+
+ // Create the delegate. This should be around for the lifetime of the app.
+ id<NSApplicationDelegate> delegate = [[MacApplicationDelegate alloc] init];
+ [[GeckoNSApplication sharedApplication] setDelegate:delegate];
+
+ NS_OBJC_END_TRY_ABORT_BLOCK;
+}
+
+// Indirectly make the OS process any pending GetURL Apple events. This is
+// done via _DPSNextEvent() (an undocumented AppKit function called from
+// [NSApplication nextEventMatchingMask:untilDate:inMode:dequeue:]). Apple
+// events are only processed if 'dequeue' is 'YES' -- so we need to call
+// [NSApplication sendEvent:] on any event that gets returned. 'event' will
+// never itself be an Apple event, and it may be 'nil' even when Apple events
+// are processed.
+void
+ProcessPendingGetURLAppleEvents()
+{
+ AutoAutoreleasePool pool;
+ bool keepSpinning = true;
+ while (keepSpinning) {
+ sProcessedGetURLEvent = false;
+ NSEvent *event = [NSApp nextEventMatchingMask:NSAnyEventMask
+ untilDate:nil
+ inMode:NSDefaultRunLoopMode
+ dequeue:YES];
+ if (event)
+ [NSApp sendEvent:event];
+ keepSpinning = sProcessedGetURLEvent;
+ }
+}
+
+@implementation MacApplicationDelegate
+
+- (id)init
+{
+ NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN;
+
+ if ((self = [super init])) {
+ NSAppleEventManager *aeMgr = [NSAppleEventManager sharedAppleEventManager];
+
+ [aeMgr setEventHandler:self
+ andSelector:@selector(handleAppleEvent:withReplyEvent:)
+ forEventClass:kInternetEventClass
+ andEventID:kAEGetURL];
+
+ [aeMgr setEventHandler:self
+ andSelector:@selector(handleAppleEvent:withReplyEvent:)
+ forEventClass:'WWW!'
+ andEventID:'OURL'];
+
+ [aeMgr setEventHandler:self
+ andSelector:@selector(handleAppleEvent:withReplyEvent:)
+ forEventClass:kCoreEventClass
+ andEventID:kAEOpenDocuments];
+
+ if (![NSApp windowsMenu]) {
+ // If the application has a windows menu, it will keep it up to date and
+ // prepend the window list to the Dock menu automatically.
+ NSMenu* windowsMenu = [[NSMenu alloc] initWithTitle:@"Window"];
+ [NSApp setWindowsMenu:windowsMenu];
+ [windowsMenu release];
+ }
+ }
+ return self;
+
+ NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(nil);
+}
+
+- (void)dealloc
+{
+ NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
+
+ NSAppleEventManager *aeMgr = [NSAppleEventManager sharedAppleEventManager];
+ [aeMgr removeEventHandlerForEventClass:kInternetEventClass andEventID:kAEGetURL];
+ [aeMgr removeEventHandlerForEventClass:'WWW!' andEventID:'OURL'];
+ [aeMgr removeEventHandlerForEventClass:kCoreEventClass andEventID:kAEOpenDocuments];
+ [super dealloc];
+
+ NS_OBJC_END_TRY_ABORT_BLOCK;
+}
+
+// The method that NSApplication calls upon a request to reopen, such as when
+// the Dock icon is clicked and no windows are open. A "visible" window may be
+// miniaturized, so we can't skip nsCocoaNativeReOpen() if 'flag' is 'true'.
+- (BOOL)applicationShouldHandleReopen:(NSApplication*)theApp hasVisibleWindows:(BOOL)flag
+{
+ nsCOMPtr<nsINativeAppSupport> nas = do_CreateInstance(NS_NATIVEAPPSUPPORT_CONTRACTID);
+ NS_ENSURE_TRUE(nas, NO);
+
+ // Go to the common Carbon/Cocoa reopen method.
+ nsresult rv = nas->ReOpen();
+ NS_ENSURE_SUCCESS(rv, NO);
+
+ // NO says we don't want NSApplication to do anything else for us.
+ return NO;
+}
+
+// The method that NSApplication calls when documents are requested to be opened.
+// It will be called once for each selected document.
+- (BOOL)application:(NSApplication*)theApplication openFile:(NSString*)filename
+{
+ NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN;
+
+ NSURL *url = [NSURL fileURLWithPath:filename];
+ if (!url)
+ return NO;
+
+ NSString *urlString = [url absoluteString];
+ if (!urlString)
+ return NO;
+
+ // Add the URL to any command line we're currently setting up.
+ if (CommandLineServiceMac::AddURLToCurrentCommandLine([urlString UTF8String]))
+ return YES;
+
+ nsCOMPtr<nsILocalFileMac> inFile;
+ nsresult rv = NS_NewLocalFileWithCFURL((CFURLRef)url, true, getter_AddRefs(inFile));
+ if (NS_FAILED(rv))
+ return NO;
+
+ nsCOMPtr<nsICommandLineRunner> cmdLine(do_CreateInstance("@mozilla.org/toolkit/command-line;1"));
+ if (!cmdLine) {
+ NS_ERROR("Couldn't create command line!");
+ return NO;
+ }
+
+ nsCString filePath;
+ rv = inFile->GetNativePath(filePath);
+ if (NS_FAILED(rv))
+ return NO;
+
+ nsCOMPtr<nsIFile> workingDir;
+ rv = NS_GetSpecialDirectory(NS_OS_CURRENT_WORKING_DIR, getter_AddRefs(workingDir));
+ if (NS_FAILED(rv))
+ return NO;
+
+ const char *argv[3] = {nullptr, "-file", filePath.get()};
+ rv = cmdLine->Init(3, argv, workingDir, nsICommandLine::STATE_REMOTE_EXPLICIT);
+ if (NS_FAILED(rv))
+ return NO;
+
+ if (NS_SUCCEEDED(cmdLine->Run()))
+ return YES;
+
+ return NO;
+
+ NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(NO);
+}
+
+// The method that NSApplication calls when documents are requested to be printed
+// from the Finder (under the "File" menu).
+// It will be called once for each selected document.
+- (BOOL)application:(NSApplication*)theApplication printFile:(NSString*)filename
+{
+ return NO;
+}
+
+// Create the menu that shows up in the Dock.
+- (NSMenu*)applicationDockMenu:(NSApplication*)sender
+{
+ NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
+
+ // Create the NSMenu that will contain the dock menu items.
+ NSMenu *menu = [[[NSMenu alloc] initWithTitle:@""] autorelease];
+ [menu setAutoenablesItems:NO];
+
+ // Add application-specific dock menu items. On error, do not insert the
+ // dock menu items.
+ nsresult rv;
+ nsCOMPtr<nsIMacDockSupport> dockSupport = do_GetService("@mozilla.org/widget/macdocksupport;1", &rv);
+ if (NS_FAILED(rv) || !dockSupport)
+ return menu;
+
+ nsCOMPtr<nsIStandaloneNativeMenu> dockMenu;
+ rv = dockSupport->GetDockMenu(getter_AddRefs(dockMenu));
+ if (NS_FAILED(rv) || !dockMenu)
+ return menu;
+
+ // Determine if the dock menu items should be displayed. This also gives
+ // the menu the opportunity to update itself before display.
+ bool shouldShowItems;
+ rv = dockMenu->MenuWillOpen(&shouldShowItems);
+ if (NS_FAILED(rv) || !shouldShowItems)
+ return menu;
+
+ // Obtain a copy of the native menu.
+ NSMenu * nativeDockMenu;
+ rv = dockMenu->GetNativeMenu(reinterpret_cast<void **>(&nativeDockMenu));
+ if (NS_FAILED(rv) || !nativeDockMenu)
+ return menu;
+
+ // Loop through the application-specific dock menu and insert its
+ // contents into the dock menu that we are building for Cocoa.
+ int numDockMenuItems = [nativeDockMenu numberOfItems];
+ if (numDockMenuItems > 0) {
+ if ([menu numberOfItems] > 0)
+ [menu addItem:[NSMenuItem separatorItem]];
+
+ for (int i = 0; i < numDockMenuItems; i++) {
+ NSMenuItem * itemCopy = [[nativeDockMenu itemAtIndex:i] copy];
+ [menu addItem:itemCopy];
+ [itemCopy release];
+ }
+ }
+
+ return menu;
+
+ NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
+}
+
+// If we don't handle applicationShouldTerminate:, a call to [NSApp terminate:]
+// (from the browser or from the OS) can result in an unclean shutdown.
+- (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)sender
+{
+ nsCOMPtr<nsIObserverService> obsServ =
+ do_GetService("@mozilla.org/observer-service;1");
+ if (!obsServ)
+ return NSTerminateNow;
+
+ nsCOMPtr<nsISupportsPRBool> cancelQuit =
+ do_CreateInstance(NS_SUPPORTS_PRBOOL_CONTRACTID);
+ if (!cancelQuit)
+ return NSTerminateNow;
+
+ cancelQuit->SetData(false);
+ obsServ->NotifyObservers(cancelQuit, "quit-application-requested", nullptr);
+
+ bool abortQuit;
+ cancelQuit->GetData(&abortQuit);
+ if (abortQuit)
+ return NSTerminateCancel;
+
+ nsCOMPtr<nsIAppStartup> appService =
+ do_GetService("@mozilla.org/toolkit/app-startup;1");
+ if (appService)
+ appService->Quit(nsIAppStartup::eForceQuit);
+
+ return NSTerminateNow;
+}
+
+- (void)handleAppleEvent:(NSAppleEventDescriptor*)event withReplyEvent:(NSAppleEventDescriptor*)replyEvent
+{
+ if (!event)
+ return;
+
+ AutoAutoreleasePool pool;
+
+ bool isGetURLEvent =
+ ([event eventClass] == kInternetEventClass && [event eventID] == kAEGetURL);
+ if (isGetURLEvent)
+ sProcessedGetURLEvent = true;
+
+ if (isGetURLEvent ||
+ ([event eventClass] == 'WWW!' && [event eventID] == 'OURL')) {
+ NSString* urlString = [[event paramDescriptorForKeyword:keyDirectObject] stringValue];
+
+ // don't open chrome URLs
+ NSString* schemeString = [[NSURL URLWithString:urlString] scheme];
+ if (!schemeString ||
+ [schemeString compare:@"chrome"
+ options:NSCaseInsensitiveSearch
+ range:NSMakeRange(0, [schemeString length])] == NSOrderedSame) {
+ return;
+ }
+
+ // Add the URL to any command line we're currently setting up.
+ if (CommandLineServiceMac::AddURLToCurrentCommandLine([urlString UTF8String]))
+ return;
+
+ nsCOMPtr<nsICommandLineRunner> cmdLine(do_CreateInstance("@mozilla.org/toolkit/command-line;1"));
+ if (!cmdLine) {
+ NS_ERROR("Couldn't create command line!");
+ return;
+ }
+ nsCOMPtr<nsIFile> workingDir;
+ nsresult rv = NS_GetSpecialDirectory(NS_OS_CURRENT_WORKING_DIR, getter_AddRefs(workingDir));
+ if (NS_FAILED(rv))
+ return;
+ const char *argv[3] = {nullptr, "-url", [urlString UTF8String]};
+ rv = cmdLine->Init(3, argv, workingDir, nsICommandLine::STATE_REMOTE_EXPLICIT);
+ if (NS_FAILED(rv))
+ return;
+ rv = cmdLine->Run();
+ }
+ else if ([event eventClass] == kCoreEventClass && [event eventID] == kAEOpenDocuments) {
+ NSAppleEventDescriptor* fileListDescriptor = [event paramDescriptorForKeyword:keyDirectObject];
+ if (!fileListDescriptor)
+ return;
+
+ // Descriptor list indexing is one-based...
+ NSInteger numberOfFiles = [fileListDescriptor numberOfItems];
+ for (NSInteger i = 1; i <= numberOfFiles; i++) {
+ NSString* urlString = [[fileListDescriptor descriptorAtIndex:i] stringValue];
+ if (!urlString)
+ continue;
+
+ // We need a path, not a URL
+ NSURL* url = [NSURL URLWithString:urlString];
+ if (!url)
+ continue;
+
+ [self application:NSApp openFile:[url path]];
+ }
+ }
+}
+
+@end
diff --git a/toolkit/xre/MacAutoreleasePool.h b/toolkit/xre/MacAutoreleasePool.h
new file mode 100644
index 000000000..7668957f1
--- /dev/null
+++ b/toolkit/xre/MacAutoreleasePool.h
@@ -0,0 +1,31 @@
+/* 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 MacAutoreleasePool_h_
+#define MacAutoreleasePool_h_
+
+// This needs to be #include-able from non-ObjC code in nsAppRunner.cpp
+#ifdef __OBJC__
+@class NSAutoreleasePool;
+#else
+class NSAutoreleasePool;
+#endif
+
+namespace mozilla {
+
+class MacAutoreleasePool {
+public:
+ MacAutoreleasePool();
+ ~MacAutoreleasePool();
+
+private:
+ NSAutoreleasePool *mPool;
+
+ MacAutoreleasePool(const MacAutoreleasePool&);
+ void operator=(const MacAutoreleasePool&);
+};
+
+} // namespace mozilla
+
+#endif // MacAutoreleasePool_h_
diff --git a/toolkit/xre/MacAutoreleasePool.mm b/toolkit/xre/MacAutoreleasePool.mm
new file mode 100644
index 000000000..ae8ad51ff
--- /dev/null
+++ b/toolkit/xre/MacAutoreleasePool.mm
@@ -0,0 +1,20 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "MacAutoreleasePool.h"
+#include "nsDebug.h"
+
+#import <Foundation/Foundation.h>
+
+using mozilla::MacAutoreleasePool;
+
+MacAutoreleasePool::MacAutoreleasePool()
+{
+ mPool = [[NSAutoreleasePool alloc] init];
+ NS_ASSERTION(mPool != nullptr, "failed to create pool, objects will leak");
+}
+
+MacAutoreleasePool::~MacAutoreleasePool() {
+ [mPool release];
+}
diff --git a/toolkit/xre/MacLaunchHelper.h b/toolkit/xre/MacLaunchHelper.h
new file mode 100644
index 000000000..08035c53b
--- /dev/null
+++ b/toolkit/xre/MacLaunchHelper.h
@@ -0,0 +1,23 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef MacLaunchHelper_h_
+#define MacLaunchHelper_h_
+
+#include <stdint.h>
+
+#include <unistd.h>
+
+extern "C" {
+ /**
+ * Passing an aPid parameter to LaunchChildMac will wait for the launched
+ * process to terminate. When the process terminates, aPid will be set to the
+ * pid of the terminated process to confirm that it executed successfully.
+ */
+ void LaunchChildMac(int aArgc, char** aArgv, pid_t* aPid = 0);
+ bool LaunchElevatedUpdate(int aArgc, char** aArgv, pid_t* aPid = 0);
+}
+
+#endif
diff --git a/toolkit/xre/MacLaunchHelper.mm b/toolkit/xre/MacLaunchHelper.mm
new file mode 100644
index 000000000..0dadb8de8
--- /dev/null
+++ b/toolkit/xre/MacLaunchHelper.mm
@@ -0,0 +1,137 @@
+/* -*- 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 "MacLaunchHelper.h"
+
+#include "MacAutoreleasePool.h"
+#include "mozilla/UniquePtr.h"
+#include "nsIAppStartup.h"
+#include "nsMemory.h"
+
+#include <Cocoa/Cocoa.h>
+#include <crt_externs.h>
+#include <ServiceManagement/ServiceManagement.h>
+#include <Security/Authorization.h>
+#include <spawn.h>
+#include <stdio.h>
+
+using namespace mozilla;
+
+void LaunchChildMac(int aArgc, char** aArgv, pid_t* aPid)
+{
+ MacAutoreleasePool pool;
+
+ @try {
+ NSString* launchPath = [NSString stringWithUTF8String:aArgv[0]];
+ NSMutableArray* arguments = [NSMutableArray arrayWithCapacity:aArgc - 1];
+ for (int i = 1; i < aArgc; i++) {
+ [arguments addObject:[NSString stringWithUTF8String:aArgv[i]]];
+ }
+ NSTask* child = [NSTask launchedTaskWithLaunchPath:launchPath
+ arguments:arguments];
+ if (aPid) {
+ *aPid = [child processIdentifier];
+ // We used to use waitpid to wait for the process to terminate. This is
+ // incompatible with NSTask and we wait for the process to exit here
+ // instead.
+ [child waitUntilExit];
+ }
+ } @catch (NSException* e) {
+ NSLog(@"%@: %@", e.name, e.reason);
+ }
+}
+
+BOOL InstallPrivilegedHelper()
+{
+ AuthorizationRef authRef = NULL;
+ OSStatus status = AuthorizationCreate(NULL,
+ kAuthorizationEmptyEnvironment,
+ kAuthorizationFlagDefaults |
+ kAuthorizationFlagInteractionAllowed,
+ &authRef);
+ if (status != errAuthorizationSuccess) {
+ // AuthorizationCreate really shouldn't fail.
+ NSLog(@"AuthorizationCreate failed! NSOSStatusErrorDomain / %d",
+ (int)status);
+ return NO;
+ }
+
+ BOOL result = NO;
+ AuthorizationItem authItem = { kSMRightBlessPrivilegedHelper, 0, NULL, 0 };
+ AuthorizationRights authRights = { 1, &authItem };
+ AuthorizationFlags flags = kAuthorizationFlagDefaults |
+ kAuthorizationFlagInteractionAllowed |
+ kAuthorizationFlagPreAuthorize |
+ kAuthorizationFlagExtendRights;
+
+ // Obtain the right to install our privileged helper tool.
+ status = AuthorizationCopyRights(authRef,
+ &authRights,
+ kAuthorizationEmptyEnvironment,
+ flags,
+ NULL);
+ if (status != errAuthorizationSuccess) {
+ NSLog(@"AuthorizationCopyRights failed! NSOSStatusErrorDomain / %d",
+ (int)status);
+ } else {
+ CFErrorRef cfError;
+ // This does all the work of verifying the helper tool against the
+ // application and vice-versa. Once verification has passed, the embedded
+ // launchd.plist is extracted and placed in /Library/LaunchDaemons and then
+ // loaded. The executable is placed in /Library/PrivilegedHelperTools.
+ result = (BOOL)SMJobBless(kSMDomainSystemLaunchd,
+ (CFStringRef)@"org.mozilla.updater",
+ authRef,
+ &cfError);
+ if (!result) {
+ NSLog(@"Unable to install helper!");
+ CFRelease(cfError);
+ }
+ }
+
+ return result;
+}
+
+void AbortElevatedUpdate()
+{
+ mozilla::MacAutoreleasePool pool;
+
+ id updateServer = nil;
+ int currTry = 0;
+ const int numRetries = 10; // Number of IPC connection retries before
+ // giving up.
+ while (currTry < numRetries) {
+ @try {
+ updateServer = (id)[NSConnection
+ rootProxyForConnectionWithRegisteredName:
+ @"org.mozilla.updater.server"
+ host:nil
+ usingNameServer:[NSSocketPortNameServer sharedInstance]];
+ if (updateServer &&
+ [updateServer respondsToSelector:@selector(abort)]) {
+ [updateServer performSelector:@selector(abort)];
+ return;
+ }
+ NSLog(@"Server doesn't exist or doesn't provide correct selectors.");
+ sleep(1); // Wait 1 second.
+ currTry++;
+ } @catch (NSException* e) {
+ NSLog(@"Encountered exception, retrying: %@: %@", e.name, e.reason);
+ sleep(1); // Wait 1 second.
+ currTry++;
+ }
+ }
+ NSLog(@"Unable to clean up updater.");
+}
+
+bool LaunchElevatedUpdate(int aArgc, char** aArgv, pid_t* aPid)
+{
+ LaunchChildMac(aArgc, aArgv, aPid);
+ bool didSucceed = InstallPrivilegedHelper();
+ if (!didSucceed) {
+ AbortElevatedUpdate();
+ }
+ return didSucceed;
+}
diff --git a/toolkit/xre/Makefile.in b/toolkit/xre/Makefile.in
new file mode 100644
index 000000000..225dc3bbb
--- /dev/null
+++ b/toolkit/xre/Makefile.in
@@ -0,0 +1,21 @@
+# -*- makefile -*-
+# vim:set ts=8 sw=8 sts=8 noet:
+
+# 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/.
+
+milestone_txt = $(topsrcdir)/config/milestone.txt
+
+include $(topsrcdir)/config/rules.mk
+
+MOZ_BUILDID := $(shell awk '{print $$3}' $(DEPTH)/buildid.h)
+$(call errorIfEmpty,GRE_MILESTONE MOZ_BUILDID)
+
+# Note these dependencies are broken because the target is *not* the cpp file.
+# BUT, actually fixing it would make libxul rebuilt on every single incremental
+# build because of the automatic buildid change. This is why we can't actually
+# include buildid.h there, because it would add the dependency.
+$(srcdir)/nsAppRunner.cpp: $(DEPTH)/buildid.h $(milestone_txt)
+
+nsAppRunner.$(OBJ_SUFFIX): DEFINES += -DMOZ_BUILDID=$(MOZ_BUILDID)
diff --git a/toolkit/xre/MozMeegoAppService.h b/toolkit/xre/MozMeegoAppService.h
new file mode 100644
index 000000000..063d03e02
--- /dev/null
+++ b/toolkit/xre/MozMeegoAppService.h
@@ -0,0 +1,28 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=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 MOZMEEGOAPPSERVICE_H
+#define MOZMEEGOAPPSERVICE_H
+
+#include <MApplicationService>
+
+/**
+ * App service class, which prevents registration to d-bus
+ * and allows multiple instances of application. This is
+ * required for Mozillas remote service to work, because
+ * it is initialized after MApplication.
+ */
+class MozMeegoAppService: public MApplicationService
+{
+ Q_OBJECT
+public:
+ MozMeegoAppService(): MApplicationService(QString()) {}
+public Q_SLOTS:
+ virtual QString registeredName() { return QString(); }
+ virtual bool isRegistered() { return false; }
+ virtual bool registerService() { return true; }
+};
+#endif // MOZMEEGOAPPSERVICE_H
diff --git a/toolkit/xre/ProfileReset.cpp b/toolkit/xre/ProfileReset.cpp
new file mode 100644
index 000000000..aef2d7746
--- /dev/null
+++ b/toolkit/xre/ProfileReset.cpp
@@ -0,0 +1,175 @@
+/* -*- 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 "nsIAppStartup.h"
+#include "nsIFile.h"
+#include "nsIStringBundle.h"
+#include "nsIToolkitProfile.h"
+#include "nsIWindowWatcher.h"
+
+#include "ProfileReset.h"
+
+#include "nsDirectoryServiceDefs.h"
+#include "nsDirectoryServiceUtils.h"
+#include "nsPIDOMWindow.h"
+#include "nsPrintfCString.h"
+#include "nsToolkitCompsCID.h"
+#include "nsXPCOMCIDInternal.h"
+#include "nsXREAppData.h"
+
+#include "mozilla/Services.h"
+#include "prtime.h"
+
+extern const nsXREAppData* 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, nsIToolkitProfile* *aNewProfile)
+{
+ MOZ_ASSERT(aProfileSvc, "NULL profile service");
+
+ nsCOMPtr<nsIToolkitProfile> newProfile;
+ // Make the new profile "default-" + the time in seconds since epoch for uniqueness.
+ nsAutoCString newProfileName("default-");
+ newProfileName.Append(nsPrintfCString("%lld", PR_Now() / 1000));
+ nsresult rv = aProfileSvc->CreateProfile(nullptr, // choose a default dir for us
+ newProfileName,
+ getter_AddRefs(newProfile));
+ if (NS_FAILED(rv)) return rv;
+
+ rv = aProfileSvc->Flush();
+ if (NS_FAILED(rv)) return rv;
+
+ newProfile.swap(*aNewProfile);
+
+ return NS_OK;
+}
+
+/**
+ * Delete the profile directory being reset after a backup and delete the local profile directory.
+ */
+nsresult
+ProfileResetCleanup(nsIToolkitProfile* aOldProfile)
+{
+ nsresult rv;
+ nsCOMPtr<nsIFile> profileDir;
+ rv = aOldProfile->GetRootDir(getter_AddRefs(profileDir));
+ if (NS_FAILED(rv)) return rv;
+
+ nsCOMPtr<nsIFile> profileLocalDir;
+ rv = aOldProfile->GetLocalDir(getter_AddRefs(profileLocalDir));
+ if (NS_FAILED(rv)) return rv;
+
+ // Get the friendly name for the backup directory.
+ nsCOMPtr<nsIStringBundleService> sbs = mozilla::services::GetStringBundleService();
+ if (!sbs) return NS_ERROR_FAILURE;
+
+ nsCOMPtr<nsIStringBundle> sb;
+ rv = sbs->CreateBundle(kProfileProperties, getter_AddRefs(sb));
+ if (!sb) return NS_ERROR_FAILURE;
+
+ NS_ConvertUTF8toUTF16 appName(gAppData->name);
+ const char16_t* params[] = {appName.get(), appName.get()};
+
+ nsXPIDLString resetBackupDirectoryName;
+
+ static const char16_t* kResetBackupDirectory = u"resetBackupDirectory";
+ rv = sb->FormatStringFromName(kResetBackupDirectory, params, 2,
+ getter_Copies(resetBackupDirectoryName));
+
+ // Get info to copy the old root profile dir to the desktop as a backup.
+ nsCOMPtr<nsIFile> backupDest, containerDest, profileDest;
+ rv = NS_GetSpecialDirectory(NS_OS_DESKTOP_DIR, getter_AddRefs(backupDest));
+ if (NS_FAILED(rv)) {
+ // Fall back to the home directory if the desktop is not available.
+ rv = NS_GetSpecialDirectory(NS_OS_HOME_DIR, getter_AddRefs(backupDest));
+ if (NS_FAILED(rv)) return rv;
+ }
+
+ // Try to create a directory for all the backups
+ backupDest->Clone(getter_AddRefs(containerDest));
+ containerDest->Append(resetBackupDirectoryName);
+ rv = containerDest->Create(nsIFile::DIRECTORY_TYPE, 0700);
+ // It's OK if it already exists, if and only if it is a directory
+ if (rv == NS_ERROR_FILE_ALREADY_EXISTS) {
+ bool containerIsDir;
+ rv = containerDest->IsDirectory(&containerIsDir);
+ if (NS_FAILED(rv) || !containerIsDir) {
+ return rv;
+ }
+ } else if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ // Get the name of the profile
+ nsAutoString leafName;
+ rv = profileDir->GetLeafName(leafName);
+ if (NS_FAILED(rv)) return rv;
+
+ // Try to create a unique directory for the profile:
+ containerDest->Clone(getter_AddRefs(profileDest));
+ profileDest->Append(leafName);
+ rv = profileDest->CreateUnique(nsIFile::DIRECTORY_TYPE, 0700);
+ if (NS_FAILED(rv)) return rv;
+
+ // Get the unique profile name
+ rv = profileDest->GetLeafName(leafName);
+ if (NS_FAILED(rv)) return rv;
+
+ // Delete the empty directory that CreateUnique just created.
+ rv = profileDest->Remove(false);
+ if (NS_FAILED(rv)) return rv;
+
+ // Show a progress window while the cleanup happens since the disk I/O can take time.
+ nsCOMPtr<nsIWindowWatcher> windowWatcher(do_GetService(NS_WINDOWWATCHER_CONTRACTID));
+ if (!windowWatcher) return NS_ERROR_FAILURE;
+
+ nsCOMPtr<nsIAppStartup> appStartup(do_GetService(NS_APPSTARTUP_CONTRACTID));
+ if (!appStartup) return NS_ERROR_FAILURE;
+
+ nsCOMPtr<mozIDOMWindowProxy> progressWindow;
+ rv = windowWatcher->OpenWindow(nullptr,
+ kResetProgressURL,
+ "_blank",
+ "centerscreen,chrome,titlebar",
+ nullptr,
+ getter_AddRefs(progressWindow));
+ if (NS_FAILED(rv)) return rv;
+
+ // Create a new thread to do the bulk of profile cleanup to stay responsive.
+ nsCOMPtr<nsIThreadManager> tm = do_GetService(NS_THREADMANAGER_CONTRACTID);
+ nsCOMPtr<nsIThread> cleanupThread;
+ rv = tm->NewThread(0, 0, getter_AddRefs(cleanupThread));
+ if (NS_SUCCEEDED(rv)) {
+ nsCOMPtr<nsIRunnable> runnable = new ProfileResetCleanupAsyncTask(profileDir, profileLocalDir,
+ containerDest, leafName);
+ cleanupThread->Dispatch(runnable, nsIThread::DISPATCH_NORMAL);
+ // The result callback will shut down the worker thread.
+
+ nsIThread *thread = NS_GetCurrentThread();
+ // Wait for the cleanup thread to complete.
+ while(!gProfileResetCleanupCompleted) {
+ NS_ProcessNextEvent(thread);
+ }
+ } else {
+ gProfileResetCleanupCompleted = true;
+ NS_WARNING("Cleanup thread creation failed");
+ return rv;
+ }
+ // Close the progress window now that the cleanup thread is done.
+ auto* piWindow = nsPIDOMWindowOuter::From(progressWindow);
+ piWindow->Close();
+
+ // Delete the old profile from profiles.ini. The folder was already deleted by the thread above.
+ rv = aOldProfile->Remove(false);
+ if (NS_FAILED(rv)) NS_WARNING("Could not remove the profile");
+
+ return rv;
+}
diff --git a/toolkit/xre/ProfileReset.h b/toolkit/xre/ProfileReset.h
new file mode 100644
index 000000000..7b5efbc4e
--- /dev/null
+++ b/toolkit/xre/ProfileReset.h
@@ -0,0 +1,81 @@
+/* -*- 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 "nsIToolkitProfileService.h"
+#include "nsIFile.h"
+#include "nsThreadUtils.h"
+
+static bool gProfileResetCleanupCompleted = false;
+static const char kResetProgressURL[] = "chrome://global/content/resetProfileProgress.xul";
+
+nsresult CreateResetProfile(nsIToolkitProfileService* aProfileSvc,
+ nsIToolkitProfile* *aNewProfile);
+
+nsresult ProfileResetCleanup(nsIToolkitProfile* aOldProfile);
+
+class ProfileResetCleanupResultTask : public mozilla::Runnable
+{
+public:
+ ProfileResetCleanupResultTask()
+ : mWorkerThread(do_GetCurrentThread())
+ {
+ MOZ_ASSERT(!NS_IsMainThread());
+ }
+
+ NS_IMETHOD Run() override {
+ MOZ_ASSERT(NS_IsMainThread());
+ mWorkerThread->Shutdown();
+ return NS_OK;
+ }
+
+private:
+ nsCOMPtr<nsIThread> mWorkerThread;
+};
+
+class ProfileResetCleanupAsyncTask : public mozilla::Runnable
+{
+public:
+ ProfileResetCleanupAsyncTask(nsIFile* aProfileDir, nsIFile* aProfileLocalDir,
+ nsIFile* aTargetDir, const nsAString &aLeafName)
+ : mProfileDir(aProfileDir)
+ , mProfileLocalDir(aProfileLocalDir)
+ , mTargetDir(aTargetDir)
+ , mLeafName(aLeafName)
+ { }
+
+/**
+ * Copy a root profile to a backup folder before deleting it. Then delete the local profile dir.
+ */
+ NS_IMETHOD Run() override
+ {
+ // Copy to the destination then delete the profile. A move doesn't follow links.
+ nsresult rv = mProfileDir->CopyToFollowingLinks(mTargetDir, mLeafName);
+ if (NS_SUCCEEDED(rv))
+ rv = mProfileDir->Remove(true);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ NS_WARNING("Could not backup the root profile directory");
+ }
+
+ // If we have a separate local cache profile directory, just delete it.
+ // Don't return an error if this fails so that reset can proceed if it can't be deleted.
+ bool sameDir;
+ nsresult rvLocal = mProfileDir->Equals(mProfileLocalDir, &sameDir);
+ if (NS_SUCCEEDED(rvLocal) && !sameDir) {
+ rvLocal = mProfileLocalDir->Remove(true);
+ if (NS_FAILED(rvLocal)) NS_WARNING("Could not remove the old local profile directory (cache)");
+ }
+ gProfileResetCleanupCompleted = true;
+
+ nsCOMPtr<nsIRunnable> resultRunnable = new ProfileResetCleanupResultTask();
+ NS_DispatchToMainThread(resultRunnable);
+ return NS_OK;
+ }
+
+private:
+ nsCOMPtr<nsIFile> mProfileDir;
+ nsCOMPtr<nsIFile> mProfileLocalDir;
+ nsCOMPtr<nsIFile> mTargetDir;
+ nsString mLeafName;
+};
diff --git a/toolkit/xre/UIKitDirProvider.h b/toolkit/xre/UIKitDirProvider.h
new file mode 100644
index 000000000..c74596125
--- /dev/null
+++ b/toolkit/xre/UIKitDirProvider.h
@@ -0,0 +1,13 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef TOOLKIT_XRE_UIKITDIRPROVIDER_H_
+#define TOOLKIT_XRE_UIKITDIRPROVIDER_H_
+
+#include "nsString.h"
+
+bool GetUIKitDirectory(bool aLocal, nsACString& aUserDir);
+
+#endif // TOOLKIT_XRE_UIKITDIRPROVIDER_H_
diff --git a/toolkit/xre/UIKitDirProvider.mm b/toolkit/xre/UIKitDirProvider.mm
new file mode 100644
index 000000000..b5bd03c6e
--- /dev/null
+++ b/toolkit/xre/UIKitDirProvider.mm
@@ -0,0 +1,19 @@
+/* -*- 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 <Foundation/Foundation.h>
+
+#include "UIKitDirProvider.h"
+
+bool GetUIKitDirectory(bool aLocal, nsACString& aUserDir)
+{
+ NSSearchPathDirectory directory = aLocal ? NSCachesDirectory : NSApplicationSupportDirectory;
+ NSArray* paths = NSSearchPathForDirectoriesInDomains(directory, NSUserDomainMask, YES);
+ if ([paths count] == 0) {
+ return false;
+ }
+ aUserDir = [[paths objectAtIndex:0] UTF8String];
+ return true;
+}
diff --git a/toolkit/xre/glxtest.cpp b/toolkit/xre/glxtest.cpp
new file mode 100644
index 000000000..519a5e68b
--- /dev/null
+++ b/toolkit/xre/glxtest.cpp
@@ -0,0 +1,349 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: sw=2 ts=8 et :
+ */
+/* 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/. */
+
+
+//////////////////////////////////////////////////////////////////////////////
+//
+// Explanation: See bug 639842. Safely getting GL driver info on X11 is hard, because the only way to do
+// that is to create a GL context and call glGetString(), but with bad drivers,
+// just creating a GL context may crash.
+//
+// This file implements the idea to do that in a separate process.
+//
+// The only non-static function here is fire_glxtest_process(). It creates a pipe, publishes its 'read' end as the
+// mozilla::widget::glxtest_pipe global variable, forks, and runs that GLX probe in the child process,
+// which runs the glxtest() static function. This creates a X connection, a GLX context, calls glGetString, and writes that
+// to the 'write' end of the pipe.
+
+#include <cstdio>
+#include <cstdlib>
+#include <unistd.h>
+#include <dlfcn.h>
+#include "nscore.h"
+#include <fcntl.h>
+#include "stdint.h"
+
+#if MOZ_WIDGET_GTK == 2
+#include <glib.h>
+#endif
+
+#ifdef __SUNPRO_CC
+#include <stdio.h>
+#endif
+
+#include "X11/Xlib.h"
+#include "X11/Xutil.h"
+
+#include "mozilla/Unused.h"
+
+// stuff from glx.h
+typedef struct __GLXcontextRec *GLXContext;
+typedef XID GLXPixmap;
+typedef XID GLXDrawable;
+/* GLX 1.3 and later */
+typedef struct __GLXFBConfigRec *GLXFBConfig;
+typedef XID GLXFBConfigID;
+typedef XID GLXContextID;
+typedef XID GLXWindow;
+typedef XID GLXPbuffer;
+#define GLX_RGBA 4
+#define GLX_RED_SIZE 8
+#define GLX_GREEN_SIZE 9
+#define GLX_BLUE_SIZE 10
+
+// stuff from gl.h
+typedef uint8_t GLubyte;
+typedef uint32_t GLenum;
+#define GL_VENDOR 0x1F00
+#define GL_RENDERER 0x1F01
+#define GL_VERSION 0x1F02
+
+namespace mozilla {
+namespace widget {
+// the read end of the pipe, which will be used by GfxInfo
+extern int glxtest_pipe;
+// the PID of the glxtest process, to pass to waitpid()
+extern pid_t glxtest_pid;
+}
+}
+
+// the write end of the pipe, which we're going to write to
+static int write_end_of_the_pipe = -1;
+
+#if MOZ_WIDGET_GTK == 2
+static int gtk_write_end_of_the_pipe = -1;
+int gtk_read_end_of_the_pipe = -1;
+#endif
+
+// C++ standard collides with C standard in that it doesn't allow casting void* to function pointer types.
+// So the work-around is to convert first to size_t.
+// http://www.trilithium.com/johan/2004/12/problem-with-dlsym/
+template<typename func_ptr_type>
+static func_ptr_type cast(void *ptr)
+{
+ return reinterpret_cast<func_ptr_type>(
+ reinterpret_cast<size_t>(ptr)
+ );
+}
+
+static void fatal_error(const char *str)
+{
+ mozilla::Unused << write(write_end_of_the_pipe, str, strlen(str));
+ mozilla::Unused << write(write_end_of_the_pipe, "\n", 1);
+ _exit(EXIT_FAILURE);
+}
+
+static int
+x_error_handler(Display *, XErrorEvent *ev)
+{
+ enum { bufsize = 1024 };
+ char buf[bufsize];
+ int length = snprintf(buf, bufsize,
+ "X error occurred in GLX probe, error_code=%d, request_code=%d, minor_code=%d\n",
+ ev->error_code,
+ ev->request_code,
+ ev->minor_code);
+ mozilla::Unused << write(write_end_of_the_pipe, buf, length);
+ _exit(EXIT_FAILURE);
+ return 0;
+}
+
+
+// glxtest is declared inside extern "C" so that the name is not mangled.
+// The name is used in build/valgrind/x86_64-redhat-linux-gnu.sup to suppress
+// memory leak errors because we run it inside a short lived fork and we don't
+// care about leaking memory
+extern "C" {
+
+void glxtest()
+{
+ // we want to redirect to /dev/null stdout, stderr, and while we're at it,
+ // any PR logging file descriptors. To that effect, we redirect all positive
+ // file descriptors up to what open() returns here. In particular, 1 is stdout and 2 is stderr.
+ int fd = open("/dev/null", O_WRONLY);
+ for (int i = 1; i < fd; i++)
+ dup2(fd, i);
+ close(fd);
+
+#if MOZ_WIDGET_GTK == 2
+ // On Gtk+2 builds, try to get the Gtk+3 version if it's installed, and
+ // use that in nsSystemInfo for secondaryLibrary. Better safe than sorry,
+ // we want to load the Gtk+3 library in a subprocess, and since we already
+ // have such a subprocess for the GLX test, we piggy back on it.
+ void *gtk3 = dlopen("libgtk-3.so.0", RTLD_LOCAL | RTLD_LAZY);
+ if (gtk3) {
+ auto gtk_get_major_version = reinterpret_cast<guint (*)(void)>(
+ dlsym(gtk3, "gtk_get_major_version"));
+ auto gtk_get_minor_version = reinterpret_cast<guint (*)(void)>(
+ dlsym(gtk3, "gtk_get_minor_version"));
+ auto gtk_get_micro_version = reinterpret_cast<guint (*)(void)>(
+ dlsym(gtk3, "gtk_get_micro_version"));
+
+ if (gtk_get_major_version && gtk_get_minor_version &&
+ gtk_get_micro_version) {
+ // 64 bytes is going to be well enough for "GTK " followed by 3 integers
+ // separated with dots.
+ char gtkver[64];
+ int len = snprintf(gtkver, sizeof(gtkver), "GTK %u.%u.%u",
+ gtk_get_major_version(), gtk_get_minor_version(),
+ gtk_get_micro_version());
+ if (len > 0 && size_t(len) < sizeof(gtkver)) {
+ mozilla::Unused << write(gtk_write_end_of_the_pipe, gtkver, len);
+ }
+ }
+ }
+#endif
+
+
+ if (getenv("MOZ_AVOID_OPENGL_ALTOGETHER"))
+ fatal_error("The MOZ_AVOID_OPENGL_ALTOGETHER environment variable is defined");
+
+ ///// Open libGL and load needed symbols /////
+#ifdef __OpenBSD__
+ #define LIBGL_FILENAME "libGL.so"
+#else
+ #define LIBGL_FILENAME "libGL.so.1"
+#endif
+ void *libgl = dlopen(LIBGL_FILENAME, RTLD_LAZY);
+ if (!libgl)
+ fatal_error("Unable to load " LIBGL_FILENAME);
+
+ typedef void* (* PFNGLXGETPROCADDRESS) (const char *);
+ PFNGLXGETPROCADDRESS glXGetProcAddress = cast<PFNGLXGETPROCADDRESS>(dlsym(libgl, "glXGetProcAddress"));
+
+ if (!glXGetProcAddress)
+ fatal_error("Unable to find glXGetProcAddress in " LIBGL_FILENAME);
+
+ typedef GLXFBConfig* (* PFNGLXQUERYEXTENSION) (Display *, int *, int *);
+ PFNGLXQUERYEXTENSION glXQueryExtension = cast<PFNGLXQUERYEXTENSION>(glXGetProcAddress("glXQueryExtension"));
+
+ typedef GLXFBConfig* (* PFNGLXQUERYVERSION) (Display *, int *, int *);
+ PFNGLXQUERYVERSION glXQueryVersion = cast<PFNGLXQUERYVERSION>(dlsym(libgl, "glXQueryVersion"));
+
+ typedef XVisualInfo* (* PFNGLXCHOOSEVISUAL) (Display *, int, int *);
+ PFNGLXCHOOSEVISUAL glXChooseVisual = cast<PFNGLXCHOOSEVISUAL>(glXGetProcAddress("glXChooseVisual"));
+
+ typedef GLXContext (* PFNGLXCREATECONTEXT) (Display *, XVisualInfo *, GLXContext, Bool);
+ PFNGLXCREATECONTEXT glXCreateContext = cast<PFNGLXCREATECONTEXT>(glXGetProcAddress("glXCreateContext"));
+
+ typedef Bool (* PFNGLXMAKECURRENT) (Display*, GLXDrawable, GLXContext);
+ PFNGLXMAKECURRENT glXMakeCurrent = cast<PFNGLXMAKECURRENT>(glXGetProcAddress("glXMakeCurrent"));
+
+ typedef void (* PFNGLXDESTROYCONTEXT) (Display*, GLXContext);
+ PFNGLXDESTROYCONTEXT glXDestroyContext = cast<PFNGLXDESTROYCONTEXT>(glXGetProcAddress("glXDestroyContext"));
+
+ typedef GLubyte* (* PFNGLGETSTRING) (GLenum);
+ PFNGLGETSTRING glGetString = cast<PFNGLGETSTRING>(glXGetProcAddress("glGetString"));
+
+ if (!glXQueryExtension ||
+ !glXQueryVersion ||
+ !glXChooseVisual ||
+ !glXCreateContext ||
+ !glXMakeCurrent ||
+ !glXDestroyContext ||
+ !glGetString)
+ {
+ fatal_error("glXGetProcAddress couldn't find required functions");
+ }
+ ///// Open a connection to the X server /////
+ Display *dpy = XOpenDisplay(nullptr);
+ if (!dpy)
+ fatal_error("Unable to open a connection to the X server");
+
+ ///// Check that the GLX extension is present /////
+ if (!glXQueryExtension(dpy, nullptr, nullptr))
+ fatal_error("GLX extension missing");
+
+ XSetErrorHandler(x_error_handler);
+
+ ///// Get a visual /////
+ int attribs[] = {
+ GLX_RGBA,
+ GLX_RED_SIZE, 1,
+ GLX_GREEN_SIZE, 1,
+ GLX_BLUE_SIZE, 1,
+ None };
+ XVisualInfo *vInfo = glXChooseVisual(dpy, DefaultScreen(dpy), attribs);
+ if (!vInfo)
+ fatal_error("No visuals found");
+
+ // using a X11 Window instead of a GLXPixmap does not crash
+ // fglrx in indirect rendering. bug 680644
+ Window window;
+ XSetWindowAttributes swa;
+ swa.colormap = XCreateColormap(dpy, RootWindow(dpy, vInfo->screen),
+ vInfo->visual, AllocNone);
+
+ swa.border_pixel = 0;
+ window = XCreateWindow(dpy, RootWindow(dpy, vInfo->screen),
+ 0, 0, 16, 16,
+ 0, vInfo->depth, InputOutput, vInfo->visual,
+ CWBorderPixel | CWColormap, &swa);
+
+ ///// Get a GL context and make it current //////
+ GLXContext context = glXCreateContext(dpy, vInfo, nullptr, True);
+ glXMakeCurrent(dpy, window, context);
+
+ ///// Look for this symbol to determine texture_from_pixmap support /////
+ void* glXBindTexImageEXT = glXGetProcAddress("glXBindTexImageEXT");
+
+ ///// Get GL vendor/renderer/versions strings /////
+ enum { bufsize = 1024 };
+ char buf[bufsize];
+ const GLubyte *vendorString = glGetString(GL_VENDOR);
+ const GLubyte *rendererString = glGetString(GL_RENDERER);
+ const GLubyte *versionString = glGetString(GL_VERSION);
+
+ if (!vendorString || !rendererString || !versionString)
+ fatal_error("glGetString returned null");
+
+ int length = snprintf(buf, bufsize,
+ "VENDOR\n%s\nRENDERER\n%s\nVERSION\n%s\nTFP\n%s\n",
+ vendorString,
+ rendererString,
+ versionString,
+ glXBindTexImageEXT ? "TRUE" : "FALSE");
+ if (length >= bufsize)
+ fatal_error("GL strings length too large for buffer size");
+
+ ///// Clean up. Indeed, the parent process might fail to kill us (e.g. if it doesn't need to check GL info)
+ ///// so we might be staying alive for longer than expected, so it's important to consume as little memory as
+ ///// possible. Also we want to check that we're able to do that too without generating X errors.
+ glXMakeCurrent(dpy, None, nullptr); // must release the GL context before destroying it
+ glXDestroyContext(dpy, context);
+ XDestroyWindow(dpy, window);
+ XFreeColormap(dpy, swa.colormap);
+
+#ifdef NS_FREE_PERMANENT_DATA // conditionally defined in nscore.h, don't forget to #include it above
+ XCloseDisplay(dpy);
+#else
+ // This XSync call wanted to be instead:
+ // XCloseDisplay(dpy);
+ // but this can cause 1-minute stalls on certain setups using Nouveau, see bug 973192
+ XSync(dpy, False);
+#endif
+
+ dlclose(libgl);
+
+ ///// Finally write data to the pipe
+ mozilla::Unused << write(write_end_of_the_pipe, buf, length);
+}
+
+}
+
+/** \returns true in the child glxtest process, false in the parent process */
+bool fire_glxtest_process()
+{
+ int pfd[2];
+ if (pipe(pfd) == -1) {
+ perror("pipe");
+ return false;
+ }
+#if MOZ_WIDGET_GTK == 2
+ int gtkpfd[2];
+ if (pipe(gtkpfd) == -1) {
+ perror("pipe");
+ return false;
+ }
+#endif
+ pid_t pid = fork();
+ if (pid < 0) {
+ perror("fork");
+ close(pfd[0]);
+ close(pfd[1]);
+#if MOZ_WIDGET_GTK == 2
+ close(gtkpfd[0]);
+ close(gtkpfd[1]);
+#endif
+ return false;
+ }
+ // The child exits early to avoid running the full shutdown sequence and avoid conflicting with threads
+ // we have already spawned (like the profiler).
+ if (pid == 0) {
+ close(pfd[0]);
+ write_end_of_the_pipe = pfd[1];
+#if MOZ_WIDGET_GTK == 2
+ close(gtkpfd[0]);
+ gtk_write_end_of_the_pipe = gtkpfd[1];
+#endif
+ glxtest();
+ close(pfd[1]);
+#if MOZ_WIDGET_GTK == 2
+ close(gtkpfd[1]);
+#endif
+ _exit(0);
+ }
+
+ close(pfd[1]);
+ mozilla::widget::glxtest_pipe = pfd[0];
+ mozilla::widget::glxtest_pid = pid;
+#if MOZ_WIDGET_GTK == 2
+ close(gtkpfd[1]);
+ gtk_read_end_of_the_pipe = gtkpfd[0];
+#endif
+ return false;
+}
diff --git a/toolkit/xre/moz.build b/toolkit/xre/moz.build
new file mode 100644
index 000000000..dd15dc0bd
--- /dev/null
+++ b/toolkit/xre/moz.build
@@ -0,0 +1,185 @@
+# -*- 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/.
+
+if CONFIG['OS_ARCH'] == 'WINNT':
+ TEST_DIRS += ['test/win']
+
+MOCHITEST_MANIFESTS += ['test/mochitest.ini']
+BROWSER_CHROME_MANIFESTS += ['test/browser.ini']
+
+XPIDL_SOURCES += [
+ 'nsINativeAppSupport.idl',
+]
+
+if CONFIG['OS_ARCH'] == 'WINNT':
+ XPIDL_SOURCES += [
+ 'nsIWinAppHelper.idl',
+ ]
+
+XPIDL_MODULE = 'xulapp'
+
+EXPORTS += ['nsAppRunner.h']
+
+if CONFIG['MOZ_INSTRUMENT_EVENT_LOOP']:
+ EXPORTS += ['EventTracer.h']
+
+if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'windows':
+ UNIFIED_SOURCES += [
+ 'nsNativeAppSupportWin.cpp',
+ ]
+elif CONFIG['MOZ_WIDGET_TOOLKIT'] == 'cocoa':
+ UNIFIED_SOURCES += [
+ 'MacApplicationDelegate.mm',
+ 'MacAutoreleasePool.mm',
+ 'MacLaunchHelper.mm',
+ 'nsCommandLineServiceMac.cpp',
+ 'nsNativeAppSupportCocoa.mm',
+ 'updaterfileutils_osx.mm',
+ ]
+elif CONFIG['MOZ_WIDGET_TOOLKIT'] == 'uikit':
+ UNIFIED_SOURCES += [
+ 'nsNativeAppSupportDefault.cpp',
+ 'UIKitDirProvider.mm',
+ ]
+elif 'gtk' in CONFIG['MOZ_WIDGET_TOOLKIT']:
+ UNIFIED_SOURCES += [
+ 'nsNativeAppSupportUnix.cpp',
+ ]
+else:
+ UNIFIED_SOURCES += [
+ 'nsNativeAppSupportDefault.cpp',
+ ]
+
+if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'gtk3':
+ UNIFIED_SOURCES += [
+ 'nsGDKErrorHandler.cpp',
+ ]
+
+if CONFIG['MOZ_X11']:
+ EXPORTS += ['nsX11ErrorHandler.h']
+ UNIFIED_SOURCES += [
+ 'nsX11ErrorHandler.cpp',
+ ]
+
+if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'android':
+ UNIFIED_SOURCES += [
+ 'nsAndroidStartup.cpp',
+ ]
+
+UNIFIED_SOURCES += [
+ 'CreateAppData.cpp',
+ 'nsConsoleWriter.cpp',
+ 'nsNativeAppSupportBase.cpp',
+ 'nsSigHandlers.cpp',
+ 'nsXREDirProvider.cpp',
+]
+
+# nsAppRunner.cpp and ProfileReset.cpp cannot be built in unified mode because
+# they pull in OS X system headers.
+# nsEmbedFunctions.cpp cannot be built in unified mode because it pulls in X11 headers.
+SOURCES += [
+ 'nsAppRunner.cpp',
+ 'nsEmbedFunctions.cpp',
+ 'ProfileReset.cpp',
+]
+
+if CONFIG['MOZ_GL_DEFAULT_PROVIDER'] == 'GLX':
+ UNIFIED_SOURCES += [
+ 'glxtest.cpp',
+ ]
+
+if CONFIG['MOZ_INSTRUMENT_EVENT_LOOP']:
+ UNIFIED_SOURCES += [
+ 'EventTracer.cpp',
+ ]
+
+if CONFIG['MOZ_UPDATER']:
+ if CONFIG['MOZ_WIDGET_TOOLKIT'] != 'android':
+ UNIFIED_SOURCES += [
+ 'nsUpdateDriver.cpp',
+ ]
+
+include('/ipc/chromium/chromium-config.mozbuild')
+
+FINAL_LIBRARY = 'xul'
+
+if CONFIG['MOZ_GL_DEFAULT_PROVIDER'] == 'GLX':
+ DEFINES['USE_GLX_TEST'] = True
+
+for var in ('MOZ_APP_NAME', 'MOZ_APP_BASENAME', 'MOZ_APP_VERSION', 'OS_TARGET',
+ 'MOZ_WIDGET_TOOLKIT'):
+ DEFINES[var] = '"%s"' % CONFIG[var]
+
+if CONFIG['MOZ_UPDATER'] and CONFIG['MOZ_WIDGET_TOOLKIT'] != 'android':
+ DEFINES['MOZ_UPDATER'] = True
+
+if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'windows':
+ DEFINES['WIN32_LEAN_AND_MEAN'] = True
+ DEFINES['UNICODE'] = True
+ DEFINES['_UNICODE'] = True
+
+if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'android':
+ DEFINES['ANDROID_PACKAGE_NAME'] = '"%s"' % CONFIG['ANDROID_PACKAGE_NAME']
+
+if CONFIG['TARGET_XPCOM_ABI']:
+ DEFINES['TARGET_OS_ABI'] = '"%s_%s"' % (CONFIG['OS_TARGET'],
+ CONFIG['TARGET_XPCOM_ABI'])
+
+if CONFIG['OS_ARCH'] == 'Linux' and 'lib64' in CONFIG['libdir']:
+ DEFINES['HAVE_USR_LIB64_DIR'] = True
+
+DEFINES['GRE_MILESTONE'] = CONFIG['GRE_MILESTONE']
+
+for var in ('APP_VERSION', 'APP_ID'):
+ DEFINES[var] = CONFIG['MOZ_%s' % var]
+
+if CONFIG['MOZ_BUILD_APP'] == 'browser':
+ DEFINES['MOZ_BUILD_APP_IS_BROWSER'] = True
+
+LOCAL_INCLUDES += [
+ '../profile',
+ '/config',
+ '/dom/base',
+ '/dom/ipc',
+ '/testing/gtest/mozilla',
+ '/toolkit/crashreporter',
+ '/xpcom/build',
+]
+
+if CONFIG['MOZ_SANDBOX'] and CONFIG['OS_ARCH'] == 'WINNT':
+ LOCAL_INCLUDES += [
+ '/security/sandbox/chromium',
+ '/security/sandbox/chromium-shim',
+ ]
+
+if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'cocoa':
+ LOCAL_INCLUDES += [
+ '/widget',
+ '/widget/cocoa',
+ ]
+
+if CONFIG['MOZ_ENABLE_XREMOTE']:
+ LOCAL_INCLUDES += [
+ '/widget/xremoteclient',
+ ]
+
+CXXFLAGS += CONFIG['TK_CFLAGS']
+CXXFLAGS += CONFIG['MOZ_DBUS_CFLAGS']
+CXXFLAGS += CONFIG['MOZ_DBUS_GLIB_CFLAGS']
+
+if 'gtk' in CONFIG['MOZ_WIDGET_TOOLKIT']:
+ CXXFLAGS += CONFIG['MOZ_PANGO_CFLAGS']
+
+DEFINES['TOPOBJDIR'] = TOPOBJDIR
+FINAL_TARGET_PP_FILES += [
+ 'platform.ini'
+]
+
+if CONFIG['GNU_CXX']:
+ CXXFLAGS += ['-Wno-error=shadow']
+
+if CONFIG['MOZ_IPDL_TESTS']:
+ DEFINES['MOZ_IPDL_TESTS'] = True
diff --git a/toolkit/xre/nsAndroidStartup.cpp b/toolkit/xre/nsAndroidStartup.cpp
new file mode 100644
index 000000000..a88c58e5d
--- /dev/null
+++ b/toolkit/xre/nsAndroidStartup.cpp
@@ -0,0 +1,55 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * 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 <android/log.h>
+
+#include <jni.h>
+
+#include <stdlib.h>
+#include <string.h>
+#include <pthread.h>
+
+#include "mozilla/jni/Utils.h"
+#include "nsTArray.h"
+#include "nsString.h"
+#include "nsIFile.h"
+#include "nsAppRunner.h"
+#include "APKOpen.h"
+#include "nsExceptionHandler.h"
+
+#define LOG(args...) __android_log_print(ANDROID_LOG_INFO, MOZ_APP_NAME, args)
+
+extern "C" NS_EXPORT void
+GeckoStart(JNIEnv* env, char* data, const nsXREAppData* appData)
+{
+ mozilla::jni::SetGeckoThreadEnv(env);
+
+#ifdef MOZ_CRASHREPORTER
+ const struct mapping_info *info = getLibraryMapping();
+ while (info->name) {
+ CrashReporter::AddLibraryMapping(info->name, info->base,
+ info->len, info->offset);
+ info++;
+ }
+#endif
+
+ if (!data) {
+ LOG("Failed to get arguments for GeckoStart\n");
+ return;
+ }
+
+ nsTArray<char *> targs;
+ char *arg = strtok(data, " ");
+ while (arg) {
+ targs.AppendElement(arg);
+ arg = strtok(nullptr, " ");
+ }
+ targs.AppendElement(static_cast<char *>(nullptr));
+
+ int result = XRE_main(targs.Length() - 1, targs.Elements(), appData, 0);
+
+ if (result)
+ LOG("XRE_main returned %d", result);
+}
diff --git a/toolkit/xre/nsAppRunner.cpp b/toolkit/xre/nsAppRunner.cpp
new file mode 100644
index 000000000..4979e1652
--- /dev/null
+++ b/toolkit/xre/nsAppRunner.cpp
@@ -0,0 +1,5068 @@
+/* -*- 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/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
+
+#if (defined(XP_WIN) || defined(XP_MACOSX)) && defined(MOZ_CONTENT_SANDBOX)
+#include "nsIUUIDGenerator.h"
+#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
+
+#ifdef MOZ_JPROF
+#include "jprof.h"
+#endif
+
+#ifdef MOZ_CRASHREPORTER
+#include "nsExceptionHandler.h"
+#include "nsICrashReporter.h"
+#define NS_CRASHREPORTER_CONTRACTID "@mozilla.org/toolkit/crash-reporter;1"
+#include "nsIPrefService.h"
+#include "nsIMemoryInfoDumper.h"
+#if defined(XP_LINUX) && !defined(ANDROID)
+#include "mozilla/widget/LSBUtils.h"
+#endif
+#endif
+
+#include "base/command_line.h"
+#include "GTestRunner.h"
+
+#ifdef MOZ_WIDGET_ANDROID
+#include "GeneratedJNIWrappers.h"
+#endif
+
+#if defined(MOZ_SANDBOX)
+#if defined(XP_LINUX) && !defined(ANDROID)
+#include "mozilla/SandboxInfo.h"
+#elif defined(XP_WIN)
+#include "SandboxBroker.h"
+#endif
+#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
+#ifdef MOZ_CRASHREPORTER
+ public nsICrashReporter,
+ public nsIFinishDumpingCallback,
+#endif
+ public nsIXULRuntime
+
+{
+public:
+ constexpr nsXULAppInfo() {}
+ NS_DECL_ISUPPORTS_INHERITED
+ NS_DECL_NSIPLATFORMINFO
+ NS_DECL_NSIXULAPPINFO
+ NS_DECL_NSIXULRUNTIME
+ NS_DECL_NSIOBSERVER
+#ifdef MOZ_CRASHREPORTER
+ NS_DECL_NSICRASHREPORTER
+ NS_DECL_NSIFINISHDUMPINGCALLBACK
+#endif
+#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
+#ifdef MOZ_CRASHREPORTER
+ NS_INTERFACE_MAP_ENTRY(nsICrashReporter)
+ NS_INTERFACE_MAP_ENTRY(nsIFinishDumpingCallback)
+#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)
+{
+#ifdef MOZ_CRASHREPORTER
+ CrashReporter::GetLastRunCrashID(aLastRunCrashID);
+ return NS_OK;
+#else
+ return NS_ERROR_NOT_IMPLEMENTED;
+#endif
+}
+
+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 MOZILLA_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
+
+#ifdef MOZ_CRASHREPORTER
+NS_IMETHODIMP
+nsXULAppInfo::GetEnabled(bool *aEnabled)
+{
+ *aEnabled = CrashReporter::GetEnabled();
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsXULAppInfo::SetEnabled(bool aEnabled)
+{
+ if (aEnabled) {
+ if (CrashReporter::GetEnabled()) {
+ // no point in erroring for double-enabling
+ return NS_OK;
+ }
+
+ nsCOMPtr<nsIFile> greBinDir;
+ NS_GetSpecialDirectory(NS_GRE_BIN_DIR, getter_AddRefs(greBinDir));
+ if (!greBinDir) {
+ return NS_ERROR_FAILURE;
+ }
+
+ nsCOMPtr<nsIFile> xreBinDirectory = do_QueryInterface(greBinDir);
+ if (!xreBinDirectory) {
+ return NS_ERROR_FAILURE;
+ }
+
+ return CrashReporter::SetExceptionHandler(xreBinDirectory, true);
+ }
+ else {
+ if (!CrashReporter::GetEnabled()) {
+ // no point in erroring for double-disabling
+ return NS_OK;
+ }
+
+ return CrashReporter::UnsetExceptionHandler();
+ }
+}
+
+NS_IMETHODIMP
+nsXULAppInfo::GetServerURL(nsIURL** aServerURL)
+{
+ if (!CrashReporter::GetEnabled())
+ return NS_ERROR_NOT_INITIALIZED;
+
+ nsAutoCString data;
+ if (!CrashReporter::GetServerURL(data)) {
+ return NS_ERROR_FAILURE;
+ }
+ nsCOMPtr<nsIURI> uri;
+ NS_NewURI(getter_AddRefs(uri), data);
+ if (!uri)
+ return NS_ERROR_FAILURE;
+
+ nsCOMPtr<nsIURL> url;
+ url = do_QueryInterface(uri);
+ NS_ADDREF(*aServerURL = url);
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsXULAppInfo::SetServerURL(nsIURL* aServerURL)
+{
+ bool schemeOk;
+ // only allow https or http URLs
+ nsresult rv = aServerURL->SchemeIs("https", &schemeOk);
+ NS_ENSURE_SUCCESS(rv, rv);
+ if (!schemeOk) {
+ rv = aServerURL->SchemeIs("http", &schemeOk);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (!schemeOk)
+ return NS_ERROR_INVALID_ARG;
+ }
+ nsAutoCString spec;
+ rv = aServerURL->GetSpec(spec);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ return CrashReporter::SetServerURL(spec);
+}
+
+NS_IMETHODIMP
+nsXULAppInfo::GetMinidumpPath(nsIFile** aMinidumpPath)
+{
+ if (!CrashReporter::GetEnabled())
+ return NS_ERROR_NOT_INITIALIZED;
+
+ nsAutoString path;
+ if (!CrashReporter::GetMinidumpPath(path))
+ return NS_ERROR_FAILURE;
+
+ nsresult rv = NS_NewLocalFile(path, false, aMinidumpPath);
+ NS_ENSURE_SUCCESS(rv, rv);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsXULAppInfo::SetMinidumpPath(nsIFile* aMinidumpPath)
+{
+ nsAutoString path;
+ nsresult rv = aMinidumpPath->GetPath(path);
+ NS_ENSURE_SUCCESS(rv, rv);
+ return CrashReporter::SetMinidumpPath(path);
+}
+
+NS_IMETHODIMP
+nsXULAppInfo::AnnotateCrashReport(const nsACString& key,
+ const nsACString& data)
+{
+ return CrashReporter::AnnotateCrashReport(key, data);
+}
+
+NS_IMETHODIMP
+nsXULAppInfo::AppendAppNotesToCrashReport(const nsACString& data)
+{
+ return CrashReporter::AppendAppNotesToCrashReport(data);
+}
+
+NS_IMETHODIMP
+nsXULAppInfo::RegisterAppMemory(uint64_t pointer,
+ uint64_t len)
+{
+ return CrashReporter::RegisterAppMemory((void *)pointer, len);
+}
+
+NS_IMETHODIMP
+nsXULAppInfo::WriteMinidumpForException(void* aExceptionInfo)
+{
+#ifdef XP_WIN32
+ return CrashReporter::WriteMinidumpForException(static_cast<EXCEPTION_POINTERS*>(aExceptionInfo));
+#else
+ return NS_ERROR_NOT_IMPLEMENTED;
+#endif
+}
+
+NS_IMETHODIMP
+nsXULAppInfo::AppendObjCExceptionInfoToAppNotes(void* aException)
+{
+#ifdef XP_MACOSX
+ return CrashReporter::AppendObjCExceptionInfoToAppNotes(aException);
+#else
+ return NS_ERROR_NOT_IMPLEMENTED;
+#endif
+}
+
+NS_IMETHODIMP
+nsXULAppInfo::GetSubmitReports(bool* aEnabled)
+{
+ return CrashReporter::GetSubmitReports(aEnabled);
+}
+
+NS_IMETHODIMP
+nsXULAppInfo::SetSubmitReports(bool aEnabled)
+{
+ return CrashReporter::SetSubmitReports(aEnabled);
+}
+
+NS_IMETHODIMP
+nsXULAppInfo::UpdateCrashEventsDir()
+{
+ CrashReporter::UpdateCrashEventsDir();
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsXULAppInfo::SaveMemoryReport()
+{
+ if (!CrashReporter::GetEnabled()) {
+ return NS_ERROR_NOT_INITIALIZED;
+ }
+ nsCOMPtr<nsIFile> file;
+ nsresult rv = CrashReporter::GetDefaultMemoryReportFile(getter_AddRefs(file));
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ nsString path;
+ file->GetPath(path);
+
+ nsCOMPtr<nsIMemoryInfoDumper> dumper =
+ do_GetService("@mozilla.org/memory-info-dumper;1");
+ if (NS_WARN_IF(!dumper)) {
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ rv = dumper->DumpMemoryReportsToNamedFile(path, this, file, true /* anonymize */);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsXULAppInfo::SetTelemetrySessionId(const nsACString& id)
+{
+ CrashReporter::SetTelemetrySessionId(id);
+ return NS_OK;
+}
+
+// This method is from nsIFInishDumpingCallback.
+NS_IMETHODIMP
+nsXULAppInfo::Callback(nsISupports* aData)
+{
+ nsCOMPtr<nsIFile> file = do_QueryInterface(aData);
+ MOZ_ASSERT(file);
+
+ CrashReporter::SetMemoryReportFile(file);
+ 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 },
+#ifdef MOZ_CRASHREPORTER
+ { NS_CRASHREPORTER_CONTRACTID, &kAPPINFO_CID },
+#endif
+ { 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"
+ " --migration Start with migration wizard.\n"
+ " --ProfileManager Start with ProfileManager.\n"
+ " --no-remote Do not accept or send remote commands; implies\n"
+ " --new-instance.\n"
+ " --new-instance Open new instance, not a new window in running instance.\n"
+ " --UILocale <locale> Start with <locale> resources as UI Locale.\n"
+ " --safe-mode Disables extensions and themes for this session.\n", gAppData->name);
+
+#if defined(XP_WIN)
+ printf(" --console Start %s with a debugging console.\n", 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.
+
+#ifdef MOZ_JPROF
+ // make sure JPROF doesn't think we're E10s
+ unsetenv("JPROF_SLAVE");
+#endif
+
+ 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);
+}
+
+/**
+ * Set the currently running profile as the default/selected one.
+ *
+ * @param aCurrentProfileRoot The root directory of the current profile.
+ * @return an error if aCurrentProfileRoot is not found or the profile could not
+ * be set as the default.
+ */
+static nsresult
+SetCurrentProfileAsDefault(nsIToolkitProfileService* aProfileSvc,
+ nsIFile* aCurrentProfileRoot)
+{
+ NS_ENSURE_ARG_POINTER(aProfileSvc);
+
+ 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) {
+ return aProfileSvc->SetSelectedProfile(profile);
+ }
+ 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, 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);
+ }
+
+ nsCOMPtr<nsIToolkitProfile> newProfile;
+ rv = CreateResetProfile(aProfileSvc, getter_AddRefs(newProfile));
+ if (NS_FAILED(rv)) {
+ NS_WARNING("Failed to create a profile to reset to.");
+ gDoProfileReset = false;
+ } else {
+ nsresult gotName = profile->GetName(gResetOldProfileName);
+ if (NS_SUCCEEDED(gotName)) {
+ 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);
+ }
+
+#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) {
+ gDoMigration = true;
+ 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 (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;
+ }
+ }
+ }
+
+ 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);
+ }
+
+ nsCOMPtr<nsIToolkitProfile> newProfile;
+ rv = CreateResetProfile(aProfileSvc, getter_AddRefs(newProfile));
+ if (NS_FAILED(rv)) {
+ NS_WARNING("Failed to create a profile to reset to.");
+ gDoProfileReset = false;
+ } else {
+ nsresult gotName = profile->GetName(gResetOldProfileName);
+ if (NS_SUCCEEDED(gotName)) {
+ 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) || defined(XP_BEOS)
+#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);
+ }
+}
+
+#ifdef MOZ_CRASHREPORTER
+// When we first initialize the crash reporter we don't have a profile,
+// so we set the minidump path to $TEMP. Once we have a profile,
+// we set it to $PROFILE/minidumps, creating the directory
+// if needed.
+static void MakeOrSetMinidumpPath(nsIFile* profD)
+{
+ nsCOMPtr<nsIFile> dumpD;
+ profD->Clone(getter_AddRefs(dumpD));
+
+ if (dumpD) {
+ bool fileExists;
+ //XXX: do some more error checking here
+ dumpD->Append(NS_LITERAL_STRING("minidumps"));
+ dumpD->Exists(&fileExists);
+ if (!fileExists) {
+ nsresult rv = dumpD->Create(nsIFile::DIRECTORY_TYPE, 0700);
+ NS_ENSURE_SUCCESS_VOID(rv);
+ }
+
+ nsAutoString pathStr;
+ if (NS_SUCCEEDED(dumpD->GetPath(pathStr)))
+ CrashReporter::SetMinidumpPath(pathStr);
+ }
+}
+#endif
+
+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.
+#ifdef cairo_debug_reset_static_data
+#error "Looks like we're including Mozilla's cairo instead of system cairo"
+#endif
+ cairo_debug_reset_static_data();
+ // FIXME: Do we need to call this in non-GTK2 cases as well?
+ FcFini();
+
+ if (buggyCairoShutdown) {
+ if (!skip_display_close)
+ gdk_display_close(display);
+ }
+#else // not CLEANUP_MEMORY
+ // Don't do anything to avoid running into driver bugs under XCloseDisplay().
+ // See bug 973192.
+ (void) display;
+#endif
+}
+
+static const char* detectDisplay(void)
+{
+ bool tryX11 = false;
+ bool tryWayland = false;
+ bool tryBroadway = false;
+
+ // Honor user backend selection
+ const char *backend = PR_GetEnv("GDK_BACKEND");
+ if (!backend || strstr(backend, "*")) {
+ // Try all backends
+ tryX11 = true;
+ tryWayland = true;
+ tryBroadway = true;
+ } else if (backend) {
+ if (strstr(backend, "x11"))
+ tryX11 = true;
+ if (strstr(backend, "wayland"))
+ tryWayland = true;
+ if (strstr(backend, "broadway"))
+ tryBroadway = true;
+ }
+
+ const char *display_name;
+ if (tryX11 && (display_name = PR_GetEnv("DISPLAY"))) {
+ return display_name;
+ } else if (tryWayland && (display_name = PR_GetEnv("WAYLAND_DISPLAY"))) {
+ return display_name;
+ } else if (tryBroadway && (display_name = PR_GetEnv("BROADWAY_DISPLAY"))) {
+ return display_name;
+ }
+
+ PR_fprintf(PR_STDERR, "Error: GDK_BACKEND does not match available displays\n");
+ return nullptr;
+}
+#endif // MOZ_WIDGET_GTK
+
+/**
+ * NSPR will search for the "nspr_use_zone_allocator" symbol throughout
+ * the process and use it to determine whether the application defines its own
+ * memory allocator or not.
+ *
+ * Since most applications (e.g. Firefox and Thunderbird) don't use any special
+ * allocators and therefore don't define this symbol, NSPR must search the
+ * entire process, which reduces startup performance.
+ *
+ * By defining the symbol here, we can avoid the wasted lookup and hopefully
+ * improve startup performance.
+ */
+NS_VISIBILITY_DEFAULT PRBool nspr_use_zone_allocator = PR_FALSE;
+
+#ifdef CAIRO_HAS_DWRITE_FONT
+
+#include <dwrite.h>
+
+#ifdef DEBUG_DWRITE_STARTUP
+
+#define LOGREGISTRY(msg) LogRegistryEvent(msg)
+
+// for use when monitoring process
+static void LogRegistryEvent(const wchar_t *msg)
+{
+ HKEY dummyKey;
+ HRESULT hr;
+ wchar_t buf[512];
+
+ wsprintf(buf, L" log %s", msg);
+ hr = RegOpenKeyEx(HKEY_LOCAL_MACHINE, buf, 0, KEY_READ, &dummyKey);
+ if (SUCCEEDED(hr)) {
+ RegCloseKey(dummyKey);
+ }
+}
+#else
+
+#define LOGREGISTRY(msg)
+
+#endif
+
+static DWORD WINAPI InitDwriteBG(LPVOID lpdwThreadParam)
+{
+ SetThreadPriority(GetCurrentThread(), THREAD_MODE_BACKGROUND_BEGIN);
+ LOGREGISTRY(L"loading dwrite.dll");
+ HMODULE dwdll = LoadLibraryW(L"dwrite.dll");
+ if (dwdll) {
+ decltype(DWriteCreateFactory)* createDWriteFactory = (decltype(DWriteCreateFactory)*)
+ GetProcAddress(dwdll, "DWriteCreateFactory");
+ if (createDWriteFactory) {
+ LOGREGISTRY(L"creating dwrite factory");
+ IDWriteFactory *factory;
+ HRESULT hr = createDWriteFactory(
+ DWRITE_FACTORY_TYPE_SHARED,
+ __uuidof(IDWriteFactory),
+ reinterpret_cast<IUnknown**>(&factory));
+ if (SUCCEEDED(hr)) {
+ LOGREGISTRY(L"dwrite factory done");
+ factory->Release();
+ LOGREGISTRY(L"freed factory");
+ } else {
+ LOGREGISTRY(L"failed to create factory");
+ }
+ }
+ }
+ SetThreadPriority(GetCurrentThread(), THREAD_MODE_BACKGROUND_END);
+ return 0;
+}
+#endif
+
+#ifdef USE_GLX_TEST
+bool fire_glxtest_process();
+#endif
+
+#include "GeckoProfiler.h"
+
+// Encapsulates startup and shutdown state for XRE_main
+class XREMain
+{
+public:
+ XREMain() :
+ mStartOffline(false)
+ , mShuttingDown(false)
+#ifdef MOZ_ENABLE_XREMOTE
+ , mDisableRemote(false)
+#endif
+#if defined(MOZ_WIDGET_GTK)
+ , mGdkDisplay(nullptr)
+#endif
+ {};
+
+ ~XREMain() {
+ mScopedXPCOM = nullptr;
+ mAppData = nullptr;
+ }
+
+ int XRE_main(int argc, char* argv[], const nsXREAppData* aAppData);
+ int XRE_mainInit(bool* aExitFlag);
+ int XRE_mainStartup(bool* aExitFlag);
+ nsresult XRE_mainRun();
+
+ nsCOMPtr<nsINativeAppSupport> mNativeApp;
+ nsCOMPtr<nsIToolkitProfileService> mProfileSvc;
+ nsCOMPtr<nsIFile> mProfD;
+ nsCOMPtr<nsIFile> mProfLD;
+ nsCOMPtr<nsIProfileLock> mProfileLock;
+#ifdef MOZ_ENABLE_XREMOTE
+ nsCOMPtr<nsIRemoteService> mRemoteService;
+ nsProfileLock mRemoteLock;
+ nsCOMPtr<nsIFile> mRemoteLockDir;
+#endif
+
+ UniquePtr<ScopedXPCOMStartup> mScopedXPCOM;
+ nsAutoPtr<mozilla::ScopedAppData> mAppData;
+
+ nsXREDirProvider mDirProvider;
+ nsAutoCString mProfileName;
+ nsAutoCString mDesktopStartupID;
+
+ bool mStartOffline;
+ bool mShuttingDown;
+#ifdef MOZ_ENABLE_XREMOTE
+ bool mDisableRemote;
+#endif
+
+#if defined(MOZ_WIDGET_GTK)
+ GdkDisplay* mGdkDisplay;
+#endif
+};
+
+/*
+ * XRE_mainInit - Initial setup and command line parameter processing.
+ * Main() will exit early if either return value != 0 or if aExitFlag is
+ * true.
+ */
+int
+XREMain::XRE_mainInit(bool* aExitFlag)
+{
+ if (!aExitFlag)
+ return 1;
+ *aExitFlag = false;
+
+ atexit(UnexpectedExit);
+ auto expectedShutdown = mozilla::MakeScopeExit([&] {
+ MozExpectedExit();
+ });
+
+ StartupTimeline::Record(StartupTimeline::MAIN);
+
+ if (PR_GetEnv("MOZ_CHAOSMODE")) {
+ ChaosFeature feature = ChaosFeature::Any;
+ long featureInt = strtol(PR_GetEnv("MOZ_CHAOSMODE"), nullptr, 16);
+ if (featureInt) {
+ // NOTE: MOZ_CHAOSMODE=0 or a non-hex value maps to Any feature.
+ feature = static_cast<ChaosFeature>(featureInt);
+ }
+ ChaosMode::SetChaosFeature(feature);
+ }
+
+ if (ChaosMode::isActive(ChaosFeature::Any)) {
+ printf_stderr("*** You are running in chaos test mode. See ChaosMode.h. ***\n");
+ }
+
+ nsresult rv;
+ ArgResult ar;
+
+#ifdef DEBUG
+ if (PR_GetEnv("XRE_MAIN_BREAK"))
+ NS_BREAK();
+#endif
+
+#ifdef USE_GLX_TEST
+ // bug 639842 - it's very important to fire this process BEFORE we set up
+ // error handling. indeed, this process is expected to be crashy, and we
+ // don't want the user to see its crashes. That's the whole reason for
+ // doing this in a separate process.
+ //
+ // This call will cause a fork and the fork will terminate itself separately
+ // from the usual shutdown sequence
+ fire_glxtest_process();
+#endif
+
+ SetupErrorHandling(gArgv[0]);
+
+#ifdef CAIRO_HAS_DWRITE_FONT
+ {
+ // Bug 602792 - when DWriteCreateFactory is called the dwrite client dll
+ // starts the FntCache service if it isn't already running (it's set
+ // to manual startup by default in Windows 7 RTM). Subsequent DirectWrite
+ // calls cause the IDWriteFactory object to communicate with the FntCache
+ // service with a timeout; if there's no response after the timeout, the
+ // DirectWrite client library will assume the service isn't around and do
+ // manual font file I/O on _all_ system fonts. To avoid this, load the
+ // dwrite library and create a factory as early as possible so that the
+ // FntCache service is ready by the time it's needed.
+
+ if (IsVistaOrLater()) {
+ CreateThread(nullptr, 0, &InitDwriteBG, nullptr, 0, nullptr);
+ }
+ }
+#endif
+
+#ifdef XP_UNIX
+ const char *home = PR_GetEnv("HOME");
+ if (!home || !*home) {
+ struct passwd *pw = getpwuid(geteuid());
+ if (!pw || !pw->pw_dir) {
+ Output(true, "Could not determine HOME directory");
+ return 1;
+ }
+ SaveWordToEnv("HOME", nsDependentCString(pw->pw_dir));
+ }
+#endif
+
+#ifdef MOZ_ACCESSIBILITY_ATK
+ // Suppress atk-bridge init at startup, until mozilla accessibility is
+ // initialized. This works after gnome 2.24.2.
+ SaveToEnv("NO_AT_BRIDGE=1");
+#endif
+
+ // Check for application.ini overrides
+ const char* override = nullptr;
+ ar = CheckArg("override", true, &override);
+ if (ar == ARG_BAD) {
+ Output(true, "Incorrect number of arguments passed to --override");
+ return 1;
+ }
+ else if (ar == ARG_FOUND) {
+ nsCOMPtr<nsIFile> overrideLF;
+ rv = XRE_GetFileFromPath(override, getter_AddRefs(overrideLF));
+ if (NS_FAILED(rv)) {
+ Output(true, "Error: unrecognized override.ini path.\n");
+ return 1;
+ }
+
+ rv = XRE_ParseAppData(overrideLF, mAppData.get());
+ if (NS_FAILED(rv)) {
+ Output(true, "Couldn't read override.ini");
+ return 1;
+ }
+ }
+
+ // Check sanity and correctness of app data.
+
+ if (!mAppData->name) {
+ Output(true, "Error: App:Name not specified in application.ini\n");
+ return 1;
+ }
+ if (!mAppData->buildID) {
+ Output(true, "Error: App:BuildID not specified in application.ini\n");
+ return 1;
+ }
+
+ // XXX Originally ScopedLogging was here? Now it's in XRE_main above
+ // XRE_mainInit.
+
+ if (!mAppData->xreDirectory) {
+ nsCOMPtr<nsIFile> lf;
+ rv = XRE_GetBinaryPath(gArgv[0], getter_AddRefs(lf));
+ if (NS_FAILED(rv))
+ return 2;
+
+ nsCOMPtr<nsIFile> greDir;
+ rv = lf->GetParent(getter_AddRefs(greDir));
+ if (NS_FAILED(rv))
+ return 2;
+
+#ifdef XP_MACOSX
+ nsCOMPtr<nsIFile> parent;
+ greDir->GetParent(getter_AddRefs(parent));
+ greDir = parent.forget();
+ greDir->AppendNative(NS_LITERAL_CSTRING("Resources"));
+#endif
+
+ greDir.forget(&mAppData->xreDirectory);
+ }
+
+ if (!mAppData->directory) {
+ NS_IF_ADDREF(mAppData->directory = mAppData->xreDirectory);
+ }
+
+ if (mAppData->size > offsetof(nsXREAppData, minVersion)) {
+ if (!mAppData->minVersion) {
+ Output(true, "Error: Gecko:MinVersion not specified in application.ini\n");
+ return 1;
+ }
+
+ if (!mAppData->maxVersion) {
+ // If no maxVersion is specified, we assume the app is only compatible
+ // with the initial preview release. Do not increment this number ever!
+ SetAllocatedString(mAppData->maxVersion, "1.*");
+ }
+
+ if (mozilla::Version(mAppData->minVersion) > gToolkitVersion ||
+ mozilla::Version(mAppData->maxVersion) < gToolkitVersion) {
+ Output(true, "Error: Platform version '%s' is not compatible with\n"
+ "minVersion >= %s\nmaxVersion <= %s\n",
+ gToolkitVersion,
+ mAppData->minVersion, mAppData->maxVersion);
+ return 1;
+ }
+ }
+
+ rv = mDirProvider.Initialize(mAppData->directory, mAppData->xreDirectory);
+ if (NS_FAILED(rv))
+ return 1;
+
+#ifdef MOZ_CRASHREPORTER
+ if (EnvHasValue("MOZ_CRASHREPORTER")) {
+ mAppData->flags |= NS_XRE_ENABLE_CRASH_REPORTER;
+ }
+
+ nsCOMPtr<nsIFile> xreBinDirectory;
+ xreBinDirectory = mDirProvider.GetGREBinDir();
+
+ if ((mAppData->flags & NS_XRE_ENABLE_CRASH_REPORTER) &&
+ NS_SUCCEEDED(
+ CrashReporter::SetExceptionHandler(xreBinDirectory))) {
+ nsCOMPtr<nsIFile> file;
+ rv = mDirProvider.GetUserAppDataDirectory(getter_AddRefs(file));
+ if (NS_SUCCEEDED(rv)) {
+ CrashReporter::SetUserAppDataDirectory(file);
+ }
+ if (mAppData->crashReporterURL)
+ CrashReporter::SetServerURL(nsDependentCString(mAppData->crashReporterURL));
+
+ // We overwrite this once we finish starting up.
+ CrashReporter::AnnotateCrashReport(NS_LITERAL_CSTRING("StartupCrash"),
+ NS_LITERAL_CSTRING("1"));
+
+ // pass some basic info from the app data
+ if (mAppData->vendor)
+ CrashReporter::AnnotateCrashReport(NS_LITERAL_CSTRING("Vendor"),
+ nsDependentCString(mAppData->vendor));
+ if (mAppData->name)
+ CrashReporter::AnnotateCrashReport(NS_LITERAL_CSTRING("ProductName"),
+ nsDependentCString(mAppData->name));
+ if (mAppData->ID)
+ CrashReporter::AnnotateCrashReport(NS_LITERAL_CSTRING("ProductID"),
+ nsDependentCString(mAppData->ID));
+ if (mAppData->version)
+ CrashReporter::AnnotateCrashReport(NS_LITERAL_CSTRING("Version"),
+ nsDependentCString(mAppData->version));
+ if (mAppData->buildID)
+ CrashReporter::AnnotateCrashReport(NS_LITERAL_CSTRING("BuildID"),
+ nsDependentCString(mAppData->buildID));
+
+ nsDependentCString releaseChannel(NS_STRINGIFY(MOZ_UPDATE_CHANNEL));
+ CrashReporter::AnnotateCrashReport(NS_LITERAL_CSTRING("ReleaseChannel"),
+ releaseChannel);
+#ifdef MOZ_LINKER
+ CrashReporter::AnnotateCrashReport(NS_LITERAL_CSTRING("CrashAddressLikelyWrong"),
+ IsSignalHandlingBroken() ? NS_LITERAL_CSTRING("1")
+ : NS_LITERAL_CSTRING("0"));
+#endif
+
+#ifdef XP_WIN
+ nsAutoString appInitDLLs;
+ if (widget::WinUtils::GetAppInitDLLs(appInitDLLs)) {
+ CrashReporter::AnnotateCrashReport(NS_LITERAL_CSTRING("AppInitDLLs"),
+ NS_ConvertUTF16toUTF8(appInitDLLs));
+ }
+#endif
+
+ CrashReporter::SetRestartArgs(gArgc, gArgv);
+
+ // annotate other data (user id etc)
+ nsCOMPtr<nsIFile> userAppDataDir;
+ if (NS_SUCCEEDED(mDirProvider.GetUserAppDataDirectory(
+ getter_AddRefs(userAppDataDir)))) {
+ CrashReporter::SetupExtraData(userAppDataDir,
+ nsDependentCString(mAppData->buildID));
+
+ // see if we have a crashreporter-override.ini in the application directory
+ nsCOMPtr<nsIFile> overrideini;
+ bool exists;
+ if (NS_SUCCEEDED(mDirProvider.GetAppDir()->Clone(getter_AddRefs(overrideini))) &&
+ NS_SUCCEEDED(overrideini->AppendNative(NS_LITERAL_CSTRING("crashreporter-override.ini"))) &&
+ NS_SUCCEEDED(overrideini->Exists(&exists)) &&
+ exists) {
+#ifdef XP_WIN
+ nsAutoString overridePathW;
+ overrideini->GetPath(overridePathW);
+ NS_ConvertUTF16toUTF8 overridePath(overridePathW);
+#else
+ nsAutoCString overridePath;
+ overrideini->GetNativePath(overridePath);
+#endif
+
+ SaveWordToEnv("MOZ_CRASHREPORTER_STRINGS_OVERRIDE", overridePath);
+ }
+ }
+ }
+#endif
+
+#if defined(MOZ_SANDBOX) && defined(XP_WIN)
+ if (mAppData->sandboxBrokerServices) {
+ SandboxBroker::Initialize(mAppData->sandboxBrokerServices);
+ Telemetry::Accumulate(Telemetry::SANDBOX_BROKER_INITIALIZED, true);
+ } else {
+ Telemetry::Accumulate(Telemetry::SANDBOX_BROKER_INITIALIZED, false);
+#if defined(MOZ_CONTENT_SANDBOX)
+ // If we're sandboxing content and we fail to initialize, then crashing here
+ // seems like the sensible option.
+ if (BrowserTabsRemoteAutostart()) {
+ MOZ_CRASH("Failed to initialize broker services, can't continue.");
+ }
+#endif
+ // Otherwise just warn for the moment, as most things will work.
+ NS_WARNING("Failed to initialize broker services, sandboxed processes will "
+ "fail to start.");
+ }
+#endif
+
+#ifdef XP_MACOSX
+ // Set up ability to respond to system (Apple) events. This must occur before
+ // ProcessUpdates to ensure that links clicked in external applications aren't
+ // lost when updates are pending.
+ SetupMacApplicationDelegate();
+
+ if (EnvHasValue("MOZ_LAUNCHED_CHILD")) {
+ // This is needed, on relaunch, to force the OS to use the "Cocoa Dock
+ // API". Otherwise the call to ReceiveNextEvent() below will make it
+ // use the "Carbon Dock API". For more info see bmo bug 377166.
+ EnsureUseCocoaDockAPI();
+
+ // When the app relaunches, the original process exits. This causes
+ // the dock tile to stop bouncing, lose the "running" triangle, and
+ // if the tile does not permanently reside in the Dock, even disappear.
+ // This can be confusing to the user, who is expecting the app to launch.
+ // Calling ReceiveNextEvent without requesting any event is enough to
+ // cause a dock tile for the child process to appear.
+ const EventTypeSpec kFakeEventList[] = { { INT_MAX, INT_MAX } };
+ EventRef event;
+ ::ReceiveNextEvent(GetEventTypeCount(kFakeEventList), kFakeEventList,
+ kEventDurationNoWait, false, &event);
+ }
+
+ if (CheckArg("foreground")) {
+ // The original process communicates that it was in the foreground by
+ // adding this argument. This new process, which is taking over for
+ // the old one, should make itself the active application.
+ ProcessSerialNumber psn;
+ if (::GetCurrentProcess(&psn) == noErr)
+ ::SetFrontProcess(&psn);
+ }
+#endif
+
+ SaveToEnv("MOZ_LAUNCHED_CHILD=");
+
+ gRestartArgc = gArgc;
+ gRestartArgv = (char**) malloc(sizeof(char*) * (gArgc + 1 + (override ? 2 : 0)));
+ if (!gRestartArgv) {
+ return 1;
+ }
+
+ int i;
+ for (i = 0; i < gArgc; ++i) {
+ gRestartArgv[i] = gArgv[i];
+ }
+
+ // Add the -override argument back (it is removed automatically be CheckArg) if there is one
+ if (override) {
+ gRestartArgv[gRestartArgc++] = const_cast<char*>("-override");
+ gRestartArgv[gRestartArgc++] = const_cast<char*>(override);
+ }
+
+ gRestartArgv[gRestartArgc] = nullptr;
+
+
+ if (EnvHasValue("MOZ_SAFE_MODE_RESTART")) {
+ gSafeMode = true;
+ // unset the env variable
+ SaveToEnv("MOZ_SAFE_MODE_RESTART=");
+ }
+
+ ar = CheckArg("safe-mode", true);
+ if (ar == ARG_BAD) {
+ PR_fprintf(PR_STDERR, "Error: argument --safe-mode is invalid when argument --osint is specified\n");
+ return 1;
+ } else if (ar == ARG_FOUND) {
+ gSafeMode = true;
+ }
+
+#ifdef XP_WIN
+ // If the shift key is pressed and the ctrl and / or alt keys are not pressed
+ // during startup start in safe mode. GetKeyState returns a short and the high
+ // order bit will be 1 if the key is pressed. By masking the returned short
+ // with 0x8000 the result will be 0 if the key is not pressed and non-zero
+ // otherwise.
+ if ((GetKeyState(VK_SHIFT) & 0x8000) &&
+ !(GetKeyState(VK_CONTROL) & 0x8000) &&
+ !(GetKeyState(VK_MENU) & 0x8000) &&
+ !EnvHasValue("MOZ_DISABLE_SAFE_MODE_KEY")) {
+ gSafeMode = true;
+ }
+#endif
+
+#ifdef XP_MACOSX
+ if ((GetCurrentEventKeyModifiers() & optionKey) &&
+ !EnvHasValue("MOZ_DISABLE_SAFE_MODE_KEY"))
+ gSafeMode = true;
+#endif
+
+#ifdef XP_WIN
+ {
+ // Add CPU microcode version to the crash report as "CPUMicrocodeVersion".
+ // It feels like this code may belong in nsSystemInfo instead.
+ int cpuUpdateRevision = -1;
+ HKEY key;
+ static const WCHAR keyName[] =
+ L"HARDWARE\\DESCRIPTION\\System\\CentralProcessor\\0";
+
+ if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, keyName , 0, KEY_QUERY_VALUE, &key) == ERROR_SUCCESS) {
+
+ DWORD updateRevision[2];
+ DWORD len = sizeof(updateRevision);
+ DWORD vtype;
+
+ // Windows 7 uses "Update Signature", 8 uses "Update Revision".
+ // For AMD CPUs, "CurrentPatchLevel" is sometimes used.
+ // Take the first one we find.
+ LPCWSTR choices[] = {L"Update Signature", L"Update Revision", L"CurrentPatchLevel"};
+ for (size_t oneChoice=0; oneChoice<ArrayLength(choices); oneChoice++) {
+ if (RegQueryValueExW(key, choices[oneChoice],
+ 0, &vtype,
+ reinterpret_cast<LPBYTE>(updateRevision),
+ &len) == ERROR_SUCCESS) {
+ if (vtype == REG_BINARY && len == sizeof(updateRevision)) {
+ // The first word is unused
+ cpuUpdateRevision = static_cast<int>(updateRevision[1]);
+ break;
+ } else if (vtype == REG_DWORD && len == sizeof(updateRevision[0])) {
+ cpuUpdateRevision = static_cast<int>(updateRevision[0]);
+ break;
+ }
+ }
+ }
+ }
+
+#ifdef MOZ_CRASHREPORTER
+ if (cpuUpdateRevision > 0) {
+ CrashReporter::AnnotateCrashReport(NS_LITERAL_CSTRING("CPUMicrocodeVersion"),
+ nsPrintfCString("0x%x",
+ cpuUpdateRevision));
+ }
+#endif
+ }
+#endif
+
+#ifdef MOZ_CRASHREPORTER
+ CrashReporter::AnnotateCrashReport(NS_LITERAL_CSTRING("SafeMode"),
+ gSafeMode ? NS_LITERAL_CSTRING("1") :
+ NS_LITERAL_CSTRING("0"));
+#endif
+
+ // Handle --no-remote and --new-instance command line arguments. Setup
+ // the environment to better accommodate other components and various
+ // restart scenarios.
+ ar = CheckArg("no-remote", true);
+ if (ar == ARG_BAD) {
+ PR_fprintf(PR_STDERR, "Error: argument --no-remote is invalid when argument --osint is specified\n");
+ return 1;
+ } else if (ar == ARG_FOUND) {
+ SaveToEnv("MOZ_NO_REMOTE=1");
+ }
+
+ ar = CheckArg("new-instance", true);
+ if (ar == ARG_BAD) {
+ PR_fprintf(PR_STDERR, "Error: argument --new-instance is invalid when argument --osint is specified\n");
+ return 1;
+ } else if (ar == ARG_FOUND) {
+ SaveToEnv("MOZ_NEW_INSTANCE=1");
+ }
+
+ // Handle --help and --version command line arguments.
+ // They should return quickly, so we deal with them here.
+ if (CheckArg("h") || CheckArg("help") || CheckArg("?")) {
+ DumpHelp();
+ *aExitFlag = true;
+ return 0;
+ }
+
+ if (CheckArg("v") || CheckArg("version")) {
+ DumpVersion();
+ *aExitFlag = true;
+ return 0;
+ }
+
+ rv = XRE_InitCommandLine(gArgc, gArgv);
+ NS_ENSURE_SUCCESS(rv, 1);
+
+ // Check for --register, which registers chrome and then exits immediately.
+ ar = CheckArg("register", true);
+ if (ar == ARG_BAD) {
+ PR_fprintf(PR_STDERR, "Error: argument --register is invalid when argument --osint is specified\n");
+ return 1;
+ } else if (ar == ARG_FOUND) {
+ ScopedXPCOMStartup xpcom;
+ rv = xpcom.Initialize();
+ NS_ENSURE_SUCCESS(rv, 1);
+ {
+ nsCOMPtr<nsIChromeRegistry> chromeReg =
+ mozilla::services::GetChromeRegistryService();
+ NS_ENSURE_TRUE(chromeReg, 1);
+
+ chromeReg->CheckForNewChrome();
+ }
+ *aExitFlag = true;
+ return 0;
+ }
+
+ return 0;
+}
+
+#ifdef MOZ_CRASHREPORTER
+#ifdef XP_WIN
+/**
+ * Uses WMI to read some manufacturer information that may be useful for
+ * diagnosing hardware-specific crashes. This function is best-effort; failures
+ * shouldn't burden the caller. COM must be initialized before calling.
+ */
+static void AnnotateSystemManufacturer()
+{
+ RefPtr<IWbemLocator> locator;
+
+ HRESULT hr = CoCreateInstance(CLSID_WbemLocator, nullptr, CLSCTX_INPROC_SERVER,
+ IID_IWbemLocator, getter_AddRefs(locator));
+
+ if (FAILED(hr)) {
+ return;
+ }
+
+ RefPtr<IWbemServices> services;
+
+ hr = locator->ConnectServer(_bstr_t(L"ROOT\\CIMV2"), nullptr, nullptr, nullptr,
+ 0, nullptr, nullptr, getter_AddRefs(services));
+
+ if (FAILED(hr)) {
+ return;
+ }
+
+ hr = CoSetProxyBlanket(services, RPC_C_AUTHN_WINNT, RPC_C_AUTHZ_NONE, nullptr,
+ RPC_C_AUTHN_LEVEL_CALL, RPC_C_IMP_LEVEL_IMPERSONATE,
+ nullptr, EOAC_NONE);
+
+ if (FAILED(hr)) {
+ return;
+ }
+
+ RefPtr<IEnumWbemClassObject> enumerator;
+
+ hr = services->ExecQuery(_bstr_t(L"WQL"), _bstr_t(L"SELECT * FROM Win32_BIOS"),
+ WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY,
+ nullptr, getter_AddRefs(enumerator));
+
+ if (FAILED(hr) || !enumerator) {
+ return;
+ }
+
+ RefPtr<IWbemClassObject> classObject;
+ ULONG results;
+
+ hr = enumerator->Next(WBEM_INFINITE, 1, getter_AddRefs(classObject), &results);
+
+ if (FAILED(hr) || results == 0) {
+ return;
+ }
+
+ VARIANT value;
+ VariantInit(&value);
+
+ hr = classObject->Get(L"Manufacturer", 0, &value, 0, 0);
+
+ if (SUCCEEDED(hr) && V_VT(&value) == VT_BSTR) {
+ CrashReporter::AnnotateCrashReport(NS_LITERAL_CSTRING("BIOS_Manufacturer"),
+ NS_ConvertUTF16toUTF8(V_BSTR(&value)));
+ }
+
+ VariantClear(&value);
+}
+
+static void PR_CALLBACK AnnotateSystemManufacturer_ThreadStart(void*)
+{
+ HRESULT hr = CoInitialize(nullptr);
+
+ if (FAILED(hr)) {
+ return;
+ }
+
+ AnnotateSystemManufacturer();
+
+ CoUninitialize();
+}
+#endif // XP_WIN
+
+#if defined(XP_LINUX) && !defined(ANDROID)
+
+static void
+AnnotateLSBRelease(void*)
+{
+ nsCString dist, desc, release, codename;
+ if (widget::lsb::GetLSBRelease(dist, desc, release, codename)) {
+ CrashReporter::AppendAppNotesToCrashReport(desc);
+ }
+}
+
+#endif // defined(XP_LINUX) && !defined(ANDROID)
+
+#endif
+
+namespace mozilla {
+ ShutdownChecksMode gShutdownChecks = SCM_NOTHING;
+} // namespace mozilla
+
+static void SetShutdownChecks() {
+ // Set default first. On debug builds we crash. On nightly and local
+ // builds we record. Nightlies will then send the info via telemetry,
+ // but it is usefull to have the data in about:telemetry in local builds
+ // too.
+
+#ifdef DEBUG
+ gShutdownChecks = SCM_CRASH;
+#else
+ const char* releaseChannel = NS_STRINGIFY(MOZ_UPDATE_CHANNEL);
+ if (strcmp(releaseChannel, "nightly") == 0 ||
+ strcmp(releaseChannel, "default") == 0) {
+ gShutdownChecks = SCM_RECORD;
+ } else {
+ gShutdownChecks = SCM_NOTHING;
+ }
+#endif
+
+ // We let an environment variable override the default so that addons
+ // authors can use it for debugging shutdown with released firefox versions.
+ const char* mozShutdownChecksEnv = PR_GetEnv("MOZ_SHUTDOWN_CHECKS");
+ if (mozShutdownChecksEnv) {
+ if (strcmp(mozShutdownChecksEnv, "crash") == 0) {
+ gShutdownChecks = SCM_CRASH;
+ } else if (strcmp(mozShutdownChecksEnv, "record") == 0) {
+ gShutdownChecks = SCM_RECORD;
+ } else if (strcmp(mozShutdownChecksEnv, "nothing") == 0) {
+ gShutdownChecks = SCM_NOTHING;
+ }
+ }
+
+}
+
+/*
+ * XRE_mainStartup - Initializes the profile and various other services.
+ * Main() will exit early if either return value != 0 or if aExitFlag is
+ * true.
+ */
+int
+XREMain::XRE_mainStartup(bool* aExitFlag)
+{
+ nsresult rv;
+
+ if (!aExitFlag)
+ return 1;
+ *aExitFlag = false;
+
+ SetShutdownChecks();
+
+ // Enable Telemetry IO Reporting on DEBUG, nightly and local builds
+#ifdef DEBUG
+ mozilla::Telemetry::InitIOReporting(gAppData->xreDirectory);
+#else
+ {
+ const char* releaseChannel = NS_STRINGIFY(MOZ_UPDATE_CHANNEL);
+ if (strcmp(releaseChannel, "nightly") == 0 ||
+ strcmp(releaseChannel, "default") == 0) {
+ mozilla::Telemetry::InitIOReporting(gAppData->xreDirectory);
+ }
+ }
+#endif /* DEBUG */
+
+#if defined(MOZ_WIDGET_GTK) || defined(MOZ_ENABLE_XREMOTE)
+ // Stash DESKTOP_STARTUP_ID in malloc'ed memory because gtk_init will clear it.
+#define HAVE_DESKTOP_STARTUP_ID
+ const char* desktopStartupIDEnv = PR_GetEnv("DESKTOP_STARTUP_ID");
+ if (desktopStartupIDEnv) {
+ mDesktopStartupID.Assign(desktopStartupIDEnv);
+ }
+#endif
+
+#if defined(MOZ_WIDGET_GTK)
+ // setup for private colormap. Ideally we'd like to do this
+ // in nsAppShell::Create, but we need to get in before gtk
+ // has been initialized to make sure everything is running
+ // consistently.
+#if (MOZ_WIDGET_GTK == 2)
+ if (CheckArg("install"))
+ gdk_rgb_set_install(TRUE);
+#endif
+
+ // Set program name to the one defined in application.ini.
+ {
+ nsAutoCString program(gAppData->name);
+ ToLowerCase(program);
+ g_set_prgname(program.get());
+ }
+
+ // Initialize GTK here for splash.
+
+#if (MOZ_WIDGET_GTK == 3) && defined(MOZ_X11)
+ // Disable XInput2 support due to focus bugginess. See bugs 1182700, 1170342.
+ const char* useXI2 = PR_GetEnv("MOZ_USE_XINPUT2");
+ if (!useXI2 || (*useXI2 == '0'))
+ gdk_disable_multidevice();
+#endif
+
+ // Open the display ourselves instead of using gtk_init, so that we can
+ // close it without fear that one day gtk might clean up the display it
+ // opens.
+ if (!gtk_parse_args(&gArgc, &gArgv))
+ return 1;
+#endif /* MOZ_WIDGET_GTK */
+
+#ifdef LIBFUZZER
+ if (PR_GetEnv("LIBFUZZER")) {
+ *aExitFlag = true;
+ return mozilla::libFuzzerRunner->Run();
+ }
+#endif
+
+ if (PR_GetEnv("MOZ_RUN_GTEST")) {
+ int result;
+#ifdef XP_WIN
+ UseParentConsole();
+#endif
+ // RunGTest will only be set if we're in xul-unit
+ if (mozilla::RunGTest) {
+ gIsGtest = true;
+ result = mozilla::RunGTest();
+ gIsGtest = false;
+ } else {
+ result = 1;
+ printf("TEST-UNEXPECTED-FAIL | gtest | Not compiled with enable-tests\n");
+ }
+ *aExitFlag = true;
+ return result;
+ }
+
+#if defined(MOZ_WIDGET_GTK)
+ // display_name is owned by gdk.
+ const char *display_name = gdk_get_display_arg_name();
+ bool saveDisplayArg = false;
+ if (display_name) {
+ saveDisplayArg = true;
+ } else {
+ display_name = detectDisplay();
+ if (!display_name) {
+ return 1;
+ }
+ }
+#endif /* MOZ_WIDGET_GTK */
+#ifdef MOZ_X11
+ // Init X11 in thread-safe mode. Must be called prior to the first call to XOpenDisplay
+ // (called inside gdk_display_open). This is a requirement for off main tread compositing.
+ XInitThreads();
+#endif
+#if defined(MOZ_WIDGET_GTK)
+ mGdkDisplay = gdk_display_open(display_name);
+ if (!mGdkDisplay) {
+ PR_fprintf(PR_STDERR, "Error: cannot open display: %s\n", display_name);
+ return 1;
+ }
+ gdk_display_manager_set_default_display (gdk_display_manager_get(),
+ mGdkDisplay);
+ if (GDK_IS_X11_DISPLAY(mGdkDisplay)) {
+ if (saveDisplayArg) {
+ SaveWordToEnv("DISPLAY", nsDependentCString(display_name));
+ }
+ } else {
+ mDisableRemote = true;
+ }
+#endif
+#ifdef MOZ_ENABLE_XREMOTE
+ // handle --remote now that xpcom is fired up
+ bool newInstance;
+ {
+ char *e = PR_GetEnv("MOZ_NO_REMOTE");
+ mDisableRemote = (mDisableRemote || (e && *e));
+ if (mDisableRemote) {
+ newInstance = true;
+ } else {
+ e = PR_GetEnv("MOZ_NEW_INSTANCE");
+ newInstance = (e && *e);
+ }
+ }
+
+ if (!newInstance) {
+ nsAutoCString program(gAppData->remotingName);
+ ToLowerCase(program);
+
+ const char* username = getenv("LOGNAME");
+ const char* profile = nullptr;
+
+ RemoteResult rr = ParseRemoteCommandLine(program, &profile, &username);
+ if (rr == REMOTE_ARG_BAD) {
+ return 1;
+ }
+
+ if (!username) {
+ struct passwd *pw = getpwuid(geteuid());
+ if (pw && pw->pw_name) {
+ // Beware that another call to getpwent/getpwname/getpwuid will overwrite
+ // pw, but we don't have such another call between here and when username
+ // is used last.
+ username = pw->pw_name;
+ }
+ }
+
+ nsCOMPtr<nsIFile> mutexDir;
+ rv = GetSpecialSystemDirectory(OS_TemporaryDirectory, getter_AddRefs(mutexDir));
+ if (NS_SUCCEEDED(rv)) {
+ nsAutoCString mutexPath = program + NS_LITERAL_CSTRING("_");
+ // In the unlikely even that LOGNAME is not set and getpwuid failed, just
+ // don't put the username in the mutex directory. It will conflict with
+ // other users mutex, but the worst that can happen is that they wait for
+ // MOZ_XREMOTE_START_TIMEOUT_SEC during startup in that case.
+ if (username) {
+ mutexPath.Append(username);
+ }
+ if (profile) {
+ mutexPath.Append(NS_LITERAL_CSTRING("_") + nsDependentCString(profile));
+ }
+ mutexDir->AppendNative(mutexPath);
+
+ rv = mutexDir->Create(nsIFile::DIRECTORY_TYPE, 0700);
+ if (NS_SUCCEEDED(rv) || rv == NS_ERROR_FILE_ALREADY_EXISTS) {
+ mRemoteLockDir = mutexDir;
+ }
+ }
+
+ if (mRemoteLockDir) {
+ const TimeStamp epoch = mozilla::TimeStamp::Now();
+ do {
+ rv = mRemoteLock.Lock(mRemoteLockDir, nullptr);
+ if (NS_SUCCEEDED(rv))
+ break;
+ sched_yield();
+ } while ((TimeStamp::Now() - epoch)
+ < TimeDuration::FromSeconds(MOZ_XREMOTE_START_TIMEOUT_SEC));
+ if (NS_FAILED(rv)) {
+ NS_WARNING("Cannot lock XRemote start mutex");
+ }
+ }
+
+ // Try to remote the entire command line. If this fails, start up normally.
+ const char* desktopStartupIDPtr =
+ mDesktopStartupID.IsEmpty() ? nullptr : mDesktopStartupID.get();
+
+ rr = StartRemoteClient(desktopStartupIDPtr, program, profile, username);
+ if (rr == REMOTE_FOUND) {
+ *aExitFlag = true;
+ return 0;
+ } else if (rr == REMOTE_ARG_BAD) {
+ return 1;
+ }
+ }
+#endif
+#if defined(MOZ_WIDGET_GTK)
+ g_set_application_name(mAppData->name);
+ gtk_window_set_auto_startup_notification(false);
+
+#if (MOZ_WIDGET_GTK == 2)
+ gtk_widget_set_default_colormap(gdk_rgb_get_colormap());
+#endif /* (MOZ_WIDGET_GTK == 2) */
+#endif /* defined(MOZ_WIDGET_GTK) */
+#ifdef MOZ_X11
+ // Do this after initializing GDK, or GDK will install its own handler.
+ XRE_InstallX11ErrorHandler();
+#endif
+
+ // Call the code to install our handler
+#ifdef MOZ_JPROF
+ setupProfilingStuff();
+#endif
+
+ rv = NS_CreateNativeAppSupport(getter_AddRefs(mNativeApp));
+ if (NS_FAILED(rv))
+ return 1;
+
+ bool canRun = false;
+ rv = mNativeApp->Start(&canRun);
+ if (NS_FAILED(rv) || !canRun) {
+ return 1;
+ }
+
+#if defined(HAVE_DESKTOP_STARTUP_ID) && defined(MOZ_WIDGET_GTK)
+ // DESKTOP_STARTUP_ID is cleared now,
+ // we recover it in case we need a restart.
+ if (!mDesktopStartupID.IsEmpty()) {
+ nsAutoCString desktopStartupEnv;
+ desktopStartupEnv.AssignLiteral("DESKTOP_STARTUP_ID=");
+ desktopStartupEnv.Append(mDesktopStartupID);
+ // Leak it with extreme prejudice!
+ PR_SetEnv(ToNewCString(desktopStartupEnv));
+ }
+#endif
+
+#if defined(MOZ_UPDATER) && !defined(MOZ_WIDGET_ANDROID)
+ // Check for and process any available updates
+ nsCOMPtr<nsIFile> updRoot;
+ bool persistent;
+ rv = mDirProvider.GetFile(XRE_UPDATE_ROOT_DIR, &persistent,
+ getter_AddRefs(updRoot));
+ // XRE_UPDATE_ROOT_DIR may fail. Fallback to appDir if failed
+ if (NS_FAILED(rv))
+ updRoot = mDirProvider.GetAppDir();
+
+ // If the MOZ_TEST_PROCESS_UPDATES environment variable already exists, then
+ // we are being called from the callback application.
+ if (EnvHasValue("MOZ_TEST_PROCESS_UPDATES")) {
+ // If the caller has asked us to log our arguments, do so. This is used
+ // to make sure that the maintenance service successfully launches the
+ // callback application.
+ const char *logFile = nullptr;
+ if (ARG_FOUND == CheckArg("dump-args", false, &logFile)) {
+ FILE* logFP = fopen(logFile, "wb");
+ if (logFP) {
+ for (int i = 1; i < gRestartArgc; ++i) {
+ fprintf(logFP, "%s\n", gRestartArgv[i]);
+ }
+ fclose(logFP);
+ }
+ }
+ *aExitFlag = true;
+ return 0;
+ }
+
+ // Support for processing an update and exiting. The MOZ_TEST_PROCESS_UPDATES
+ // environment variable will be part of the updater's environment and the
+ // application that is relaunched by the updater. When the application is
+ // relaunched by the updater it will be removed below and the application
+ // will exit.
+ if (CheckArg("test-process-updates")) {
+ SaveToEnv("MOZ_TEST_PROCESS_UPDATES=1");
+ }
+ nsCOMPtr<nsIFile> exeFile, exeDir;
+ rv = mDirProvider.GetFile(XRE_EXECUTABLE_FILE, &persistent,
+ getter_AddRefs(exeFile));
+ NS_ENSURE_SUCCESS(rv, 1);
+ rv = exeFile->GetParent(getter_AddRefs(exeDir));
+ NS_ENSURE_SUCCESS(rv, 1);
+ ProcessUpdates(mDirProvider.GetGREDir(),
+ exeDir,
+ updRoot,
+ gRestartArgc,
+ gRestartArgv,
+ mAppData->version);
+ if (EnvHasValue("MOZ_TEST_PROCESS_UPDATES")) {
+ SaveToEnv("MOZ_TEST_PROCESS_UPDATES=");
+ *aExitFlag = true;
+ return 0;
+ }
+#endif
+
+ rv = NS_NewToolkitProfileService(getter_AddRefs(mProfileSvc));
+ if (rv == NS_ERROR_FILE_ACCESS_DENIED) {
+ PR_fprintf(PR_STDERR, "Error: Access was denied while trying to open files in " \
+ "your profile directory.\n");
+ }
+ if (NS_FAILED(rv)) {
+ // We failed to choose or create profile - notify user and quit
+ ProfileMissingDialog(mNativeApp);
+ return 1;
+ }
+
+ rv = SelectProfile(getter_AddRefs(mProfileLock), mProfileSvc, mNativeApp, &mStartOffline,
+ &mProfileName);
+ if (rv == NS_ERROR_LAUNCHED_CHILD_PROCESS ||
+ rv == NS_ERROR_ABORT) {
+ *aExitFlag = true;
+ return 0;
+ }
+
+ if (NS_FAILED(rv)) {
+ // We failed to choose or create profile - notify user and quit
+ ProfileMissingDialog(mNativeApp);
+ return 1;
+ }
+ gProfileLock = mProfileLock;
+
+ rv = mProfileLock->GetDirectory(getter_AddRefs(mProfD));
+ NS_ENSURE_SUCCESS(rv, 1);
+
+ rv = mProfileLock->GetLocalDirectory(getter_AddRefs(mProfLD));
+ NS_ENSURE_SUCCESS(rv, 1);
+
+ rv = mDirProvider.SetProfile(mProfD, mProfLD);
+ NS_ENSURE_SUCCESS(rv, 1);
+
+ //////////////////////// NOW WE HAVE A PROFILE ////////////////////////
+
+ mozilla::Telemetry::SetProfileDir(mProfD);
+
+#ifdef MOZ_CRASHREPORTER
+ if (mAppData->flags & NS_XRE_ENABLE_CRASH_REPORTER)
+ MakeOrSetMinidumpPath(mProfD);
+
+ CrashReporter::SetProfileDirectory(mProfD);
+#endif
+
+ nsAutoCString version;
+ BuildVersion(version);
+
+#ifdef TARGET_OS_ABI
+ NS_NAMED_LITERAL_CSTRING(osABI, TARGET_OS_ABI);
+#else
+ // No TARGET_XPCOM_ABI, but at least the OS is known
+ NS_NAMED_LITERAL_CSTRING(osABI, OS_TARGET "_UNKNOWN");
+#endif
+
+ // Check for version compatibility with the last version of the app this
+ // profile was started with. The format of the version stamp is defined
+ // by the BuildVersion function.
+ // Also check to see if something has happened to invalidate our
+ // fastload caches, like an extension upgrade or installation.
+
+ // If we see .purgecaches, that means someone did a make.
+ // Re-register components to catch potential changes.
+ nsCOMPtr<nsIFile> flagFile;
+
+ rv = NS_ERROR_FILE_NOT_FOUND;
+ nsCOMPtr<nsIFile> fFlagFile;
+ if (mAppData->directory) {
+ rv = mAppData->directory->Clone(getter_AddRefs(fFlagFile));
+ }
+ flagFile = do_QueryInterface(fFlagFile);
+ if (flagFile) {
+ flagFile->AppendNative(FILE_INVALIDATE_CACHES);
+ }
+
+ bool cachesOK;
+ bool versionOK = CheckCompatibility(mProfD, version, osABI,
+ mDirProvider.GetGREDir(),
+ mAppData->directory, flagFile,
+ &cachesOK);
+ if (CheckArg("purgecaches")) {
+ cachesOK = false;
+ }
+ if (PR_GetEnv("MOZ_PURGE_CACHES")) {
+ cachesOK = false;
+ }
+
+ // Every time a profile is loaded by a build with a different version,
+ // it updates the compatibility.ini file saying what version last wrote
+ // the fastload caches. On subsequent launches if the version matches,
+ // there is no need for re-registration. If the user loads the same
+ // profile in different builds the component registry must be
+ // re-generated to prevent mysterious component loading failures.
+ //
+ bool startupCacheValid = true;
+ if (gSafeMode) {
+ startupCacheValid = RemoveComponentRegistries(mProfD, mProfLD, false);
+ WriteVersion(mProfD, NS_LITERAL_CSTRING("Safe Mode"), osABI,
+ mDirProvider.GetGREDir(), mAppData->directory, !startupCacheValid);
+ }
+ else if (versionOK) {
+ if (!cachesOK) {
+ // Remove caches, forcing component re-registration.
+ // The new list of additional components directories is derived from
+ // information in "extensions.ini".
+ startupCacheValid = RemoveComponentRegistries(mProfD, mProfLD, false);
+
+ // Rewrite compatibility.ini to remove the flag
+ WriteVersion(mProfD, version, osABI,
+ mDirProvider.GetGREDir(), mAppData->directory, !startupCacheValid);
+ }
+ // Nothing need be done for the normal startup case.
+ }
+ else {
+ // Remove caches, forcing component re-registration
+ // with the default set of components (this disables any potentially
+ // troublesome incompatible XPCOM components).
+ startupCacheValid = RemoveComponentRegistries(mProfD, mProfLD, true);
+
+ // Write out version
+ WriteVersion(mProfD, version, osABI,
+ mDirProvider.GetGREDir(), mAppData->directory, !startupCacheValid);
+ }
+
+ if (!startupCacheValid)
+ StartupCache::IgnoreDiskCache();
+
+ if (flagFile) {
+ flagFile->Remove(true);
+ }
+
+ return 0;
+}
+
+#if defined(MOZ_CRASHREPORTER)
+#if defined(MOZ_CONTENT_SANDBOX) && !defined(MOZ_WIDGET_GONK)
+void AddSandboxAnnotations()
+{
+ // Include the sandbox content level, regardless of platform
+ int level = Preferences::GetInt("security.sandbox.content.level");
+
+ nsAutoCString levelString;
+ levelString.AppendInt(level);
+
+ CrashReporter::AnnotateCrashReport(
+ NS_LITERAL_CSTRING("ContentSandboxLevel"), levelString);
+
+ // Include whether or not this instance is capable of content sandboxing
+ bool sandboxCapable = false;
+
+#if defined(XP_WIN)
+ // All supported Windows versions support some level of content sandboxing
+ sandboxCapable = true;
+#elif defined(XP_MACOSX)
+ // All supported OS X versions are capable
+ sandboxCapable = true;
+#elif defined(XP_LINUX)
+ sandboxCapable = SandboxInfo::Get().CanSandboxContent();
+#endif
+
+ CrashReporter::AnnotateCrashReport(
+ NS_LITERAL_CSTRING("ContentSandboxCapable"),
+ sandboxCapable ? NS_LITERAL_CSTRING("1") : NS_LITERAL_CSTRING("0"));
+}
+#endif /* MOZ_CONTENT_SANDBOX && !MOZ_WIDGET_GONK */
+#endif /* MOZ_CRASHREPORTER */
+
+/*
+ * XRE_mainRun - Command line startup, profile migration, and
+ * the calling of appStartup->Run().
+ */
+nsresult
+XREMain::XRE_mainRun()
+{
+ nsresult rv = NS_OK;
+ NS_ASSERTION(mScopedXPCOM, "Scoped xpcom not initialized.");
+
+#ifdef NS_FUNCTION_TIMER
+ // initialize some common services, so we don't pay the cost for these at odd times later on;
+ // SetWindowCreator -> ChromeRegistry -> IOService -> SocketTransportService -> (nspr wspm init), Prefs
+ {
+ nsCOMPtr<nsISupports> comp;
+
+ comp = do_GetService("@mozilla.org/preferences-service;1");
+
+ comp = do_GetService("@mozilla.org/network/socket-transport-service;1");
+
+ comp = do_GetService("@mozilla.org/network/dns-service;1");
+
+ comp = do_GetService("@mozilla.org/network/io-service;1");
+
+ comp = do_GetService("@mozilla.org/chrome/chrome-registry;1");
+
+ comp = do_GetService("@mozilla.org/focus-event-suppressor-service;1");
+ }
+#endif
+
+ rv = mScopedXPCOM->SetWindowCreator(mNativeApp);
+ NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
+
+#ifdef MOZ_CRASHREPORTER
+ // tell the crash reporter to also send the release channel
+ nsCOMPtr<nsIPrefService> prefs = do_GetService("@mozilla.org/preferences-service;1", &rv);
+ if (NS_SUCCEEDED(rv)) {
+ nsCOMPtr<nsIPrefBranch> defaultPrefBranch;
+ rv = prefs->GetDefaultBranch(nullptr, getter_AddRefs(defaultPrefBranch));
+
+ if (NS_SUCCEEDED(rv)) {
+ nsXPIDLCString sval;
+ rv = defaultPrefBranch->GetCharPref("app.update.channel", getter_Copies(sval));
+ if (NS_SUCCEEDED(rv)) {
+ CrashReporter::AnnotateCrashReport(NS_LITERAL_CSTRING("ReleaseChannel"),
+ sval);
+ }
+ }
+ }
+ // Needs to be set after xpcom initialization.
+ CrashReporter::AnnotateCrashReport(NS_LITERAL_CSTRING("FramePoisonBase"),
+ nsPrintfCString("%.16llx", uint64_t(gMozillaPoisonBase)));
+ CrashReporter::AnnotateCrashReport(NS_LITERAL_CSTRING("FramePoisonSize"),
+ nsPrintfCString("%lu", uint32_t(gMozillaPoisonSize)));
+
+#ifdef XP_WIN
+ PR_CreateThread(PR_USER_THREAD, AnnotateSystemManufacturer_ThreadStart, 0,
+ PR_PRIORITY_LOW, PR_GLOBAL_THREAD, PR_UNJOINABLE_THREAD, 0);
+#endif
+
+#if defined(XP_LINUX) && !defined(ANDROID)
+ PR_CreateThread(PR_USER_THREAD, AnnotateLSBRelease, 0, PR_PRIORITY_LOW,
+ PR_GLOBAL_THREAD, PR_UNJOINABLE_THREAD, 0);
+#endif
+
+#endif
+
+ if (mStartOffline) {
+ nsCOMPtr<nsIIOService2> io (do_GetService("@mozilla.org/network/io-service;1"));
+ NS_ENSURE_TRUE(io, NS_ERROR_FAILURE);
+ io->SetManageOfflineStatus(false);
+ io->SetOffline(true);
+ }
+
+ {
+ nsCOMPtr<nsIObserver> startupNotifier
+ (do_CreateInstance(NS_APPSTARTUPNOTIFIER_CONTRACTID, &rv));
+ NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
+
+ startupNotifier->Observe(nullptr, APPSTARTUP_TOPIC, nullptr);
+ }
+
+ nsCOMPtr<nsIAppStartup> appStartup
+ (do_GetService(NS_APPSTARTUP_CONTRACTID));
+ NS_ENSURE_TRUE(appStartup, NS_ERROR_FAILURE);
+
+ if (gDoMigration) {
+ nsCOMPtr<nsIFile> file;
+ mDirProvider.GetAppDir()->Clone(getter_AddRefs(file));
+ file->AppendNative(NS_LITERAL_CSTRING("override.ini"));
+ nsINIParser parser;
+ nsresult rv = parser.Init(file);
+ // if override.ini doesn't exist, also check for distribution.ini
+ if (NS_FAILED(rv)) {
+ bool persistent;
+ mDirProvider.GetFile(XRE_APP_DISTRIBUTION_DIR, &persistent,
+ getter_AddRefs(file));
+ file->AppendNative(NS_LITERAL_CSTRING("distribution.ini"));
+ rv = parser.Init(file);
+ }
+ if (NS_SUCCEEDED(rv)) {
+ nsAutoCString buf;
+ rv = parser.GetString("XRE", "EnableProfileMigrator", buf);
+ if (NS_SUCCEEDED(rv)) {
+ 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;
+ }
+ }
+ }
+
+ // 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;
+ if (gDoProfileReset) {
+ // Automatically migrate from the current application if we just
+ // reset the profile.
+ aKey = MOZ_APP_NAME;
+ }
+ pm->Migrate(&mDirProvider, aKey, gResetOldProfileName);
+ }
+ }
+
+ if (gDoProfileReset) {
+ nsresult backupCreated = ProfileResetCleanup(profileBeingReset);
+ if (NS_FAILED(backupCreated)) NS_WARNING("Could not cleanup the profile that was reset");
+
+ // 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) {
+ // this is actually "broken" - see bug 1122124
+ rv = SetCurrentProfileAsDefault(mProfileSvc, mProfD);
+ if (NS_FAILED(rv)) NS_WARNING("Could not set current profile as the default");
+ }
+ // Need to write out the fact that the profile has been removed and potentially
+ // that the selected/default profile changed.
+ mProfileSvc->Flush();
+ }
+ }
+
+ mDirProvider.DoStartup();
+
+ OverrideDefaultLocaleIfNeeded();
+
+#ifdef MOZ_CRASHREPORTER
+ nsCString userAgentLocale;
+ // Try a localized string first. This pref is always a localized string in
+ // Fennec, and might be elsewhere, too.
+ if (NS_SUCCEEDED(Preferences::GetLocalizedCString("general.useragent.locale", &userAgentLocale))) {
+ CrashReporter::AnnotateCrashReport(NS_LITERAL_CSTRING("useragent_locale"), userAgentLocale);
+ } else if (NS_SUCCEEDED(Preferences::GetCString("general.useragent.locale", &userAgentLocale))) {
+ CrashReporter::AnnotateCrashReport(NS_LITERAL_CSTRING("useragent_locale"), userAgentLocale);
+ }
+#endif
+
+ appStartup->GetShuttingDown(&mShuttingDown);
+
+ nsCOMPtr<nsICommandLineRunner> cmdLine;
+
+ nsCOMPtr<nsIFile> workingDir;
+ rv = NS_GetSpecialDirectory(NS_OS_CURRENT_WORKING_DIR, getter_AddRefs(workingDir));
+ NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
+
+ if (!mShuttingDown) {
+ cmdLine = do_CreateInstance("@mozilla.org/toolkit/command-line;1");
+ NS_ENSURE_TRUE(cmdLine, NS_ERROR_FAILURE);
+
+ rv = cmdLine->Init(gArgc, gArgv, workingDir,
+ nsICommandLine::STATE_INITIAL_LAUNCH);
+ NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
+
+ /* Special-case services that need early access to the command
+ line. */
+ nsCOMPtr<nsIObserverService> obsService =
+ mozilla::services::GetObserverService();
+ if (obsService) {
+ obsService->NotifyObservers(cmdLine, "command-line-startup", nullptr);
+ }
+ }
+
+#ifdef XP_WIN
+ // Hack to sync up the various environment storages. XUL_APP_FILE is special
+ // in that it comes from a different CRT (firefox.exe's static-linked copy).
+ // Ugly details in http://bugzil.la/1175039#c27
+ char appFile[MAX_PATH];
+ if (GetEnvironmentVariableA("XUL_APP_FILE", appFile, sizeof(appFile))) {
+ char* saved = PR_smprintf("XUL_APP_FILE=%s", appFile);
+ PR_SetEnv(saved);
+ PR_smprintf_free(saved);
+ }
+#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("NO_EM_RESTART=");
+ SaveToEnv("XUL_APP_FILE=");
+ SaveToEnv("XRE_BINARY_PATH=");
+
+ if (!mShuttingDown) {
+ rv = appStartup->CreateHiddenWindow();
+ NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
+
+#ifdef MOZ_STYLO
+ // We initialize Servo here so that the hidden DOM window is available,
+ // since initializing Servo calls style struct constructors, and the
+ // HackilyFindDeviceContext stuff we have right now depends on the hidden
+ // DOM window. When we fix that, this should move back to
+ // nsLayoutStatics.cpp
+ Servo_Initialize();
+#endif
+
+#if defined(HAVE_DESKTOP_STARTUP_ID) && defined(MOZ_WIDGET_GTK)
+ nsGTKToolkit* toolkit = nsGTKToolkit::GetToolkit();
+ if (toolkit && !mDesktopStartupID.IsEmpty()) {
+ toolkit->SetDesktopStartupID(mDesktopStartupID);
+ }
+ // Clear the environment variable so it won't be inherited by
+ // child processes and confuse things.
+ g_unsetenv ("DESKTOP_STARTUP_ID");
+#endif
+
+#ifdef XP_MACOSX
+ // we re-initialize the command-line service and do appleevents munging
+ // after we are sure that we're not restarting
+ cmdLine = do_CreateInstance("@mozilla.org/toolkit/command-line;1");
+ NS_ENSURE_TRUE(cmdLine, NS_ERROR_FAILURE);
+
+ CommandLineServiceMac::SetupMacCommandLine(gArgc, gArgv, false);
+
+ rv = cmdLine->Init(gArgc, gArgv,
+ workingDir, nsICommandLine::STATE_INITIAL_LAUNCH);
+ NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
+#endif
+
+ nsCOMPtr<nsIObserverService> obsService =
+ mozilla::services::GetObserverService();
+ if (obsService)
+ obsService->NotifyObservers(nullptr, "final-ui-startup", nullptr);
+
+ (void)appStartup->DoneStartingUp();
+
+#ifdef MOZ_CRASHREPORTER
+ CrashReporter::AnnotateCrashReport(NS_LITERAL_CSTRING("StartupCrash"),
+ NS_LITERAL_CSTRING("0"));
+#endif
+
+ appStartup->GetShuttingDown(&mShuttingDown);
+ }
+
+ if (!mShuttingDown) {
+ rv = cmdLine->Run();
+ NS_ENSURE_SUCCESS_LOG(rv, NS_ERROR_FAILURE);
+
+ appStartup->GetShuttingDown(&mShuttingDown);
+ }
+
+ if (!mShuttingDown) {
+#ifdef MOZ_ENABLE_XREMOTE
+ // if we have X remote support, start listening for requests on the
+ // proxy window.
+ if (!mDisableRemote)
+ mRemoteService = do_GetService("@mozilla.org/toolkit/remote-service;1");
+ if (mRemoteService)
+ mRemoteService->Startup(mAppData->remotingName, mProfileName.get());
+ if (mRemoteLockDir) {
+ mRemoteLock.Unlock();
+ mRemoteLockDir->Remove(false);
+ }
+#endif /* MOZ_ENABLE_XREMOTE */
+
+ mNativeApp->Enable();
+ }
+
+#ifdef MOZ_INSTRUMENT_EVENT_LOOP
+ if (PR_GetEnv("MOZ_INSTRUMENT_EVENT_LOOP")) {
+ bool logToConsole = true;
+ mozilla::InitEventTracing(logToConsole);
+ }
+#endif /* MOZ_INSTRUMENT_EVENT_LOOP */
+
+#if defined(MOZ_SANDBOX) && defined(XP_LINUX) && !defined(MOZ_WIDGET_GONK)
+ // If we're on Linux, we now have information about the OS capabilities
+ // available to us.
+ SandboxInfo sandboxInfo = SandboxInfo::Get();
+ Telemetry::Accumulate(Telemetry::SANDBOX_HAS_SECCOMP_BPF,
+ sandboxInfo.Test(SandboxInfo::kHasSeccompBPF));
+ Telemetry::Accumulate(Telemetry::SANDBOX_HAS_SECCOMP_TSYNC,
+ sandboxInfo.Test(SandboxInfo::kHasSeccompTSync));
+ Telemetry::Accumulate(Telemetry::SANDBOX_HAS_USER_NAMESPACES_PRIVILEGED,
+ sandboxInfo.Test(SandboxInfo::kHasPrivilegedUserNamespaces));
+ Telemetry::Accumulate(Telemetry::SANDBOX_HAS_USER_NAMESPACES,
+ sandboxInfo.Test(SandboxInfo::kHasUserNamespaces));
+ Telemetry::Accumulate(Telemetry::SANDBOX_CONTENT_ENABLED,
+ sandboxInfo.Test(SandboxInfo::kEnabledForContent));
+ Telemetry::Accumulate(Telemetry::SANDBOX_MEDIA_ENABLED,
+ sandboxInfo.Test(SandboxInfo::kEnabledForMedia));
+#if defined(MOZ_CRASHREPORTER)
+ nsAutoCString flagsString;
+ flagsString.AppendInt(sandboxInfo.AsInteger());
+
+ CrashReporter::AnnotateCrashReport(
+ NS_LITERAL_CSTRING("ContentSandboxCapabilities"), flagsString);
+#endif /* MOZ_CRASHREPORTER */
+#endif /* MOZ_SANDBOX && XP_LINUX && !MOZ_WIDGET_GONK */
+
+#if defined(MOZ_CRASHREPORTER)
+#if defined(MOZ_CONTENT_SANDBOX) && !defined(MOZ_WIDGET_GONK)
+ AddSandboxAnnotations();
+#endif /* MOZ_CONTENT_SANDBOX && !MOZ_WIDGET_GONK */
+#endif /* MOZ_CRASHREPORTER */
+
+ {
+ rv = appStartup->Run();
+ if (NS_FAILED(rv)) {
+ NS_ERROR("failed to run appstartup");
+ gLogConsoleErrors = true;
+ }
+ }
+
+#ifdef MOZ_STYLO
+ // This, along with the call to Servo_Initialize, should eventually move back
+ // to nsLayoutStatics.cpp.
+ Servo_Shutdown();
+#endif
+
+ return rv;
+}
+
+#if MOZ_WIDGET_GTK == 2
+void XRE_GlibInit()
+{
+ static bool ran_once = false;
+
+ // glib < 2.24 doesn't want g_thread_init to be invoked twice, so ensure
+ // we only do it once. No need for thread safety here, since this is invoked
+ // well before any thread is spawned.
+ if (!ran_once) {
+ // glib version < 2.36 doesn't initialize g_slice in a static initializer.
+ // Ensure this happens through g_thread_init (glib version < 2.32) or
+ // g_type_init (2.32 <= gLib version < 2.36)."
+ g_thread_init(nullptr);
+ g_type_init();
+ ran_once = true;
+ }
+}
+#endif
+
+// Separate stub function to let us specifically suppress it in Valgrind
+void
+XRE_CreateStatsObject()
+{
+ // Initialize global variables used by histogram collection
+ // machinery that is used by by Telemetry. Note: is never de-initialised.
+ Telemetry::CreateStatisticsRecorder();
+}
+
+/*
+ * XRE_main - A class based main entry point used by most platforms.
+ * Note that on OSX, aAppData->xreDirectory will point to
+ * .app/Contents/Resources.
+ */
+int
+XREMain::XRE_main(int argc, char* argv[], const nsXREAppData* aAppData)
+{
+ ScopedLogging log;
+
+ // NB: this must happen after the creation of |ScopedLogging log| since
+ // ScopedLogging::ScopedLogging calls NS_LogInit, and
+ // XRE_CreateStatsObject calls Telemetry::CreateStatisticsRecorder,
+ // and NS_LogInit must be called before Telemetry::CreateStatisticsRecorder.
+ // NS_LogInit must be called before Telemetry::CreateStatisticsRecorder
+ // so as to avoid many log messages of the form
+ // WARNING: XPCOM objects created/destroyed from static ctor/dtor: [..]
+ // See bug 1279614.
+ XRE_CreateStatsObject();
+
+#if defined(MOZ_SANDBOX) && defined(XP_LINUX) && !defined(ANDROID)
+ SandboxInfo::ThreadingCheck();
+#endif
+
+ char aLocal;
+ GeckoProfilerInitRAII profilerGuard(&aLocal);
+
+ PROFILER_LABEL("Startup", "XRE_Main",
+ js::ProfileEntry::Category::OTHER);
+
+ nsresult rv = NS_OK;
+
+ gArgc = argc;
+ gArgv = argv;
+
+ NS_ENSURE_TRUE(aAppData, 2);
+
+ mAppData = new ScopedAppData(aAppData);
+ if (!mAppData)
+ return 1;
+ if (!mAppData->remotingName) {
+ SetAllocatedString(mAppData->remotingName, mAppData->name);
+ }
+ // used throughout this file
+ gAppData = mAppData;
+
+ nsCOMPtr<nsIFile> binFile;
+ rv = XRE_GetBinaryPath(argv[0], getter_AddRefs(binFile));
+ NS_ENSURE_SUCCESS(rv, 1);
+
+ rv = binFile->GetPath(gAbsoluteArgv0Path);
+ NS_ENSURE_SUCCESS(rv, 1);
+
+ mozilla::IOInterposerInit ioInterposerGuard;
+
+#if defined(XP_WIN)
+ // Some COM settings are global to the process and must be set before any non-
+ // trivial COM is run in the application. Since these settings may affect
+ // stability, we should instantiate COM ASAP so that we can ensure that these
+ // global settings are configured before anything can interfere.
+ mozilla::mscom::MainThreadRuntime msCOMRuntime;
+#endif
+
+#if MOZ_WIDGET_GTK == 2
+ XRE_GlibInit();
+#endif
+
+ // init
+ bool exit = false;
+ int result = XRE_mainInit(&exit);
+ if (result != 0 || exit)
+ return result;
+
+ // startup
+ result = XRE_mainStartup(&exit);
+ if (result != 0 || exit)
+ return result;
+
+ bool appInitiatedRestart = false;
+
+ // Start the real application
+ mScopedXPCOM = MakeUnique<ScopedXPCOMStartup>();
+ if (!mScopedXPCOM)
+ return 1;
+
+ rv = mScopedXPCOM->Initialize();
+ NS_ENSURE_SUCCESS(rv, 1);
+
+ // run!
+ rv = XRE_mainRun();
+
+#ifdef MOZ_INSTRUMENT_EVENT_LOOP
+ mozilla::ShutdownEventTracing();
+#endif
+
+ gAbsoluteArgv0Path.Truncate();
+
+ // Check for an application initiated restart. This is one that
+ // corresponds to nsIAppStartup.quit(eRestart)
+ if (rv == NS_SUCCESS_RESTART_APP
+ || rv == NS_SUCCESS_RESTART_APP_NOT_SAME_PROFILE) {
+ appInitiatedRestart = true;
+
+ // We have an application restart don't do any shutdown checks here
+ // In particular we don't want to poison IO for checking late-writes.
+ gShutdownChecks = SCM_NOTHING;
+ }
+
+ if (!mShuttingDown) {
+#ifdef MOZ_ENABLE_XREMOTE
+ // shut down the x remote proxy window
+ if (mRemoteService) {
+ mRemoteService->Shutdown();
+ }
+#endif /* MOZ_ENABLE_XREMOTE */
+ }
+
+ mScopedXPCOM = nullptr;
+
+#if defined(XP_WIN)
+ mozilla::widget::StopAudioSession();
+#endif
+
+ // unlock the profile after ScopedXPCOMStartup object (xpcom)
+ // has gone out of scope. see bug #386739 for more details
+ mProfileLock->Unlock();
+ gProfileLock = nullptr;
+
+ // 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
+ MOZ_gdk_display_close(mGdkDisplay);
+#endif
+
+ {
+ rv = LaunchChild(mNativeApp, true);
+ }
+
+#ifdef MOZ_CRASHREPORTER
+ if (mAppData->flags & NS_XRE_ENABLE_CRASH_REPORTER)
+ CrashReporter::UnsetExceptionHandler();
+#endif
+ return rv == NS_ERROR_LAUNCHED_CHILD_PROCESS ? 0 : 1;
+ }
+
+#ifdef MOZ_WIDGET_GTK
+ // gdk_display_close also calls gdk_display_manager_set_default_display
+ // appropriately when necessary.
+ MOZ_gdk_display_close(mGdkDisplay);
+#endif
+
+#ifdef MOZ_CRASHREPORTER
+ if (mAppData->flags & NS_XRE_ENABLE_CRASH_REPORTER)
+ CrashReporter::UnsetExceptionHandler();
+#endif
+
+ XRE_DeinitCommandLine();
+
+ return NS_FAILED(rv) ? 1 : 0;
+}
+
+void
+XRE_StopLateWriteChecks(void) {
+ mozilla::StopLateWriteChecks();
+}
+
+int
+XRE_main(int argc, char* argv[], const nsXREAppData* aAppData, uint32_t aFlags)
+{
+ XREMain main;
+
+ int result = main.XRE_main(argc, argv, aAppData);
+ mozilla::RecordShutdownEndTimeStamp();
+ return result;
+}
+
+nsresult
+XRE_InitCommandLine(int aArgc, char* aArgv[])
+{
+ nsresult rv = NS_OK;
+
+#if defined(OS_WIN)
+ CommandLine::Init(aArgc, aArgv);
+#else
+
+ // these leak on error, but that's OK: we'll just exit()
+ char** canonArgs = new char*[aArgc];
+
+ // get the canonical version of the binary's path
+ nsCOMPtr<nsIFile> binFile;
+ rv = XRE_GetBinaryPath(aArgv[0], getter_AddRefs(binFile));
+ if (NS_FAILED(rv))
+ return NS_ERROR_FAILURE;
+
+ nsAutoCString canonBinPath;
+ rv = binFile->GetNativePath(canonBinPath);
+ if (NS_FAILED(rv))
+ return NS_ERROR_FAILURE;
+
+ canonArgs[0] = strdup(canonBinPath.get());
+
+ for (int i = 1; i < aArgc; ++i) {
+ if (aArgv[i]) {
+ canonArgs[i] = strdup(aArgv[i]);
+ }
+ }
+
+ NS_ASSERTION(!CommandLine::IsInitialized(), "Bad news!");
+ CommandLine::Init(aArgc, canonArgs);
+
+ for (int i = 0; i < aArgc; ++i)
+ free(canonArgs[i]);
+ delete[] canonArgs;
+#endif
+
+ const char *path = nullptr;
+ ArgResult ar = CheckArg("greomni", false, &path);
+ if (ar == ARG_BAD) {
+ PR_fprintf(PR_STDERR, "Error: argument --greomni requires a path argument\n");
+ return NS_ERROR_FAILURE;
+ }
+
+ if (!path)
+ return rv;
+
+ nsCOMPtr<nsIFile> greOmni;
+ rv = XRE_GetFileFromPath(path, getter_AddRefs(greOmni));
+ if (NS_FAILED(rv)) {
+ PR_fprintf(PR_STDERR, "Error: argument --greomni requires a valid path\n");
+ return rv;
+ }
+
+ ar = CheckArg("appomni", false, &path);
+ if (ar == ARG_BAD) {
+ PR_fprintf(PR_STDERR, "Error: argument --appomni requires a path argument\n");
+ return NS_ERROR_FAILURE;
+ }
+
+ nsCOMPtr<nsIFile> appOmni;
+ if (path) {
+ rv = XRE_GetFileFromPath(path, getter_AddRefs(appOmni));
+ if (NS_FAILED(rv)) {
+ PR_fprintf(PR_STDERR, "Error: argument --appomni requires a valid path\n");
+ return rv;
+ }
+ }
+
+ mozilla::Omnijar::Init(greOmni, appOmni);
+ return rv;
+}
+
+nsresult
+XRE_DeinitCommandLine()
+{
+ nsresult rv = NS_OK;
+
+ CommandLine::Terminate();
+
+ return rv;
+}
+
+GeckoProcessType
+XRE_GetProcessType()
+{
+ return mozilla::startup::sChildProcessType;
+}
+
+bool
+XRE_IsGPUProcess()
+{
+ return XRE_GetProcessType() == GeckoProcessType_GPU;
+}
+
+bool
+XRE_IsParentProcess()
+{
+ return XRE_GetProcessType() == GeckoProcessType_Default;
+}
+
+bool
+XRE_IsContentProcess()
+{
+ return XRE_GetProcessType() == GeckoProcessType_Content;
+}
+
+// If you add anything to this enum, please update about:support to reflect it
+enum {
+ kE10sEnabledByUser = 0,
+ kE10sEnabledByDefault = 1,
+ kE10sDisabledByUser = 2,
+ // kE10sDisabledInSafeMode = 3, was removed in bug 1172491.
+ kE10sDisabledForAccessibility = 4,
+ // kE10sDisabledForMacGfx = 5, was removed in bug 1068674.
+ // kE10sDisabledForBidi = 6, removed in bug 1309599
+ kE10sDisabledForAddons = 7,
+ kE10sForceDisabled = 8,
+ // kE10sDisabledForXPAcceleration = 9, removed in bug 1296353
+ kE10sDisabledForOperatingSystem = 10,
+};
+
+const char* kAccessibilityLastRunDatePref = "accessibility.lastLoadDate";
+const char* kAccessibilityLoadedLastSessionPref = "accessibility.loadedInLastSession";
+
+#if defined(XP_WIN)
+static inline uint32_t
+PRTimeToSeconds(PRTime t_usec)
+{
+ PRTime usec_per_sec = PR_USEC_PER_SEC;
+ return uint32_t(t_usec /= usec_per_sec);
+}
+#endif
+
+const char* kForceEnableE10sPref = "browser.tabs.remote.force-enable";
+const char* kForceDisableE10sPref = "browser.tabs.remote.force-disable";
+
+uint32_t
+MultiprocessBlockPolicy() {
+ if (gMultiprocessBlockPolicyInitialized) {
+ return gMultiprocessBlockPolicy;
+ }
+ gMultiprocessBlockPolicyInitialized = true;
+
+ /**
+ * Avoids enabling e10s if there are add-ons installed.
+ */
+ bool addonsCanDisable = Preferences::GetBool("extensions.e10sBlocksEnabling", false);
+ bool disabledByAddons = Preferences::GetBool("extensions.e10sBlockedByAddons", false);
+
+#ifdef MOZ_CRASHREPORTER
+ CrashReporter::AnnotateCrashReport(NS_LITERAL_CSTRING("AddonsShouldHaveBlockedE10s"),
+ disabledByAddons ? NS_LITERAL_CSTRING("1")
+ : NS_LITERAL_CSTRING("0"));
+#endif
+
+ if (addonsCanDisable && disabledByAddons) {
+ gMultiprocessBlockPolicy = kE10sDisabledForAddons;
+ return gMultiprocessBlockPolicy;
+ }
+
+#if defined(XP_WIN)
+ bool disabledForA11y = false;
+ /**
+ * Avoids enabling e10s if accessibility has recently loaded. Performs the
+ * following checks:
+ * 1) Checks a pref indicating if a11y loaded in the last session. This pref
+ * is set in nsBrowserGlue.js. If a11y was loaded in the last session we
+ * do not enable e10s in this session.
+ * 2) Accessibility stores a last run date (PR_IntervalNow) when it is
+ * initialized (see nsBaseWidget.cpp). We check if this pref exists and
+ * compare it to now. If a11y hasn't run in an extended period of time or
+ * if the date pref does not exist we load e10s.
+ */
+ disabledForA11y = Preferences::GetBool(kAccessibilityLoadedLastSessionPref, false);
+ if (!disabledForA11y &&
+ Preferences::HasUserValue(kAccessibilityLastRunDatePref)) {
+ #define ONE_WEEK_IN_SECONDS (60*60*24*7)
+ uint32_t a11yRunDate = Preferences::GetInt(kAccessibilityLastRunDatePref, 0);
+ MOZ_ASSERT(0 != a11yRunDate);
+ // If a11y hasn't run for a period of time, clear the pref and load e10s
+ uint32_t now = PRTimeToSeconds(PR_Now());
+ uint32_t difference = now - a11yRunDate;
+ if (difference > ONE_WEEK_IN_SECONDS || !a11yRunDate) {
+ Preferences::ClearUser(kAccessibilityLastRunDatePref);
+ } else {
+ disabledForA11y = true;
+ }
+ }
+
+ if (disabledForA11y) {
+ gMultiprocessBlockPolicy = kE10sDisabledForAccessibility;
+ return gMultiprocessBlockPolicy;
+ }
+#endif
+
+ /**
+ * Avoids enabling e10s for Windows XP users on the release channel.
+ */
+#if defined(XP_WIN)
+ if (!IsVistaOrLater()) {
+ nsAdoptingCString channelName = Preferences::GetDefaultCString("app.update.channel");
+ if (channelName.EqualsLiteral("release") || channelName.EqualsLiteral("esr")) {
+ gMultiprocessBlockPolicy = kE10sDisabledForOperatingSystem;
+ return gMultiprocessBlockPolicy;
+ }
+ }
+#endif // XP_WIN
+
+ /*
+ * None of the blocking policies matched, so e10s is allowed to run.
+ * Cache the information and return 0, indicating success.
+ */
+ gMultiprocessBlockPolicy = 0;
+ return 0;
+}
+
+bool
+mozilla::BrowserTabsRemoteAutostart()
+{
+ if (gBrowserTabsRemoteAutostartInitialized) {
+ return gBrowserTabsRemoteAutostart;
+ }
+ gBrowserTabsRemoteAutostartInitialized = true;
+
+ // If we're in the content process, we are running E10S.
+ if (XRE_IsContentProcess()) {
+ gBrowserTabsRemoteAutostart = true;
+ return gBrowserTabsRemoteAutostart;
+ }
+
+ bool optInPref = Preferences::GetBool("browser.tabs.remote.autostart", false);
+ bool trialPref = Preferences::GetBool("browser.tabs.remote.autostart.2", false);
+ bool prefEnabled = optInPref || trialPref;
+ int status;
+ if (optInPref) {
+ status = kE10sEnabledByUser;
+ } else if (trialPref) {
+ status = kE10sEnabledByDefault;
+ } else {
+ status = kE10sDisabledByUser;
+ }
+
+ if (prefEnabled) {
+ uint32_t blockPolicy = MultiprocessBlockPolicy();
+ if (blockPolicy != 0) {
+ status = blockPolicy;
+ } else {
+ gBrowserTabsRemoteAutostart = true;
+ }
+ }
+
+ // Uber override pref for manual testing purposes
+ if (Preferences::GetBool(kForceEnableE10sPref, false)) {
+ gBrowserTabsRemoteAutostart = true;
+ prefEnabled = true;
+ status = kE10sEnabledByUser;
+ }
+
+ // Uber override pref for emergency blocking
+ if (gBrowserTabsRemoteAutostart &&
+ (Preferences::GetBool(kForceDisableE10sPref, false) ||
+ EnvHasValue("MOZ_FORCE_DISABLE_E10S"))) {
+ gBrowserTabsRemoteAutostart = false;
+ status = kE10sForceDisabled;
+ }
+
+ gBrowserTabsRemoteStatus = status;
+
+ mozilla::Telemetry::Accumulate(mozilla::Telemetry::E10S_STATUS, status);
+ if (prefEnabled) {
+ mozilla::Telemetry::Accumulate(mozilla::Telemetry::E10S_BLOCKED_FROM_RUNNING,
+ !gBrowserTabsRemoteAutostart);
+ }
+ return gBrowserTabsRemoteAutostart;
+}
+
+void
+SetupErrorHandling(const char* progname)
+{
+#ifdef XP_WIN
+ /* On Windows XPSP3 and Windows Vista if DEP is configured off-by-default
+ we still want DEP protection: enable it explicitly and programmatically.
+
+ This function is not available on WinXPSP2 so we dynamically load it.
+ */
+
+ HMODULE kernel32 = GetModuleHandleW(L"kernel32.dll");
+ SetProcessDEPPolicyFunc _SetProcessDEPPolicy =
+ (SetProcessDEPPolicyFunc) GetProcAddress(kernel32, "SetProcessDEPPolicy");
+ if (_SetProcessDEPPolicy)
+ _SetProcessDEPPolicy(PROCESS_DEP_ENABLE);
+#endif
+
+#ifdef XP_WIN32
+ // Suppress the "DLL Foo could not be found" dialog, such that if dependent
+ // libraries (such as GDI+) are not preset, we gracefully fail to load those
+ // XPCOM components, instead of being ungraceful.
+ UINT realMode = SetErrorMode(0);
+ realMode |= SEM_FAILCRITICALERRORS;
+ // If XRE_NO_WINDOWS_CRASH_DIALOG is set, suppress displaying the "This
+ // application has crashed" dialog box. This is mainly useful for
+ // automated testing environments, e.g. tinderbox, where there's no need
+ // for a dozen of the dialog boxes to litter the console
+ if (getenv("XRE_NO_WINDOWS_CRASH_DIALOG"))
+ realMode |= SEM_NOGPFAULTERRORBOX | SEM_NOOPENFILEERRORBOX;
+
+ SetErrorMode(realMode);
+
+#endif
+
+#if defined (DEBUG) && defined(XP_WIN)
+ // Send MSCRT Warnings, Errors and Assertions to stderr.
+ // See http://msdn.microsoft.com/en-us/library/1y71x448(v=VS.80).aspx
+ // and http://msdn.microsoft.com/en-us/library/a68f826y(v=VS.80).aspx.
+
+ _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE);
+ _CrtSetReportFile(_CRT_WARN, _CRTDBG_FILE_STDERR);
+ _CrtSetReportMode(_CRT_ERROR, _CRTDBG_MODE_FILE);
+ _CrtSetReportFile(_CRT_ERROR, _CRTDBG_FILE_STDERR);
+ _CrtSetReportMode(_CRT_ASSERT, _CRTDBG_MODE_FILE);
+ _CrtSetReportFile(_CRT_ASSERT, _CRTDBG_FILE_STDERR);
+
+ _CrtSetReportHook(MSCRTReportHook);
+#endif
+
+ InstallSignalHandlers(progname);
+
+ // Unbuffer stdout, needed for tinderbox tests.
+ setbuf(stdout, 0);
+}
+
+void
+OverrideDefaultLocaleIfNeeded() {
+ // Read pref to decide whether to override default locale with US English.
+ if (mozilla::Preferences::GetBool("javascript.use_us_english_locale", false)) {
+ // Set the application-wide C-locale. Needed to resist fingerprinting
+ // of Date.toLocaleFormat(). We use the locale to "C.UTF-8" if possible,
+ // to avoid interfering with non-ASCII keyboard input on some Linux desktops.
+ // Otherwise fall back to the "C" locale, which is available on all platforms.
+ setlocale(LC_ALL, "C.UTF-8") || setlocale(LC_ALL, "C");
+ }
+}
+
+void
+XRE_EnableSameExecutableForContentProc() {
+ if (!PR_GetEnv("MOZ_SEPARATE_CHILD_PROCESS")) {
+ mozilla::ipc::GeckoChildProcessHost::EnableSameExecutableForContentProc();
+ }
+}
diff --git a/toolkit/xre/nsAppRunner.h b/toolkit/xre/nsAppRunner.h
new file mode 100644
index 000000000..b8d955319
--- /dev/null
+++ b/toolkit/xre/nsAppRunner.h
@@ -0,0 +1,138 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nsAppRunner_h__
+#define nsAppRunner_h__
+
+#ifdef XP_WIN
+#include <windows.h>
+#else
+#include <limits.h>
+#endif
+
+#ifndef MAXPATHLEN
+#ifdef PATH_MAX
+#define MAXPATHLEN PATH_MAX
+#elif defined(_MAX_PATH)
+#define MAXPATHLEN _MAX_PATH
+#elif defined(CCHMAXPATH)
+#define MAXPATHLEN CCHMAXPATH
+#else
+#define MAXPATHLEN 1024
+#endif
+#endif
+
+#include "nsXULAppAPI.h"
+
+// This directory service key is a lot like NS_APP_LOCALSTORE_50_FILE,
+// but it is always the "main" localstore file, even when we're in safe mode
+// and we load localstore from somewhere else.
+#define NS_LOCALSTORE_UNSAFE_FILE "LStoreS"
+
+class nsINativeAppSupport;
+class nsXREDirProvider;
+class nsIToolkitProfileService;
+class nsIFile;
+class nsIProfileLock;
+class nsIProfileUnlocker;
+class nsIFactory;
+class nsString;
+
+extern nsXREDirProvider* gDirServiceProvider;
+
+// NOTE: gAppData will be null in embedded contexts. The "size" parameter
+// will be the size of the original structure passed to XRE_main, but the
+// structure will have all of the members available.
+extern const nsXREAppData* gAppData;
+extern bool gSafeMode;
+
+extern int gArgc;
+extern char **gArgv;
+extern int gRestartArgc;
+extern char **gRestartArgv;
+extern bool gLogConsoleErrors;
+extern nsString gAbsoluteArgv0Path;
+
+extern bool gIsGtest;
+
+/**
+ * Create the nativeappsupport implementation.
+ *
+ * @note XPCOMInit has not happened yet.
+ */
+nsresult NS_CreateNativeAppSupport(nsINativeAppSupport* *aResult);
+
+nsresult
+NS_NewToolkitProfileService(nsIToolkitProfileService* *aResult);
+
+nsresult
+NS_NewToolkitProfileFactory(nsIFactory* *aResult);
+
+/**
+ * Try to acquire exclusive access to the specified profile directory.
+ *
+ * @param aPath
+ * The profile directory to lock.
+ * @param aTempPath
+ * The corresponding profile temporary directory.
+ * @param aUnlocker
+ * A callback interface used to attempt to unlock a profile that
+ * appears to be locked.
+ * @param aResult
+ * The resulting profile lock object (or null if the profile could
+ * not be locked).
+ *
+ * @return NS_ERROR_FILE_ACCESS_DENIED to indicate that the profile
+ * directory cannot be unlocked.
+ */
+nsresult
+NS_LockProfilePath(nsIFile* aPath, nsIFile* aTempPath,
+ nsIProfileUnlocker* *aUnlocker, nsIProfileLock* *aResult);
+
+void
+WriteConsoleLog();
+
+void
+OverrideDefaultLocaleIfNeeded();
+
+/**
+ * Allow exit() calls to complete. This should be done from a proper Gecko
+ * shutdown path. Otherwise we aim to catch improper shutdowns.
+ */
+void
+MozExpectedExit();
+
+#ifdef XP_WIN
+void
+UseParentConsole();
+
+BOOL
+WinLaunchChild(const wchar_t *exePath, int argc,
+ char **argv, HANDLE userToken = nullptr,
+ HANDLE *hProcess = nullptr);
+#endif
+
+#define NS_NATIVEAPPSUPPORT_CONTRACTID "@mozilla.org/toolkit/native-app-support;1"
+
+namespace mozilla {
+namespace startup {
+extern GeckoProcessType sChildProcessType;
+} // namespace startup
+} // namespace mozilla
+
+/**
+ * Set up platform specific error handling such as suppressing DLL load dialog
+ * and the JIT debugger on Windows, and install unix signal handlers.
+ */
+void SetupErrorHandling(const char* progname);
+
+/**
+ * A numeric value indicating whether multiprocess might be blocked.
+ * Possible values can be found at nsAppRunner.cpp. A value of 0
+ * represents not blocking.
+ */
+uint32_t MultiprocessBlockPolicy();
+
+#endif // nsAppRunner_h__
diff --git a/toolkit/xre/nsCommandLineServiceMac.cpp b/toolkit/xre/nsCommandLineServiceMac.cpp
new file mode 100644
index 000000000..f5964bfe3
--- /dev/null
+++ b/toolkit/xre/nsCommandLineServiceMac.cpp
@@ -0,0 +1,102 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsCommandLineServiceMac.h"
+#include "MacApplicationDelegate.h"
+
+#include <CoreFoundation/CoreFoundation.h>
+#include <Carbon/Carbon.h>
+
+namespace CommandLineServiceMac {
+
+static const int kArgsGrowSize = 20;
+
+static char** sArgs = nullptr;
+static int sArgsAllocated = 0;
+static int sArgsUsed = 0;
+
+static bool sBuildingCommandLine = false;
+
+void AddToCommandLine(const char* inArgText)
+{
+ if (sArgsUsed >= sArgsAllocated - 1) {
+ // realloc does not free the given pointer if allocation fails
+ char **temp = static_cast<char**>(realloc(sArgs, (sArgsAllocated + kArgsGrowSize) * sizeof(char*)));
+ if (!temp)
+ return;
+ sArgs = temp;
+ sArgsAllocated += kArgsGrowSize;
+ }
+
+ char *temp2 = strdup(inArgText);
+ if (!temp2)
+ return;
+
+ sArgs[sArgsUsed++] = temp2;
+ sArgs[sArgsUsed] = nullptr;
+
+ return;
+}
+
+void SetupMacCommandLine(int& argc, char**& argv, bool forRestart)
+{
+ sArgs = static_cast<char **>(malloc(kArgsGrowSize * sizeof(char*)));
+ if (!sArgs)
+ return;
+ sArgsAllocated = kArgsGrowSize;
+ sArgs[0] = nullptr;
+ sArgsUsed = 0;
+
+ sBuildingCommandLine = true;
+
+ // Copy args, stripping anything we don't want.
+ for (int arg = 0; arg < argc; arg++) {
+ char* flag = argv[arg];
+ // Don't pass on the psn (Process Serial Number) flag from the OS, or
+ // the "-foreground" flag since it will be set below if necessary.
+ if (strncmp(flag, "-psn_", 5) != 0 &&
+ strncmp(flag, "-foreground", 11) != 0)
+ AddToCommandLine(flag);
+ }
+
+ // Force processing of any pending Apple GetURL Events while we're building
+ // the command line. The handlers will append to the command line rather than
+ // act directly so there is no chance we'll process them during a XUL window
+ // load and accidentally open unnecessary windows and home pages.
+ ProcessPendingGetURLAppleEvents();
+
+ // If the process will be relaunched, the child should be in the foreground
+ // if the parent is in the foreground. This will be communicated in a
+ // command-line argument to the child.
+ if (forRestart) {
+ Boolean isForeground = false;
+ ProcessSerialNumber psnSelf, psnFront;
+ if (::GetCurrentProcess(&psnSelf) == noErr &&
+ ::GetFrontProcess(&psnFront) == noErr &&
+ ::SameProcess(&psnSelf, &psnFront, &isForeground) == noErr &&
+ isForeground) {
+ AddToCommandLine("-foreground");
+ }
+ }
+
+ sBuildingCommandLine = false;
+
+ argc = sArgsUsed;
+ argv = sArgs;
+}
+
+bool AddURLToCurrentCommandLine(const char* aURL)
+{
+ if (!sBuildingCommandLine) {
+ return false;
+ }
+
+ AddToCommandLine("-url");
+ AddToCommandLine(aURL);
+
+ return true;
+}
+
+} // namespace CommandLineServiceMac
diff --git a/toolkit/xre/nsCommandLineServiceMac.h b/toolkit/xre/nsCommandLineServiceMac.h
new file mode 100644
index 000000000..f21596856
--- /dev/null
+++ b/toolkit/xre/nsCommandLineServiceMac.h
@@ -0,0 +1,20 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nsCommandLineServiceMac_h_
+#define nsCommandLineServiceMac_h_
+
+#include "nscore.h"
+
+namespace CommandLineServiceMac {
+ void SetupMacCommandLine(int& argc, char**& argv, bool forRestart);
+
+ // Add a URL to the command line currently being set up via
+ // SetupMacCommandLine. Returns false if no command line is
+ // being set up or the addition fails for any other reason.
+ bool AddURLToCurrentCommandLine(const char* aURL);
+} // namespace CommandLineServiceMac
+
+#endif // nsCommandLineServiceMac_h_
diff --git a/toolkit/xre/nsConsoleWriter.cpp b/toolkit/xre/nsConsoleWriter.cpp
new file mode 100644
index 000000000..952e88003
--- /dev/null
+++ b/toolkit/xre/nsConsoleWriter.cpp
@@ -0,0 +1,95 @@
+/* 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 "nsAppRunner.h"
+
+#include "prio.h"
+#include "prprf.h"
+#include "prenv.h"
+
+#include "nsCRT.h"
+#include "nsNativeCharsetUtils.h"
+#include "nsString.h"
+#include "nsXREDirProvider.h"
+#include "nsXULAppAPI.h"
+
+#include "nsIConsoleService.h"
+#include "nsIConsoleMessage.h"
+
+void
+WriteConsoleLog()
+{
+ nsresult rv;
+
+ nsCOMPtr<nsIFile> lfile;
+
+ char* logFileEnv = PR_GetEnv("XRE_CONSOLE_LOG");
+ if (logFileEnv && *logFileEnv) {
+ rv = XRE_GetFileFromPath(logFileEnv, getter_AddRefs(lfile));
+ if (NS_FAILED(rv))
+ return;
+ }
+ else {
+ if (!gLogConsoleErrors)
+ return;
+
+ rv = gDirServiceProvider->GetUserAppDataDirectory(getter_AddRefs(lfile));
+ if (NS_FAILED(rv))
+ return;
+
+ lfile->AppendNative(NS_LITERAL_CSTRING("console.log"));
+ }
+
+ PRFileDesc *file;
+ rv = lfile->OpenNSPRFileDesc(PR_WRONLY | PR_APPEND | PR_CREATE_FILE,
+ 0660, &file);
+ if (NS_FAILED(rv))
+ return;
+
+ nsCOMPtr<nsIConsoleService> csrv
+ (do_GetService(NS_CONSOLESERVICE_CONTRACTID));
+ if (!csrv) {
+ PR_Close(file);
+ return;
+ }
+
+ nsIConsoleMessage** messages;
+ uint32_t mcount;
+
+ rv = csrv->GetMessageArray(&mcount, &messages);
+ if (NS_FAILED(rv)) {
+ PR_Close(file);
+ return;
+ }
+
+ if (mcount) {
+ PRExplodedTime etime;
+ PR_ExplodeTime(PR_Now(), PR_LocalTimeParameters, &etime);
+ char datetime[512];
+ PR_FormatTimeUSEnglish(datetime, sizeof(datetime),
+ "%Y-%m-%d %H:%M:%S", &etime);
+
+ PR_fprintf(file, NS_LINEBREAK
+ "*** Console log: %s ***" NS_LINEBREAK,
+ datetime);
+ }
+
+ // From this point on, we have to release all the messages, and free
+ // the memory allocated for the messages array. XPCOM arrays suck.
+
+ nsXPIDLString msg;
+ nsAutoCString nativemsg;
+
+ for (uint32_t i = 0; i < mcount; ++i) {
+ rv = messages[i]->GetMessageMoz(getter_Copies(msg));
+ if (NS_SUCCEEDED(rv)) {
+ NS_CopyUnicodeToNative(msg, nativemsg);
+ PR_fprintf(file, "%s" NS_LINEBREAK, nativemsg.get());
+ }
+ NS_IF_RELEASE(messages[i]);
+ }
+
+ PR_Close(file);
+ free(messages);
+}
diff --git a/toolkit/xre/nsEmbedFunctions.cpp b/toolkit/xre/nsEmbedFunctions.cpp
new file mode 100644
index 000000000..0e85532b9
--- /dev/null
+++ b/toolkit/xre/nsEmbedFunctions.cpp
@@ -0,0 +1,966 @@
+/* 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/DebugOnly.h"
+
+#include "base/basictypes.h"
+
+#include "nsXULAppAPI.h"
+
+#include <stdlib.h>
+#if defined(MOZ_WIDGET_GTK)
+#include <glib.h>
+#endif
+
+#include "prenv.h"
+
+#include "nsIAppShell.h"
+#include "nsIAppStartupNotifier.h"
+#include "nsIDirectoryService.h"
+#include "nsIFile.h"
+#include "nsIToolkitChromeRegistry.h"
+#include "nsIToolkitProfile.h"
+
+#ifdef XP_WIN
+#include <process.h>
+#include "mozilla/ipc/WindowsMessageLoop.h"
+#endif
+
+#include "nsAppDirectoryServiceDefs.h"
+#include "nsAppRunner.h"
+#include "nsAutoRef.h"
+#include "nsDirectoryServiceDefs.h"
+#include "nsExceptionHandler.h"
+#include "nsString.h"
+#include "nsThreadUtils.h"
+#include "nsJSUtils.h"
+#include "nsWidgetsCID.h"
+#include "nsXREDirProvider.h"
+
+#include "mozilla/Omnijar.h"
+#if defined(XP_MACOSX)
+#include "nsVersionComparator.h"
+#include "chrome/common/mach_ipc_mac.h"
+#endif
+#include "nsX11ErrorHandler.h"
+#include "nsGDKErrorHandler.h"
+#include "base/at_exit.h"
+#include "base/command_line.h"
+#include "base/message_loop.h"
+#include "base/process_util.h"
+#include "chrome/common/child_process.h"
+
+#include "mozilla/ipc/BrowserProcessSubThread.h"
+#include "mozilla/ipc/GeckoChildProcessHost.h"
+#include "mozilla/ipc/IOThreadChild.h"
+#include "mozilla/ipc/ProcessChild.h"
+#include "ScopedXREEmbed.h"
+
+#include "mozilla/plugins/PluginProcessChild.h"
+#include "mozilla/dom/ContentProcess.h"
+#include "mozilla/dom/ContentParent.h"
+#include "mozilla/dom/ContentChild.h"
+
+#include "mozilla/ipc/TestShellParent.h"
+#include "mozilla/ipc/XPCShellEnvironment.h"
+#include "mozilla/WindowsDllBlocklist.h"
+
+#include "GMPProcessChild.h"
+#include "GMPLoader.h"
+#include "mozilla/gfx/GPUProcessImpl.h"
+
+#include "GeckoProfiler.h"
+
+#include "mozilla/Telemetry.h"
+
+#if defined(MOZ_SANDBOX) && defined(XP_WIN)
+#include "mozilla/sandboxTarget.h"
+#include "mozilla/sandboxing/loggingCallbacks.h"
+#endif
+
+#if defined(MOZ_CONTENT_SANDBOX) && !defined(MOZ_WIDGET_GONK)
+#include "mozilla/Preferences.h"
+#endif
+
+#ifdef MOZ_IPDL_TESTS
+#include "mozilla/_ipdltest/IPDLUnitTests.h"
+#include "mozilla/_ipdltest/IPDLUnitTestProcessChild.h"
+
+using mozilla::_ipdltest::IPDLUnitTestProcessChild;
+#endif // ifdef MOZ_IPDL_TESTS
+
+#ifdef MOZ_JPROF
+#include "jprof.h"
+#endif
+
+using namespace mozilla;
+
+using mozilla::ipc::BrowserProcessSubThread;
+using mozilla::ipc::GeckoChildProcessHost;
+using mozilla::ipc::IOThreadChild;
+using mozilla::ipc::ProcessChild;
+using mozilla::ipc::ScopedXREEmbed;
+
+using mozilla::plugins::PluginProcessChild;
+using mozilla::dom::ContentProcess;
+using mozilla::dom::ContentParent;
+using mozilla::dom::ContentChild;
+
+using mozilla::gmp::GMPLoader;
+using mozilla::gmp::CreateGMPLoader;
+using mozilla::gmp::GMPProcessChild;
+
+using mozilla::ipc::TestShellParent;
+using mozilla::ipc::TestShellCommandParent;
+using mozilla::ipc::XPCShellEnvironment;
+
+using mozilla::startup::sChildProcessType;
+
+static NS_DEFINE_CID(kAppShellCID, NS_APPSHELL_CID);
+
+#ifdef XP_WIN
+static const wchar_t kShellLibraryName[] = L"shell32.dll";
+#endif
+
+nsresult
+XRE_LockProfileDirectory(nsIFile* aDirectory,
+ nsISupports* *aLockObject)
+{
+ nsCOMPtr<nsIProfileLock> lock;
+
+ nsresult rv = NS_LockProfilePath(aDirectory, nullptr, nullptr,
+ getter_AddRefs(lock));
+ if (NS_SUCCEEDED(rv))
+ NS_ADDREF(*aLockObject = lock);
+
+ return rv;
+}
+
+static int32_t sInitCounter;
+
+nsresult
+XRE_InitEmbedding2(nsIFile *aLibXULDirectory,
+ nsIFile *aAppDirectory,
+ nsIDirectoryServiceProvider *aAppDirProvider)
+{
+ // Initialize some globals to make nsXREDirProvider happy
+ static char* kNullCommandLine[] = { nullptr };
+ gArgv = kNullCommandLine;
+ gArgc = 0;
+
+ NS_ENSURE_ARG(aLibXULDirectory);
+
+ if (++sInitCounter > 1) // XXXbsmedberg is this really the right solution?
+ return NS_OK;
+
+ if (!aAppDirectory)
+ aAppDirectory = aLibXULDirectory;
+
+ nsresult rv;
+
+ new nsXREDirProvider; // This sets gDirServiceProvider
+ if (!gDirServiceProvider)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ rv = gDirServiceProvider->Initialize(aAppDirectory, aLibXULDirectory,
+ aAppDirProvider);
+ if (NS_FAILED(rv))
+ return rv;
+
+ rv = NS_InitXPCOM2(nullptr, aAppDirectory, gDirServiceProvider);
+ if (NS_FAILED(rv))
+ return rv;
+
+ // We do not need to autoregister components here. The CheckCompatibility()
+ // bits in nsAppRunner.cpp check for an invalidation flag in
+ // compatibility.ini.
+ // If the app wants to autoregister every time (for instance, if it's debug),
+ // it can do so after we return from this function.
+
+ nsCOMPtr<nsIObserver> startupNotifier
+ (do_CreateInstance(NS_APPSTARTUPNOTIFIER_CONTRACTID));
+ if (!startupNotifier)
+ return NS_ERROR_FAILURE;
+
+ startupNotifier->Observe(nullptr, APPSTARTUP_TOPIC, nullptr);
+
+ return NS_OK;
+}
+
+void
+XRE_NotifyProfile()
+{
+ NS_ASSERTION(gDirServiceProvider, "XRE_InitEmbedding was not called!");
+ gDirServiceProvider->DoStartup();
+}
+
+void
+XRE_TermEmbedding()
+{
+ if (--sInitCounter != 0)
+ return;
+
+ NS_ASSERTION(gDirServiceProvider,
+ "XRE_TermEmbedding without XRE_InitEmbedding");
+
+ gDirServiceProvider->DoShutdown();
+ NS_ShutdownXPCOM(nullptr);
+ delete gDirServiceProvider;
+}
+
+const char*
+XRE_ChildProcessTypeToString(GeckoProcessType aProcessType)
+{
+ return (aProcessType < GeckoProcessType_End) ?
+ kGeckoProcessTypeString[aProcessType] : "invalid";
+}
+
+namespace mozilla {
+namespace startup {
+GeckoProcessType sChildProcessType = GeckoProcessType_Default;
+} // namespace startup
+} // namespace mozilla
+
+void
+XRE_SetProcessType(const char* aProcessTypeString)
+{
+ static bool called = false;
+ if (called) {
+ MOZ_CRASH();
+ }
+ called = true;
+
+ sChildProcessType = GeckoProcessType_Invalid;
+ for (int i = 0;
+ i < (int) ArrayLength(kGeckoProcessTypeString);
+ ++i) {
+ if (!strcmp(kGeckoProcessTypeString[i], aProcessTypeString)) {
+ sChildProcessType = static_cast<GeckoProcessType>(i);
+ return;
+ }
+ }
+}
+
+#if defined(MOZ_CRASHREPORTER)
+// FIXME/bug 539522: this out-of-place function is stuck here because
+// IPDL wants access to this crashreporter interface, and
+// crashreporter is built in such a way to make that awkward
+bool
+XRE_TakeMinidumpForChild(uint32_t aChildPid, nsIFile** aDump,
+ uint32_t* aSequence)
+{
+ return CrashReporter::TakeMinidumpForChild(aChildPid, aDump, aSequence);
+}
+
+bool
+XRE_SetRemoteExceptionHandler(const char* aPipe/*= 0*/)
+{
+#if defined(XP_WIN) || defined(XP_MACOSX)
+ return CrashReporter::SetRemoteExceptionHandler(nsDependentCString(aPipe));
+#elif defined(OS_LINUX)
+ return CrashReporter::SetRemoteExceptionHandler();
+#else
+# error "OOP crash reporter unsupported on this platform"
+#endif
+}
+#endif // if defined(MOZ_CRASHREPORTER)
+
+#if defined(XP_WIN)
+void
+SetTaskbarGroupId(const nsString& aId)
+{
+ typedef HRESULT (WINAPI * SetCurrentProcessExplicitAppUserModelIDPtr)(PCWSTR AppID);
+
+ SetCurrentProcessExplicitAppUserModelIDPtr funcAppUserModelID = nullptr;
+
+ HMODULE hDLL = ::LoadLibraryW(kShellLibraryName);
+
+ funcAppUserModelID = (SetCurrentProcessExplicitAppUserModelIDPtr)
+ GetProcAddress(hDLL, "SetCurrentProcessExplicitAppUserModelID");
+
+ if (!funcAppUserModelID) {
+ ::FreeLibrary(hDLL);
+ return;
+ }
+
+ if (FAILED(funcAppUserModelID(aId.get()))) {
+ NS_WARNING("SetCurrentProcessExplicitAppUserModelID failed for child process.");
+ }
+
+ if (hDLL)
+ ::FreeLibrary(hDLL);
+}
+#endif
+
+#if defined(MOZ_CRASHREPORTER)
+#if defined(MOZ_CONTENT_SANDBOX) && !defined(MOZ_WIDGET_GONK)
+void
+AddContentSandboxLevelAnnotation()
+{
+ if (XRE_GetProcessType() == GeckoProcessType_Content) {
+ int level = Preferences::GetInt("security.sandbox.content.level");
+ nsAutoCString levelString;
+ levelString.AppendInt(level);
+ CrashReporter::AnnotateCrashReport(
+ NS_LITERAL_CSTRING("ContentSandboxLevel"), levelString);
+ }
+}
+#endif /* MOZ_CONTENT_SANDBOX && !MOZ_WIDGET_GONK */
+#endif /* MOZ_CRASHREPORTER */
+
+nsresult
+XRE_InitChildProcess(int aArgc,
+ char* aArgv[],
+ const XREChildData* aChildData)
+{
+ NS_ENSURE_ARG_MIN(aArgc, 2);
+ NS_ENSURE_ARG_POINTER(aArgv);
+ NS_ENSURE_ARG_POINTER(aArgv[0]);
+ MOZ_ASSERT(aChildData);
+
+#ifdef MOZ_JPROF
+ // Call the code to install our handler
+ setupProfilingStuff();
+#endif
+
+#if !defined(MOZ_WIDGET_ANDROID) && !defined(MOZ_WIDGET_GONK)
+ // On non-Fennec Gecko, the GMPLoader code resides in plugin-container,
+ // and we must forward it through to the GMP code here.
+ GMPProcessChild::SetGMPLoader(aChildData->gmpLoader.get());
+#else
+ // On Fennec, the GMPLoader's code resides inside XUL (because for the time
+ // being GMPLoader relies upon NSPR, which we can't use in plugin-container
+ // on Android), so we create it here inside XUL and pass it to the GMP code.
+ UniquePtr<GMPLoader> loader = CreateGMPLoader(nullptr);
+ GMPProcessChild::SetGMPLoader(loader.get());
+#endif
+
+#if defined(XP_WIN)
+ // From the --attach-console support in nsNativeAppSupportWin.cpp, but
+ // here we are a content child process, so we always attempt to attach
+ // to the parent's (ie, the browser's) console.
+ // Try to attach console to the parent process.
+ // It will succeed when the parent process is a command line,
+ // so that stdio will be displayed in it.
+ if (AttachConsole(ATTACH_PARENT_PROCESS)) {
+ // Change std handles to refer to new console handles.
+ // Before doing so, ensure that stdout/stderr haven't been
+ // redirected to a valid file
+ if (_fileno(stdout) == -1 ||
+ _get_osfhandle(fileno(stdout)) == -1)
+ freopen("CONOUT$", "w", stdout);
+ // Merge stderr into CONOUT$ since there isn't any `CONERR$`.
+ // http://msdn.microsoft.com/en-us/library/windows/desktop/ms683231%28v=vs.85%29.aspx
+ if (_fileno(stderr) == -1 ||
+ _get_osfhandle(fileno(stderr)) == -1)
+ freopen("CONOUT$", "w", stderr);
+ if (_fileno(stdin) == -1 || _get_osfhandle(fileno(stdin)) == -1)
+ freopen("CONIN$", "r", stdin);
+ }
+
+#if defined(MOZ_SANDBOX)
+ if (aChildData->sandboxTargetServices) {
+ SandboxTarget::Instance()->SetTargetServices(aChildData->sandboxTargetServices);
+ }
+#endif
+#endif
+
+ // NB: This must be called before profiler_init
+ ScopedLogging logger;
+
+ // This is needed by Telemetry to initialize histogram collection.
+ // NB: This must be called after NS_LogInit().
+ // NS_LogInit must be called before Telemetry::CreateStatisticsRecorder
+ // so as to avoid many log messages of the form
+ // WARNING: XPCOM objects created/destroyed from static ctor/dtor: [..]
+ // See bug 1279614.
+ Telemetry::CreateStatisticsRecorder();
+
+ mozilla::LogModule::Init();
+
+ char aLocal;
+ GeckoProfilerInitRAII profiler(&aLocal);
+
+ PROFILER_LABEL("Startup", "XRE_InitChildProcess",
+ js::ProfileEntry::Category::OTHER);
+
+ // Complete 'task_t' exchange for Mac OS X. This structure has the same size
+ // regardless of architecture so we don't have any cross-arch issues here.
+#ifdef XP_MACOSX
+ if (aArgc < 1)
+ return NS_ERROR_FAILURE;
+ const char* const mach_port_name = aArgv[--aArgc];
+
+ const int kTimeoutMs = 1000;
+
+ MachSendMessage child_message(0);
+ if (!child_message.AddDescriptor(MachMsgPortDescriptor(mach_task_self()))) {
+ NS_WARNING("child AddDescriptor(mach_task_self()) failed.");
+ return NS_ERROR_FAILURE;
+ }
+
+ ReceivePort child_recv_port;
+ mach_port_t raw_child_recv_port = child_recv_port.GetPort();
+ if (!child_message.AddDescriptor(MachMsgPortDescriptor(raw_child_recv_port))) {
+ NS_WARNING("Adding descriptor to message failed");
+ return NS_ERROR_FAILURE;
+ }
+
+ ReceivePort* ports_out_receiver = new ReceivePort();
+ if (!child_message.AddDescriptor(MachMsgPortDescriptor(ports_out_receiver->GetPort()))) {
+ NS_WARNING("Adding descriptor to message failed");
+ return NS_ERROR_FAILURE;
+ }
+
+ ReceivePort* ports_in_receiver = new ReceivePort();
+ if (!child_message.AddDescriptor(MachMsgPortDescriptor(ports_in_receiver->GetPort()))) {
+ NS_WARNING("Adding descriptor to message failed");
+ return NS_ERROR_FAILURE;
+ }
+
+ MachPortSender child_sender(mach_port_name);
+ kern_return_t err = child_sender.SendMessage(child_message, kTimeoutMs);
+ if (err != KERN_SUCCESS) {
+ NS_WARNING("child SendMessage() failed");
+ return NS_ERROR_FAILURE;
+ }
+
+ MachReceiveMessage parent_message;
+ err = child_recv_port.WaitForMessage(&parent_message, kTimeoutMs);
+ if (err != KERN_SUCCESS) {
+ NS_WARNING("child WaitForMessage() failed");
+ return NS_ERROR_FAILURE;
+ }
+
+ if (parent_message.GetTranslatedPort(0) == MACH_PORT_NULL) {
+ NS_WARNING("child GetTranslatedPort(0) failed");
+ return NS_ERROR_FAILURE;
+ }
+
+ err = task_set_bootstrap_port(mach_task_self(),
+ parent_message.GetTranslatedPort(0));
+
+ if (parent_message.GetTranslatedPort(1) == MACH_PORT_NULL) {
+ NS_WARNING("child GetTranslatedPort(1) failed");
+ return NS_ERROR_FAILURE;
+ }
+ MachPortSender* ports_out_sender = new MachPortSender(parent_message.GetTranslatedPort(1));
+
+ if (parent_message.GetTranslatedPort(2) == MACH_PORT_NULL) {
+ NS_WARNING("child GetTranslatedPort(2) failed");
+ return NS_ERROR_FAILURE;
+ }
+ MachPortSender* ports_in_sender = new MachPortSender(parent_message.GetTranslatedPort(2));
+
+ if (err != KERN_SUCCESS) {
+ NS_WARNING("child task_set_bootstrap_port() failed");
+ return NS_ERROR_FAILURE;
+ }
+
+#endif
+
+ SetupErrorHandling(aArgv[0]);
+
+#if defined(MOZ_CRASHREPORTER)
+ if (aArgc < 1)
+ return NS_ERROR_FAILURE;
+ const char* const crashReporterArg = aArgv[--aArgc];
+
+# if defined(XP_WIN) || defined(XP_MACOSX)
+ // on windows and mac, |crashReporterArg| is the named pipe on which the
+ // server is listening for requests, or "-" if crash reporting is
+ // disabled.
+ if (0 != strcmp("-", crashReporterArg) &&
+ !XRE_SetRemoteExceptionHandler(crashReporterArg)) {
+ // Bug 684322 will add better visibility into this condition
+ NS_WARNING("Could not setup crash reporting\n");
+ }
+# elif defined(OS_LINUX)
+ // on POSIX, |crashReporterArg| is "true" if crash reporting is
+ // enabled, false otherwise
+ if (0 != strcmp("false", crashReporterArg) &&
+ !XRE_SetRemoteExceptionHandler(nullptr)) {
+ // Bug 684322 will add better visibility into this condition
+ NS_WARNING("Could not setup crash reporting\n");
+ }
+# else
+# error "OOP crash reporting unsupported on this platform"
+# endif
+#endif // if defined(MOZ_CRASHREPORTER)
+
+ gArgv = aArgv;
+ gArgc = aArgc;
+
+#ifdef MOZ_X11
+ XInitThreads();
+#endif
+#if MOZ_WIDGET_GTK == 2
+ XRE_GlibInit();
+#endif
+#ifdef MOZ_WIDGET_GTK
+ // Setting the name here avoids the need to pass this through to gtk_init().
+ g_set_prgname(aArgv[0]);
+#endif
+
+#ifdef OS_POSIX
+ if (PR_GetEnv("MOZ_DEBUG_CHILD_PROCESS") ||
+ PR_GetEnv("MOZ_DEBUG_CHILD_PAUSE")) {
+ printf_stderr("\n\nCHILDCHILDCHILDCHILD\n debug me @ %d\n\n",
+ base::GetCurrentProcId());
+ sleep(30);
+ }
+#elif defined(OS_WIN)
+ if (PR_GetEnv("MOZ_DEBUG_CHILD_PROCESS")) {
+ NS_DebugBreak(NS_DEBUG_BREAK,
+ "Invoking NS_DebugBreak() to debug child process",
+ nullptr, __FILE__, __LINE__);
+ } else if (PR_GetEnv("MOZ_DEBUG_CHILD_PAUSE")) {
+ printf_stderr("\n\nCHILDCHILDCHILDCHILD\n debug me @ %d\n\n",
+ base::GetCurrentProcId());
+ ::Sleep(10000);
+ }
+#endif
+
+ // child processes launched by GeckoChildProcessHost get this magic
+ // argument appended to their command lines
+ const char* const parentPIDString = aArgv[aArgc-1];
+ MOZ_ASSERT(parentPIDString, "NULL parent PID");
+ --aArgc;
+
+ char* end = 0;
+ base::ProcessId parentPID = strtol(parentPIDString, &end, 10);
+ MOZ_ASSERT(!*end, "invalid parent PID");
+
+#ifdef XP_MACOSX
+ mozilla::ipc::SharedMemoryBasic::SetupMachMemory(parentPID, ports_in_receiver, ports_in_sender,
+ ports_out_sender, ports_out_receiver, true);
+#endif
+
+#if defined(XP_WIN)
+ // On Win7+, register the application user model id passed in by
+ // parent. This insures windows created by the container properly
+ // group with the parent app on the Win7 taskbar.
+ const char* const appModelUserId = aArgv[--aArgc];
+ if (appModelUserId) {
+ // '-' implies no support
+ if (*appModelUserId != '-') {
+ nsString appId;
+ appId.AssignWithConversion(nsDependentCString(appModelUserId));
+ // The version string is encased in quotes
+ appId.Trim(NS_LITERAL_CSTRING("\"").get());
+ // Set the id
+ SetTaskbarGroupId(appId);
+ }
+ }
+#endif
+
+ base::AtExitManager exitManager;
+
+ nsresult rv = XRE_InitCommandLine(aArgc, aArgv);
+ if (NS_FAILED(rv)) {
+ return NS_ERROR_FAILURE;
+ }
+
+ MessageLoop::Type uiLoopType;
+ switch (XRE_GetProcessType()) {
+ case GeckoProcessType_Content:
+ case GeckoProcessType_GPU:
+ // Content processes need the XPCOM/chromium frankenventloop
+ uiLoopType = MessageLoop::TYPE_MOZILLA_CHILD;
+ break;
+ case GeckoProcessType_GMPlugin:
+ uiLoopType = MessageLoop::TYPE_DEFAULT;
+ break;
+ default:
+ uiLoopType = MessageLoop::TYPE_UI;
+ break;
+ }
+
+ {
+ // This is a lexical scope for the MessageLoop below. We want it
+ // to go out of scope before NS_LogTerm() so that we don't get
+ // spurious warnings about XPCOM objects being destroyed from a
+ // static context.
+
+ // Associate this thread with a UI MessageLoop
+ MessageLoop uiMessageLoop(uiLoopType);
+ {
+ nsAutoPtr<ProcessChild> process;
+
+#ifdef XP_WIN
+ mozilla::ipc::windows::InitUIThread();
+#endif
+
+ switch (XRE_GetProcessType()) {
+ case GeckoProcessType_Default:
+ NS_RUNTIMEABORT("This makes no sense");
+ break;
+
+ case GeckoProcessType_Plugin:
+ process = new PluginProcessChild(parentPID);
+ break;
+
+ case GeckoProcessType_Content: {
+ process = new ContentProcess(parentPID);
+ // If passed in grab the application path for xpcom init
+ bool foundAppdir = false;
+
+#if defined(XP_MACOSX) && defined(MOZ_CONTENT_SANDBOX)
+ // If passed in grab the profile path for sandboxing
+ bool foundProfile = false;
+#endif
+
+ for (int idx = aArgc; idx > 0; idx--) {
+ if (aArgv[idx] && !strcmp(aArgv[idx], "-appdir")) {
+ MOZ_ASSERT(!foundAppdir);
+ if (foundAppdir) {
+ continue;
+ }
+ nsCString appDir;
+ appDir.Assign(nsDependentCString(aArgv[idx+1]));
+ static_cast<ContentProcess*>(process.get())->SetAppDir(appDir);
+ foundAppdir = true;
+ }
+
+ if (aArgv[idx] && !strcmp(aArgv[idx], "-safeMode")) {
+ gSafeMode = true;
+ }
+
+#if defined(XP_MACOSX) && defined(MOZ_CONTENT_SANDBOX)
+ if (aArgv[idx] && !strcmp(aArgv[idx], "-profile")) {
+ MOZ_ASSERT(!foundProfile);
+ if (foundProfile) {
+ continue;
+ }
+ nsCString profile;
+ profile.Assign(nsDependentCString(aArgv[idx+1]));
+ static_cast<ContentProcess*>(process.get())->SetProfile(profile);
+ foundProfile = true;
+ }
+#endif /* XP_MACOSX && MOZ_CONTENT_SANDBOX */
+ }
+ }
+ break;
+
+ case GeckoProcessType_IPDLUnitTest:
+#ifdef MOZ_IPDL_TESTS
+ process = new IPDLUnitTestProcessChild(parentPID);
+#else
+ NS_RUNTIMEABORT("rebuild with --enable-ipdl-tests");
+#endif
+ break;
+
+ case GeckoProcessType_GMPlugin:
+ process = new gmp::GMPProcessChild(parentPID);
+ break;
+
+ case GeckoProcessType_GPU:
+ process = new gfx::GPUProcessImpl(parentPID);
+ break;
+
+ default:
+ NS_RUNTIMEABORT("Unknown main thread class");
+ }
+
+ if (!process->Init()) {
+ return NS_ERROR_FAILURE;
+ }
+
+#ifdef MOZ_CRASHREPORTER
+#if defined(XP_WIN) || defined(XP_MACOSX)
+ CrashReporter::InitChildProcessTmpDir();
+#endif
+#endif
+
+#if defined(XP_WIN)
+ // Set child processes up such that they will get killed after the
+ // chrome process is killed in cases where the user shuts the system
+ // down or logs off.
+ ::SetProcessShutdownParameters(0x280 - 1, SHUTDOWN_NORETRY);
+#endif
+
+#if defined(MOZ_SANDBOX) && defined(XP_WIN)
+ // We need to do this after the process has been initialised, as
+ // InitLoggingIfRequired may need access to prefs.
+ mozilla::sandboxing::InitLoggingIfRequired(aChildData->ProvideLogFunction);
+#endif
+
+ OverrideDefaultLocaleIfNeeded();
+
+#if defined(MOZ_CRASHREPORTER)
+#if defined(MOZ_CONTENT_SANDBOX) && !defined(MOZ_WIDGET_GONK)
+ AddContentSandboxLevelAnnotation();
+#endif
+#endif
+
+ // Run the UI event loop on the main thread.
+ uiMessageLoop.MessageLoop::Run();
+
+ // Allow ProcessChild to clean up after itself before going out of
+ // scope and being deleted
+ process->CleanUp();
+ mozilla::Omnijar::CleanUp();
+
+#if defined(XP_MACOSX)
+ // Everybody should be done using shared memory by now.
+ mozilla::ipc::SharedMemoryBasic::Shutdown();
+#endif
+ }
+ }
+
+ Telemetry::DestroyStatisticsRecorder();
+ return XRE_DeinitCommandLine();
+}
+
+MessageLoop*
+XRE_GetIOMessageLoop()
+{
+ if (sChildProcessType == GeckoProcessType_Default) {
+ return BrowserProcessSubThread::GetMessageLoop(BrowserProcessSubThread::IO);
+ }
+ return IOThreadChild::message_loop();
+}
+
+namespace {
+
+class MainFunctionRunnable : public Runnable
+{
+public:
+ NS_DECL_NSIRUNNABLE
+
+ MainFunctionRunnable(MainFunction aFunction,
+ void* aData)
+ : mFunction(aFunction),
+ mData(aData)
+ {
+ NS_ASSERTION(aFunction, "Don't give me a null pointer!");
+ }
+
+private:
+ MainFunction mFunction;
+ void* mData;
+};
+
+} /* anonymous namespace */
+
+NS_IMETHODIMP
+MainFunctionRunnable::Run()
+{
+ mFunction(mData);
+ return NS_OK;
+}
+
+nsresult
+XRE_InitParentProcess(int aArgc,
+ char* aArgv[],
+ MainFunction aMainFunction,
+ void* aMainFunctionData)
+{
+ NS_ENSURE_ARG_MIN(aArgc, 1);
+ NS_ENSURE_ARG_POINTER(aArgv);
+ NS_ENSURE_ARG_POINTER(aArgv[0]);
+
+ ScopedXREEmbed embed;
+
+ gArgc = aArgc;
+ gArgv = aArgv;
+ nsresult rv = XRE_InitCommandLine(gArgc, gArgv);
+ if (NS_FAILED(rv))
+ return NS_ERROR_FAILURE;
+
+ {
+ embed.Start();
+
+ nsCOMPtr<nsIAppShell> appShell(do_GetService(kAppShellCID));
+ NS_ENSURE_TRUE(appShell, NS_ERROR_FAILURE);
+
+ if (aMainFunction) {
+ nsCOMPtr<nsIRunnable> runnable =
+ new MainFunctionRunnable(aMainFunction, aMainFunctionData);
+ NS_ENSURE_TRUE(runnable, NS_ERROR_OUT_OF_MEMORY);
+
+ nsresult rv = NS_DispatchToCurrentThread(runnable);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+
+ // Do event loop
+ if (NS_FAILED(appShell->Run())) {
+ NS_WARNING("Failed to run appshell");
+ return NS_ERROR_FAILURE;
+ }
+ }
+
+ return XRE_DeinitCommandLine();
+}
+
+#ifdef MOZ_IPDL_TESTS
+//-----------------------------------------------------------------------------
+// IPDL unit test
+
+int
+XRE_RunIPDLTest(int aArgc, char** aArgv)
+{
+ if (aArgc < 2) {
+ fprintf(stderr, "TEST-UNEXPECTED-FAIL | <---> | insufficient #args, need at least 2\n");
+ return 1;
+ }
+
+ void* data = reinterpret_cast<void*>(aArgv[aArgc-1]);
+
+ nsresult rv =
+ XRE_InitParentProcess(
+ --aArgc, aArgv, mozilla::_ipdltest::IPDLUnitTestMain, data);
+ NS_ENSURE_SUCCESS(rv, 1);
+
+ return 0;
+}
+#endif // ifdef MOZ_IPDL_TESTS
+
+nsresult
+XRE_RunAppShell()
+{
+ nsCOMPtr<nsIAppShell> appShell(do_GetService(kAppShellCID));
+ NS_ENSURE_TRUE(appShell, NS_ERROR_FAILURE);
+#if defined(XP_MACOSX)
+ {
+ // In content processes that want XPCOM (and hence want
+ // AppShell), we usually run our hybrid event loop through
+ // MessagePump::Run(), by way of nsBaseAppShell::Run(). The
+ // Cocoa nsAppShell impl, however, implements its own Run()
+ // that's unaware of MessagePump. That's all rather suboptimal,
+ // but oddly enough not a problem... usually.
+ //
+ // The problem with this setup comes during startup.
+ // XPCOM-in-subprocesses depends on IPC, e.g. to init the pref
+ // service, so we have to init IPC first. But, IPC also
+ // indirectly kinda-depends on XPCOM, because MessagePump
+ // schedules work from off-main threads (e.g. IO thread) by
+ // using NS_DispatchToMainThread(). If the IO thread receives a
+ // Message from the parent before nsThreadManager is
+ // initialized, then DispatchToMainThread() will fail, although
+ // MessagePump will remember the task. This race condition
+ // isn't a problem when appShell->Run() ends up in
+ // MessagePump::Run(), because MessagePump will immediate see it
+ // has work to do. It *is* a problem when we end up in [NSApp
+ // run], because it's not aware that MessagePump has work that
+ // needs to be processed; that was supposed to be signaled by
+ // nsIRunnable(s).
+ //
+ // So instead of hacking Cocoa nsAppShell or rewriting the
+ // event-loop system, we compromise here by processing any tasks
+ // that might have been enqueued on MessagePump, *before*
+ // MessagePump::ScheduleWork was able to successfully
+ // DispatchToMainThread().
+ MessageLoop* loop = MessageLoop::current();
+ bool couldNest = loop->NestableTasksAllowed();
+
+ loop->SetNestableTasksAllowed(true);
+ RefPtr<Runnable> task = new MessageLoop::QuitTask();
+ loop->PostTask(task.forget());
+ loop->Run();
+
+ loop->SetNestableTasksAllowed(couldNest);
+ }
+#endif // XP_MACOSX
+ return appShell->Run();
+}
+
+void
+XRE_ShutdownChildProcess()
+{
+ MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!");
+
+ mozilla::DebugOnly<MessageLoop*> ioLoop = XRE_GetIOMessageLoop();
+ MOZ_ASSERT(!!ioLoop, "Bad shutdown order");
+
+ // Quit() sets off the following chain of events
+ // (1) UI loop starts quitting
+ // (2) UI loop returns from Run() in XRE_InitChildProcess()
+ // (3) ProcessChild goes out of scope and terminates the IO thread
+ // (4) ProcessChild joins the IO thread
+ // (5) exit()
+ MessageLoop::current()->Quit();
+#if defined(XP_MACOSX)
+ nsCOMPtr<nsIAppShell> appShell(do_GetService(kAppShellCID));
+ if (appShell) {
+ // On Mac, we might be only above nsAppShell::Run(), not
+ // MessagePump::Run(). See XRE_RunAppShell(). To account for
+ // that case, we fire off an Exit() here. If we were indeed
+ // above MessagePump::Run(), this Exit() is just superfluous.
+ appShell->Exit();
+ }
+#endif // XP_MACOSX
+}
+
+namespace {
+ContentParent* gContentParent; //long-lived, manually refcounted
+TestShellParent* GetOrCreateTestShellParent()
+{
+ if (!gContentParent) {
+ RefPtr<ContentParent> parent = ContentParent::GetNewOrUsedBrowserProcess();
+ parent.forget(&gContentParent);
+ } else if (!gContentParent->IsAlive()) {
+ return nullptr;
+ }
+ TestShellParent* tsp = gContentParent->GetTestShellSingleton();
+ if (!tsp) {
+ tsp = gContentParent->CreateTestShell();
+ }
+ return tsp;
+}
+
+} // namespace
+
+bool
+XRE_SendTestShellCommand(JSContext* aCx,
+ JSString* aCommand,
+ void* aCallback)
+{
+ JS::RootedString cmd(aCx, aCommand);
+ TestShellParent* tsp = GetOrCreateTestShellParent();
+ NS_ENSURE_TRUE(tsp, false);
+
+ nsAutoJSString command;
+ NS_ENSURE_TRUE(command.init(aCx, cmd), false);
+
+ if (!aCallback) {
+ return tsp->SendExecuteCommand(command);
+ }
+
+ TestShellCommandParent* callback = static_cast<TestShellCommandParent*>(
+ tsp->SendPTestShellCommandConstructor(command));
+ NS_ENSURE_TRUE(callback, false);
+
+ JS::Value callbackVal = *reinterpret_cast<JS::Value*>(aCallback);
+ NS_ENSURE_TRUE(callback->SetCallback(aCx, callbackVal), false);
+
+ return true;
+}
+
+bool
+XRE_ShutdownTestShell()
+{
+ if (!gContentParent) {
+ return true;
+ }
+ bool ret = true;
+ if (gContentParent->IsAlive()) {
+ ret = gContentParent->DestroyTestShell(
+ gContentParent->GetTestShellSingleton());
+ }
+ NS_RELEASE(gContentParent);
+ return ret;
+}
+
+#ifdef MOZ_X11
+void
+XRE_InstallX11ErrorHandler()
+{
+#if (MOZ_WIDGET_GTK == 3)
+ InstallGdkErrorHandler();
+#else
+ InstallX11ErrorHandler();
+#endif
+}
+#endif
diff --git a/toolkit/xre/nsGDKErrorHandler.cpp b/toolkit/xre/nsGDKErrorHandler.cpp
new file mode 100644
index 000000000..a01a7c53b
--- /dev/null
+++ b/toolkit/xre/nsGDKErrorHandler.cpp
@@ -0,0 +1,100 @@
+/* -*- Mode: C++; tab-width: 40; 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 "nsGDKErrorHandler.h"
+
+#include <gtk/gtk.h>
+#include <gdk/gdkx.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "nsDebug.h"
+#include "nsString.h"
+#include "nsX11ErrorHandler.h"
+
+#include "prenv.h"
+
+
+/* See https://bugzilla.gnome.org/show_bug.cgi?id=629608#c8
+ *
+ * GDK implements X11 error traps to ignore X11 errors.
+ * Unfortunatelly We don't know which X11 events can be ignored
+ * so we have to utilize the Gdk error handler to avoid
+ * false alarms in Gtk3.
+ */
+static void
+GdkErrorHandler(const gchar *log_domain, GLogLevelFlags log_level,
+ const gchar *message, gpointer user_data)
+{
+ if (strstr(message, "X Window System error")) {
+ XErrorEvent event;
+ nsDependentCString buffer(message);
+ char *endptr;
+
+ /* Parse Gdk X Window error message which has this format:
+ * (Details: serial XXXX error_code XXXX request_code XXXX (XXXX) minor_code XXXX)
+ */
+ NS_NAMED_LITERAL_CSTRING(serialString, "(Details: serial ");
+ int32_t start = buffer.Find(serialString);
+ if (start == kNotFound)
+ NS_RUNTIMEABORT(message);
+
+ start += serialString.Length();
+ errno = 0;
+ event.serial = strtol(buffer.BeginReading() + start, &endptr, 10);
+ if (errno)
+ NS_RUNTIMEABORT(message);
+
+ NS_NAMED_LITERAL_CSTRING(errorCodeString, " error_code ");
+ if (!StringBeginsWith(Substring(endptr, buffer.EndReading()), errorCodeString))
+ NS_RUNTIMEABORT(message);
+
+ errno = 0;
+ event.error_code = strtol(endptr + errorCodeString.Length(), &endptr, 10);
+ if (errno)
+ NS_RUNTIMEABORT(message);
+
+ NS_NAMED_LITERAL_CSTRING(requestCodeString, " request_code ");
+ if (!StringBeginsWith(Substring(endptr, buffer.EndReading()), requestCodeString))
+ NS_RUNTIMEABORT(message);
+
+ errno = 0;
+ event.request_code = strtol(endptr + requestCodeString.Length(), &endptr, 10);
+ if (errno)
+ NS_RUNTIMEABORT(message);
+
+ NS_NAMED_LITERAL_CSTRING(minorCodeString, " minor_code ");
+ start = buffer.Find(minorCodeString, endptr - buffer.BeginReading());
+ if (!start)
+ NS_RUNTIMEABORT(message);
+
+ errno = 0;
+ event.minor_code = strtol(buffer.BeginReading() + start + minorCodeString.Length(), nullptr, 10);
+ if (errno)
+ NS_RUNTIMEABORT(message);
+
+ event.display = GDK_DISPLAY_XDISPLAY(gdk_display_get_default());
+ // Gdk does not provide resource ID
+ event.resourceid = 0;
+
+ X11Error(event.display, &event);
+ } else {
+ g_log_default_handler(log_domain, log_level, message, user_data);
+ NS_RUNTIMEABORT(message);
+ }
+}
+
+void
+InstallGdkErrorHandler()
+{
+ g_log_set_handler("Gdk",
+ (GLogLevelFlags)(G_LOG_LEVEL_ERROR | G_LOG_FLAG_FATAL | G_LOG_FLAG_RECURSION),
+ GdkErrorHandler,
+ nullptr);
+ if (PR_GetEnv("MOZ_X_SYNC")) {
+ XSynchronize(GDK_DISPLAY_XDISPLAY(gdk_display_get_default()), True);
+ }
+}
diff --git a/toolkit/xre/nsGDKErrorHandler.h b/toolkit/xre/nsGDKErrorHandler.h
new file mode 100644
index 000000000..dc7459616
--- /dev/null
+++ b/toolkit/xre/nsGDKErrorHandler.h
@@ -0,0 +1,8 @@
+/* -*- Mode: C++; tab-width: 40; 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/. */
+
+#if (MOZ_WIDGET_GTK == 3)
+void InstallGdkErrorHandler();
+#endif
diff --git a/toolkit/xre/nsINativeAppSupport.idl b/toolkit/xre/nsINativeAppSupport.idl
new file mode 100644
index 000000000..c9a316a98
--- /dev/null
+++ b/toolkit/xre/nsINativeAppSupport.idl
@@ -0,0 +1,104 @@
+/* -*- 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 "nsISupports.idl"
+
+/* nsINativeAppSupport
+ *
+ * This "pseudo" (in the XPCOM sense) interface provides for
+ * platform-specific general application support:
+ * o It manages the details of the simple DDE communication
+ * supported on the Win32 platform (it is the addition of this
+ * item that prompted the creation of this interface.
+ *
+ * Due to the nature of the beast, this interface is not a full-blown
+ * XPCOM component. The primary reason is that objects that implement
+ * this interface generally must be operational *before* XPCOM (or any
+ * of the rest of Mozilla) are initialized. As a result, this
+ * interface is instantiated by somewhat unconventional means.
+ *
+ * To create the implementor of this interface, you call the function
+ * NS_CreateNativeAppSupport. This is done in the startup code
+ * in nsAppRunner.cpp
+ *
+ * The interface provides these functions:
+ * start - You call this to inform the native app support that the
+ * application is starting. In addition, it serves as a
+ * query as to whether the application should continue to
+ * run.
+ *
+ * If the returned boolean result is PR_FALSE, then the
+ * application should exit without further processing. In
+ * such cases, the returned nsresult indicates whether the
+ * reason to exit is due to an error or not.
+ *
+ * Win32 Note: In the case of starting a second instance
+ * of this executable, this function will return
+ * PR_FALSE and nsresult==NS_OK. This means that
+ * the command line arguments have been
+ * successfully passed to the instance of the
+ * application acting as a DDE server.
+ *
+ * stop - You call this to inform the native app support that the
+ * application *wishes* to terminate. If the returned boolean
+ * value is PR_FALSE, then the application should continue
+ * (as if there were still additional top-level windows open).
+ *
+ * Win32 Note: If this is the instance of the application
+ * acting as the DDE server, and there are current
+ * DDE conversations active with other instances
+ * acting as DDE clients, then this function will
+ * return PR_FALSE.
+ *
+ * quit - Like Stop, but this method *forces* termination (or more
+ * precisely, indicates that the application is about to be
+ * terminated regardless of what a call to Stop might have
+ * returned.
+ *
+ * This method is intended to be called when the user selects
+ * the "Quit" option (close all windows and exit).
+ *
+ * Win32 Note: Stop is problematic in the case of "Quit" (close
+ * all windows and exit the application) because
+ * either we don't Quit or (potentially) we lose
+ * requests coming from other instances of the
+ * application. The strategy is to give preference
+ * to the user's explicit Quit request. In the
+ * unlikely event that a request is pending from
+ * another instance of the application, then such
+ * requests are essentially ignored. This is
+ * roughly equivalent to handling that request by
+ * opening a new window, followed by immediately
+ * closing it. Since this is the same as if the
+ * request came in immediately before the Quit
+ * call (versus immediately after it), no harm.
+ *
+ * There is an exposure here: Upon return from this
+ * function, any DDE connect request (for Mozilla)
+ * will fail and other instances of the application
+ * will start up as a DDE server. In that case,
+ * those instances may do things that conflict with
+ * the subsequent shutting down of the instance that
+ * is quitting. For this reason, the call to Quit
+ * should be deferred as long as possible.
+ *
+ * onLastWindowClosing - Called when the last window is closed. Used as a
+ * "soft" shutdown, passwords are flushed.
+ */
+
+interface nsIXULWindow;
+interface nsICmdLineService;
+
+[scriptable, uuid(5fdf8480-1f98-11d4-8077-00600811a9c3)]
+interface nsINativeAppSupport : nsISupports {
+ // Startup/shutdown.
+ boolean start();
+ void enable();
+ boolean stop();
+ void quit();
+
+ void onLastWindowClosing();
+ void ReOpen();
+};
diff --git a/toolkit/xre/nsIWinAppHelper.idl b/toolkit/xre/nsIWinAppHelper.idl
new file mode 100644
index 000000000..c8a602977
--- /dev/null
+++ b/toolkit/xre/nsIWinAppHelper.idl
@@ -0,0 +1,19 @@
+/* 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 "nsISupports.idl"
+
+/**
+ * A scriptable interface used on Windows only to do some work from
+ * a special process that gets created with elevated privileges.
+ *
+ * @status UNSTABLE - This interface is not frozen and will probably change in
+ * future releases.
+ */
+
+[scriptable, uuid(dc263ca8-b257-47eb-b5b7-339d9e0b90f7)]
+interface nsIWinAppHelper : nsISupports
+{
+ readonly attribute boolean userCanElevate;
+};
diff --git a/toolkit/xre/nsNativeAppSupportBase.cpp b/toolkit/xre/nsNativeAppSupportBase.cpp
new file mode 100644
index 000000000..877bd3d8a
--- /dev/null
+++ b/toolkit/xre/nsNativeAppSupportBase.cpp
@@ -0,0 +1,55 @@
+/* -*- 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 "nsNativeAppSupportBase.h"
+
+nsNativeAppSupportBase::nsNativeAppSupportBase()
+{
+}
+
+nsNativeAppSupportBase::~nsNativeAppSupportBase()
+{
+}
+
+NS_IMPL_ISUPPORTS(nsNativeAppSupportBase, nsINativeAppSupport)
+
+// Start answer defaults to OK.
+NS_IMETHODIMP
+nsNativeAppSupportBase::Start( bool *result )
+{
+ *result = true;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsNativeAppSupportBase::Enable()
+{
+ return NS_OK;
+}
+
+// Stop answer defaults to OK.
+NS_IMETHODIMP
+nsNativeAppSupportBase::Stop( bool *result )
+{
+ *result = true;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsNativeAppSupportBase::Quit()
+{
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsNativeAppSupportBase::ReOpen()
+{
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsNativeAppSupportBase::OnLastWindowClosing() {
+ return NS_OK;
+}
diff --git a/toolkit/xre/nsNativeAppSupportBase.h b/toolkit/xre/nsNativeAppSupportBase.h
new file mode 100644
index 000000000..a3996b728
--- /dev/null
+++ b/toolkit/xre/nsNativeAppSupportBase.h
@@ -0,0 +1,28 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nsNativeAppSupportBase_h__
+#define nsNativeAppSupportBase_h__
+
+#include "nsAppRunner.h"
+#include "nsINativeAppSupport.h"
+
+// nsNativeAppSupportBase
+//
+// This is a default implementation of the nsINativeAppSupport interface
+// declared in mozilla/xpfe/appshell/public/nsINativeAppSupport.h.
+
+class nsNativeAppSupportBase : public nsINativeAppSupport {
+public:
+ nsNativeAppSupportBase();
+
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSINATIVEAPPSUPPORT
+
+protected:
+ virtual ~nsNativeAppSupportBase();
+};
+
+#endif
diff --git a/toolkit/xre/nsNativeAppSupportCocoa.mm b/toolkit/xre/nsNativeAppSupportCocoa.mm
new file mode 100644
index 000000000..6da6790c1
--- /dev/null
+++ b/toolkit/xre/nsNativeAppSupportCocoa.mm
@@ -0,0 +1,198 @@
+/* -*- 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 "nsString.h"
+
+#import <CoreServices/CoreServices.h>
+#import <Cocoa/Cocoa.h>
+
+#include "nsCOMPtr.h"
+#include "nsCocoaFeatures.h"
+#include "nsNativeAppSupportBase.h"
+
+#include "nsIAppShellService.h"
+#include "nsIAppStartup.h"
+#include "nsIBaseWindow.h"
+#include "nsICommandLineRunner.h"
+#include "mozIDOMWindow.h"
+#include "nsIDocShellTreeItem.h"
+#include "nsIDocShellTreeOwner.h"
+#include "nsIInterfaceRequestorUtils.h"
+#include "nsIObserver.h"
+#include "nsIServiceManager.h"
+#include "nsIWebNavigation.h"
+#include "nsIWidget.h"
+#include "nsIWindowMediator.h"
+
+// This must be included last:
+#include "nsObjCExceptions.h"
+
+nsresult
+GetNativeWindowPointerFromDOMWindow(mozIDOMWindowProxy *a_window, NSWindow **a_nativeWindow)
+{
+ *a_nativeWindow = nil;
+ if (!a_window)
+ return NS_ERROR_INVALID_ARG;
+
+ nsCOMPtr<nsIWebNavigation> mruWebNav(do_GetInterface(a_window));
+ if (mruWebNav) {
+ nsCOMPtr<nsIDocShellTreeItem> mruTreeItem(do_QueryInterface(mruWebNav));
+ nsCOMPtr<nsIDocShellTreeOwner> mruTreeOwner = nullptr;
+ mruTreeItem->GetTreeOwner(getter_AddRefs(mruTreeOwner));
+ if(mruTreeOwner) {
+ nsCOMPtr<nsIBaseWindow> mruBaseWindow(do_QueryInterface(mruTreeOwner));
+ if (mruBaseWindow) {
+ nsCOMPtr<nsIWidget> mruWidget = nullptr;
+ mruBaseWindow->GetMainWidget(getter_AddRefs(mruWidget));
+ if (mruWidget) {
+ *a_nativeWindow = (NSWindow*)mruWidget->GetNativeData(NS_NATIVE_WINDOW);
+ }
+ }
+ }
+ }
+
+ return NS_OK;
+}
+
+class nsNativeAppSupportCocoa : public nsNativeAppSupportBase
+{
+public:
+ nsNativeAppSupportCocoa() :
+ mCanShowUI(false) { }
+
+ NS_IMETHOD Start(bool* aRetVal);
+ NS_IMETHOD ReOpen();
+ NS_IMETHOD Enable();
+
+private:
+ bool mCanShowUI;
+};
+
+NS_IMETHODIMP
+nsNativeAppSupportCocoa::Enable()
+{
+ mCanShowUI = true;
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsNativeAppSupportCocoa::Start(bool *_retval)
+{
+ int major, minor, bugfix;
+ nsCocoaFeatures::GetSystemVersion(major, minor, bugfix);
+
+ NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
+
+ // Check that the OS version is supported, if not return false,
+ // which will make the browser quit. In principle we could display an
+ // alert here. But the alert's message and buttons would require custom
+ // localization. So (for now at least) we just log an English message
+ // to the console before quitting.
+ if (major < 10 || minor < 6) {
+ NSLog(@"Minimum OS version requirement not met!");
+ return NS_OK;
+ }
+
+ *_retval = true;
+ return NS_OK;
+
+ NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
+}
+
+NS_IMETHODIMP
+nsNativeAppSupportCocoa::ReOpen()
+{
+ NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
+
+ if (!mCanShowUI)
+ return NS_ERROR_FAILURE;
+
+ bool haveNonMiniaturized = false;
+ bool haveOpenWindows = false;
+ bool done = false;
+
+ nsCOMPtr<nsIWindowMediator>
+ wm(do_GetService(NS_WINDOWMEDIATOR_CONTRACTID));
+ if (!wm) {
+ return NS_ERROR_FAILURE;
+ }
+ else {
+ nsCOMPtr<nsISimpleEnumerator> windowList;
+ wm->GetXULWindowEnumerator(nullptr, getter_AddRefs(windowList));
+ bool more;
+ windowList->HasMoreElements(&more);
+ while (more) {
+ nsCOMPtr<nsISupports> nextWindow = nullptr;
+ windowList->GetNext(getter_AddRefs(nextWindow));
+ nsCOMPtr<nsIBaseWindow> baseWindow(do_QueryInterface(nextWindow));
+ if (!baseWindow) {
+ windowList->HasMoreElements(&more);
+ continue;
+ }
+ else {
+ haveOpenWindows = true;
+ }
+
+ nsCOMPtr<nsIWidget> widget = nullptr;
+ baseWindow->GetMainWidget(getter_AddRefs(widget));
+ if (!widget) {
+ windowList->HasMoreElements(&more);
+ continue;
+ }
+ NSWindow *cocoaWindow = (NSWindow*)widget->GetNativeData(NS_NATIVE_WINDOW);
+ if (![cocoaWindow isMiniaturized]) {
+ haveNonMiniaturized = true;
+ break; //have un-minimized windows, nothing to do
+ }
+ windowList->HasMoreElements(&more);
+ } // end while
+
+ if (!haveNonMiniaturized) {
+ // Deminiaturize the most recenty used window
+ nsCOMPtr<mozIDOMWindowProxy> mru;
+ wm->GetMostRecentWindow(nullptr, getter_AddRefs(mru));
+
+ if (mru) {
+ NSWindow *cocoaMru = nil;
+ GetNativeWindowPointerFromDOMWindow(mru, &cocoaMru);
+ if (cocoaMru) {
+ [cocoaMru deminiaturize:nil];
+ done = true;
+ }
+ }
+ } // end if have non miniaturized
+
+ if (!haveOpenWindows && !done) {
+ char* argv[] = { nullptr };
+
+ // use an empty command line to make the right kind(s) of window open
+ nsCOMPtr<nsICommandLineRunner> cmdLine
+ (do_CreateInstance("@mozilla.org/toolkit/command-line;1"));
+ NS_ENSURE_TRUE(cmdLine, NS_ERROR_FAILURE);
+
+ nsresult rv;
+ rv = cmdLine->Init(0, argv, nullptr,
+ nsICommandLine::STATE_REMOTE_EXPLICIT);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ return cmdLine->Run();
+ }
+
+ } // got window mediator
+ return NS_OK;
+
+ NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
+}
+
+#pragma mark -
+
+// Create and return an instance of class nsNativeAppSupportCocoa.
+nsresult NS_CreateNativeAppSupport(nsINativeAppSupport**aResult)
+{
+ *aResult = new nsNativeAppSupportCocoa;
+ if (!*aResult) return NS_ERROR_OUT_OF_MEMORY;
+
+ NS_ADDREF(*aResult);
+ return NS_OK;
+}
diff --git a/toolkit/xre/nsNativeAppSupportDefault.cpp b/toolkit/xre/nsNativeAppSupportDefault.cpp
new file mode 100644
index 000000000..1b6c3f487
--- /dev/null
+++ b/toolkit/xre/nsNativeAppSupportDefault.cpp
@@ -0,0 +1,17 @@
+/* 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 "nsNativeAppSupportBase.h"
+
+nsresult
+NS_CreateNativeAppSupport( nsINativeAppSupport **aResult )
+{
+ nsNativeAppSupportBase* native = new nsNativeAppSupportBase();
+ if (!native) return NS_ERROR_OUT_OF_MEMORY;
+
+ *aResult = native;
+ NS_ADDREF( *aResult );
+
+ return NS_OK;
+}
diff --git a/toolkit/xre/nsNativeAppSupportUnix.cpp b/toolkit/xre/nsNativeAppSupportUnix.cpp
new file mode 100644
index 000000000..a04a79953
--- /dev/null
+++ b/toolkit/xre/nsNativeAppSupportUnix.cpp
@@ -0,0 +1,708 @@
+/* -*- 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/. */
+
+#include "nsNativeAppSupportBase.h"
+#include "nsCOMPtr.h"
+#include "nsXPCOM.h"
+#include "nsISupportsPrimitives.h"
+#include "nsIObserverService.h"
+#include "nsIAppStartup.h"
+#include "nsServiceManagerUtils.h"
+#include "prlink.h"
+#include "nsXREDirProvider.h"
+#include "nsReadableUtils.h"
+
+#include "nsIFile.h"
+#include "nsDirectoryServiceDefs.h"
+#include "nsICommandLineRunner.h"
+#include "nsIWindowMediator.h"
+#include "nsPIDOMWindow.h"
+#include "nsIDocShell.h"
+#include "nsIBaseWindow.h"
+#include "nsIWidget.h"
+#include "nsIWritablePropertyBag2.h"
+#include "nsIPrefService.h"
+#include "mozilla/Services.h"
+
+#include <stdlib.h>
+#include <glib.h>
+#include <glib-object.h>
+#include <gtk/gtk.h>
+
+#ifdef MOZ_X11
+#include <gdk/gdkx.h>
+#include <X11/ICE/ICElib.h>
+#include <X11/SM/SMlib.h>
+#include <fcntl.h>
+#include "nsThreadUtils.h"
+
+#include <pwd.h>
+#endif
+
+#ifdef MOZ_ENABLE_DBUS
+#include <dbus/dbus.h>
+#endif
+
+#define MIN_GTK_MAJOR_VERSION 2
+#define MIN_GTK_MINOR_VERSION 10
+#define UNSUPPORTED_GTK_MSG "We're sorry, this application requires a version of the GTK+ library that is not installed on your computer.\n\n\
+You have GTK+ %d.%d.\nThis application requires GTK+ %d.%d or newer.\n\n\
+Please upgrade your GTK+ library if you wish to use this application."
+
+#if MOZ_X11
+#undef IceSetIOErrorHandler
+#undef IceAddConnectionWatch
+#undef IceConnectionNumber
+#undef IceProcessMessages
+#undef IceGetConnectionContext
+#undef SmcInteractDone
+#undef SmcSaveYourselfDone
+#undef SmcInteractRequest
+#undef SmcCloseConnection
+#undef SmcOpenConnection
+#undef SmcSetProperties
+
+typedef IceIOErrorHandler (*IceSetIOErrorHandlerFn) (IceIOErrorHandler);
+typedef int (*IceAddConnectionWatchFn) (IceWatchProc, IcePointer);
+typedef int (*IceConnectionNumberFn) (IceConn);
+typedef IceProcessMessagesStatus (*IceProcessMessagesFn) (IceConn, IceReplyWaitInfo*, Bool*);
+typedef IcePointer (*IceGetConnectionContextFn) (IceConn);
+
+typedef void (*SmcInteractDoneFn) (SmcConn, Bool);
+typedef void (*SmcSaveYourselfDoneFn) (SmcConn, Bool);
+typedef int (*SmcInteractRequestFn) (SmcConn, int, SmcInteractProc, SmPointer);
+typedef SmcCloseStatus (*SmcCloseConnectionFn) (SmcConn, int, char**);
+typedef SmcConn (*SmcOpenConnectionFn) (char*, SmPointer, int, int,
+ unsigned long, SmcCallbacks*,
+ const char*, char**, int, char*);
+typedef void (*SmcSetPropertiesFn) (SmcConn, int, SmProp**);
+
+static IceSetIOErrorHandlerFn IceSetIOErrorHandlerPtr;
+static IceAddConnectionWatchFn IceAddConnectionWatchPtr;
+static IceConnectionNumberFn IceConnectionNumberPtr;
+static IceProcessMessagesFn IceProcessMessagesPtr;
+static IceGetConnectionContextFn IceGetConnectionContextPtr;
+static SmcInteractDoneFn SmcInteractDonePtr;
+static SmcSaveYourselfDoneFn SmcSaveYourselfDonePtr;
+static SmcInteractRequestFn SmcInteractRequestPtr;
+static SmcCloseConnectionFn SmcCloseConnectionPtr;
+static SmcOpenConnectionFn SmcOpenConnectionPtr;
+static SmcSetPropertiesFn SmcSetPropertiesPtr;
+
+#define IceSetIOErrorHandler IceSetIOErrorHandlerPtr
+#define IceAddConnectionWatch IceAddConnectionWatchPtr
+#define IceConnectionNumber IceConnectionNumberPtr
+#define IceProcessMessages IceProcessMessagesPtr
+#define IceGetConnectionContext IceGetConnectionContextPtr
+#define SmcInteractDone SmcInteractDonePtr
+#define SmcSaveYourselfDone SmcSaveYourselfDonePtr
+#define SmcInteractRequest SmcInteractRequestPtr
+#define SmcCloseConnection SmcCloseConnectionPtr
+#define SmcOpenConnection SmcOpenConnectionPtr
+#define SmcSetProperties SmcSetPropertiesPtr
+
+enum ClientState {
+ STATE_DISCONNECTED,
+ STATE_REGISTERING,
+ STATE_IDLE,
+ STATE_INTERACTING,
+ STATE_SHUTDOWN_CANCELLED
+};
+
+static const char *gClientStateTable[] = {
+ "DISCONNECTED",
+ "REGISTERING",
+ "IDLE",
+ "INTERACTING",
+ "SHUTDOWN_CANCELLED"
+};
+
+static LazyLogModule sMozSMLog("MozSM");
+#endif /* MOZ_X11 */
+
+class nsNativeAppSupportUnix : public nsNativeAppSupportBase
+{
+public:
+#if MOZ_X11
+ nsNativeAppSupportUnix(): mSessionConnection(nullptr),
+ mClientState(STATE_DISCONNECTED) {};
+ ~nsNativeAppSupportUnix()
+ {
+ // this goes out of scope after "web-workers-shutdown" async shutdown phase
+ // so it's safe to disconnect here (i.e. the application won't lose data)
+ DisconnectFromSM();
+ };
+
+ void DisconnectFromSM();
+#endif
+ NS_IMETHOD Start(bool* aRetVal);
+ NS_IMETHOD Stop(bool *aResult);
+ NS_IMETHOD Enable();
+
+private:
+#if MOZ_X11
+ static void SaveYourselfCB(SmcConn smc_conn, SmPointer client_data,
+ int save_style, Bool shutdown, int interact_style,
+ Bool fast);
+ static void DieCB(SmcConn smc_conn, SmPointer client_data);
+ static void InteractCB(SmcConn smc_conn, SmPointer client_data);
+ static void SaveCompleteCB(SmcConn smc_conn, SmPointer client_data) {};
+ static void ShutdownCancelledCB(SmcConn smc_conn, SmPointer client_data);
+ void DoInteract();
+ void SetClientState(ClientState aState)
+ {
+ mClientState = aState;
+ MOZ_LOG(sMozSMLog, LogLevel::Debug, ("New state = %s\n", gClientStateTable[aState]));
+ }
+
+ SmcConn mSessionConnection;
+ ClientState mClientState;
+#endif
+};
+
+#if MOZ_X11
+static gboolean
+process_ice_messages(IceConn connection)
+{
+ IceProcessMessagesStatus status;
+
+ status = IceProcessMessages(connection, nullptr, nullptr);
+
+ switch (status) {
+ case IceProcessMessagesSuccess:
+ return TRUE;
+
+ case IceProcessMessagesIOError: {
+ nsNativeAppSupportUnix *native =
+ static_cast<nsNativeAppSupportUnix *>(IceGetConnectionContext(connection));
+ native->DisconnectFromSM();
+ }
+ return FALSE;
+
+ case IceProcessMessagesConnectionClosed:
+ return FALSE;
+
+ default:
+ g_assert_not_reached ();
+ }
+}
+
+static gboolean
+ice_iochannel_watch(GIOChannel *channel, GIOCondition condition,
+ gpointer client_data)
+{
+ return process_ice_messages(static_cast<IceConn>(client_data));
+}
+
+static void
+ice_connection_watch(IceConn connection, IcePointer client_data,
+ Bool opening, IcePointer *watch_data)
+{
+ guint watch_id;
+
+ if (opening) {
+ GIOChannel *channel;
+ int fd = IceConnectionNumber(connection);
+
+ fcntl(fd, F_SETFD, fcntl(fd, F_GETFD, 0) | FD_CLOEXEC);
+ channel = g_io_channel_unix_new(fd);
+ watch_id = g_io_add_watch(channel,
+ static_cast<GIOCondition>(G_IO_IN | G_IO_ERR),
+ ice_iochannel_watch, connection);
+ g_io_channel_unref(channel);
+
+ *watch_data = GUINT_TO_POINTER(watch_id);
+ } else {
+ watch_id = GPOINTER_TO_UINT(*watch_data);
+ g_source_remove(watch_id);
+ }
+}
+
+static void
+ice_io_error_handler(IceConn connection)
+{
+ // override the default handler which would exit the application;
+ // do nothing and let ICELib handle the failure of the connection gracefully.
+}
+
+static void
+ice_init(void)
+{
+ static bool initted = false;
+
+ if (!initted) {
+ IceSetIOErrorHandler(ice_io_error_handler);
+ IceAddConnectionWatch(ice_connection_watch, nullptr);
+ initted = true;
+ }
+}
+
+void
+nsNativeAppSupportUnix::InteractCB(SmcConn smc_conn, SmPointer client_data)
+{
+ nsNativeAppSupportUnix *self =
+ static_cast<nsNativeAppSupportUnix *>(client_data);
+
+ self->SetClientState(STATE_INTERACTING);
+
+ // We do this asynchronously, as we spin the event loop recursively if
+ // a dialog is displayed. If we do this synchronously, we don't finish
+ // processing the current ICE event whilst the dialog is displayed, which
+ // means we won't process any more. libsm hates us if we do the InteractDone
+ // with a pending ShutdownCancelled, and we would certainly like to handle Die
+ // whilst a dialog is displayed
+ NS_DispatchToCurrentThread(NewRunnableMethod(self, &nsNativeAppSupportUnix::DoInteract));
+}
+
+void
+nsNativeAppSupportUnix::DoInteract()
+{
+ nsCOMPtr<nsIObserverService> obsServ =
+ mozilla::services::GetObserverService();
+ if (!obsServ) {
+ SmcInteractDone(mSessionConnection, False);
+ SmcSaveYourselfDone(mSessionConnection, True);
+ SetClientState(STATE_IDLE);
+ return;
+ }
+
+ nsCOMPtr<nsISupportsPRBool> cancelQuit =
+ do_CreateInstance(NS_SUPPORTS_PRBOOL_CONTRACTID);
+
+ bool abortQuit = false;
+ if (cancelQuit) {
+ cancelQuit->SetData(false);
+ obsServ->NotifyObservers(cancelQuit, "quit-application-requested", nullptr);
+
+ cancelQuit->GetData(&abortQuit);
+ }
+
+ if (!abortQuit && mClientState == STATE_DISCONNECTED) {
+ // The session manager disappeared, whilst we were interacting, so
+ // quit now
+ nsCOMPtr<nsIAppStartup> appService =
+ do_GetService("@mozilla.org/toolkit/app-startup;1");
+
+ if (appService) {
+ appService->Quit(nsIAppStartup::eForceQuit);
+ }
+ } else {
+ if (mClientState != STATE_SHUTDOWN_CANCELLED) {
+ // Only do this if the shutdown wasn't cancelled
+ SmcInteractDone(mSessionConnection, !!abortQuit);
+ SmcSaveYourselfDone(mSessionConnection, !abortQuit);
+ }
+
+ SetClientState(STATE_IDLE);
+ }
+}
+
+void
+nsNativeAppSupportUnix::SaveYourselfCB(SmcConn smc_conn, SmPointer client_data,
+ int save_style, Bool shutdown,
+ int interact_style, Bool fast)
+{
+ nsNativeAppSupportUnix *self =
+ static_cast<nsNativeAppSupportUnix *>(client_data);
+
+ // Expect a SaveYourselfCB if we're registering a new client.
+ // All properties are already set in Start() so just reply with
+ // SmcSaveYourselfDone if the callback matches the expected signature.
+ //
+ // Ancient versions (?) of xsm do not follow such an early SaveYourself with
+ // SaveComplete. This is a problem if the application freezes interaction
+ // while waiting for a response to SmcSaveYourselfDone. So never freeze
+ // interaction when in STATE_REGISTERING.
+ //
+ // That aside, we could treat each combination of flags appropriately and not
+ // special-case this.
+ if (self->mClientState == STATE_REGISTERING) {
+ self->SetClientState(STATE_IDLE);
+
+ if (save_style == SmSaveLocal && interact_style == SmInteractStyleNone &&
+ !shutdown && !fast) {
+ SmcSaveYourselfDone(self->mSessionConnection, True);
+ return;
+ }
+ }
+
+ if (self->mClientState == STATE_SHUTDOWN_CANCELLED) {
+ // The last shutdown request was cancelled whilst we were interacting,
+ // and we haven't finished interacting yet. Switch the state back again
+ self->SetClientState(STATE_INTERACTING);
+ }
+
+ nsCOMPtr<nsIObserverService> obsServ =
+ mozilla::services::GetObserverService();
+ if (!obsServ) {
+ SmcSaveYourselfDone(smc_conn, True);
+ return;
+ }
+
+ bool status = false;
+ if (save_style != SmSaveGlobal) {
+ nsCOMPtr<nsISupportsPRBool> didSaveSession =
+ do_CreateInstance(NS_SUPPORTS_PRBOOL_CONTRACTID);
+
+ if (!didSaveSession) {
+ SmcSaveYourselfDone(smc_conn, True);
+ return;
+ }
+
+ // Notify observers to save the session state
+ didSaveSession->SetData(false);
+ obsServ->NotifyObservers(didSaveSession, "session-save", nullptr);
+
+ didSaveSession->GetData(&status);
+ }
+
+ // If the interact style permits us to, we are shutting down and we didn't
+ // manage to (or weren't asked to) save the local state, then notify the user
+ // in advance that we are doing to quit (assuming that we aren't already
+ // doing so)
+ if (!status && shutdown && interact_style != SmInteractStyleNone) {
+ if (self->mClientState != STATE_INTERACTING) {
+ SmcInteractRequest(smc_conn, SmDialogNormal,
+ nsNativeAppSupportUnix::InteractCB, client_data);
+ }
+ } else {
+ SmcSaveYourselfDone(smc_conn, True);
+ }
+}
+
+void
+nsNativeAppSupportUnix::DieCB(SmcConn smc_conn, SmPointer client_data)
+{
+ nsCOMPtr<nsIAppStartup> appService =
+ do_GetService("@mozilla.org/toolkit/app-startup;1");
+
+ if (appService) {
+ appService->Quit(nsIAppStartup::eForceQuit);
+ }
+ // Quit causes the shutdown to begin but the shutdown process is asynchronous
+ // so we can't DisconnectFromSM() yet
+}
+
+void
+nsNativeAppSupportUnix::ShutdownCancelledCB(SmcConn smc_conn,
+ SmPointer client_data)
+{
+ nsNativeAppSupportUnix *self =
+ static_cast<nsNativeAppSupportUnix *>(client_data);
+
+ // Interacting is the only time when we wouldn't already have called
+ // SmcSaveYourselfDone. Do that now, then set the state to make sure we
+ // don't send it again after finishing interacting
+ if (self->mClientState == STATE_INTERACTING) {
+ SmcSaveYourselfDone(smc_conn, False);
+ self->SetClientState(STATE_SHUTDOWN_CANCELLED);
+ }
+}
+
+void
+nsNativeAppSupportUnix::DisconnectFromSM()
+{
+ // the SM is free to exit any time after we disconnect, so callers must be
+ // sure to have reached a sufficiently advanced phase of shutdown that there
+ // is no risk of data loss:
+ // e.g. all async writes are complete by the end of "profile-before-change"
+ if (mSessionConnection) {
+ SetClientState(STATE_DISCONNECTED);
+ SmcCloseConnection(mSessionConnection, 0, nullptr);
+ mSessionConnection = nullptr;
+ gdk_x11_set_sm_client_id(nullptr); // follow gnome-client behaviour
+ }
+}
+
+static void
+SetSMValue(SmPropValue& val, const nsCString& data)
+{
+ val.value = static_cast<SmPointer>(const_cast<char*>(data.get()));
+ val.length = data.Length();
+}
+
+static void
+SetSMProperty(SmProp& prop, const char* name, const char* type, int numVals,
+ SmPropValue vals[])
+{
+ prop.name = const_cast<char*>(name);
+ prop.type = const_cast<char*>(type);
+ prop.num_vals = numVals;
+ prop.vals = vals;
+}
+#endif /* MOZ_X11 */
+
+static void RemoveArg(char **argv)
+{
+ do {
+ *argv = *(argv + 1);
+ ++argv;
+ } while (*argv);
+
+ --gArgc;
+}
+
+NS_IMETHODIMP
+nsNativeAppSupportUnix::Start(bool *aRetVal)
+{
+ NS_ASSERTION(gAppData, "gAppData must not be null.");
+
+// The dbus library is used by both nsWifiScannerDBus and BluetoothDBusService,
+// from diffrent threads. This could lead to race conditions if the dbus is not
+// initialized before making any other library calls.
+#ifdef MOZ_ENABLE_DBUS
+ dbus_threads_init_default();
+#endif
+
+#if (MOZ_WIDGET_GTK == 2)
+ if (gtk_major_version < MIN_GTK_MAJOR_VERSION ||
+ (gtk_major_version == MIN_GTK_MAJOR_VERSION && gtk_minor_version < MIN_GTK_MINOR_VERSION)) {
+ GtkWidget* versionErrDialog = gtk_message_dialog_new(nullptr,
+ GtkDialogFlags(GTK_DIALOG_MODAL |
+ GTK_DIALOG_DESTROY_WITH_PARENT),
+ GTK_MESSAGE_ERROR,
+ GTK_BUTTONS_OK,
+ UNSUPPORTED_GTK_MSG,
+ gtk_major_version,
+ gtk_minor_version,
+ MIN_GTK_MAJOR_VERSION,
+ MIN_GTK_MINOR_VERSION);
+ gtk_dialog_run(GTK_DIALOG(versionErrDialog));
+ gtk_widget_destroy(versionErrDialog);
+ MozExpectedExit();
+ exit(0);
+ }
+#endif
+
+ *aRetVal = true;
+
+#ifdef MOZ_X11
+ gboolean sm_disable = FALSE;
+ if (!getenv("SESSION_MANAGER")) {
+ sm_disable = TRUE;
+ }
+
+ nsAutoCString prev_client_id;
+
+ char **curarg = gArgv + 1;
+ while (*curarg) {
+ char *arg = *curarg;
+ if (arg[0] == '-' && arg[1] == '-') {
+ arg += 2;
+ if (!strcmp(arg, "sm-disable")) {
+ RemoveArg(curarg);
+ sm_disable = TRUE;
+ continue;
+ } else if (!strcmp(arg, "sm-client-id")) {
+ RemoveArg(curarg);
+ if (*curarg[0] != '-') {
+ prev_client_id = *curarg;
+ RemoveArg(curarg);
+ }
+ continue;
+ }
+ }
+
+ ++curarg;
+ }
+
+ if (prev_client_id.IsEmpty()) {
+ prev_client_id = getenv("DESKTOP_AUTOSTART_ID");
+ }
+
+ // We don't want child processes to use the same ID
+ unsetenv("DESKTOP_AUTOSTART_ID");
+
+ char *client_id = nullptr;
+ if (!sm_disable) {
+ PRLibrary *iceLib = PR_LoadLibrary("libICE.so.6");
+ if (!iceLib) {
+ return NS_OK;
+ }
+
+ PRLibrary *smLib = PR_LoadLibrary("libSM.so.6");
+ if (!smLib) {
+ PR_UnloadLibrary(iceLib);
+ return NS_OK;
+ }
+
+ IceSetIOErrorHandler = (IceSetIOErrorHandlerFn)PR_FindFunctionSymbol(iceLib, "IceSetIOErrorHandler");
+ IceAddConnectionWatch = (IceAddConnectionWatchFn)PR_FindFunctionSymbol(iceLib, "IceAddConnectionWatch");
+ IceConnectionNumber = (IceConnectionNumberFn)PR_FindFunctionSymbol(iceLib, "IceConnectionNumber");
+ IceProcessMessages = (IceProcessMessagesFn)PR_FindFunctionSymbol(iceLib, "IceProcessMessages");
+ IceGetConnectionContext = (IceGetConnectionContextFn)PR_FindFunctionSymbol(iceLib, "IceGetConnectionContext");
+ if (!IceSetIOErrorHandler || !IceAddConnectionWatch ||
+ !IceConnectionNumber || !IceProcessMessages || !IceGetConnectionContext) {
+ PR_UnloadLibrary(iceLib);
+ PR_UnloadLibrary(smLib);
+ return NS_OK;
+ }
+
+ SmcInteractDone = (SmcInteractDoneFn)PR_FindFunctionSymbol(smLib, "SmcInteractDone");
+ SmcSaveYourselfDone = (SmcSaveYourselfDoneFn)PR_FindFunctionSymbol(smLib, "SmcSaveYourselfDone");
+ SmcInteractRequest = (SmcInteractRequestFn)PR_FindFunctionSymbol(smLib, "SmcInteractRequest");
+ SmcCloseConnection = (SmcCloseConnectionFn)PR_FindFunctionSymbol(smLib, "SmcCloseConnection");
+ SmcOpenConnection = (SmcOpenConnectionFn)PR_FindFunctionSymbol(smLib, "SmcOpenConnection");
+ SmcSetProperties = (SmcSetPropertiesFn)PR_FindFunctionSymbol(smLib, "SmcSetProperties");
+ if (!SmcInteractDone || !SmcSaveYourselfDone || !SmcInteractRequest ||
+ !SmcCloseConnection || !SmcOpenConnection || !SmcSetProperties) {
+ PR_UnloadLibrary(iceLib);
+ PR_UnloadLibrary(smLib);
+ return NS_OK;
+ }
+
+ ice_init();
+
+ // all callbacks are mandatory in libSM 1.0, so listen even if we don't care.
+ unsigned long mask = SmcSaveYourselfProcMask | SmcDieProcMask |
+ SmcSaveCompleteProcMask | SmcShutdownCancelledProcMask;
+
+ SmcCallbacks callbacks;
+ callbacks.save_yourself.callback = nsNativeAppSupportUnix::SaveYourselfCB;
+ callbacks.save_yourself.client_data = static_cast<SmPointer>(this);
+
+ callbacks.die.callback = nsNativeAppSupportUnix::DieCB;
+ callbacks.die.client_data = static_cast<SmPointer>(this);
+
+ callbacks.save_complete.callback = nsNativeAppSupportUnix::SaveCompleteCB;
+ callbacks.save_complete.client_data = nullptr;
+
+ callbacks.shutdown_cancelled.callback =
+ nsNativeAppSupportUnix::ShutdownCancelledCB;
+ callbacks.shutdown_cancelled.client_data = static_cast<SmPointer>(this);
+
+ char errbuf[256];
+ mSessionConnection = SmcOpenConnection(nullptr, this, SmProtoMajor,
+ SmProtoMinor, mask, &callbacks,
+ prev_client_id.get(), &client_id,
+ sizeof(errbuf), errbuf);
+ }
+
+ if (!mSessionConnection) {
+ return NS_OK;
+ }
+
+ LogModule::Init(); // need to make sure initialized before SetClientState
+ if (prev_client_id.IsEmpty() ||
+ (client_id && !prev_client_id.Equals(client_id))) {
+ SetClientState(STATE_REGISTERING);
+ } else {
+ SetClientState(STATE_IDLE);
+ }
+
+ gdk_x11_set_sm_client_id(client_id);
+
+ // Set SM Properties
+ // SmCloneCommand, SmProgram, SmRestartCommand, SmUserID are required
+ // properties so must be set, and must have a sensible fallback value.
+
+ // Determine executable path to use for XSMP session restore
+
+ // Is there a request to suppress default binary launcher?
+ nsAutoCString path(getenv("MOZ_APP_LAUNCHER"));
+
+ if (path.IsEmpty()) {
+ NS_ASSERTION(gDirServiceProvider, "gDirServiceProvider is NULL! This shouldn't happen!");
+ nsCOMPtr<nsIFile> executablePath;
+ nsresult rv;
+
+ bool dummy;
+ rv = gDirServiceProvider->GetFile(XRE_EXECUTABLE_FILE, &dummy, getter_AddRefs(executablePath));
+
+ if (NS_SUCCEEDED(rv)) {
+ // Strip off the -bin suffix to get the shell script we should run; this is what Breakpad does
+ nsAutoCString leafName;
+ rv = executablePath->GetNativeLeafName(leafName);
+ if (NS_SUCCEEDED(rv) && StringEndsWith(leafName, NS_LITERAL_CSTRING("-bin"))) {
+ leafName.SetLength(leafName.Length() - strlen("-bin"));
+ executablePath->SetNativeLeafName(leafName);
+ }
+
+ executablePath->GetNativePath(path);
+ }
+ }
+
+ if (path.IsEmpty()) {
+ // can't determine executable path. Best fallback is name from
+ // application.ini but it might not resolve to the same executable at
+ // launch time.
+ path = gAppData->name; // will always be set
+ ToLowerCase(path);
+ MOZ_LOG(sMozSMLog, LogLevel::Warning,
+ ("Could not determine executable path. Falling back to %s.", path.get()));
+ }
+
+ SmProp propRestart, propClone, propProgram, propUser, *props[4];
+ SmPropValue valsRestart[3], valsClone[1], valsProgram[1], valsUser[1];
+ int n = 0;
+
+ NS_NAMED_LITERAL_CSTRING(kClientIDParam, "--sm-client-id");
+
+ SetSMValue(valsRestart[0], path);
+ SetSMValue(valsRestart[1], kClientIDParam);
+ SetSMValue(valsRestart[2], nsDependentCString(client_id));
+ SetSMProperty(propRestart, SmRestartCommand, SmLISTofARRAY8, 3, valsRestart);
+ props[n++] = &propRestart;
+
+ SetSMValue(valsClone[0], path);
+ SetSMProperty(propClone, SmCloneCommand, SmLISTofARRAY8, 1, valsClone);
+ props[n++] = &propClone;
+
+ nsAutoCString appName(gAppData->name); // will always be set
+ ToLowerCase(appName);
+
+ SetSMValue(valsProgram[0], appName);
+ SetSMProperty(propProgram, SmProgram, SmARRAY8, 1, valsProgram);
+ props[n++] = &propProgram;
+
+ nsAutoCString userName; // username that started the program
+ struct passwd* pw = getpwuid(getuid());
+ if (pw && pw->pw_name) {
+ userName = pw->pw_name;
+ } else {
+ userName = NS_LITERAL_CSTRING("nobody");
+ MOZ_LOG(sMozSMLog, LogLevel::Warning,
+ ("Could not determine user-name. Falling back to %s.", userName.get()));
+ }
+
+ SetSMValue(valsUser[0], userName);
+ SetSMProperty(propUser, SmUserID, SmARRAY8, 1, valsUser);
+ props[n++] = &propUser;
+
+ SmcSetProperties(mSessionConnection, n, props);
+
+ g_free(client_id);
+#endif /* MOZ_X11 */
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsNativeAppSupportUnix::Stop(bool *aResult)
+{
+ NS_ENSURE_ARG(aResult);
+ *aResult = true;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsNativeAppSupportUnix::Enable()
+{
+ return NS_OK;
+}
+
+nsresult
+NS_CreateNativeAppSupport(nsINativeAppSupport **aResult)
+{
+ nsNativeAppSupportBase* native = new nsNativeAppSupportUnix();
+ if (!native)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ *aResult = native;
+ NS_ADDREF(*aResult);
+
+ return NS_OK;
+}
diff --git a/toolkit/xre/nsNativeAppSupportWin.cpp b/toolkit/xre/nsNativeAppSupportWin.cpp
new file mode 100644
index 000000000..0605ccb5a
--- /dev/null
+++ b/toolkit/xre/nsNativeAppSupportWin.cpp
@@ -0,0 +1,1541 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* 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 "nsNativeAppSupportBase.h"
+#include "nsNativeAppSupportWin.h"
+#include "nsAppRunner.h"
+#include "nsXULAppAPI.h"
+#include "nsString.h"
+#include "nsIBrowserDOMWindow.h"
+#include "nsICommandLineRunner.h"
+#include "nsCOMPtr.h"
+#include "nsXPIDLString.h"
+#include "nsIComponentManager.h"
+#include "nsIServiceManager.h"
+#include "nsIDOMChromeWindow.h"
+#include "nsXPCOM.h"
+#include "nsISupportsPrimitives.h"
+#include "nsIWindowWatcher.h"
+#include "nsPIDOMWindow.h"
+#include "nsGlobalWindow.h"
+#include "nsIDocShell.h"
+#include "nsIDocShellTreeItem.h"
+#include "nsIBaseWindow.h"
+#include "nsIWidget.h"
+#include "nsIAppShellService.h"
+#include "nsIXULWindow.h"
+#include "nsIInterfaceRequestor.h"
+#include "nsIInterfaceRequestorUtils.h"
+#include "nsIPromptService.h"
+#include "nsNetCID.h"
+#include "nsNetUtil.h"
+#include "mozilla/Services.h"
+#include "nsIFile.h"
+#include "nsIObserver.h"
+#include "nsIObserverService.h"
+#include "nsIDOMLocation.h"
+#include "nsIWebNavigation.h"
+#include "nsIWindowMediator.h"
+#include "nsNativeCharsetUtils.h"
+#include "nsIAppStartup.h"
+
+#include <windows.h>
+#include <shellapi.h>
+#include <ddeml.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <io.h>
+#include <direct.h>
+#include <fcntl.h>
+
+using namespace mozilla;
+
+static HWND hwndForDOMWindow( mozIDOMWindowProxy * );
+
+static
+nsresult
+GetMostRecentWindow(const char16_t* aType, mozIDOMWindowProxy** aWindow) {
+ nsresult rv;
+ nsCOMPtr<nsIWindowMediator> med( do_GetService( NS_WINDOWMEDIATOR_CONTRACTID, &rv ) );
+ if ( NS_FAILED( rv ) )
+ return rv;
+
+ if ( med )
+ return med->GetMostRecentWindow( aType, aWindow );
+
+ return NS_ERROR_FAILURE;
+}
+
+static
+void
+activateWindow( mozIDOMWindowProxy *win ) {
+ // Try to get native window handle.
+ HWND hwnd = hwndForDOMWindow( win );
+ if ( hwnd ) {
+ // Restore the window if it is minimized.
+ if ( ::IsIconic( hwnd ) ) {
+ ::ShowWindow( hwnd, SW_RESTORE );
+ }
+ // Use the OS call, if possible.
+ ::SetForegroundWindow( hwnd );
+ } else {
+ // Use internal method.
+ nsCOMPtr<nsPIDOMWindowOuter> piWin = nsPIDOMWindowOuter::From(win);
+ piWin->Focus();
+ }
+}
+
+
+#ifdef DEBUG_law
+#undef MOZ_DEBUG_DDE
+#define MOZ_DEBUG_DDE 1
+#endif
+
+// Simple Win32 mutex wrapper.
+struct Win32Mutex {
+ Win32Mutex( const char16_t *name )
+ : mName( name ),
+ mHandle( 0 ),
+ mState( -1 ) {
+ mHandle = CreateMutexW( 0, FALSE, mName.get() );
+#if MOZ_DEBUG_DDE
+ printf( "CreateMutex error = 0x%08X\n", (int)GetLastError() );
+#endif
+ }
+ ~Win32Mutex() {
+ if ( mHandle ) {
+ // Make sure we release it if we own it.
+ Unlock();
+
+ BOOL rc = CloseHandle( mHandle );
+#if MOZ_DEBUG_DDE
+ if ( !rc ) {
+ printf( "CloseHandle error = 0x%08X\n", (int)GetLastError() );
+ }
+#endif
+ }
+ }
+ BOOL Lock( DWORD timeout ) {
+ if ( mHandle ) {
+#if MOZ_DEBUG_DDE
+ printf( "Waiting (%d msec) for DDE mutex...\n", (int)timeout );
+#endif
+ mState = WaitForSingleObject( mHandle, timeout );
+#if MOZ_DEBUG_DDE
+ printf( "...wait complete, result = 0x%08X, GetLastError=0x%08X\n", (int)mState, (int)::GetLastError() );
+#endif
+ return mState == WAIT_OBJECT_0 || mState == WAIT_ABANDONED;
+ } else {
+ return FALSE;
+ }
+ }
+ void Unlock() {
+ if ( mHandle && mState == WAIT_OBJECT_0 ) {
+#if MOZ_DEBUG_DDE
+ printf( "Releasing DDE mutex\n" );
+#endif
+ ReleaseMutex( mHandle );
+ mState = -1;
+ }
+ }
+private:
+ nsString mName;
+ HANDLE mHandle;
+ DWORD mState;
+};
+
+/* DDE Notes
+ *
+ * This section describes the Win32 DDE service implementation for
+ * Mozilla. DDE is used on Win32 platforms to communicate between
+ * separate instances of mozilla.exe (or other Mozilla-based
+ * executables), or, between the Win32 desktop shell and Mozilla.
+ *
+ * The first instance of Mozilla will become the "server" and
+ * subsequent executables (and the shell) will use DDE to send
+ * requests to that process. The requests are DDE "execute" requests
+ * that pass the command line arguments.
+ *
+ * Mozilla registers the DDE application "Mozilla" and currently
+ * supports only the "WWW_OpenURL" topic. This should be reasonably
+ * compatible with applications that interfaced with Netscape
+ * Communicator (and its predecessors?). Note that even that topic
+ * may not be supported in a compatible fashion as the command-line
+ * options for Mozilla are different than for Communiator.
+ *
+ * It is imperative that at most one instance of Mozilla execute in
+ * "server mode" at any one time. The "native app support" in Mozilla
+ * on Win32 ensures that only the server process performs XPCOM
+ * initialization (that is not required for subsequent client processes
+ * to communicate with the server process).
+ *
+ * To guarantee that only one server starts up, a Win32 "mutex" is used
+ * to ensure only one process executes the server-detection code. That
+ * code consists of initializing DDE and doing a DdeConnect to Mozilla's
+ * application/topic. If that connection succeeds, then a server process
+ * must be running already.
+ *
+ * Otherwise, no server has started. In that case, the current process
+ * calls DdeNameService to register that application/topic. Only at that
+ * point does the mutex get released.
+ *
+ * There are a couple of subtleties that one should be aware of:
+ *
+ * 1. It is imperative that DdeInitialize be called only after the mutex
+ * lock has been obtained. The reason is that at shutdown, DDE
+ * notifications go out to all initialized DDE processes. Thus, if
+ * the mutex is owned by a terminating intance of Mozilla, then
+ * calling DdeInitialize and then WaitForSingleObject will cause the
+ * DdeUninitialize from the terminating process to "hang" until the
+ * process waiting for the mutex times out (and can then service the
+ * notification that the DDE server is terminating). So, don't mess
+ * with the sequence of things in the startup/shutdown logic.
+ *
+ * 2. All mutex requests are made with a reasonably long timeout value and
+ * are designed to "fail safe" (i.e., a timeout is treated as failure).
+ *
+ * 3. An attempt has been made to minimize the degree to which the main
+ * Mozilla application logic needs to be aware of the DDE mechanisms
+ * implemented herein. As a result, this module surfaces a very
+ * large-grained interface, consisting of simple start/stop methods.
+ * As a consequence, details of certain scenarios can be "lost."
+ * Particularly, incoming DDE requests can arrive after this module
+ * initiates the DDE server, but before Mozilla is initialized to the
+ * point where those requests can be serviced (e.g., open a browser
+ * window to a particular URL). Since the client process sends the
+ * request early on, it may not be prepared to respond to that error.
+ * Thus, such situations may fail silently. The design goal is that
+ * they fail harmlessly. Refinements on this point will be made as
+ * details emerge (and time permits).
+ */
+
+/* Update 2001 March
+ *
+ * A significant DDE bug in Windows is causing Mozilla to get wedged at
+ * startup. This is detailed in Bugzill bug 53952
+ * (http://bugzilla.mozilla.org/show_bug.cgi?id=53952).
+ *
+ * To resolve this, we are using a new strategy:
+ * o Use a "message window" to detect that Mozilla is already running and
+ * to pass requests from a second instance back to the first;
+ * o Run only as a "DDE server" (not as DDE client); this avoids the
+ * problematic call to DDEConnect().
+ *
+ * We still use the mutex semaphore to protect the code that detects
+ * whether Mozilla is already running.
+ */
+
+/* Update 2007 January
+ *
+ * A change in behavior was implemented in July 2004 which made the
+ * application on launch to add and on quit to remove the ddexec registry key.
+ * See bug 246078.
+ * Windows Vista has changed the methods used to set an application as default
+ * and the new methods are incompatible with removing the ddeexec registry key.
+ * See bug 353089.
+ *
+ * OS DDE Sequence:
+ * 1. OS checks if the dde name is registered.
+ * 2. If it is registered the OS sends a DDE request with the WWW_OpenURL topic
+ * and the params as specified in the default value of the ddeexec registry
+ * key for the verb (e.g. open).
+ * 3. If it isn't registered the OS launches the executable defined in the
+ * verb's (e.g. open) command registry key.
+ * 4. If the ifexec registry key is not present the OS sends a DDE request with
+ * the WWW_OpenURL topic and the params as specified in the default value of
+ * the ddeexec registry key for the verb (e.g. open).
+ * 5. If the ifexec registry key is present the OS sends a DDE request with the
+ * WWW_OpenURL topic and the params as specified in the ifexec registry key
+ * for the verb (e.g. open).
+ *
+ * Application DDE Sequence:
+ * 1. If the application is running a DDE request is received with the
+ * WWW_OpenURL topic and the params as specified in the default value of the
+ * ddeexec registry key (e.g. "%1",,0,0,,,, where '%1' is the url to open)
+ * for the verb (e.g. open).
+ * 2. If the application is not running it is launched with the --requestPending
+ * and the --url argument.
+ * 2.1 If the application does not need to restart and the --requestPending
+ * argument is present the accompanying url will not be used. Instead the
+ * application will wait for the DDE message to open the url.
+ * 2.2 If the application needs to restart the --requestPending argument is
+ * removed from the arguments used to restart the application and the url
+ * will be handled normally.
+ *
+ * Note: Due to a bug in IE the ifexec key should not be used (see bug 355650).
+ */
+
+class nsNativeAppSupportWin : public nsNativeAppSupportBase,
+ public nsIObserver
+{
+public:
+ NS_DECL_NSIOBSERVER
+ NS_DECL_ISUPPORTS_INHERITED
+
+ // Overrides of base implementation.
+ NS_IMETHOD Start( bool *aResult );
+ NS_IMETHOD Stop( bool *aResult );
+ NS_IMETHOD Quit();
+ NS_IMETHOD Enable();
+ // The "old" Start method (renamed).
+ NS_IMETHOD StartDDE();
+ // Utility function to handle a Win32-specific command line
+ // option: "--console", which dynamically creates a Windows
+ // console.
+ void CheckConsole();
+
+private:
+ ~nsNativeAppSupportWin() {}
+ static void HandleCommandLine(const char* aCmdLineString, nsIFile* aWorkingDir, uint32_t aState);
+ static HDDEDATA CALLBACK HandleDDENotification( UINT uType,
+ UINT uFmt,
+ HCONV hconv,
+ HSZ hsz1,
+ HSZ hsz2,
+ HDDEDATA hdata,
+ ULONG_PTR dwData1,
+ ULONG_PTR dwData2 );
+ static void ParseDDEArg( HSZ args, int index, nsString& string);
+ static void ParseDDEArg( const WCHAR* args, int index, nsString& aString);
+ static HDDEDATA CreateDDEData( DWORD value );
+ static HDDEDATA CreateDDEData( LPBYTE value, DWORD len );
+ static bool InitTopicStrings();
+ static int FindTopic( HSZ topic );
+ static void ActivateLastWindow();
+ static nsresult OpenWindow( const char *urlstr, const char *args );
+ static nsresult OpenBrowserWindow();
+ static void SetupSysTrayIcon();
+ static void RemoveSysTrayIcon();
+
+ static int mConversations;
+ enum {
+ topicOpenURL,
+ topicActivate,
+ topicCancelProgress,
+ topicVersion,
+ topicRegisterViewer,
+ topicUnRegisterViewer,
+ topicGetWindowInfo,
+ // Note: Insert new values above this line!!!!!
+ topicCount // Count of the number of real topics
+ };
+ static HSZ mApplication, mTopics[ topicCount ];
+ static DWORD mInstance;
+ static bool mCanHandleRequests;
+ static char16_t mMutexName[];
+ friend struct MessageWindow;
+}; // nsNativeAppSupportWin
+
+NS_INTERFACE_MAP_BEGIN(nsNativeAppSupportWin)
+ NS_INTERFACE_MAP_ENTRY(nsIObserver)
+NS_INTERFACE_MAP_END_INHERITING(nsNativeAppSupportBase)
+
+NS_IMPL_ADDREF_INHERITED(nsNativeAppSupportWin, nsNativeAppSupportBase)
+NS_IMPL_RELEASE_INHERITED(nsNativeAppSupportWin, nsNativeAppSupportBase)
+
+void
+UseParentConsole()
+{
+ // Try to attach console to the parent process.
+ // It will succeed when the parent process is a command line,
+ // so that stdio will be displayed in it.
+ if (AttachConsole(ATTACH_PARENT_PROCESS)) {
+ // Change std handles to refer to new console handles.
+ // Before doing so, ensure that stdout/stderr haven't been
+ // redirected to a valid file.
+ // The return value for _fileno(<a std handle>) for GUI apps was changed over.
+ // Until VC7, it was -1. Starting from VC8, it was changed to -2.
+ // http://msdn.microsoft.com/en-us/library/zs6wbdhx%28v=vs.80%29.aspx
+ // Starting from VC11, the return value was cahnged to 0 for stdin,
+ // 1 for stdout, 2 for stdout. Accroding to Microsoft, this is a bug
+ // which will be fixed in VC14.
+ // https://connect.microsoft.com/VisualStudio/feedback/details/785119/
+ // Although the document does not make it explicit, it looks like
+ // the return value from _get_osfhandle(_fileno(<a std handle>)) also
+ // changed to -2 and VC11 and 12 do not have a bug about _get_osfhandle().
+ // We support VC10 or later, so it's sufficient to compare the return
+ // value with -2.
+ if (_fileno(stdout) == -2 ||
+ _get_osfhandle(fileno(stdout)) == -2)
+ freopen("CONOUT$", "w", stdout);
+ // Merge stderr into CONOUT$ since there isn't any `CONERR$`.
+ // http://msdn.microsoft.com/en-us/library/windows/desktop/ms683231%28v=vs.85%29.aspx
+ if (_fileno(stderr) == -2 ||
+ _get_osfhandle(fileno(stderr)) == -2)
+ freopen("CONOUT$", "w", stderr);
+ if (_fileno(stdin) == -2 || _get_osfhandle(fileno(stdin)) == -2)
+ freopen("CONIN$", "r", stdin);
+ }
+}
+
+void
+nsNativeAppSupportWin::CheckConsole() {
+ for ( int i = 1; i < gArgc; i++ ) {
+ if ( strcmp( "-console", gArgv[i] ) == 0 ||
+ strcmp( "--console", gArgv[i] ) == 0 ||
+ strcmp( "/console", gArgv[i] ) == 0 ) {
+ // Users wants to make sure we have a console.
+ // Try to allocate one.
+ BOOL rc = ::AllocConsole();
+ if ( rc ) {
+ // Console allocated. Fix it up so that output works in
+ // all cases. See http://support.microsoft.com/support/kb/articles/q105/3/05.asp.
+
+ // stdout
+ int hCrt = ::_open_osfhandle( (intptr_t)GetStdHandle( STD_OUTPUT_HANDLE ),
+ _O_TEXT );
+ if ( hCrt != -1 ) {
+ FILE *hf = ::_fdopen( hCrt, "w" );
+ if ( hf ) {
+ *stdout = *hf;
+#ifdef DEBUG
+ ::fprintf( stdout, "stdout directed to dynamic console\n" );
+#endif
+ }
+ }
+
+ // stderr
+ hCrt = ::_open_osfhandle( (intptr_t)::GetStdHandle( STD_ERROR_HANDLE ),
+ _O_TEXT );
+ if ( hCrt != -1 ) {
+ FILE *hf = ::_fdopen( hCrt, "w" );
+ if ( hf ) {
+ *stderr = *hf;
+#ifdef DEBUG
+ ::fprintf( stderr, "stderr directed to dynamic console\n" );
+#endif
+ }
+ }
+
+ // stdin?
+ /* Don't bother for now.
+ hCrt = ::_open_osfhandle( (long)::GetStdHandle( STD_INPUT_HANDLE ),
+ _O_TEXT );
+ if ( hCrt != -1 ) {
+ FILE *hf = ::_fdopen( hCrt, "r" );
+ if ( hf ) {
+ *stdin = *hf;
+ }
+ }
+ */
+ } else {
+ // Failed. Probably because there already is one.
+ // There's little we can do, in any case.
+ }
+ // Remove the console argument from the command line.
+ do {
+ gArgv[i] = gArgv[i + 1];
+ ++i;
+ } while (gArgv[i]);
+
+ --gArgc;
+
+ } else if ( strcmp( "-attach-console", gArgv[i] ) == 0
+ ||
+ strcmp( "/attach-console", gArgv[i] ) == 0 ) {
+ UseParentConsole();
+ }
+ }
+
+ return;
+}
+
+
+// Create and return an instance of class nsNativeAppSupportWin.
+nsresult
+NS_CreateNativeAppSupport( nsINativeAppSupport **aResult ) {
+ nsNativeAppSupportWin *pNative = new nsNativeAppSupportWin;
+ if (!pNative) return NS_ERROR_OUT_OF_MEMORY;
+
+ // Check for dynamic console creation request.
+ pNative->CheckConsole();
+
+ *aResult = pNative;
+ NS_ADDREF( *aResult );
+
+ return NS_OK;
+}
+
+// Constants
+#define MOZ_DDE_APPLICATION "Mozilla"
+#define MOZ_MUTEX_NAMESPACE L"Local\\"
+#define MOZ_STARTUP_MUTEX_NAME L"StartupMutex"
+#define MOZ_DDE_START_TIMEOUT 30000
+#define MOZ_DDE_STOP_TIMEOUT 15000
+#define MOZ_DDE_EXEC_TIMEOUT 15000
+
+// The array entries must match the enum ordering!
+const char * const topicNames[] = { "WWW_OpenURL",
+ "WWW_Activate",
+ "WWW_CancelProgress",
+ "WWW_Version",
+ "WWW_RegisterViewer",
+ "WWW_UnRegisterViewer",
+ "WWW_GetWindowInfo" };
+
+// Static member definitions.
+int nsNativeAppSupportWin::mConversations = 0;
+HSZ nsNativeAppSupportWin::mApplication = 0;
+HSZ nsNativeAppSupportWin::mTopics[nsNativeAppSupportWin::topicCount] = { 0 };
+DWORD nsNativeAppSupportWin::mInstance = 0;
+bool nsNativeAppSupportWin::mCanHandleRequests = false;
+
+char16_t nsNativeAppSupportWin::mMutexName[ 128 ] = { 0 };
+
+
+// Message window encapsulation.
+struct MessageWindow {
+ // ctor/dtor are simplistic
+ MessageWindow() {
+ // Try to find window.
+ mHandle = ::FindWindowW( className(), 0 );
+ }
+
+ // Act like an HWND.
+ operator HWND() {
+ return mHandle;
+ }
+
+ // Class name: appName + "MessageWindow"
+ static const wchar_t *className() {
+ static wchar_t classNameBuffer[128];
+ static wchar_t *mClassName = 0;
+ if ( !mClassName ) {
+ ::_snwprintf(classNameBuffer,
+ 128, // size of classNameBuffer in PRUnichars
+ L"%s%s",
+ static_cast<const wchar_t*>(NS_ConvertUTF8toUTF16(gAppData->remotingName).get()),
+ L"MessageWindow" );
+ mClassName = classNameBuffer;
+ }
+ return mClassName;
+ }
+
+ // Create: Register class and create window.
+ NS_IMETHOD Create() {
+ WNDCLASSW classStruct = { 0, // style
+ &MessageWindow::WindowProc, // lpfnWndProc
+ 0, // cbClsExtra
+ 0, // cbWndExtra
+ 0, // hInstance
+ 0, // hIcon
+ 0, // hCursor
+ 0, // hbrBackground
+ 0, // lpszMenuName
+ className() }; // lpszClassName
+
+ // Register the window class.
+ NS_ENSURE_TRUE( ::RegisterClassW( &classStruct ), NS_ERROR_FAILURE );
+
+ // Create the window.
+ NS_ENSURE_TRUE( ( mHandle = ::CreateWindowW(className(),
+ 0, // title
+ WS_CAPTION, // style
+ 0,0,0,0, // x, y, cx, cy
+ 0, // parent
+ 0, // menu
+ 0, // instance
+ 0 ) ), // create struct
+ NS_ERROR_FAILURE );
+
+#if MOZ_DEBUG_DDE
+ printf( "Message window = 0x%08X\n", (int)mHandle );
+#endif
+
+ return NS_OK;
+ }
+
+ // Destory: Get rid of window and reset mHandle.
+ NS_IMETHOD Destroy() {
+ nsresult retval = NS_OK;
+
+ if ( mHandle ) {
+ // DestroyWindow can only destroy windows created from
+ // the same thread.
+ BOOL desRes = DestroyWindow( mHandle );
+ if ( FALSE != desRes ) {
+ mHandle = nullptr;
+ }
+ else {
+ retval = NS_ERROR_FAILURE;
+ }
+ }
+
+ return retval;
+ }
+
+ // SendRequest: Pass the command line via WM_COPYDATA to message window.
+ NS_IMETHOD SendRequest() {
+ WCHAR *cmd = ::GetCommandLineW();
+ WCHAR cwd[MAX_PATH];
+ _wgetcwd(cwd, MAX_PATH);
+
+ // Construct a narrow UTF8 buffer <commandline>\0<workingdir>\0
+ NS_ConvertUTF16toUTF8 utf8buffer(cmd);
+ utf8buffer.Append('\0');
+ AppendUTF16toUTF8(cwd, utf8buffer);
+ utf8buffer.Append('\0');
+
+ // We used to set dwData to zero, when we didn't send the working dir.
+ // Now we're using it as a version number.
+ COPYDATASTRUCT cds = {
+ 1,
+ utf8buffer.Length(),
+ (void*) utf8buffer.get()
+ };
+ // Bring the already running Mozilla process to the foreground.
+ // nsWindow will restore the window (if minimized) and raise it.
+ ::SetForegroundWindow( mHandle );
+ ::SendMessage( mHandle, WM_COPYDATA, 0, (LPARAM)&cds );
+ return NS_OK;
+ }
+
+ // Window proc.
+ static LRESULT CALLBACK WindowProc( HWND msgWindow, UINT msg, WPARAM wp, LPARAM lp ) {
+ if ( msg == WM_COPYDATA ) {
+ if (!nsNativeAppSupportWin::mCanHandleRequests)
+ return FALSE;
+
+ // This is an incoming request.
+ COPYDATASTRUCT *cds = (COPYDATASTRUCT*)lp;
+#if MOZ_DEBUG_DDE
+ printf( "Incoming request: %s\n", (const char*)cds->lpData );
+#endif
+ nsCOMPtr<nsIFile> workingDir;
+
+ if (1 >= cds->dwData) {
+ char* wdpath = (char*) cds->lpData;
+ // skip the command line, and get the working dir of the
+ // other process, which is after the first null char
+ while (*wdpath)
+ ++wdpath;
+
+ ++wdpath;
+
+#ifdef MOZ_DEBUG_DDE
+ printf( "Working dir: %s\n", wdpath);
+#endif
+
+ NS_NewLocalFile(NS_ConvertUTF8toUTF16(wdpath),
+ false,
+ getter_AddRefs(workingDir));
+ }
+ (void)nsNativeAppSupportWin::HandleCommandLine((char*)cds->lpData, workingDir, nsICommandLine::STATE_REMOTE_AUTO);
+
+ // Get current window and return its window handle.
+ nsCOMPtr<mozIDOMWindowProxy> win;
+ GetMostRecentWindow( 0, getter_AddRefs( win ) );
+ return win ? (LRESULT)hwndForDOMWindow( win ) : 0;
+ }
+ return DefWindowProc( msgWindow, msg, wp, lp );
+ }
+
+private:
+ HWND mHandle;
+}; // struct MessageWindow
+
+/* Start: Tries to find the "message window" to determine if it
+ * exists. If so, then Mozilla is already running. In that
+ * case, we use the handle to the "message" window and send
+ * a request corresponding to this process's command line
+ * options.
+ *
+ * If not, then this is the first instance of Mozilla. In
+ * that case, we create and set up the message window.
+ *
+ * The checking for existence of the message window must
+ * be protected by use of a mutex semaphore.
+ */
+NS_IMETHODIMP
+nsNativeAppSupportWin::Start( bool *aResult ) {
+ NS_ENSURE_ARG( aResult );
+ NS_ENSURE_TRUE( mInstance == 0, NS_ERROR_NOT_INITIALIZED );
+ NS_ENSURE_STATE( gAppData );
+
+ if (getenv("MOZ_NO_REMOTE"))
+ {
+ *aResult = true;
+ return NS_OK;
+ }
+
+ nsresult rv = NS_ERROR_FAILURE;
+ *aResult = false;
+
+ // Grab mutex first.
+
+ // Build mutex name from app name.
+ ::_snwprintf(reinterpret_cast<wchar_t*>(mMutexName),
+ sizeof mMutexName / sizeof(char16_t), L"%s%s%s",
+ MOZ_MUTEX_NAMESPACE,
+ static_cast<const wchar_t*>(NS_ConvertUTF8toUTF16(gAppData->name).get()),
+ MOZ_STARTUP_MUTEX_NAME );
+ Win32Mutex startupLock = Win32Mutex( mMutexName );
+
+ NS_ENSURE_TRUE( startupLock.Lock( MOZ_DDE_START_TIMEOUT ), NS_ERROR_FAILURE );
+
+ // Search for existing message window.
+ MessageWindow msgWindow;
+ if ( (HWND)msgWindow ) {
+ // We are a client process. Pass request to message window.
+ rv = msgWindow.SendRequest();
+ } else {
+ // We will be server.
+ rv = msgWindow.Create();
+ if ( NS_SUCCEEDED( rv ) ) {
+ // Start up DDE server.
+ this->StartDDE();
+ // Tell caller to spin message loop.
+ *aResult = true;
+ }
+ }
+
+ startupLock.Unlock();
+
+ return rv;
+}
+
+bool
+nsNativeAppSupportWin::InitTopicStrings() {
+ for ( int i = 0; i < topicCount; i++ ) {
+ if ( !( mTopics[ i ] = DdeCreateStringHandleA( mInstance, const_cast<char *>(topicNames[ i ]), CP_WINANSI ) ) ) {
+ return false;
+ }
+ }
+ return true;
+}
+
+int
+nsNativeAppSupportWin::FindTopic( HSZ topic ) {
+ for ( int i = 0; i < topicCount; i++ ) {
+ if ( DdeCmpStringHandles( topic, mTopics[i] ) == 0 ) {
+ return i;
+ }
+ }
+ return -1;
+}
+
+
+// Start DDE server.
+//
+// This used to be the Start() method when we were using DDE as the
+// primary IPC mechanism between secondary Mozilla processes and the
+// initial "server" process.
+//
+// Now, it simply initializes the DDE server. The caller must check
+// that this process is to be the server, and, must acquire the DDE
+// startup mutex semaphore prior to calling this routine. See ::Start(),
+// above.
+NS_IMETHODIMP
+nsNativeAppSupportWin::StartDDE() {
+ NS_ENSURE_TRUE( mInstance == 0, NS_ERROR_NOT_INITIALIZED );
+
+ // Initialize DDE.
+ NS_ENSURE_TRUE( DMLERR_NO_ERROR == DdeInitialize( &mInstance,
+ nsNativeAppSupportWin::HandleDDENotification,
+ APPCLASS_STANDARD,
+ 0 ),
+ NS_ERROR_FAILURE );
+
+ // Allocate DDE strings.
+ NS_ENSURE_TRUE( ( mApplication = DdeCreateStringHandleA( mInstance, (char*) gAppData->name, CP_WINANSI ) ) && InitTopicStrings(),
+ NS_ERROR_FAILURE );
+
+ // Next step is to register a DDE service.
+ NS_ENSURE_TRUE( DdeNameService( mInstance, mApplication, 0, DNS_REGISTER ), NS_ERROR_FAILURE );
+
+#if MOZ_DEBUG_DDE
+ printf( "DDE server started\n" );
+#endif
+
+ return NS_OK;
+}
+
+// If no DDE conversations are pending, terminate DDE.
+NS_IMETHODIMP
+nsNativeAppSupportWin::Stop( bool *aResult ) {
+ NS_ENSURE_ARG( aResult );
+ NS_ENSURE_TRUE( mInstance, NS_ERROR_NOT_INITIALIZED );
+
+ nsresult rv = NS_OK;
+ *aResult = true;
+
+ Win32Mutex ddeLock( mMutexName );
+
+ if ( ddeLock.Lock( MOZ_DDE_STOP_TIMEOUT ) ) {
+ if ( mConversations == 0 ) {
+ this->Quit();
+ } else {
+ *aResult = false;
+ }
+
+ ddeLock.Unlock();
+ }
+ else {
+ // No DDE application name specified, but that's OK. Just
+ // forge ahead.
+ *aResult = true;
+ }
+
+ return rv;
+}
+
+NS_IMETHODIMP
+nsNativeAppSupportWin::Observe(nsISupports* aSubject, const char* aTopic,
+ const char16_t* aData)
+{
+ if (strcmp(aTopic, "quit-application") == 0) {
+ Quit();
+ } else {
+ NS_ERROR("Unexpected observer topic.");
+ }
+
+ return NS_OK;
+}
+
+// Terminate DDE regardless.
+NS_IMETHODIMP
+nsNativeAppSupportWin::Quit() {
+ // If another process wants to look for the message window, they need
+ // to wait to hold the lock, in which case they will not find the
+ // window as we will destroy ours under our lock.
+ // When the mutex goes off the stack, it is unlocked via destructor.
+ Win32Mutex mutexLock(mMutexName);
+ NS_ENSURE_TRUE(mutexLock.Lock(MOZ_DDE_START_TIMEOUT), NS_ERROR_FAILURE);
+
+ // If we've got a message window to receive IPC or new window requests,
+ // get rid of it as we are shutting down.
+ // Note: Destroy calls DestroyWindow, which will only work on a window
+ // created by the same thread.
+ MessageWindow mw;
+ mw.Destroy();
+
+ if ( mInstance ) {
+ // Unregister application name.
+ DdeNameService( mInstance, mApplication, 0, DNS_UNREGISTER );
+ // Clean up strings.
+ if ( mApplication ) {
+ DdeFreeStringHandle( mInstance, mApplication );
+ mApplication = 0;
+ }
+ for ( int i = 0; i < topicCount; i++ ) {
+ if ( mTopics[i] ) {
+ DdeFreeStringHandle( mInstance, mTopics[i] );
+ mTopics[i] = 0;
+ }
+ }
+ DdeUninitialize( mInstance );
+ mInstance = 0;
+#if MOZ_DEBUG_DDE
+ printf( "DDE server stopped\n" );
+#endif
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsNativeAppSupportWin::Enable()
+{
+ mCanHandleRequests = true;
+
+ nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
+ if (obs) {
+ obs->AddObserver(this, "quit-application", false);
+ } else {
+ NS_ERROR("No observer service?");
+ }
+
+ return NS_OK;
+}
+
+#if MOZ_DEBUG_DDE
+// Macro to generate case statement for a given XTYP value.
+#define XTYP_CASE(t) \
+ case t: result = #t; break
+
+static nsCString uTypeDesc( UINT uType ) {
+ nsCString result;
+ switch ( uType ) {
+ XTYP_CASE(XTYP_ADVSTART);
+ XTYP_CASE(XTYP_CONNECT);
+ XTYP_CASE(XTYP_ADVREQ);
+ XTYP_CASE(XTYP_REQUEST);
+ XTYP_CASE(XTYP_WILDCONNECT);
+ XTYP_CASE(XTYP_ADVDATA);
+ XTYP_CASE(XTYP_EXECUTE);
+ XTYP_CASE(XTYP_POKE);
+ XTYP_CASE(XTYP_ADVSTOP);
+ XTYP_CASE(XTYP_CONNECT_CONFIRM);
+ XTYP_CASE(XTYP_DISCONNECT);
+ XTYP_CASE(XTYP_ERROR);
+ XTYP_CASE(XTYP_MONITOR);
+ XTYP_CASE(XTYP_REGISTER);
+ XTYP_CASE(XTYP_XACT_COMPLETE);
+ XTYP_CASE(XTYP_UNREGISTER);
+ default: result = "XTYP_?????";
+ }
+ return result;
+}
+
+static nsCString hszValue( DWORD instance, HSZ hsz ) {
+ // Extract string from HSZ.
+ nsCString result("[");
+ DWORD len = DdeQueryString( instance, hsz, nullptr, nullptr, CP_WINANSI );
+ if ( len ) {
+ char buffer[ 256 ];
+ DdeQueryString( instance, hsz, buffer, sizeof buffer, CP_WINANSI );
+ result += buffer;
+ }
+ result += "]";
+ return result;
+}
+#else
+// These are purely a safety measure to avoid the infamous "won't
+// build non-debug" type Tinderbox flames.
+static nsCString uTypeDesc( UINT ) {
+ return nsCString( "?" );
+}
+static nsCString hszValue( DWORD, HSZ ) {
+ return nsCString( "?" );
+}
+#endif
+
+
+// Utility function to escape double-quotes within a string.
+static void escapeQuotes( nsAString &aString ) {
+ int32_t offset = -1;
+ while( 1 ) {
+ // Find next '"'.
+ offset = aString.FindChar( '"', ++offset );
+ if ( offset == kNotFound ) {
+ // No more quotes, exit.
+ break;
+ } else {
+ // Insert back-slash ahead of the '"'.
+ aString.Insert( char16_t('\\'), offset );
+ // Increment offset because we just inserted a slash
+ offset++;
+ }
+ }
+ return;
+}
+
+HDDEDATA CALLBACK
+nsNativeAppSupportWin::HandleDDENotification( UINT uType, // transaction type
+ UINT uFmt, // clipboard data format
+ HCONV hconv, // handle to the conversation
+ HSZ hsz1, // handle to a string
+ HSZ hsz2, // handle to a string
+ HDDEDATA hdata, // handle to a global memory object
+ ULONG_PTR dwData1, // transaction-specific data
+ ULONG_PTR dwData2 ) { // transaction-specific data
+
+ if (!mCanHandleRequests)
+ return 0;
+
+
+#if MOZ_DEBUG_DDE
+ printf( "DDE: uType =%s\n", uTypeDesc( uType ).get() );
+ printf( " uFmt =%u\n", (unsigned)uFmt );
+ printf( " hconv =%08x\n", (int)hconv );
+ printf( " hsz1 =%08x:%s\n", (int)hsz1, hszValue( mInstance, hsz1 ).get() );
+ printf( " hsz2 =%08x:%s\n", (int)hsz2, hszValue( mInstance, hsz2 ).get() );
+ printf( " hdata =%08x\n", (int)hdata );
+ printf( " dwData1=%08x\n", (int)dwData1 );
+ printf( " dwData2=%08x\n", (int)dwData2 );
+#endif
+
+ HDDEDATA result = 0;
+ if ( uType & XCLASS_BOOL ) {
+ switch ( uType ) {
+ case XTYP_CONNECT:
+ // Make sure its for our service/topic.
+ if ( FindTopic( hsz1 ) != -1 ) {
+ // We support this connection.
+ result = (HDDEDATA)1;
+ }
+ break;
+ case XTYP_CONNECT_CONFIRM:
+ // We don't care about the conversation handle, at this point.
+ result = (HDDEDATA)1;
+ break;
+ }
+ } else if ( uType & XCLASS_DATA ) {
+ if ( uType == XTYP_REQUEST ) {
+ switch ( FindTopic( hsz1 ) ) {
+ case topicOpenURL: {
+ // Open a given URL...
+
+ // Get the URL from the first argument in the command.
+ nsAutoString url;
+ ParseDDEArg(hsz2, 0, url);
+
+ // Read the 3rd argument in the command to determine if a
+ // new window is to be used.
+ nsAutoString windowID;
+ ParseDDEArg(hsz2, 2, windowID);
+ // "" means to open the URL in a new window.
+ if ( windowID.IsEmpty() ) {
+ url.Insert(NS_LITERAL_STRING("mozilla -new-window "), 0);
+ }
+ else {
+ url.Insert(NS_LITERAL_STRING("mozilla -url "), 0);
+ }
+
+#if MOZ_DEBUG_DDE
+ printf( "Handling dde XTYP_REQUEST request: [%s]...\n", NS_ConvertUTF16toUTF8(url).get() );
+#endif
+ // Now handle it.
+ HandleCommandLine(NS_ConvertUTF16toUTF8(url).get(), nullptr, nsICommandLine::STATE_REMOTE_EXPLICIT);
+
+ // Return pseudo window ID.
+ result = CreateDDEData( 1 );
+ break;
+ }
+ case topicGetWindowInfo: {
+ // This topic has to get the current URL, get the current
+ // page title and then format the output into the DDE
+ // return string. The return value is "URL","Page Title",
+ // "Window ID" however the window ID is not used for this
+ // command, therefore it is returned as a null string
+
+ // This isn't really a loop. We just use "break"
+ // statements to bypass the remaining steps when
+ // something goes wrong.
+ do {
+ // Get most recently used Nav window.
+ nsCOMPtr<mozIDOMWindowProxy> navWin;
+ GetMostRecentWindow( NS_LITERAL_STRING( "navigator:browser" ).get(),
+ getter_AddRefs( navWin ) );
+ nsCOMPtr<nsPIDOMWindowOuter> piNavWin = do_QueryInterface(navWin);
+ if ( !piNavWin ) {
+ // There is not a window open
+ break;
+ }
+
+ // Get content window.
+ nsCOMPtr<nsPIDOMWindowOuter> internalContent = nsGlobalWindow::Cast(piNavWin)->GetContent();
+ if ( !internalContent ) {
+ break;
+ }
+ // Get location.
+ nsCOMPtr<nsIDOMLocation> location = internalContent->GetLocation();
+ if ( !location ) {
+ break;
+ }
+ // Get href for URL.
+ nsAutoString url;
+ if ( NS_FAILED( location->GetHref( url ) ) ) {
+ break;
+ }
+ // Escape any double-quotes.
+ escapeQuotes( url );
+
+ // Now for the title...
+
+ // Get the base window from the doc shell...
+ nsCOMPtr<nsIBaseWindow> baseWindow =
+ do_QueryInterface( internalContent->GetDocShell() );
+ if ( !baseWindow ) {
+ break;
+ }
+ // And from the base window we can get the title.
+ nsXPIDLString title;
+ if(!baseWindow) {
+ break;
+ }
+ baseWindow->GetTitle(getter_Copies(title));
+ // Escape any double-quotes in the title.
+ escapeQuotes( title );
+
+ // Use a string buffer for the output data, first
+ // save a quote.
+ nsAutoCString outpt( NS_LITERAL_CSTRING("\"") );
+ // Now copy the URL converting the Unicode string
+ // to a single-byte ASCII string
+ nsAutoCString tmpNativeStr;
+ NS_CopyUnicodeToNative( url, tmpNativeStr );
+ outpt.Append( tmpNativeStr );
+ // Add the "," used to separate the URL and the page
+ // title
+ outpt.Append( NS_LITERAL_CSTRING("\",\"") );
+ // Now copy the current page title to the return string
+ NS_CopyUnicodeToNative( title, tmpNativeStr );
+ outpt.Append( tmpNativeStr );
+ // Fill out the return string with the remainin ",""
+ outpt.Append( NS_LITERAL_CSTRING( "\",\"\"" ));
+
+ // Create a DDE handle to a char string for the data
+ // being returned, this copies and creates a "shared"
+ // copy of the DDE response until the calling APP
+ // reads it and says it can be freed.
+ result = CreateDDEData( (LPBYTE)(const char*)outpt.get(),
+ outpt.Length() + 1 );
+#if MOZ_DEBUG_DDE
+ printf( "WWW_GetWindowInfo->%s\n", outpt.get() );
+#endif
+ } while ( false );
+ break;
+ }
+ case topicActivate: {
+ // Activate a Nav window...
+ nsAutoString windowID;
+ ParseDDEArg(hsz2, 0, windowID);
+ // 4294967295 is decimal for 0xFFFFFFFF which is also a
+ // correct value to do that Activate last window stuff
+ if ( windowID.EqualsLiteral( "-1" ) ||
+ windowID.EqualsLiteral( "4294967295" ) ) {
+ // We only support activating the most recent window (or a new one).
+ ActivateLastWindow();
+ // Return pseudo window ID.
+ result = CreateDDEData( 1 );
+ }
+ break;
+ }
+ case topicVersion: {
+ // Return version. We're restarting at 1.0!
+ DWORD version = 1 << 16; // "1.0"
+ result = CreateDDEData( version );
+ break;
+ }
+ case topicRegisterViewer: {
+ // Register new viewer (not implemented).
+ result = CreateDDEData( false );
+ break;
+ }
+ case topicUnRegisterViewer: {
+ // Unregister new viewer (not implemented).
+ result = CreateDDEData( false );
+ break;
+ }
+ default:
+ break;
+ }
+ } else if ( uType & XTYP_POKE ) {
+ switch ( FindTopic( hsz1 ) ) {
+ case topicCancelProgress: {
+ // "Handle" progress cancel (actually, pretty much ignored).
+ result = (HDDEDATA)DDE_FACK;
+ break;
+ }
+ default:
+ break;
+ }
+ }
+ } else if ( uType & XCLASS_FLAGS ) {
+ if ( uType == XTYP_EXECUTE ) {
+ // Prove that we received the request.
+ DWORD bytes;
+ LPBYTE request = DdeAccessData( hdata, &bytes );
+#if MOZ_DEBUG_DDE
+ printf( "Handling dde request: [%s]...\n", (char*)request );
+#endif
+
+ nsAutoString url;
+ ParseDDEArg((const WCHAR*) request, 0, url);
+
+ // Read the 3rd argument in the command to determine if a
+ // new window is to be used.
+ nsAutoString windowID;
+ ParseDDEArg((const WCHAR*) request, 2, windowID);
+
+ // "" means to open the URL in a new window.
+ if ( windowID.IsEmpty() ) {
+ url.Insert(NS_LITERAL_STRING("mozilla -new-window "), 0);
+ }
+ else {
+ url.Insert(NS_LITERAL_STRING("mozilla -url "), 0);
+ }
+#if MOZ_DEBUG_DDE
+ printf( "Handling dde XTYP_REQUEST request: [%s]...\n", NS_ConvertUTF16toUTF8(url).get() );
+#endif
+ // Now handle it.
+ HandleCommandLine(NS_ConvertUTF16toUTF8(url).get(), nullptr, nsICommandLine::STATE_REMOTE_EXPLICIT);
+
+ // Release the data.
+ DdeUnaccessData( hdata );
+ result = (HDDEDATA)DDE_FACK;
+ } else {
+ result = (HDDEDATA)DDE_FNOTPROCESSED;
+ }
+ } else if ( uType & XCLASS_NOTIFICATION ) {
+ }
+#if MOZ_DEBUG_DDE
+ printf( "DDE result=%d (0x%08X)\n", (int)result, (int)result );
+#endif
+ return result;
+}
+
+// Utility function to advance to end of quoted string.
+// p+offset must point to the comma preceding the arg on entry.
+// On return, p+result points to the closing '"' (or end of the string
+// if the closing '"' is missing) if the arg is quoted. If the arg
+// is not quoted, then p+result will point to the first character
+// of the arg.
+static int32_t advanceToEndOfQuotedArg( const WCHAR *p, int32_t offset, int32_t len ) {
+ // Check whether the current arg is quoted.
+ if ( p[++offset] == '"' ) {
+ // Advance past the closing quote.
+ while ( offset < len && p[++offset] != '"' ) {
+ // If the current character is a backslash, then the
+ // next character can't be a *real* '"', so skip it.
+ if ( p[offset] == '\\' ) {
+ offset++;
+ }
+ }
+ }
+ return offset;
+}
+
+void nsNativeAppSupportWin::ParseDDEArg( const WCHAR* args, int index, nsString& aString) {
+ if ( args ) {
+ nsDependentString temp(args);
+
+ // offset points to the comma preceding the desired arg.
+ int32_t offset = -1;
+ // Skip commas till we get to the arg we want.
+ while( index-- ) {
+ // If this arg is quoted, then go to closing quote.
+ offset = advanceToEndOfQuotedArg( args, offset, temp.Length());
+ // Find next comma.
+ offset = temp.FindChar( ',', offset );
+ if ( offset == kNotFound ) {
+ // No more commas, give up.
+ aString = args;
+ return;
+ }
+ }
+ // The desired argument starts just past the preceding comma,
+ // which offset points to, and extends until the following
+ // comma (or the end of the string).
+ //
+ // Since the argument might be enclosed in quotes, we need to
+ // deal with that before searching for the terminating comma.
+ // We advance offset so it ends up pointing to the start of
+ // the argument we want.
+ int32_t end = advanceToEndOfQuotedArg( args, offset++, temp.Length() );
+ // Find next comma (or end of string).
+ end = temp.FindChar( ',', end );
+ if ( end == kNotFound ) {
+ // Arg is the rest of the string.
+ end = temp.Length();
+ }
+ // Extract result.
+ aString.Assign( args + offset, end - offset );
+ }
+ return;
+}
+
+// Utility to parse out argument from a DDE item string.
+void nsNativeAppSupportWin::ParseDDEArg( HSZ args, int index, nsString& aString) {
+ DWORD argLen = DdeQueryStringW( mInstance, args, nullptr, 0, CP_WINUNICODE );
+ // there wasn't any string, so return empty string
+ if ( !argLen ) return;
+ nsAutoString temp;
+ // Ensure result's buffer is sufficiently big.
+ temp.SetLength( argLen );
+ // Now get the string contents.
+ DdeQueryString( mInstance, args, reinterpret_cast<wchar_t*>(temp.BeginWriting()), temp.Length(), CP_WINUNICODE );
+ // Parse out the given arg.
+ ParseDDEArg(temp.get(), index, aString);
+ return;
+}
+
+HDDEDATA nsNativeAppSupportWin::CreateDDEData( DWORD value ) {
+ return CreateDDEData( (LPBYTE)&value, sizeof value );
+}
+
+HDDEDATA nsNativeAppSupportWin::CreateDDEData( LPBYTE value, DWORD len ) {
+ HDDEDATA result = DdeCreateDataHandle( mInstance,
+ value,
+ len,
+ 0,
+ mApplication,
+ CF_TEXT,
+ 0 );
+ return result;
+}
+
+void nsNativeAppSupportWin::ActivateLastWindow() {
+ nsCOMPtr<mozIDOMWindowProxy> navWin;
+ GetMostRecentWindow( u"navigator:browser", getter_AddRefs( navWin ) );
+ if ( navWin ) {
+ // Activate that window.
+ activateWindow( navWin );
+ } else {
+ // Need to create a Navigator window, then.
+ OpenBrowserWindow();
+ }
+}
+
+void
+nsNativeAppSupportWin::HandleCommandLine(const char* aCmdLineString,
+ nsIFile* aWorkingDir,
+ uint32_t aState)
+{
+ nsresult rv;
+
+ int justCounting = 1;
+ char **argv = 0;
+ // Flags, etc.
+ int init = 1;
+ int between, quoted, bSlashCount;
+ int argc;
+ const char *p;
+ nsAutoCString arg;
+
+ nsCOMPtr<nsICommandLineRunner> cmdLine
+ (do_CreateInstance("@mozilla.org/toolkit/command-line;1"));
+ if (!cmdLine) {
+ NS_ERROR("Couldn't create command line!");
+ return;
+ }
+
+ // Parse command line args according to MS spec
+ // (see "Parsing C++ Command-Line Arguments" at
+ // http://msdn.microsoft.com/library/devprods/vs6/visualc/vclang/_pluslang_parsing_c.2b2b_.command.2d.line_arguments.htm).
+ // We loop if we've not finished the second pass through.
+ while ( 1 ) {
+ // Initialize if required.
+ if ( init ) {
+ p = aCmdLineString;
+ between = 1;
+ argc = quoted = bSlashCount = 0;
+
+ init = 0;
+ }
+ if ( between ) {
+ // We are traversing whitespace between args.
+ // Check for start of next arg.
+ if ( *p != 0 && !isspace( *p ) ) {
+ // Start of another arg.
+ between = 0;
+ arg = "";
+ switch ( *p ) {
+ case '\\':
+ // Count the backslash.
+ bSlashCount = 1;
+ break;
+ case '"':
+ // Remember we're inside quotes.
+ quoted = 1;
+ break;
+ default:
+ // Add character to arg.
+ arg += *p;
+ break;
+ }
+ } else {
+ // Another space between args, ignore it.
+ }
+ } else {
+ // We are processing the contents of an argument.
+ // Check for whitespace or end.
+ if ( *p == 0 || ( !quoted && isspace( *p ) ) ) {
+ // Process pending backslashes (interpret them
+ // literally since they're not followed by a ").
+ while( bSlashCount ) {
+ arg += '\\';
+ bSlashCount--;
+ }
+ // End current arg.
+ if ( !justCounting ) {
+ argv[argc] = new char[ arg.Length() + 1 ];
+ strcpy( argv[argc], arg.get() );
+ }
+ argc++;
+ // We're now between args.
+ between = 1;
+ } else {
+ // Still inside argument, process the character.
+ switch ( *p ) {
+ case '"':
+ // First, digest preceding backslashes (if any).
+ while ( bSlashCount > 1 ) {
+ // Put one backsplash in arg for each pair.
+ arg += '\\';
+ bSlashCount -= 2;
+ }
+ if ( bSlashCount ) {
+ // Quote is literal.
+ arg += '"';
+ bSlashCount = 0;
+ } else {
+ // Quote starts or ends a quoted section.
+ if ( quoted ) {
+ // Check for special case of consecutive double
+ // quotes inside a quoted section.
+ if ( *(p+1) == '"' ) {
+ // This implies a literal double-quote. Fake that
+ // out by causing next double-quote to look as
+ // if it was preceded by a backslash.
+ bSlashCount = 1;
+ } else {
+ quoted = 0;
+ }
+ } else {
+ quoted = 1;
+ }
+ }
+ break;
+ case '\\':
+ // Add to count.
+ bSlashCount++;
+ break;
+ default:
+ // Accept any preceding backslashes literally.
+ while ( bSlashCount ) {
+ arg += '\\';
+ bSlashCount--;
+ }
+ // Just add next char to the current arg.
+ arg += *p;
+ break;
+ }
+ }
+ }
+ // Check for end of input.
+ if ( *p ) {
+ // Go to next character.
+ p++;
+ } else {
+ // If on first pass, go on to second.
+ if ( justCounting ) {
+ // Allocate argv array.
+ argv = new char*[ argc ];
+
+ // Start second pass
+ justCounting = 0;
+ init = 1;
+ } else {
+ // Quit.
+ break;
+ }
+ }
+ }
+
+ rv = cmdLine->Init(argc, argv, aWorkingDir, aState);
+
+ // Cleanup.
+ while ( argc ) {
+ delete [] argv[ --argc ];
+ }
+ delete [] argv;
+
+ if (NS_FAILED(rv)) {
+ NS_ERROR("Error initializing command line.");
+ return;
+ }
+
+ cmdLine->Run();
+}
+
+nsresult
+nsNativeAppSupportWin::OpenWindow( const char*urlstr, const char *args ) {
+
+ nsresult rv = NS_ERROR_FAILURE;
+
+ nsCOMPtr<nsIWindowWatcher> wwatch(do_GetService(NS_WINDOWWATCHER_CONTRACTID));
+ nsCOMPtr<nsISupportsCString> sarg(do_CreateInstance(NS_SUPPORTS_CSTRING_CONTRACTID));
+ if (sarg)
+ sarg->SetData(nsDependentCString(args));
+
+ if (wwatch && sarg) {
+ nsCOMPtr<mozIDOMWindowProxy> newWindow;
+ rv = wwatch->OpenWindow(0, urlstr, "_blank", "chrome,dialog=no,all",
+ sarg, getter_AddRefs(newWindow));
+#if MOZ_DEBUG_DDE
+ } else {
+ printf("Get WindowWatcher (or create string) failed\n");
+#endif
+ }
+ return rv;
+}
+
+HWND hwndForDOMWindow(mozIDOMWindowProxy *window ) {
+ if ( !window ) {
+ return 0;
+ }
+ nsCOMPtr<nsPIDOMWindowOuter > pidomwindow = nsPIDOMWindowOuter::From(window);
+
+ nsCOMPtr<nsIBaseWindow> ppBaseWindow =
+ do_QueryInterface( pidomwindow->GetDocShell() );
+ if ( !ppBaseWindow ) {
+ return 0;
+ }
+
+ nsCOMPtr<nsIWidget> ppWidget;
+ ppBaseWindow->GetMainWidget( getter_AddRefs( ppWidget ) );
+
+ return (HWND)( ppWidget->GetNativeData( NS_NATIVE_WIDGET ) );
+}
+
+nsresult
+nsNativeAppSupportWin::OpenBrowserWindow()
+{
+ nsresult rv = NS_OK;
+
+ // Open the argument URL in the most recently used Navigator window.
+ // If there is no Nav window, open a new one.
+
+ // If at all possible, hand the request off to the most recent
+ // browser window.
+
+ nsCOMPtr<mozIDOMWindowProxy> navWin;
+ GetMostRecentWindow( NS_LITERAL_STRING( "navigator:browser" ).get(), getter_AddRefs( navWin ) );
+
+ // This isn't really a loop. We just use "break" statements to fall
+ // out to the OpenWindow call when things go awry.
+ do {
+ // If caller requires a new window, then don't use an existing one.
+ if ( !navWin ) {
+ // Have to open a new one.
+ break;
+ }
+
+ nsCOMPtr<nsIBrowserDOMWindow> bwin;
+ { // scope a bunch of temporary cruft used to generate bwin
+ nsCOMPtr<nsIWebNavigation> navNav( do_GetInterface( navWin ) );
+ nsCOMPtr<nsIDocShellTreeItem> navItem( do_QueryInterface( navNav ) );
+ if ( navItem ) {
+ nsCOMPtr<nsIDocShellTreeItem> rootItem;
+ navItem->GetRootTreeItem( getter_AddRefs( rootItem ) );
+ nsCOMPtr<nsPIDOMWindowOuter> rootWin =
+ rootItem ? rootItem->GetWindow() : nullptr;
+ nsCOMPtr<nsIDOMChromeWindow> chromeWin(do_QueryInterface(rootWin));
+ if ( chromeWin )
+ chromeWin->GetBrowserDOMWindow( getter_AddRefs ( bwin ) );
+ }
+ }
+ if ( bwin ) {
+ nsCOMPtr<nsIURI> uri;
+ NS_NewURI( getter_AddRefs( uri ), NS_LITERAL_CSTRING("about:blank"), 0, 0 );
+ if ( uri ) {
+ nsCOMPtr<mozIDOMWindowProxy> container;
+ rv = bwin->OpenURI( uri, 0,
+ nsIBrowserDOMWindow::OPEN_DEFAULTWINDOW,
+ nsIBrowserDOMWindow::OPEN_EXTERNAL,
+ getter_AddRefs( container ) );
+ if ( NS_SUCCEEDED( rv ) )
+ return NS_OK;
+ }
+ }
+
+ NS_ERROR("failed to hand off external URL to extant window");
+ } while ( false );
+
+ // open a new window if caller requested it or if anything above failed
+
+ char* argv[] = { 0 };
+ nsCOMPtr<nsICommandLineRunner> cmdLine
+ (do_CreateInstance("@mozilla.org/toolkit/command-line;1"));
+ NS_ENSURE_TRUE(cmdLine, NS_ERROR_FAILURE);
+
+ rv = cmdLine->Init(0, argv, nullptr, nsICommandLine::STATE_REMOTE_EXPLICIT);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ return cmdLine->Run();
+}
+
diff --git a/toolkit/xre/nsNativeAppSupportWin.h b/toolkit/xre/nsNativeAppSupportWin.h
new file mode 100644
index 000000000..3e6fb67e5
--- /dev/null
+++ b/toolkit/xre/nsNativeAppSupportWin.h
@@ -0,0 +1,39 @@
+/* -*- 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/. */
+
+/* This file has *public* stuff needed for the Win32 implementation of
+ * the nsINativeAppSupport interface. It has to be broken out into a
+ * separate file in order to ensure that the generated .h file can be
+ * used in a Win32 .rc file. See /mozilla/xpfe/bootstrap/splash.rc.
+ *
+ * This file, and the generated .h, are only needed on Win32 platforms.
+ */
+
+// Constants identifying Win32 "native" resources.
+
+#ifdef MOZ_PHOENIX
+
+// Splash screen dialog ID.
+#define IDD_SPLASH 100
+
+// Splash screen bitmap ID.
+#define IDB_SPLASH 101
+
+// DDE application name
+#define ID_DDE_APPLICATION_NAME 102
+
+#define IDI_APPICON 1
+#define IDI_DOCUMENT 2
+#define IDI_NEWWINDOW 3
+#define IDI_NEWTAB 4
+#define IDI_PBMODE 5
+#ifndef IDI_APPLICATION
+#define IDI_APPLICATION 32512
+#endif
+
+#endif
+
+// String that goes in the WinXP Start Menu.
+#define IDS_STARTMENU_APPNAME 103
diff --git a/toolkit/xre/nsSigHandlers.cpp b/toolkit/xre/nsSigHandlers.cpp
new file mode 100644
index 000000000..098d9ae7e
--- /dev/null
+++ b/toolkit/xre/nsSigHandlers.cpp
@@ -0,0 +1,404 @@
+/* -*- 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/. */
+
+/*
+ * This module is supposed to abstract signal handling away from the other
+ * platforms that do not support it.
+ */
+
+#include "nsSigHandlers.h"
+
+#ifdef XP_UNIX
+
+#include <signal.h>
+#include <stdio.h>
+#include <string.h>
+#include "prthread.h"
+#include "plstr.h"
+#include "prenv.h"
+#include "nsDebug.h"
+#include "nsXULAppAPI.h"
+
+#if defined(LINUX)
+#include <sys/time.h>
+#include <sys/resource.h>
+#include <unistd.h>
+#include <stdlib.h> // atoi
+#include <sys/prctl.h>
+#ifndef ANDROID // no Android impl
+# include <ucontext.h>
+#endif
+#endif
+
+#if defined(SOLARIS)
+#include <sys/resource.h>
+#include <ucontext.h>
+#endif
+
+static const char* gProgname = "huh?";
+
+// Note: some tests manipulate this value.
+unsigned int _gdb_sleep_duration = 300;
+
+#if defined(LINUX) && defined(DEBUG) && \
+ (defined(__i386) || defined(__x86_64) || defined(PPC))
+#define CRAWL_STACK_ON_SIGSEGV
+#endif
+
+#ifndef PR_SET_PTRACER
+#define PR_SET_PTRACER 0x59616d61
+#endif
+#ifndef PR_SET_PTRACER_ANY
+#define PR_SET_PTRACER_ANY ((unsigned long)-1)
+#endif
+
+#if defined(CRAWL_STACK_ON_SIGSEGV)
+
+#include <unistd.h>
+#include "nsISupportsUtils.h"
+#include "mozilla/StackWalk.h"
+
+// NB: keep me up to date with the same variable in
+// ipc/chromium/chrome/common/ipc_channel_posix.cc
+static const int kClientChannelFd = 3;
+
+extern "C" {
+
+static void PrintStackFrame(uint32_t aFrameNumber, void *aPC, void *aSP,
+ void *aClosure)
+{
+ char buf[1024];
+ MozCodeAddressDetails details;
+
+ MozDescribeCodeAddress(aPC, &details);
+ MozFormatCodeAddressDetails(buf, sizeof(buf), aFrameNumber, aPC, &details);
+ fprintf(stdout, "%s\n", buf);
+ fflush(stdout);
+}
+
+}
+
+void
+ah_crap_handler(int signum)
+{
+ printf("\nProgram %s (pid = %d) received signal %d.\n",
+ gProgname,
+ getpid(),
+ signum);
+
+ printf("Stack:\n");
+ MozStackWalk(PrintStackFrame, /* skipFrames */ 2, /* maxFrames */ 0,
+ nullptr, 0, nullptr);
+
+ printf("Sleeping for %d seconds.\n",_gdb_sleep_duration);
+ printf("Type 'gdb %s %d' to attach your debugger to this thread.\n",
+ gProgname,
+ getpid());
+
+ // Allow us to be ptraced by gdb on Linux with Yama restrictions enabled.
+ prctl(PR_SET_PTRACER, PR_SET_PTRACER_ANY);
+
+ sleep(_gdb_sleep_duration);
+
+ printf("Done sleeping...\n");
+
+ _exit(signum);
+}
+
+void
+child_ah_crap_handler(int signum)
+{
+ if (!getenv("MOZ_DONT_UNBLOCK_PARENT_ON_CHILD_CRASH"))
+ close(kClientChannelFd);
+ ah_crap_handler(signum);
+}
+
+#endif // CRAWL_STACK_ON_SIGSEGV
+
+#ifdef MOZ_WIDGET_GTK
+// Need this include for version test below.
+#include <glib.h>
+#endif
+
+#if defined(MOZ_WIDGET_GTK) && (GLIB_MAJOR_VERSION > 2 || (GLIB_MAJOR_VERSION == 2 && GLIB_MINOR_VERSION >= 6))
+
+static GLogFunc orig_log_func = nullptr;
+
+extern "C" {
+static void
+my_glib_log_func(const gchar *log_domain, GLogLevelFlags log_level,
+ const gchar *message, gpointer user_data);
+}
+
+/* static */ void
+my_glib_log_func(const gchar *log_domain, GLogLevelFlags log_level,
+ const gchar *message, gpointer user_data)
+{
+ if (log_level & (G_LOG_LEVEL_ERROR | G_LOG_FLAG_FATAL | G_LOG_FLAG_RECURSION)) {
+ NS_DebugBreak(NS_DEBUG_ASSERTION, message, "glib assertion", __FILE__, __LINE__);
+ } else if (log_level & (G_LOG_LEVEL_CRITICAL | G_LOG_LEVEL_WARNING)) {
+ NS_DebugBreak(NS_DEBUG_WARNING, message, "glib warning", __FILE__, __LINE__);
+ }
+
+ orig_log_func(log_domain, log_level, message, nullptr);
+}
+
+#endif
+
+#ifdef SA_SIGINFO
+static void fpehandler(int signum, siginfo_t *si, void *context)
+{
+ /* Integer divide by zero or integer overflow. */
+ /* Note: FPE_INTOVF is ignored on Intel, PowerPC and SPARC systems. */
+ if (si->si_code == FPE_INTDIV || si->si_code == FPE_INTOVF) {
+ NS_DebugBreak(NS_DEBUG_ABORT, "Divide by zero", nullptr, __FILE__, __LINE__);
+ }
+
+#ifdef XP_MACOSX
+ ucontext_t *uc = (ucontext_t *)context;
+
+#if defined(__i386__) || defined(__amd64__)
+ _STRUCT_FP_CONTROL *ctrl = &uc->uc_mcontext->__fs.__fpu_fcw;
+ ctrl->__invalid = ctrl->__denorm = ctrl->__zdiv = ctrl->__ovrfl = ctrl->__undfl = ctrl->__precis = 1;
+
+ _STRUCT_FP_STATUS *status = &uc->uc_mcontext->__fs.__fpu_fsw;
+ status->__invalid = status->__denorm = status->__zdiv = status->__ovrfl = status->__undfl =
+ status->__precis = status->__stkflt = status->__errsumm = 0;
+
+ uint32_t *mxcsr = &uc->uc_mcontext->__fs.__fpu_mxcsr;
+ *mxcsr |= SSE_EXCEPTION_MASK; /* disable all SSE exceptions */
+ *mxcsr &= ~SSE_STATUS_FLAGS; /* clear all pending SSE exceptions */
+#endif
+#endif
+#if defined(LINUX) && !defined(ANDROID)
+ ucontext_t *uc = (ucontext_t *)context;
+
+#if defined(__i386__)
+ /*
+ * It seems that we have no access to mxcsr on Linux. libc
+ * seems to be translating cw/sw to mxcsr.
+ */
+ unsigned long int *cw = &uc->uc_mcontext.fpregs->cw;
+ *cw |= FPU_EXCEPTION_MASK;
+
+ unsigned long int *sw = &uc->uc_mcontext.fpregs->sw;
+ *sw &= ~FPU_STATUS_FLAGS;
+#endif
+#if defined(__amd64__)
+ uint16_t *cw = &uc->uc_mcontext.fpregs->cwd;
+ *cw |= FPU_EXCEPTION_MASK;
+
+ uint16_t *sw = &uc->uc_mcontext.fpregs->swd;
+ *sw &= ~FPU_STATUS_FLAGS;
+
+ uint32_t *mxcsr = &uc->uc_mcontext.fpregs->mxcsr;
+ *mxcsr |= SSE_EXCEPTION_MASK; /* disable all SSE exceptions */
+ *mxcsr &= ~SSE_STATUS_FLAGS; /* clear all pending SSE exceptions */
+#endif
+#endif
+#ifdef SOLARIS
+ ucontext_t *uc = (ucontext_t *)context;
+
+#if defined(__i386)
+ uint32_t *cw = &uc->uc_mcontext.fpregs.fp_reg_set.fpchip_state.state[0];
+ *cw |= FPU_EXCEPTION_MASK;
+
+ uint32_t *sw = &uc->uc_mcontext.fpregs.fp_reg_set.fpchip_state.state[1];
+ *sw &= ~FPU_STATUS_FLAGS;
+
+ /* address of the instruction that caused the exception */
+ uint32_t *ip = &uc->uc_mcontext.fpregs.fp_reg_set.fpchip_state.state[3];
+ uc->uc_mcontext.gregs[REG_PC] = *ip;
+#endif
+#if defined(__amd64__)
+ uint16_t *cw = &uc->uc_mcontext.fpregs.fp_reg_set.fpchip_state.cw;
+ *cw |= FPU_EXCEPTION_MASK;
+
+ uint16_t *sw = &uc->uc_mcontext.fpregs.fp_reg_set.fpchip_state.sw;
+ *sw &= ~FPU_STATUS_FLAGS;
+
+ uint32_t *mxcsr = &uc->uc_mcontext.fpregs.fp_reg_set.fpchip_state.mxcsr;
+ *mxcsr |= SSE_EXCEPTION_MASK; /* disable all SSE exceptions */
+ *mxcsr &= ~SSE_STATUS_FLAGS; /* clear all pending SSE exceptions */
+#endif
+#endif
+}
+#endif
+
+void InstallSignalHandlers(const char *aProgname)
+{
+ const char* tmp = PL_strdup(aProgname);
+ if (tmp) {
+ gProgname = tmp;
+ }
+
+ const char *gdbSleep = PR_GetEnv("MOZ_GDB_SLEEP");
+ if (gdbSleep && *gdbSleep)
+ {
+ unsigned int s;
+ if (1 == sscanf(gdbSleep, "%u", &s)) {
+ _gdb_sleep_duration = s;
+ }
+ }
+
+#if defined(CRAWL_STACK_ON_SIGSEGV)
+ if (!getenv("XRE_NO_WINDOWS_CRASH_DIALOG")) {
+ void (*crap_handler)(int) =
+ GeckoProcessType_Default != XRE_GetProcessType() ?
+ child_ah_crap_handler :
+ ah_crap_handler;
+ signal(SIGSEGV, crap_handler);
+ signal(SIGILL, crap_handler);
+ signal(SIGABRT, crap_handler);
+ }
+#endif // CRAWL_STACK_ON_SIGSEGV
+
+#ifdef SA_SIGINFO
+ /* Install a handler for floating point exceptions and disable them if they occur. */
+ struct sigaction sa, osa;
+ sa.sa_flags = SA_ONSTACK | SA_RESTART | SA_SIGINFO;
+ sa.sa_sigaction = fpehandler;
+ sigemptyset(&sa.sa_mask);
+ sigaction(SIGFPE, &sa, &osa);
+#endif
+
+ if (!XRE_IsParentProcess()) {
+ /*
+ * If the user is debugging a Gecko parent process in gdb and hits ^C to
+ * suspend, a SIGINT signal will be sent to the child. We ignore this signal
+ * so the child isn't killed.
+ */
+ signal(SIGINT, SIG_IGN);
+ }
+
+#if defined(DEBUG) && defined(LINUX)
+ const char *memLimit = PR_GetEnv("MOZ_MEM_LIMIT");
+ if (memLimit && *memLimit)
+ {
+ long m = atoi(memLimit);
+ m *= (1024*1024);
+ struct rlimit r;
+ r.rlim_cur = m;
+ r.rlim_max = m;
+ setrlimit(RLIMIT_AS, &r);
+ }
+#endif
+
+#if defined(SOLARIS)
+#define NOFILES 512
+
+ // Boost Solaris file descriptors
+ {
+ struct rlimit rl;
+
+ if (getrlimit(RLIMIT_NOFILE, &rl) == 0)
+
+ if (rl.rlim_cur < NOFILES) {
+ rl.rlim_cur = NOFILES;
+
+ if (setrlimit(RLIMIT_NOFILE, &rl) < 0) {
+ perror("setrlimit(RLIMIT_NOFILE)");
+ fprintf(stderr, "Cannot exceed hard limit for open files");
+ }
+#if defined(DEBUG)
+ if (getrlimit(RLIMIT_NOFILE, &rl) == 0)
+ printf("File descriptors set to %d\n", rl.rlim_cur);
+#endif //DEBUG
+ }
+ }
+#endif //SOLARIS
+
+#if defined(MOZ_WIDGET_GTK) && (GLIB_MAJOR_VERSION > 2 || (GLIB_MAJOR_VERSION == 2 && GLIB_MINOR_VERSION >= 6))
+ const char *assertString = PR_GetEnv("XPCOM_DEBUG_BREAK");
+ if (assertString &&
+ (!strcmp(assertString, "suspend") ||
+ !strcmp(assertString, "stack") ||
+ !strcmp(assertString, "abort") ||
+ !strcmp(assertString, "trap") ||
+ !strcmp(assertString, "break"))) {
+ // Override the default glib logging function so we get stacks for it too.
+ orig_log_func = g_log_set_default_handler(my_glib_log_func, nullptr);
+ }
+#endif
+}
+
+#elif XP_WIN
+
+#include <windows.h>
+
+#ifdef _M_IX86
+/*
+ * WinNT.h prior to SDK7 does not expose the structure of the ExtendedRegisters for ia86.
+ * We known that MxCsr is at offset 0x18 and is a DWORD.
+ */
+#define MXCSR(ctx) (*(DWORD *)(((BYTE *)(ctx)->ExtendedRegisters) + 0x18))
+#endif
+
+#ifdef _M_X64
+#define MXCSR(ctx) (ctx)->MxCsr
+#endif
+
+#if defined(_M_IX86) || defined(_M_X64)
+
+#ifdef _M_X64
+#define X87CW(ctx) (ctx)->FltSave.ControlWord
+#define X87SW(ctx) (ctx)->FltSave.StatusWord
+#else
+#define X87CW(ctx) (ctx)->FloatSave.ControlWord
+#define X87SW(ctx) (ctx)->FloatSave.StatusWord
+#endif
+
+static LPTOP_LEVEL_EXCEPTION_FILTER gFPEPreviousFilter;
+
+LONG __stdcall FpeHandler(PEXCEPTION_POINTERS pe)
+{
+ PEXCEPTION_RECORD e = (PEXCEPTION_RECORD)pe->ExceptionRecord;
+ CONTEXT *c = (CONTEXT*)pe->ContextRecord;
+
+ switch (e->ExceptionCode) {
+ case STATUS_FLOAT_DENORMAL_OPERAND:
+ case STATUS_FLOAT_DIVIDE_BY_ZERO:
+ case STATUS_FLOAT_INEXACT_RESULT:
+ case STATUS_FLOAT_INVALID_OPERATION:
+ case STATUS_FLOAT_OVERFLOW:
+ case STATUS_FLOAT_STACK_CHECK:
+ case STATUS_FLOAT_UNDERFLOW:
+ case STATUS_FLOAT_MULTIPLE_FAULTS:
+ case STATUS_FLOAT_MULTIPLE_TRAPS:
+ X87CW(c) |= FPU_EXCEPTION_MASK; /* disable all FPU exceptions */
+ X87SW(c) &= ~FPU_STATUS_FLAGS; /* clear all pending FPU exceptions */
+#ifdef _M_IX86
+ if (c->ContextFlags & CONTEXT_EXTENDED_REGISTERS) {
+#endif
+ MXCSR(c) |= SSE_EXCEPTION_MASK; /* disable all SSE exceptions */
+ MXCSR(c) &= ~SSE_STATUS_FLAGS; /* clear all pending SSE exceptions */
+#ifdef _M_IX86
+ }
+#endif
+ return EXCEPTION_CONTINUE_EXECUTION;
+ }
+ LONG action = EXCEPTION_CONTINUE_SEARCH;
+ if (gFPEPreviousFilter)
+ action = gFPEPreviousFilter(pe);
+
+ return action;
+}
+
+void InstallSignalHandlers(const char *aProgname)
+{
+ gFPEPreviousFilter = SetUnhandledExceptionFilter(FpeHandler);
+}
+
+#else
+
+void InstallSignalHandlers(const char *aProgname)
+{
+}
+
+#endif
+
+#else
+#error No signal handling implementation for this platform.
+#endif
diff --git a/toolkit/xre/nsSigHandlers.h b/toolkit/xre/nsSigHandlers.h
new file mode 100644
index 000000000..a4f073cbb
--- /dev/null
+++ b/toolkit/xre/nsSigHandlers.h
@@ -0,0 +1,39 @@
+/* -*- Mode: C++; tab-width: 40; 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/. */
+
+#if defined(_M_IX86) || defined(_M_X64) || defined(__i386__) || defined(__i386) || defined(__amd64__)
+
+/*
+ * x87 FPU Control Word:
+ *
+ * 0 -> IM Invalid Operation
+ * 1 -> DM Denormalized Operand
+ * 2 -> ZM Zero Divide
+ * 3 -> OM Overflow
+ * 4 -> UM Underflow
+ * 5 -> PM Precision
+ */
+#define FPU_EXCEPTION_MASK 0x3f
+
+/*
+ * x86 FPU Status Word:
+ *
+ * 0..5 -> Exception flags (see x86 FPU Control Word)
+ * 6 -> SF Stack Fault
+ * 7 -> ES Error Summary Status
+ */
+#define FPU_STATUS_FLAGS 0xff
+
+/*
+ * MXCSR Control and Status Register:
+ *
+ * 0..5 -> Exception flags (see x86 FPU Control Word)
+ * 6 -> DAZ Denormals Are Zero
+ * 7..12 -> Exception mask (see x86 FPU Control Word)
+ */
+#define SSE_STATUS_FLAGS FPU_EXCEPTION_MASK
+#define SSE_EXCEPTION_MASK (FPU_EXCEPTION_MASK << 7)
+
+#endif
diff --git a/toolkit/xre/nsUpdateDriver.cpp b/toolkit/xre/nsUpdateDriver.cpp
new file mode 100644
index 000000000..54a711000
--- /dev/null
+++ b/toolkit/xre/nsUpdateDriver.cpp
@@ -0,0 +1,1316 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:set ts=2 sw=2 sts=2 et cindent: */
+/* 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 <stdlib.h>
+#include <stdio.h>
+#include "nsUpdateDriver.h"
+#include "nsXULAppAPI.h"
+#include "nsAppRunner.h"
+#include "nsIWritablePropertyBag.h"
+#include "nsIFile.h"
+#include "nsVariant.h"
+#include "nsCOMPtr.h"
+#include "nsString.h"
+#include "prproces.h"
+#include "mozilla/Logging.h"
+#include "prenv.h"
+#include "nsVersionComparator.h"
+#include "nsXREDirProvider.h"
+#include "SpecialSystemDirectory.h"
+#include "nsDirectoryServiceDefs.h"
+#include "nsThreadUtils.h"
+#include "nsIXULAppInfo.h"
+#include "mozilla/Preferences.h"
+#include "nsPrintfCString.h"
+#include "mozilla/DebugOnly.h"
+
+#ifdef XP_MACOSX
+#include "nsILocalFileMac.h"
+#include "nsCommandLineServiceMac.h"
+#include "MacLaunchHelper.h"
+#include "updaterfileutils_osx.h"
+#endif
+
+#if defined(XP_WIN)
+# include <direct.h>
+# include <process.h>
+# include <windows.h>
+# include <shlwapi.h>
+# include "nsWindowsHelpers.h"
+# define getcwd(path, size) _getcwd(path, size)
+# define getpid() GetCurrentProcessId()
+#elif defined(XP_UNIX)
+# include <unistd.h>
+# include <sys/wait.h>
+#endif
+
+using namespace mozilla;
+
+static PRLogModuleInfo *
+GetUpdateLog()
+{
+ static PRLogModuleInfo *sUpdateLog;
+ if (!sUpdateLog)
+ sUpdateLog = PR_NewLogModule("updatedriver");
+ return sUpdateLog;
+}
+#define LOG(args) MOZ_LOG(GetUpdateLog(), mozilla::LogLevel::Debug, args)
+
+#ifdef XP_WIN
+#define UPDATER_BIN "updater.exe"
+#elif XP_MACOSX
+#define UPDATER_BIN "org.mozilla.updater"
+#else
+#define UPDATER_BIN "updater"
+#endif
+#define UPDATER_INI "updater.ini"
+#ifdef XP_MACOSX
+#define UPDATER_APP "updater.app"
+#endif
+#if defined(XP_UNIX) && !defined(XP_MACOSX)
+#define UPDATER_PNG "updater.png"
+#endif
+
+#if defined(MOZ_WIDGET_GONK)
+#include <linux/ioprio.h>
+
+static const int kB2GServiceArgc = 2;
+static const char *kB2GServiceArgv[] = { "/system/bin/start", "b2g" };
+
+static const char kAppUpdaterPrio[] = "app.update.updater.prio";
+static const char kAppUpdaterOomScoreAdj[] = "app.update.updater.oom_score_adj";
+static const char kAppUpdaterIOPrioClass[] = "app.update.updater.ioprio.class";
+static const char kAppUpdaterIOPrioLevel[] = "app.update.updater.ioprio.level";
+
+static const int kAppUpdaterPrioDefault = 19; // -20..19 where 19 = lowest priority
+static const int kAppUpdaterOomScoreAdjDefault = -1000; // -1000 = Never kill
+static const int kAppUpdaterIOPrioClassDefault = IOPRIO_CLASS_IDLE;
+static const int kAppUpdaterIOPrioLevelDefault = 0; // Doesn't matter for CLASS IDLE
+#endif
+
+static nsresult
+GetCurrentWorkingDir(char *buf, size_t size)
+{
+ // Cannot use NS_GetSpecialDirectory because XPCOM is not yet initialized.
+ // This code is duplicated from xpcom/io/SpecialSystemDirectory.cpp:
+
+#if defined(XP_WIN)
+ wchar_t wpath[MAX_PATH];
+ if (!_wgetcwd(wpath, size))
+ return NS_ERROR_FAILURE;
+ NS_ConvertUTF16toUTF8 path(wpath);
+ strncpy(buf, path.get(), size);
+#else
+ if(!getcwd(buf, size))
+ return NS_ERROR_FAILURE;
+#endif
+ return NS_OK;
+}
+
+/**
+ * Get the path to the installation directory. For Mac OS X this will be the
+ * bundle directory.
+ *
+ * @param appDir the application directory file object
+ * @param installDirPath the path to the installation directory
+ */
+static nsresult
+GetInstallDirPath(nsIFile *appDir, nsACString& installDirPath)
+{
+ nsresult rv;
+#ifdef XP_MACOSX
+ nsCOMPtr<nsIFile> parentDir1, parentDir2;
+ rv = appDir->GetParent(getter_AddRefs(parentDir1));
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+ rv = parentDir1->GetParent(getter_AddRefs(parentDir2));
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+ rv = parentDir2->GetNativePath(installDirPath);
+#elif XP_WIN
+ nsAutoString installDirPathW;
+ rv = appDir->GetPath(installDirPathW);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+ installDirPath = NS_ConvertUTF16toUTF8(installDirPathW);
+#else
+ rv = appDir->GetNativePath(installDirPath);
+#endif
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+ return NS_OK;
+}
+
+#if defined(XP_MACOSX)
+// This is a copy of OS X's XRE_GetBinaryPath from nsAppRunner.cpp with the
+// gBinaryPath check removed so that the updater can reload the stub executable
+// instead of xulrunner-bin. See bug 349737.
+static nsresult
+GetXULRunnerStubPath(const char* argv0, nsIFile* *aResult)
+{
+ // Works even if we're not bundled.
+ CFBundleRef appBundle = ::CFBundleGetMainBundle();
+ if (!appBundle)
+ return NS_ERROR_FAILURE;
+
+ CFURLRef bundleURL = ::CFBundleCopyExecutableURL(appBundle);
+ if (!bundleURL)
+ return NS_ERROR_FAILURE;
+
+ nsCOMPtr<nsILocalFileMac> lfm;
+ nsresult rv = NS_NewLocalFileWithCFURL(bundleURL, true, getter_AddRefs(lfm));
+
+ ::CFRelease(bundleURL);
+
+ if (NS_FAILED(rv))
+ return rv;
+
+ lfm.forget(aResult);
+ return NS_OK;
+}
+#endif /* XP_MACOSX */
+
+static bool
+GetFile(nsIFile *dir, const nsCSubstring &name, nsCOMPtr<nsIFile> &result)
+{
+ nsresult rv;
+
+ nsCOMPtr<nsIFile> file;
+ rv = dir->Clone(getter_AddRefs(file));
+ if (NS_FAILED(rv))
+ return false;
+
+ rv = file->AppendNative(name);
+ if (NS_FAILED(rv))
+ return false;
+
+ result = do_QueryInterface(file, &rv);
+ return NS_SUCCEEDED(rv);
+}
+
+static bool
+GetStatusFile(nsIFile *dir, nsCOMPtr<nsIFile> &result)
+{
+ return GetFile(dir, NS_LITERAL_CSTRING("update.status"), result);
+}
+
+/**
+ * Get the contents of the update.status file.
+ *
+ * @param statusFile the status file object.
+ * @param buf the buffer holding the file contents
+ *
+ * @return true if successful, false otherwise.
+ */
+template <size_t Size>
+static bool
+GetStatusFileContents(nsIFile *statusFile, char (&buf)[Size])
+{
+ static_assert(Size > 16, "Buffer needs to be large enough to hold the known status codes");
+
+ PRFileDesc *fd = nullptr;
+ nsresult rv = statusFile->OpenNSPRFileDesc(PR_RDONLY, 0660, &fd);
+ if (NS_FAILED(rv))
+ return false;
+
+ const int32_t n = PR_Read(fd, buf, Size);
+ PR_Close(fd);
+
+ return (n >= 0);
+}
+
+typedef enum {
+ eNoUpdateAction,
+ ePendingUpdate,
+ ePendingService,
+ ePendingElevate,
+ eAppliedUpdate,
+ eAppliedService,
+} UpdateStatus;
+
+/**
+ * Returns a value indicating what needs to be done in order to handle an update.
+ *
+ * @param dir the directory in which we should look for an update.status file.
+ * @param statusFile the update.status file found in the directory.
+ *
+ * @return the update action to be performed.
+ */
+static UpdateStatus
+GetUpdateStatus(nsIFile* dir, nsCOMPtr<nsIFile> &statusFile)
+{
+ if (GetStatusFile(dir, statusFile)) {
+ char buf[32];
+ if (GetStatusFileContents(statusFile, buf)) {
+ const char kPending[] = "pending";
+ const char kPendingService[] = "pending-service";
+ const char kPendingElevate[] = "pending-elevate";
+ const char kApplied[] = "applied";
+ const char kAppliedService[] = "applied-service";
+ if (!strncmp(buf, kPendingElevate, sizeof(kPendingElevate) - 1)) {
+ return ePendingElevate;
+ }
+ if (!strncmp(buf, kPendingService, sizeof(kPendingService) - 1)) {
+ return ePendingService;
+ }
+ if (!strncmp(buf, kPending, sizeof(kPending) - 1)) {
+ return ePendingUpdate;
+ }
+ if (!strncmp(buf, kAppliedService, sizeof(kAppliedService) - 1)) {
+ return eAppliedService;
+ }
+ if (!strncmp(buf, kApplied, sizeof(kApplied) - 1)) {
+ return eAppliedUpdate;
+ }
+ }
+ }
+ return eNoUpdateAction;
+}
+
+static bool
+GetVersionFile(nsIFile *dir, nsCOMPtr<nsIFile> &result)
+{
+ return GetFile(dir, NS_LITERAL_CSTRING("update.version"), result);
+}
+
+// Compares the current application version with the update's application
+// version.
+static bool
+IsOlderVersion(nsIFile *versionFile, const char *appVersion)
+{
+ PRFileDesc *fd = nullptr;
+ nsresult rv = versionFile->OpenNSPRFileDesc(PR_RDONLY, 0660, &fd);
+ if (NS_FAILED(rv))
+ return true;
+
+ char buf[32];
+ const int32_t n = PR_Read(fd, buf, sizeof(buf));
+ PR_Close(fd);
+
+ if (n < 0)
+ return false;
+
+ // Trim off the trailing newline
+ if (buf[n - 1] == '\n')
+ buf[n - 1] = '\0';
+
+ // If the update xml doesn't provide the application version the file will
+ // contain the string "null" and it is assumed that the update is not older.
+ const char kNull[] = "null";
+ if (strncmp(buf, kNull, sizeof(kNull) - 1) == 0)
+ return false;
+
+ if (mozilla::Version(appVersion) > buf)
+ return true;
+
+ return false;
+}
+
+static bool
+CopyFileIntoUpdateDir(nsIFile *parentDir, const nsACString& leaf, nsIFile *updateDir)
+{
+ nsCOMPtr<nsIFile> file;
+
+ // Make sure there is not an existing file in the target location.
+ nsresult rv = updateDir->Clone(getter_AddRefs(file));
+ if (NS_FAILED(rv))
+ return false;
+ rv = file->AppendNative(leaf);
+ if (NS_FAILED(rv))
+ return false;
+ file->Remove(true);
+
+ // Now, copy into the target location.
+ rv = parentDir->Clone(getter_AddRefs(file));
+ if (NS_FAILED(rv))
+ return false;
+ rv = file->AppendNative(leaf);
+ if (NS_FAILED(rv))
+ return false;
+ rv = file->CopyToNative(updateDir, EmptyCString());
+ if (NS_FAILED(rv))
+ return false;
+
+ return true;
+}
+
+static bool
+CopyUpdaterIntoUpdateDir(nsIFile *greDir, nsIFile *appDir, nsIFile *updateDir,
+ nsCOMPtr<nsIFile> &updater)
+{
+ // Copy the updater application from the GRE and the updater ini from the app
+#if defined(XP_MACOSX)
+ if (!CopyFileIntoUpdateDir(appDir, NS_LITERAL_CSTRING(UPDATER_APP), updateDir))
+ return false;
+ CopyFileIntoUpdateDir(greDir, NS_LITERAL_CSTRING(UPDATER_INI), updateDir);
+#else
+ if (!CopyFileIntoUpdateDir(greDir, NS_LITERAL_CSTRING(UPDATER_BIN), updateDir))
+ return false;
+ CopyFileIntoUpdateDir(appDir, NS_LITERAL_CSTRING(UPDATER_INI), updateDir);
+#endif
+#if defined(XP_UNIX) && !defined(XP_MACOSX) && !defined(ANDROID)
+ nsCOMPtr<nsIFile> iconDir;
+ appDir->Clone(getter_AddRefs(iconDir));
+ iconDir->AppendNative(NS_LITERAL_CSTRING("icons"));
+ if (!CopyFileIntoUpdateDir(iconDir, NS_LITERAL_CSTRING(UPDATER_PNG), updateDir))
+ return false;
+#endif
+ // Finally, return the location of the updater binary.
+ nsresult rv = updateDir->Clone(getter_AddRefs(updater));
+ if (NS_FAILED(rv))
+ return false;
+#if defined(XP_MACOSX)
+ rv = updater->AppendNative(NS_LITERAL_CSTRING(UPDATER_APP));
+ nsresult tmp = updater->AppendNative(NS_LITERAL_CSTRING("Contents"));
+ if (NS_FAILED(tmp)) {
+ rv = tmp;
+ }
+ tmp = updater->AppendNative(NS_LITERAL_CSTRING("MacOS"));
+ if (NS_FAILED(tmp) || NS_FAILED(rv))
+ return false;
+#endif
+ rv = updater->AppendNative(NS_LITERAL_CSTRING(UPDATER_BIN));
+ return NS_SUCCEEDED(rv);
+}
+
+/**
+ * Appends the specified path to the library path.
+ * This is used so that updater can find libmozsqlite3.so and other shared libs.
+ *
+ * @param pathToAppend A new library path to prepend to LD_LIBRARY_PATH
+ */
+#if defined(MOZ_VERIFY_MAR_SIGNATURE) && !defined(XP_WIN) && \
+ !defined(XP_MACOSX) && !defined(MOZ_WIDGET_GONK)
+#include "prprf.h"
+#define PATH_SEPARATOR ":"
+#define LD_LIBRARY_PATH_ENVVAR_NAME "LD_LIBRARY_PATH"
+static void
+AppendToLibPath(const char *pathToAppend)
+{
+ char *pathValue = getenv(LD_LIBRARY_PATH_ENVVAR_NAME);
+ if (nullptr == pathValue || '\0' == *pathValue) {
+ char *s = PR_smprintf("%s=%s", LD_LIBRARY_PATH_ENVVAR_NAME, pathToAppend);
+ PR_SetEnv(s);
+ } else if (!strstr(pathValue, pathToAppend)) {
+ char *s = PR_smprintf("%s=%s" PATH_SEPARATOR "%s",
+ LD_LIBRARY_PATH_ENVVAR_NAME, pathToAppend, pathValue);
+ PR_SetEnv(s);
+ }
+
+ // The memory used by PR_SetEnv is not copied to the environment on all
+ // platform, it can be used by reference directly. So we purposely do not
+ // call PR_smprintf_free on s. Subsequent calls to PR_SetEnv will free
+ // the old memory first.
+}
+#endif
+
+/**
+ * Switch an existing application directory to an updated version that has been
+ * staged.
+ *
+ * @param greDir the GRE dir
+ * @param updateDir the update dir where the mar file is located
+ * @param appDir the app dir
+ * @param appArgc the number of args to the application
+ * @param appArgv the args to the application, used for restarting if needed
+ */
+static void
+SwitchToUpdatedApp(nsIFile *greDir, nsIFile *updateDir,
+ nsIFile *appDir, int appArgc, char **appArgv)
+{
+ nsresult rv;
+
+ // Steps:
+ // - copy updater into updates/0/MozUpdater/bgupdate/ dir on all platforms
+ // except Windows
+ // - run updater with the correct arguments
+#ifndef XP_WIN
+ nsCOMPtr<nsIFile> mozUpdaterDir;
+ rv = updateDir->Clone(getter_AddRefs(mozUpdaterDir));
+ if (NS_FAILED(rv)) {
+ LOG(("failed cloning update dir\n"));
+ return;
+ }
+
+ // Create a new directory named MozUpdater in the updates/0 directory to copy
+ // the updater files to that will be used to replace the installation with the
+ // staged application that has been updated. Note that we don't check for
+ // directory creation errors since the call to CopyUpdaterIntoUpdateDir will
+ // fail if the creation of the directory fails. A unique directory is created
+ // in MozUpdater in case a previous attempt locked the directory or files.
+ mozUpdaterDir->Append(NS_LITERAL_STRING("MozUpdater"));
+ mozUpdaterDir->Append(NS_LITERAL_STRING("bgupdate"));
+ rv = mozUpdaterDir->CreateUnique(nsIFile::DIRECTORY_TYPE, 0755);
+ if (NS_FAILED(rv)) {
+ LOG(("failed creating unique dir\n"));
+ return;
+ }
+
+ nsCOMPtr<nsIFile> updater;
+ if (!CopyUpdaterIntoUpdateDir(greDir, appDir, mozUpdaterDir, updater)) {
+ LOG(("failed copying updater\n"));
+ return;
+ }
+#endif
+
+ // We need to use the value returned from XRE_GetBinaryPath when attempting
+ // to restart the running application.
+ nsCOMPtr<nsIFile> appFile;
+
+#if defined(XP_MACOSX)
+ // On OS X we need to pass the location of the xulrunner-stub executable
+ // rather than xulrunner-bin. See bug 349737.
+ GetXULRunnerStubPath(appArgv[0], getter_AddRefs(appFile));
+#else
+ XRE_GetBinaryPath(appArgv[0], getter_AddRefs(appFile));
+#endif
+
+ if (!appFile)
+ return;
+
+#ifdef XP_WIN
+ nsAutoString appFilePathW;
+ rv = appFile->GetPath(appFilePathW);
+ if (NS_FAILED(rv)) {
+ return;
+ }
+ NS_ConvertUTF16toUTF8 appFilePath(appFilePathW);
+
+ nsCOMPtr<nsIFile> updater;
+ rv = greDir->Clone(getter_AddRefs(updater));
+ if (NS_FAILED(rv)) {
+ return;
+ }
+
+ rv = updater->AppendNative(NS_LITERAL_CSTRING(UPDATER_BIN));
+ if (NS_FAILED(rv)) {
+ return;
+ }
+
+ nsAutoString updaterPathW;
+ rv = updater->GetPath(updaterPathW);
+ if (NS_FAILED(rv)) {
+ return;
+ }
+ NS_ConvertUTF16toUTF8 updaterPath(updaterPathW);
+#else
+
+ nsAutoCString appFilePath;
+#if defined(MOZ_WIDGET_GONK)
+ appFilePath.Assign(kB2GServiceArgv[0]);
+ appArgc = kB2GServiceArgc;
+ appArgv = const_cast<char**>(kB2GServiceArgv);
+#else
+ rv = appFile->GetNativePath(appFilePath);
+ if (NS_FAILED(rv))
+ return;
+#endif
+
+ nsAutoCString updaterPath;
+ rv = updater->GetNativePath(updaterPath);
+ if (NS_FAILED(rv))
+ return;
+#endif
+
+ nsAutoCString installDirPath;
+ rv = GetInstallDirPath(appDir, installDirPath);
+ if (NS_FAILED(rv)) {
+ return;
+ }
+
+ // Get the directory where the update will be staged.
+ nsAutoCString applyToDir;
+ nsCOMPtr<nsIFile> updatedDir;
+#ifdef XP_MACOSX
+ if (!GetFile(updateDir, NS_LITERAL_CSTRING("Updated.app"), updatedDir)) {
+#else
+ if (!GetFile(appDir, NS_LITERAL_CSTRING("updated"), updatedDir)) {
+#endif
+ return;
+ }
+#ifdef XP_WIN
+ nsAutoString applyToDirW;
+ rv = updatedDir->GetPath(applyToDirW);
+ if (NS_FAILED(rv)) {
+ return;
+ }
+ applyToDir = NS_ConvertUTF16toUTF8(applyToDirW);
+#else
+ rv = updatedDir->GetNativePath(applyToDir);
+#endif
+ if (NS_FAILED(rv)) {
+ return;
+ }
+
+ // Make sure that the updated directory exists
+ bool updatedDirExists = false;
+ updatedDir->Exists(&updatedDirExists);
+ if (!updatedDirExists) {
+ return;
+ }
+
+#if defined(XP_WIN)
+ nsAutoString updateDirPathW;
+ rv = updateDir->GetPath(updateDirPathW);
+ NS_ConvertUTF16toUTF8 updateDirPath(updateDirPathW);
+#else
+ nsAutoCString updateDirPath;
+ rv = updateDir->GetNativePath(updateDirPath);
+#endif
+ if (NS_FAILED(rv))
+ return;
+
+ // Get the current working directory.
+ char workingDirPath[MAXPATHLEN];
+ rv = GetCurrentWorkingDir(workingDirPath, sizeof(workingDirPath));
+ if (NS_FAILED(rv))
+ return;
+
+ // Construct the PID argument for this process. We start the updater using
+ // execv on all Unix platforms except Mac, so on those platforms we pass 0
+ // instead of a good PID to signal the updater not to try and wait for us.
+#if defined(XP_UNIX) & !defined(XP_MACOSX)
+ nsAutoCString pid("0");
+#else
+ nsAutoCString pid;
+ pid.AppendInt((int32_t) getpid());
+#endif
+
+ // Append a special token to the PID in order to let the updater know that it
+ // just needs to replace the update directory.
+ pid.AppendLiteral("/replace");
+
+ int immersiveArgc = 0;
+ int argc = appArgc + 6 + immersiveArgc;
+ char **argv = new char*[argc + 1];
+ if (!argv)
+ return;
+ argv[0] = (char*) updaterPath.get();
+ argv[1] = (char*) updateDirPath.get();
+ argv[2] = (char*) installDirPath.get();
+ argv[3] = (char*) applyToDir.get();
+ argv[4] = (char*) pid.get();
+ if (appArgc) {
+ argv[5] = workingDirPath;
+ argv[6] = (char*) appFilePath.get();
+ for (int i = 1; i < appArgc; ++i)
+ argv[6 + i] = appArgv[i];
+#ifdef XP_WIN
+ if (immersiveArgc) {
+ argv[argc - 1] = "-ServerName:DefaultBrowserServer";
+ }
+#endif
+ argv[argc] = nullptr;
+ } else {
+ argc = 5;
+ argv[5] = nullptr;
+ }
+
+ if (gSafeMode) {
+ PR_SetEnv("MOZ_SAFE_MODE_RESTART=1");
+ }
+#if defined(MOZ_VERIFY_MAR_SIGNATURE) && !defined(XP_WIN) && \
+ !defined(XP_MACOSX) && !defined(MOZ_WIDGET_GONK)
+ AppendToLibPath(installDirPath.get());
+#endif
+
+ LOG(("spawning updater process for replacing [%s]\n", updaterPath.get()));
+
+#if defined(XP_UNIX) & !defined(XP_MACOSX)
+# if defined(MOZ_WIDGET_GONK)
+ // In Gonk, we preload libmozglue, which the updater process doesn't need.
+ // Since the updater will move and delete libmozglue.so, this can actually
+ // stop the /system mount from correctly being remounted as read-only.
+ unsetenv("LD_PRELOAD");
+# endif
+ exit(execv(updaterPath.get(), argv));
+#elif defined(XP_WIN)
+ // Switch the application using updater.exe
+ if (!WinLaunchChild(updaterPathW.get(), argc, argv)) {
+ return;
+ }
+ _exit(0);
+#elif defined(XP_MACOSX)
+ CommandLineServiceMac::SetupMacCommandLine(argc, argv, true);
+ LaunchChildMac(argc, argv);
+ exit(0);
+#else
+ PR_CreateProcessDetached(updaterPath.get(), argv, nullptr, nullptr);
+ exit(0);
+#endif
+}
+
+#if defined(MOZ_WIDGET_GONK)
+static nsresult
+GetOSApplyToDir(nsACString& applyToDir)
+{
+ nsCOMPtr<nsIProperties> ds =
+ do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID);
+ NS_ASSERTION(ds, "Can't get directory service");
+
+ nsCOMPtr<nsIFile> osApplyToDir;
+ nsresult rv = ds->Get(XRE_OS_UPDATE_APPLY_TO_DIR, NS_GET_IID(nsIFile),
+ getter_AddRefs(osApplyToDir));
+ if (NS_FAILED(rv)) {
+ LOG(("Can't get the OS applyTo dir"));
+ return rv;
+ }
+
+ return osApplyToDir->GetNativePath(applyToDir);
+}
+
+static void
+SetOSApplyToDir(nsIUpdate* update, const nsACString& osApplyToDir)
+{
+ nsresult rv;
+ nsCOMPtr<nsIWritablePropertyBag> updateProperties =
+ do_QueryInterface(update, &rv);
+
+ if (NS_FAILED(rv)) {
+ return;
+ }
+
+ RefPtr<nsVariant> variant = new nsVariant();
+ rv = variant->SetAsACString(osApplyToDir);
+ if (NS_FAILED(rv)) {
+ return;
+ }
+
+ updateProperties->SetProperty(NS_LITERAL_STRING("osApplyToDir"), variant);
+}
+#endif
+
+/**
+ * Apply an update. This applies to both normal and staged updates.
+ *
+ * @param greDir the GRE dir
+ * @param updateDir the update root dir
+ * @param statusFile the update.status file
+ * @param appDir the app dir
+ * @param appArgc the number of args to the application
+ * @param appArgv the args to the application, used for restarting if needed
+ * @param restart if true, apply the update in the foreground and restart the
+ * application when done. otherwise, stage the update and don't
+ * restart the application.
+ * @param outpid out parameter holding the handle to the updater application for
+ * staging updates.
+ */
+static void
+ApplyUpdate(nsIFile *greDir, nsIFile *updateDir, nsIFile *statusFile,
+ nsIFile *appDir, int appArgc, char **appArgv,
+ bool restart, bool isOSUpdate, nsIFile *osApplyToDir,
+ ProcessType *outpid)
+{
+ nsresult rv;
+
+ // Steps:
+ // - mark update as 'applying'
+ // - copy updater into update dir on all platforms except Windows
+ // - run updater w/ appDir as the current working dir
+#ifndef XP_WIN
+ nsCOMPtr<nsIFile> updater;
+ if (!CopyUpdaterIntoUpdateDir(greDir, appDir, updateDir, updater)) {
+ LOG(("failed copying updater\n"));
+ return;
+ }
+#endif
+
+ // We need to use the value returned from XRE_GetBinaryPath when attempting
+ // to restart the running application.
+ nsCOMPtr<nsIFile> appFile;
+
+#if defined(XP_MACOSX)
+ // On OS X we need to pass the location of the xulrunner-stub executable
+ // rather than xulrunner-bin. See bug 349737.
+ GetXULRunnerStubPath(appArgv[0], getter_AddRefs(appFile));
+#else
+ XRE_GetBinaryPath(appArgv[0], getter_AddRefs(appFile));
+#endif
+
+ if (!appFile)
+ return;
+
+#ifdef XP_WIN
+ nsAutoString appFilePathW;
+ rv = appFile->GetPath(appFilePathW);
+ if (NS_FAILED(rv)) {
+ return;
+ }
+ NS_ConvertUTF16toUTF8 appFilePath(appFilePathW);
+
+ nsCOMPtr<nsIFile> updater;
+ rv = greDir->Clone(getter_AddRefs(updater));
+ if (NS_FAILED(rv)) {
+ return;
+ }
+
+ rv = updater->AppendNative(NS_LITERAL_CSTRING(UPDATER_BIN));
+ if (NS_FAILED(rv)) {
+ return;
+ }
+
+ nsAutoString updaterPathW;
+ rv = updater->GetPath(updaterPathW);
+ if (NS_FAILED(rv)) {
+ return;
+ }
+ NS_ConvertUTF16toUTF8 updaterPath(updaterPathW);
+#else
+ nsAutoCString appFilePath;
+ rv = appFile->GetNativePath(appFilePath);
+ if (NS_FAILED(rv))
+ return;
+
+ nsAutoCString updaterPath;
+ rv = updater->GetNativePath(updaterPath);
+ if (NS_FAILED(rv))
+ return;
+
+#endif
+
+ nsAutoCString installDirPath;
+ rv = GetInstallDirPath(appDir, installDirPath);
+ if (NS_FAILED(rv))
+ return;
+
+ // Get the directory where the update was staged for replace and GONK OS
+ // Updates or where it will be applied.
+#ifndef MOZ_WIDGET_GONK
+ // OS Updates are only supported on GONK so force it to false on everything
+ // but GONK to simplify the following logic.
+ isOSUpdate = false;
+#endif
+ nsAutoCString applyToDir;
+ nsCOMPtr<nsIFile> updatedDir;
+ if (restart && !isOSUpdate) {
+ applyToDir.Assign(installDirPath);
+ } else {
+#ifdef XP_MACOSX
+ if (!GetFile(updateDir, NS_LITERAL_CSTRING("Updated.app"), updatedDir)) {
+#else
+ if (!GetFile(appDir, NS_LITERAL_CSTRING("updated"), updatedDir)) {
+#endif
+ return;
+ }
+#ifdef XP_WIN
+ nsAutoString applyToDirW;
+ rv = updatedDir->GetPath(applyToDirW);
+ if (NS_FAILED(rv)) {
+ return;
+ }
+ applyToDir = NS_ConvertUTF16toUTF8(applyToDirW);
+#elif MOZ_WIDGET_GONK
+ if (isOSUpdate) {
+ if (!osApplyToDir) {
+ return;
+ }
+ rv = osApplyToDir->GetNativePath(applyToDir);
+ } else {
+ rv = updatedDir->GetNativePath(applyToDir);
+ }
+#else
+ rv = updatedDir->GetNativePath(applyToDir);
+#endif
+ }
+ if (NS_FAILED(rv))
+ return;
+
+#if defined(XP_WIN)
+ nsAutoString updateDirPathW;
+ rv = updateDir->GetPath(updateDirPathW);
+ NS_ConvertUTF16toUTF8 updateDirPath(updateDirPathW);
+#else
+ nsAutoCString updateDirPath;
+ rv = updateDir->GetNativePath(updateDirPath);
+#endif
+ if (NS_FAILED(rv)) {
+ return;
+ }
+
+ // Get the current working directory.
+ char workingDirPath[MAXPATHLEN];
+ rv = GetCurrentWorkingDir(workingDirPath, sizeof(workingDirPath));
+ if (NS_FAILED(rv))
+ return;
+
+ // We used to write out "Applying" to the update.status file here.
+ // Instead we do this from within the updater application now.
+ // This is so that we don't overwrite the status of pending-service
+ // in the Windows case. This change was made for all platforms so
+ // that it stays consistent across all OS.
+
+ // On platforms where we are not calling execv, we may need to make the
+ // updater executable wait for the calling process to exit. Otherwise, the
+ // updater may have trouble modifying our executable image (because it might
+ // still be in use). This is accomplished by passing our PID to the updater so
+ // that it can wait for us to exit. This is not perfect as there is a race
+ // condition that could bite us. It's possible that the calling process could
+ // exit before the updater waits on the specified PID, and in the meantime a
+ // new process with the same PID could be created. This situation is unlikely,
+ // however, given the way most operating systems recycle PIDs. We'll take our
+ // chances ;-)
+ // Construct the PID argument for this process. If we are using execv, then
+ // we pass "0" which is then ignored by the updater.
+ nsAutoCString pid;
+ if (!restart) {
+ // Signal the updater application that it should stage the update.
+ pid.AssignASCII("-1");
+ } else {
+#if defined(XP_UNIX) & !defined(XP_MACOSX)
+ pid.AssignASCII("0");
+#else
+ pid.AppendInt((int32_t) getpid());
+#endif
+ }
+
+ int immersiveArgc = 0;
+ int argc = appArgc + 6 + immersiveArgc;
+ char **argv = new char*[argc + 1 ];
+ if (!argv)
+ return;
+ argv[0] = (char*) updaterPath.get();
+ argv[1] = (char*) updateDirPath.get();
+ argv[2] = (char*) installDirPath.get();
+ argv[3] = (char*) applyToDir.get();
+ argv[4] = (char*) pid.get();
+ if (restart && appArgc) {
+ argv[5] = workingDirPath;
+ argv[6] = (char*) appFilePath.get();
+ for (int i = 1; i < appArgc; ++i)
+ argv[6 + i] = appArgv[i];
+#ifdef XP_WIN
+ if (immersiveArgc) {
+ argv[argc - 1] = "-ServerName:DefaultBrowserServer";
+ }
+#endif
+ argv[argc] = nullptr;
+ } else {
+ argc = 5;
+ argv[5] = nullptr;
+ }
+
+ if (gSafeMode) {
+ PR_SetEnv("MOZ_SAFE_MODE_RESTART=1");
+ }
+#if defined(MOZ_VERIFY_MAR_SIGNATURE) && !defined(XP_WIN) && \
+ !defined(XP_MACOSX) && !defined(MOZ_WIDGET_GONK)
+ AppendToLibPath(installDirPath.get());
+#endif
+
+ if (isOSUpdate) {
+ PR_SetEnv("MOZ_OS_UPDATE=1");
+ }
+#if defined(MOZ_WIDGET_GONK)
+ // We want the updater to be CPU friendly and not subject to being killed by
+ // the low memory killer, so we pass in some preferences to allow it to
+ // adjust its priority.
+
+ int32_t prioVal = Preferences::GetInt(kAppUpdaterPrio,
+ kAppUpdaterPrioDefault);
+ int32_t oomScoreAdj = Preferences::GetInt(kAppUpdaterOomScoreAdj,
+ kAppUpdaterOomScoreAdjDefault);
+ int32_t ioprioClass = Preferences::GetInt(kAppUpdaterIOPrioClass,
+ kAppUpdaterIOPrioClassDefault);
+ int32_t ioprioLevel = Preferences::GetInt(kAppUpdaterIOPrioLevel,
+ kAppUpdaterIOPrioLevelDefault);
+ nsPrintfCString prioEnv("MOZ_UPDATER_PRIO=%d/%d/%d/%d",
+ prioVal, oomScoreAdj, ioprioClass, ioprioLevel);
+ // Note: we allocate a new string on heap and pass that to PR_SetEnv, since
+ // the string can be used after this function returns. This means that we
+ // will intentionally leak this buffer.
+ PR_SetEnv(ToNewCString(prioEnv));
+#endif
+
+ LOG(("spawning updater process [%s]\n", updaterPath.get()));
+
+#if defined(XP_UNIX) && !defined(XP_MACOSX)
+ // We use execv to spawn the updater process on all UNIX systems except Mac OSX
+ // since it is known to cause problems on the Mac. Windows has execv, but it
+ // is a faked implementation that doesn't really replace the current process.
+ // Instead it spawns a new process, so we gain nothing from using execv on
+ // Windows.
+ if (restart) {
+ exit(execv(updaterPath.get(), argv));
+ }
+ *outpid = fork();
+ if (*outpid == -1) {
+ return;
+ } else if (*outpid == 0) {
+ exit(execv(updaterPath.get(), argv));
+ }
+#elif defined(XP_WIN)
+ // Launch the update using updater.exe
+ if (!WinLaunchChild(updaterPathW.get(), argc, argv, nullptr, outpid)) {
+ return;
+ }
+
+ if (restart) {
+ // We are going to process an update so we should exit now
+ _exit(0);
+ }
+#elif defined(XP_MACOSX)
+ CommandLineServiceMac::SetupMacCommandLine(argc, argv, restart);
+ // We need to detect whether elevation is required for this update. This can
+ // occur when an admin user installs the application, but another admin
+ // user attempts to update (see bug 394984).
+ if (restart && !IsRecursivelyWritable(installDirPath.get())) {
+ if (!LaunchElevatedUpdate(argc, argv, outpid)) {
+ LOG(("Failed to launch elevated update!"));
+ exit(1);
+ }
+ exit(0);
+ } else {
+ if (restart) {
+ LaunchChildMac(argc, argv);
+ exit(0);
+ }
+ LaunchChildMac(argc, argv, outpid);
+ }
+#else
+ *outpid = PR_CreateProcess(updaterPath.get(), argv, nullptr, nullptr);
+ if (restart) {
+ exit(0);
+ }
+#endif
+}
+
+/**
+ * Wait briefly to see if a process terminates, then return true if it has.
+ */
+static bool
+ProcessHasTerminated(ProcessType pt)
+{
+#if defined(XP_WIN)
+ if (WaitForSingleObject(pt, 1000)) {
+ return false;
+ }
+ CloseHandle(pt);
+ return true;
+#elif defined(XP_MACOSX)
+ // We're waiting for the process to terminate in LaunchChildMac.
+ return true;
+#elif defined(XP_UNIX)
+ int exitStatus;
+ pid_t exited = waitpid(pt, &exitStatus, WNOHANG);
+ if (exited == 0) {
+ // Process is still running.
+ sleep(1);
+ return false;
+ }
+ if (exited == -1) {
+ LOG(("Error while checking if the updater process is finished"));
+ // This shouldn't happen, but if it does, the updater process is lost to us,
+ // so the best we can do is pretend that it's exited.
+ return true;
+ }
+ // If we get here, the process has exited; make sure it exited normally.
+ if (WIFEXITED(exitStatus) && (WEXITSTATUS(exitStatus) != 0)) {
+ LOG(("Error while running the updater process, check update.log"));
+ }
+ return true;
+#else
+ // No way to have a non-blocking implementation on these platforms,
+ // because we're using NSPR and it only provides a blocking wait.
+ int32_t exitCode;
+ PR_WaitProcess(pt, &exitCode);
+ if (exitCode != 0) {
+ LOG(("Error while running the updater process, check update.log"));
+ }
+ return true;
+#endif
+}
+
+nsresult
+ProcessUpdates(nsIFile *greDir, nsIFile *appDir, nsIFile *updRootDir,
+ int argc, char **argv, const char *appVersion,
+ bool restart, bool isOSUpdate, nsIFile *osApplyToDir,
+ ProcessType *pid)
+{
+ nsresult rv;
+
+ nsCOMPtr<nsIFile> updatesDir;
+ rv = updRootDir->Clone(getter_AddRefs(updatesDir));
+ if (NS_FAILED(rv))
+ return rv;
+
+ rv = updatesDir->AppendNative(NS_LITERAL_CSTRING("updates"));
+ if (NS_FAILED(rv))
+ return rv;
+
+ rv = updatesDir->AppendNative(NS_LITERAL_CSTRING("0"));
+ if (NS_FAILED(rv))
+ return rv;
+
+ // Return early since there isn't a valid update when the update application
+ // version file doesn't exist or if the update's application version is less
+ // than the current application version. The cleanup of the update will happen
+ // during post update processing in nsUpdateService.js.
+ nsCOMPtr<nsIFile> versionFile;
+ if (!GetVersionFile(updatesDir, versionFile) ||
+ IsOlderVersion(versionFile, appVersion)) {
+ return NS_OK;
+ }
+
+ nsCOMPtr<nsIFile> statusFile;
+ UpdateStatus status = GetUpdateStatus(updatesDir, statusFile);
+ switch (status) {
+ case ePendingElevate: {
+ if (NS_IsMainThread()) {
+ // Only do this if we're called from the main thread.
+ nsCOMPtr<nsIUpdatePrompt> up =
+ do_GetService("@mozilla.org/updates/update-prompt;1");
+ if (up) {
+ up->ShowUpdateElevationRequired();
+ }
+ break;
+ }
+ // Intentional fallthrough to ePendingUpdate and ePendingService.
+ MOZ_FALLTHROUGH;
+ }
+ case ePendingUpdate:
+ case ePendingService: {
+ ApplyUpdate(greDir, updatesDir, statusFile, appDir, argc, argv, restart,
+ isOSUpdate, osApplyToDir, pid);
+ break;
+ }
+ case eAppliedUpdate:
+ case eAppliedService:
+ // An update was staged and needs to be switched so the updated application
+ // is used.
+ SwitchToUpdatedApp(greDir, updatesDir, appDir, argc, argv);
+ break;
+ case eNoUpdateAction:
+ // We don't need to do any special processing here, we'll just continue to
+ // startup the application.
+ break;
+ }
+
+ return NS_OK;
+}
+
+
+
+NS_IMPL_ISUPPORTS(nsUpdateProcessor, nsIUpdateProcessor)
+
+nsUpdateProcessor::nsUpdateProcessor()
+ : mUpdaterPID(0)
+{
+}
+
+nsUpdateProcessor::~nsUpdateProcessor()
+{
+}
+
+NS_IMETHODIMP
+nsUpdateProcessor::ProcessUpdate(nsIUpdate* aUpdate)
+{
+ nsCOMPtr<nsIFile> greDir, appDir, updRoot;
+ nsAutoCString appVersion;
+ int argc;
+ char **argv;
+
+ nsAutoCString binPath;
+ nsXREDirProvider* dirProvider = nsXREDirProvider::GetSingleton();
+ if (dirProvider) { // Normal code path
+ // Check for and process any available updates
+ bool persistent;
+ nsresult rv = NS_ERROR_FAILURE; // Take the NS_FAILED path when non-GONK
+#ifdef MOZ_WIDGET_GONK
+ // Check in the sdcard for updates first, since that's our preferred
+ // download location.
+ rv = dirProvider->GetFile(XRE_UPDATE_ARCHIVE_DIR, &persistent,
+ getter_AddRefs(updRoot));
+#endif
+ if (NS_FAILED(rv)) {
+ rv = dirProvider->GetFile(XRE_UPDATE_ROOT_DIR, &persistent,
+ getter_AddRefs(updRoot));
+ }
+ // XRE_UPDATE_ROOT_DIR may fail. Fallback to appDir if failed
+ if (NS_FAILED(rv))
+ updRoot = dirProvider->GetAppDir();
+
+ greDir = dirProvider->GetGREDir();
+ nsCOMPtr<nsIFile> exeFile;
+ rv = dirProvider->GetFile(XRE_EXECUTABLE_FILE, &persistent,
+ getter_AddRefs(exeFile));
+ if (NS_SUCCEEDED(rv))
+ rv = exeFile->GetParent(getter_AddRefs(appDir));
+
+ if (NS_FAILED(rv))
+ appDir = dirProvider->GetAppDir();
+
+ appVersion = gAppData->version;
+ argc = gRestartArgc;
+ argv = gRestartArgv;
+ } else {
+ // In the xpcshell environment, the usual XRE_main is not run, so things
+ // like dirProvider and gAppData do not exist. This code path accesses
+ // XPCOM (which is not available in the previous code path) in order to get
+ // the same information.
+ nsCOMPtr<nsIProperties> ds =
+ do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID);
+ if (!ds) {
+ NS_ABORT(); // There's nothing which we can do if this fails!
+ }
+
+ nsresult rv = ds->Get(NS_GRE_DIR, NS_GET_IID(nsIFile),
+ getter_AddRefs(greDir));
+ NS_ASSERTION(NS_SUCCEEDED(rv), "Can't get the GRE dir");
+
+ nsCOMPtr<nsIFile> exeFile;
+ rv = ds->Get(XRE_EXECUTABLE_FILE, NS_GET_IID(nsIFile),
+ getter_AddRefs(exeFile));
+ if (NS_SUCCEEDED(rv))
+ rv = exeFile->GetParent(getter_AddRefs(appDir));
+
+ NS_ASSERTION(NS_SUCCEEDED(rv), "Can't get the XREExeF parent dir");
+
+ rv = ds->Get(XRE_UPDATE_ROOT_DIR, NS_GET_IID(nsIFile),
+ getter_AddRefs(updRoot));
+ NS_ASSERTION(NS_SUCCEEDED(rv), "Can't get the UpdRootD dir");
+
+ nsCOMPtr<nsIXULAppInfo> appInfo =
+ do_GetService("@mozilla.org/xre/app-info;1");
+ if (appInfo) {
+ rv = appInfo->GetVersion(appVersion);
+ NS_ENSURE_SUCCESS(rv, rv);
+ } else {
+ appVersion = MOZ_APP_VERSION;
+ }
+
+ // We need argv[0] to point to the current executable's name. The rest of
+ // the entries in this array will be ignored if argc<2. Therefore, for
+ // xpcshell, we only fill out that item, and leave the rest empty.
+ argc = 1;
+ nsCOMPtr<nsIFile> binary;
+ rv = ds->Get(XRE_EXECUTABLE_FILE, NS_GET_IID(nsIFile),
+ getter_AddRefs(binary));
+ NS_ASSERTION(NS_SUCCEEDED(rv), "Can't get the binary path");
+ binary->GetNativePath(binPath);
+ }
+
+ // Copy the parameters to the StagedUpdateInfo structure shared with the
+ // watcher thread.
+ mInfo.mGREDir = greDir;
+ mInfo.mAppDir = appDir;
+ mInfo.mUpdateRoot = updRoot;
+ mInfo.mArgc = argc;
+ mInfo.mArgv = new char*[argc];
+ if (dirProvider) {
+ for (int i = 0; i < argc; ++i) {
+ const size_t length = strlen(argv[i]);
+ mInfo.mArgv[i] = new char[length + 1];
+ strcpy(mInfo.mArgv[i], argv[i]);
+ }
+ } else {
+ MOZ_ASSERT(argc == 1); // see above
+ const size_t length = binPath.Length();
+ mInfo.mArgv[0] = new char[length + 1];
+ strcpy(mInfo.mArgv[0], binPath.get());
+ }
+ mInfo.mAppVersion = appVersion;
+
+#if defined(MOZ_WIDGET_GONK)
+ NS_ENSURE_ARG_POINTER(aUpdate);
+
+ bool isOSUpdate;
+ if (NS_SUCCEEDED(aUpdate->GetIsOSUpdate(&isOSUpdate)) &&
+ isOSUpdate) {
+ nsAutoCString osApplyToDir;
+
+ // This needs to be done on the main thread, so we pass it along in
+ // BackgroundThreadInfo
+ nsresult rv = GetOSApplyToDir(osApplyToDir);
+ if (NS_FAILED(rv)) {
+ LOG(("Can't get the OS apply to dir"));
+ return rv;
+ }
+
+ SetOSApplyToDir(aUpdate, osApplyToDir);
+
+ mInfo.mIsOSUpdate = true;
+ rv = NS_NewNativeLocalFile(osApplyToDir, false,
+ getter_AddRefs(mInfo.mOSApplyToDir));
+ if (NS_FAILED(rv)) {
+ LOG(("Can't create nsIFile for OS apply to dir"));
+ return rv;
+ }
+ }
+#endif
+
+ MOZ_ASSERT(NS_IsMainThread(), "not main thread");
+ nsCOMPtr<nsIRunnable> r = NewRunnableMethod(this, &nsUpdateProcessor::StartStagedUpdate);
+ return NS_NewThread(getter_AddRefs(mProcessWatcher), r);
+}
+
+
+
+void
+nsUpdateProcessor::StartStagedUpdate()
+{
+ MOZ_ASSERT(!NS_IsMainThread(), "main thread");
+
+ nsresult rv = ProcessUpdates(mInfo.mGREDir,
+ mInfo.mAppDir,
+ mInfo.mUpdateRoot,
+ mInfo.mArgc,
+ mInfo.mArgv,
+ mInfo.mAppVersion.get(),
+ false,
+ mInfo.mIsOSUpdate,
+ mInfo.mOSApplyToDir,
+ &mUpdaterPID);
+ NS_ENSURE_SUCCESS_VOID(rv);
+
+ if (mUpdaterPID) {
+ // Track the state of the updater process while it is staging an update.
+ rv = NS_DispatchToCurrentThread(NewRunnableMethod(this, &nsUpdateProcessor::WaitForProcess));
+ NS_ENSURE_SUCCESS_VOID(rv);
+ } else {
+ // Failed to launch the updater process for some reason.
+ // We need to shutdown the current thread as there isn't anything more for
+ // us to do...
+ rv = NS_DispatchToMainThread(NewRunnableMethod(this, &nsUpdateProcessor::ShutdownWatcherThread));
+ NS_ENSURE_SUCCESS_VOID(rv);
+ }
+}
+
+void
+nsUpdateProcessor::ShutdownWatcherThread()
+{
+ MOZ_ASSERT(NS_IsMainThread(), "not main thread");
+ mProcessWatcher->Shutdown();
+ mProcessWatcher = nullptr;
+}
+
+void
+nsUpdateProcessor::WaitForProcess()
+{
+ MOZ_ASSERT(!NS_IsMainThread(), "main thread");
+ if (ProcessHasTerminated(mUpdaterPID)) {
+ NS_DispatchToMainThread(NewRunnableMethod(this, &nsUpdateProcessor::UpdateDone));
+ } else {
+ NS_DispatchToCurrentThread(NewRunnableMethod(this, &nsUpdateProcessor::WaitForProcess));
+ }
+}
+
+void
+nsUpdateProcessor::UpdateDone()
+{
+ MOZ_ASSERT(NS_IsMainThread(), "not main thread");
+
+ nsCOMPtr<nsIUpdateManager> um =
+ do_GetService("@mozilla.org/updates/update-manager;1");
+ if (um) {
+ um->RefreshUpdateStatus();
+ }
+
+ ShutdownWatcherThread();
+}
diff --git a/toolkit/xre/nsUpdateDriver.h b/toolkit/xre/nsUpdateDriver.h
new file mode 100644
index 000000000..ed8775198
--- /dev/null
+++ b/toolkit/xre/nsUpdateDriver.h
@@ -0,0 +1,107 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:set ts=2 sw=2 sts=2 et cindent: */
+/* 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 nsUpdateDriver_h__
+#define nsUpdateDriver_h__
+
+#include "nscore.h"
+#include "nsIUpdateService.h"
+#include "nsIThread.h"
+#include "nsCOMPtr.h"
+#include "nsString.h"
+#include "mozilla/Attributes.h"
+
+class nsIFile;
+
+#if defined(XP_WIN)
+#include <windows.h>
+ typedef HANDLE ProcessType;
+#elif defined(XP_UNIX)
+ typedef pid_t ProcessType;
+#else
+#include "prproces.h"
+ typedef PRProcess* ProcessType;
+#endif
+
+/**
+ * This function processes any available updates. As part of that process, it
+ * may exit the current process and relaunch it at a later time.
+ *
+ * Two directories are passed to this function: greDir (where the actual
+ * binary resides) and appDir (which contains application.ini for XULRunner
+ * apps). If this is not a XULRunner app then appDir is identical to greDir.
+ *
+ * The argc and argv passed to this function should be what is needed to
+ * relaunch the current process.
+ *
+ * The appVersion param passed to this function is the current application's
+ * version and is used to determine if an update's version is older than the
+ * current application version.
+ *
+ * If you want the update to be processed without restarting, set the restart
+ * parameter to false.
+ *
+ * This function does not modify appDir.
+ */
+nsresult ProcessUpdates(nsIFile *greDir, nsIFile *appDir,
+ nsIFile *updRootDir,
+ int argc, char **argv,
+ const char *appVersion,
+ bool restart = true,
+ bool isOSUpdate = false,
+ nsIFile *osApplyToDir = nullptr,
+ ProcessType *pid = nullptr);
+
+// The implementation of the update processor handles the task of loading the
+// updater application for staging an update.
+// XXX ehsan this is living in this file in order to make use of the existing
+// stuff here, we might want to move it elsewhere in the future.
+class nsUpdateProcessor final : public nsIUpdateProcessor
+{
+public:
+ nsUpdateProcessor();
+
+ NS_DECL_THREADSAFE_ISUPPORTS
+ NS_DECL_NSIUPDATEPROCESSOR
+
+private:
+ ~nsUpdateProcessor();
+
+ struct StagedUpdateInfo {
+ StagedUpdateInfo()
+ : mArgc(0),
+ mArgv(nullptr),
+ mIsOSUpdate(false)
+ {}
+ ~StagedUpdateInfo() {
+ for (int i = 0; i < mArgc; ++i) {
+ delete[] mArgv[i];
+ }
+ delete[] mArgv;
+ }
+
+ nsCOMPtr<nsIFile> mGREDir;
+ nsCOMPtr<nsIFile> mAppDir;
+ nsCOMPtr<nsIFile> mUpdateRoot;
+ nsCOMPtr<nsIFile> mOSApplyToDir;
+ int mArgc;
+ char **mArgv;
+ nsCString mAppVersion;
+ bool mIsOSUpdate;
+ };
+
+private:
+ void StartStagedUpdate();
+ void WaitForProcess();
+ void UpdateDone();
+ void ShutdownWatcherThread();
+
+private:
+ ProcessType mUpdaterPID;
+ nsCOMPtr<nsIThread> mProcessWatcher;
+ StagedUpdateInfo mInfo;
+};
+#endif // nsUpdateDriver_h__
diff --git a/toolkit/xre/nsWindowsRestart.cpp b/toolkit/xre/nsWindowsRestart.cpp
new file mode 100644
index 000000000..f8a6ec48b
--- /dev/null
+++ b/toolkit/xre/nsWindowsRestart.cpp
@@ -0,0 +1,300 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// This file is not build directly. Instead, it is included in multiple
+// shared objects.
+
+#ifdef nsWindowsRestart_cpp
+#error "nsWindowsRestart.cpp is not a header file, and must only be included once."
+#else
+#define nsWindowsRestart_cpp
+#endif
+
+#include "nsUTF8Utils.h"
+
+#include <shellapi.h>
+
+// Needed for CreateEnvironmentBlock
+#include <userenv.h>
+#pragma comment(lib, "userenv.lib")
+
+/**
+ * Get the length that the string will take and takes into account the
+ * additional length if the string needs to be quoted and if characters need to
+ * be escaped.
+ */
+static int ArgStrLen(const wchar_t *s)
+{
+ int backslashes = 0;
+ int i = wcslen(s);
+ BOOL hasDoubleQuote = wcschr(s, L'"') != nullptr;
+ // Only add doublequotes if the string contains a space or a tab
+ BOOL addDoubleQuotes = wcspbrk(s, L" \t") != nullptr;
+
+ if (addDoubleQuotes) {
+ i += 2; // initial and final duoblequote
+ }
+
+ if (hasDoubleQuote) {
+ while (*s) {
+ if (*s == '\\') {
+ ++backslashes;
+ } else {
+ if (*s == '"') {
+ // Escape the doublequote and all backslashes preceding the doublequote
+ i += backslashes + 1;
+ }
+
+ backslashes = 0;
+ }
+
+ ++s;
+ }
+ }
+
+ return i;
+}
+
+/**
+ * Copy string "s" to string "d", quoting the argument as appropriate and
+ * escaping doublequotes along with any backslashes that immediately precede
+ * doublequotes.
+ * The CRT parses this to retrieve the original argc/argv that we meant,
+ * see STDARGV.C in the MSVC CRT sources.
+ *
+ * @return the end of the string
+ */
+static wchar_t* ArgToString(wchar_t *d, const wchar_t *s)
+{
+ int backslashes = 0;
+ BOOL hasDoubleQuote = wcschr(s, L'"') != nullptr;
+ // Only add doublequotes if the string contains a space or a tab
+ BOOL addDoubleQuotes = wcspbrk(s, L" \t") != nullptr;
+
+ if (addDoubleQuotes) {
+ *d = '"'; // initial doublequote
+ ++d;
+ }
+
+ if (hasDoubleQuote) {
+ int i;
+ while (*s) {
+ if (*s == '\\') {
+ ++backslashes;
+ } else {
+ if (*s == '"') {
+ // Escape the doublequote and all backslashes preceding the doublequote
+ for (i = 0; i <= backslashes; ++i) {
+ *d = '\\';
+ ++d;
+ }
+ }
+
+ backslashes = 0;
+ }
+
+ *d = *s;
+ ++d; ++s;
+ }
+ } else {
+ wcscpy(d, s);
+ d += wcslen(s);
+ }
+
+ if (addDoubleQuotes) {
+ *d = '"'; // final doublequote
+ ++d;
+ }
+
+ return d;
+}
+
+/**
+ * Creates a command line from a list of arguments. The returned
+ * string is allocated with "malloc" and should be "free"d.
+ *
+ * argv is UTF8
+ */
+wchar_t*
+MakeCommandLine(int argc, wchar_t **argv)
+{
+ int i;
+ int len = 0;
+
+ // The + 1 of the last argument handles the allocation for null termination
+ for (i = 0; i < argc; ++i)
+ len += ArgStrLen(argv[i]) + 1;
+
+ // Protect against callers that pass 0 arguments
+ if (len == 0)
+ len = 1;
+
+ wchar_t *s = (wchar_t*) malloc(len * sizeof(wchar_t));
+ if (!s)
+ return nullptr;
+
+ wchar_t *c = s;
+ for (i = 0; i < argc; ++i) {
+ c = ArgToString(c, argv[i]);
+ if (i + 1 != argc) {
+ *c = ' ';
+ ++c;
+ }
+ }
+
+ *c = '\0';
+
+ return s;
+}
+
+/**
+ * Convert UTF8 to UTF16 without using the normal XPCOM goop, which we
+ * can't link to updater.exe.
+ */
+static char16_t*
+AllocConvertUTF8toUTF16(const char *arg)
+{
+ // UTF16 can't be longer in units than UTF8
+ int len = strlen(arg);
+ char16_t *s = new char16_t[(len + 1) * sizeof(char16_t)];
+ if (!s)
+ return nullptr;
+
+ ConvertUTF8toUTF16 convert(s);
+ convert.write(arg, len);
+ convert.write_terminator();
+ return s;
+}
+
+static void
+FreeAllocStrings(int argc, wchar_t **argv)
+{
+ while (argc) {
+ --argc;
+ delete [] argv[argc];
+ }
+
+ delete [] argv;
+}
+
+
+
+/**
+ * Launch a child process with the specified arguments.
+ * @note argv[0] is ignored
+ * @note The form of this function that takes char **argv expects UTF-8
+ */
+
+BOOL
+WinLaunchChild(const wchar_t *exePath,
+ int argc, wchar_t **argv,
+ HANDLE userToken = nullptr,
+ HANDLE *hProcess = nullptr);
+
+BOOL
+WinLaunchChild(const wchar_t *exePath,
+ int argc, char **argv,
+ HANDLE userToken,
+ HANDLE *hProcess)
+{
+ wchar_t** argvConverted = new wchar_t*[argc];
+ if (!argvConverted)
+ return FALSE;
+
+ for (int i = 0; i < argc; ++i) {
+ argvConverted[i] = reinterpret_cast<wchar_t*>(AllocConvertUTF8toUTF16(argv[i]));
+ if (!argvConverted[i]) {
+ FreeAllocStrings(i, argvConverted);
+ return FALSE;
+ }
+ }
+
+ BOOL ok = WinLaunchChild(exePath, argc, argvConverted, userToken, hProcess);
+ FreeAllocStrings(argc, argvConverted);
+ return ok;
+}
+
+BOOL
+WinLaunchChild(const wchar_t *exePath,
+ int argc,
+ wchar_t **argv,
+ HANDLE userToken,
+ HANDLE *hProcess)
+{
+ wchar_t *cl;
+ BOOL ok;
+
+ cl = MakeCommandLine(argc, argv);
+ if (!cl) {
+ return FALSE;
+ }
+
+ STARTUPINFOW si = {0};
+ si.cb = sizeof(STARTUPINFOW);
+ si.lpDesktop = L"winsta0\\Default";
+ PROCESS_INFORMATION pi = {0};
+
+ if (userToken == nullptr) {
+ ok = CreateProcessW(exePath,
+ cl,
+ nullptr, // no special security attributes
+ nullptr, // no special thread attributes
+ FALSE, // don't inherit filehandles
+ 0, // creation flags
+ nullptr, // inherit my environment
+ nullptr, // use my current directory
+ &si,
+ &pi);
+ } else {
+ // Create an environment block for the process we're about to start using
+ // the user's token.
+ LPVOID environmentBlock = nullptr;
+ if (!CreateEnvironmentBlock(&environmentBlock, userToken, TRUE)) {
+ environmentBlock = nullptr;
+ }
+
+ ok = CreateProcessAsUserW(userToken,
+ exePath,
+ cl,
+ nullptr, // no special security attributes
+ nullptr, // no special thread attributes
+ FALSE, // don't inherit filehandles
+ 0, // creation flags
+ environmentBlock,
+ nullptr, // use my current directory
+ &si,
+ &pi);
+
+ if (environmentBlock) {
+ DestroyEnvironmentBlock(environmentBlock);
+ }
+ }
+
+ if (ok) {
+ if (hProcess) {
+ *hProcess = pi.hProcess; // the caller now owns the HANDLE
+ } else {
+ CloseHandle(pi.hProcess);
+ }
+ CloseHandle(pi.hThread);
+ } else {
+ LPVOID lpMsgBuf = nullptr;
+ FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
+ FORMAT_MESSAGE_FROM_SYSTEM |
+ FORMAT_MESSAGE_IGNORE_INSERTS,
+ nullptr,
+ GetLastError(),
+ MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
+ (LPTSTR) &lpMsgBuf,
+ 0,
+ nullptr);
+ wprintf(L"Error restarting: %s\n", lpMsgBuf ? static_cast<const wchar_t*>(lpMsgBuf) : L"(null)");
+ if (lpMsgBuf)
+ LocalFree(lpMsgBuf);
+ }
+
+ free(cl);
+
+ return ok;
+}
diff --git a/toolkit/xre/nsWindowsWMain.cpp b/toolkit/xre/nsWindowsWMain.cpp
new file mode 100644
index 000000000..788d25a90
--- /dev/null
+++ b/toolkit/xre/nsWindowsWMain.cpp
@@ -0,0 +1,122 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// This file is a .cpp file meant to be included in nsBrowserApp.cpp and other
+// similar bootstrap code. It converts wide-character windows wmain into UTF-8
+// narrow-character strings.
+
+#ifndef XP_WIN
+#error This file only makes sense on Windows.
+#endif
+
+#include "nsUTF8Utils.h"
+#include <intrin.h>
+#include <math.h>
+
+#ifndef XRE_DONT_PROTECT_DLL_LOAD
+#include "nsSetDllDirectory.h"
+#endif
+
+#if defined(__GNUC__)
+#define XRE_DONT_SUPPORT_XPSP2
+#endif
+
+#ifdef __MINGW32__
+
+/* MingW currently does not implement a wide version of the
+ startup routines. Workaround is to implement something like
+ it ourselves. See bug 411826 */
+
+#include <shellapi.h>
+
+int wmain(int argc, WCHAR **argv);
+
+int main(int argc, char **argv)
+{
+ LPWSTR commandLine = GetCommandLineW();
+ int argcw = 0;
+ LPWSTR *argvw = CommandLineToArgvW(commandLine, &argcw);
+ if (!argvw)
+ return 127;
+
+ int result = wmain(argcw, argvw);
+ LocalFree(argvw);
+ return result;
+}
+#endif /* __MINGW32__ */
+
+#define main NS_internal_main
+
+#ifndef XRE_WANT_ENVIRON
+int main(int argc, char **argv);
+#else
+int main(int argc, char **argv, char **envp);
+#endif
+
+static char*
+AllocConvertUTF16toUTF8(char16ptr_t arg)
+{
+ // be generous... UTF16 units can expand up to 3 UTF8 units
+ int len = wcslen(arg);
+ char *s = new char[len * 3 + 1];
+ if (!s)
+ return nullptr;
+
+ ConvertUTF16toUTF8 convert(s);
+ convert.write(arg, len);
+ convert.write_terminator();
+ return s;
+}
+
+static void
+FreeAllocStrings(int argc, char **argv)
+{
+ while (argc) {
+ --argc;
+ delete [] argv[argc];
+ }
+
+ delete [] argv;
+}
+
+int wmain(int argc, WCHAR **argv)
+{
+#ifndef XRE_DONT_PROTECT_DLL_LOAD
+ mozilla::SanitizeEnvironmentVariables();
+ SetDllDirectoryW(L"");
+#endif
+
+ char **argvConverted = new char*[argc + 1];
+ if (!argvConverted)
+ return 127;
+
+ for (int i = 0; i < argc; ++i) {
+ argvConverted[i] = AllocConvertUTF16toUTF8(argv[i]);
+ if (!argvConverted[i]) {
+ return 127;
+ }
+ }
+ argvConverted[argc] = nullptr;
+
+ // need to save argvConverted copy for later deletion.
+ char **deleteUs = new char*[argc+1];
+ if (!deleteUs) {
+ FreeAllocStrings(argc, argvConverted);
+ return 127;
+ }
+ for (int i = 0; i < argc; i++)
+ deleteUs[i] = argvConverted[i];
+#ifndef XRE_WANT_ENVIRON
+ int result = main(argc, argvConverted);
+#else
+ // Force creation of the multibyte _environ variable.
+ getenv("PATH");
+ int result = main(argc, argvConverted, _environ);
+#endif
+
+ delete[] argvConverted;
+ FreeAllocStrings(argc, deleteUs);
+
+ return result;
+}
diff --git a/toolkit/xre/nsX11ErrorHandler.cpp b/toolkit/xre/nsX11ErrorHandler.cpp
new file mode 100644
index 000000000..0db24e58b
--- /dev/null
+++ b/toolkit/xre/nsX11ErrorHandler.cpp
@@ -0,0 +1,162 @@
+/* -*- Mode: C++; tab-width: 40; 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 "nsX11ErrorHandler.h"
+
+#include "prenv.h"
+#include "nsXULAppAPI.h"
+#include "nsExceptionHandler.h"
+#include "nsDebug.h"
+
+#include "mozilla/X11Util.h"
+#include <X11/Xlib.h>
+
+#define BUFSIZE 2048 // What Xlib uses with XGetErrorDatabaseText
+
+extern "C" {
+int
+X11Error(Display *display, XErrorEvent *event) {
+ // Get an indication of how long ago the request that caused the error was
+ // made.
+ unsigned long age = NextRequest(display) - event->serial;
+
+ // Get a string to represent the request that caused the error.
+ nsAutoCString message;
+ if (event->request_code < 128) {
+ // Core protocol request
+ message.AppendInt(event->request_code);
+ } else {
+ // Extension request
+
+ // man XSetErrorHandler says "the error handler should not call any
+ // functions (directly or indirectly) on the display that will generate
+ // protocol requests or that will look for input events" so we use another
+ // temporary Display to request extension information. This assumes on
+ // the DISPLAY environment variable has been set and matches what was used
+ // to open |display|.
+ Display *tmpDisplay = XOpenDisplay(nullptr);
+ if (tmpDisplay) {
+ int nExts;
+ char** extNames = XListExtensions(tmpDisplay, &nExts);
+ int first_error;
+ if (extNames) {
+ for (int i = 0; i < nExts; ++i) {
+ int major_opcode, first_event;
+ if (XQueryExtension(tmpDisplay, extNames[i],
+ &major_opcode, &first_event, &first_error)
+ && major_opcode == event->request_code) {
+ message.Append(extNames[i]);
+ message.Append('.');
+ message.AppendInt(event->minor_code);
+ break;
+ }
+ }
+
+ XFreeExtensionList(extNames);
+ }
+ XCloseDisplay(tmpDisplay);
+
+#if (MOZ_WIDGET_GTK == 2)
+ // GDK2 calls XCloseDevice the devices that it opened on startup, but
+ // the XI protocol no longer ensures that the devices will still exist.
+ // If they have been removed, then a BadDevice error results. Ignore
+ // this error.
+ if (message.EqualsLiteral("XInputExtension.4") &&
+ event->error_code == first_error + 0) {
+ return 0;
+ }
+#endif
+ }
+ }
+
+ char buffer[BUFSIZE];
+ if (message.IsEmpty()) {
+ buffer[0] = '\0';
+ } else {
+ XGetErrorDatabaseText(display, "XRequest", message.get(), "",
+ buffer, sizeof(buffer));
+ }
+
+ nsAutoCString notes;
+ if (buffer[0]) {
+ notes.Append(buffer);
+ } else {
+ notes.AppendLiteral("Request ");
+ notes.AppendInt(event->request_code);
+ notes.Append('.');
+ notes.AppendInt(event->minor_code);
+ }
+
+ notes.AppendLiteral(": ");
+
+ // Get a string to describe the error.
+ XGetErrorText(display, event->error_code, buffer, sizeof(buffer));
+ notes.Append(buffer);
+
+ // For requests where Xlib gets the reply synchronously, |age| will be 1
+ // and the stack will include the function making the request. For
+ // asynchronous requests, the current stack will often be unrelated to the
+ // point of making the request, even if |age| is 1, but sometimes this may
+ // help us count back to the point of the request. With XSynchronize on,
+ // the stack will include the function making the request, even though
+ // |age| will be 2 for asynchronous requests because XSynchronize is
+ // implemented by an empty request from an XSync, which has not yet been
+ // processed.
+ if (age > 1) {
+ // XSynchronize returns the previous "after function". If a second
+ // XSynchronize call returns the same function after an enable call then
+ // synchronization must have already been enabled.
+ if (XSynchronize(display, True) == XSynchronize(display, False)) {
+ notes.AppendLiteral("; sync");
+ } else {
+ notes.AppendLiteral("; ");
+ notes.AppendInt(uint32_t(age));
+ notes.AppendLiteral(" requests ago");
+ }
+ }
+
+#ifdef MOZ_CRASHREPORTER
+ switch (XRE_GetProcessType()) {
+ case GeckoProcessType_Default:
+ case GeckoProcessType_Plugin:
+ case GeckoProcessType_Content:
+ CrashReporter::AppendAppNotesToCrashReport(notes);
+ break;
+ default:
+ ; // crash report notes not supported.
+ }
+#endif
+
+#ifdef DEBUG
+ // The resource id is unlikely to be useful in a crash report without
+ // context of other ids, but add it to the debug console output.
+ notes.AppendLiteral("; id=0x");
+ notes.AppendInt(uint32_t(event->resourceid), 16);
+#ifdef MOZ_X11
+ // Actually, for requests where Xlib gets the reply synchronously,
+ // MOZ_X_SYNC=1 will not be necessary, but we don't have a table to tell us
+ // which requests get a synchronous reply.
+ if (!PR_GetEnv("MOZ_X_SYNC")) {
+ notes.AppendLiteral("\nRe-running with MOZ_X_SYNC=1 in the environment may give a more helpful backtrace.");
+ }
+#endif
+#endif
+
+ NS_RUNTIMEABORT(notes.get());
+ return 0; // not reached
+}
+}
+
+void
+InstallX11ErrorHandler()
+{
+ XSetErrorHandler(X11Error);
+
+ Display *display = mozilla::DefaultXDisplay();
+ NS_ASSERTION(display, "No X display");
+ if (PR_GetEnv("MOZ_X_SYNC")) {
+ XSynchronize(display, True);
+ }
+}
diff --git a/toolkit/xre/nsX11ErrorHandler.h b/toolkit/xre/nsX11ErrorHandler.h
new file mode 100644
index 000000000..abd15fb42
--- /dev/null
+++ b/toolkit/xre/nsX11ErrorHandler.h
@@ -0,0 +1,17 @@
+/* -*- Mode: C++; tab-width: 40; 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/. */
+
+#ifdef MOZ_X11
+#include <X11/Xlib.h>
+
+/**
+ * InstallX11ErrorHandler is not suitable for processes running with GTK3 as
+ * GDK3 will replace the handler. This is still used for the plugin process,
+ * which runs with GTK2.
+ **/
+void InstallX11ErrorHandler();
+
+extern "C" int X11Error(Display *display, XErrorEvent *event);
+#endif
diff --git a/toolkit/xre/nsXREDirProvider.cpp b/toolkit/xre/nsXREDirProvider.cpp
new file mode 100644
index 000000000..2fbd324b7
--- /dev/null
+++ b/toolkit/xre/nsXREDirProvider.cpp
@@ -0,0 +1,1905 @@
+/* -*- 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 "nsAppRunner.h"
+#include "nsToolkitCompsCID.h"
+#include "nsXREDirProvider.h"
+
+#include "jsapi.h"
+#include "xpcpublic.h"
+
+#include "nsIAddonInterposition.h"
+#include "nsIAppStartup.h"
+#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 "nsAppDirectoryServiceDefs.h"
+#include "nsDirectoryServiceDefs.h"
+#include "nsDirectoryServiceUtils.h"
+#include "nsXULAppAPI.h"
+#include "nsCategoryManagerUtils.h"
+
+#include "nsINIParser.h"
+#include "nsDependentString.h"
+#include "nsCOMArray.h"
+#include "nsArrayEnumerator.h"
+#include "nsEnumeratorUtils.h"
+#include "nsReadableUtils.h"
+
+#include "SpecialSystemDirectory.h"
+
+#include "mozilla/dom/ScriptSettings.h"
+
+#include "mozilla/Services.h"
+#include "mozilla/Omnijar.h"
+#include "mozilla/Preferences.h"
+#include "mozilla/Telemetry.h"
+
+#include <stdlib.h>
+
+#ifdef XP_WIN
+#include <windows.h>
+#include <shlobj.h>
+#include "mozilla/WindowsVersion.h"
+#endif
+#ifdef XP_MACOSX
+#include "nsILocalFileMac.h"
+// for chflags()
+#include <sys/stat.h>
+#include <unistd.h>
+#endif
+#ifdef XP_UNIX
+#include <ctype.h>
+#endif
+#ifdef XP_IOS
+#include "UIKitDirProvider.h"
+#endif
+
+#if (defined(XP_WIN) || defined(XP_MACOSX)) && defined(MOZ_CONTENT_SANDBOX)
+#include "nsIUUIDGenerator.h"
+#include "mozilla/Unused.h"
+#endif
+
+#if defined(XP_MACOSX)
+#define APP_REGISTRY_NAME "Application Registry"
+#elif defined(XP_WIN)
+#define APP_REGISTRY_NAME "registry.dat"
+#else
+#define APP_REGISTRY_NAME "appreg"
+#endif
+
+#define PREF_OVERRIDE_DIRNAME "preferences"
+
+#if (defined(XP_WIN) || defined(XP_MACOSX)) && defined(MOZ_CONTENT_SANDBOX)
+static already_AddRefed<nsIFile> GetContentProcessSandboxTempDir();
+static nsresult DeleteDirIfExists(nsIFile *dir);
+static bool IsContentSandboxDisabled();
+static const char* GetContentProcessTempBaseDirKey();
+static already_AddRefed<nsIFile> CreateContentProcessSandboxTempDir();
+#endif
+
+static already_AddRefed<nsIFile>
+CloneAndAppend(nsIFile* aFile, const char* name)
+{
+ nsCOMPtr<nsIFile> file;
+ aFile->Clone(getter_AddRefs(file));
+ file->AppendNative(nsDependentCString(name));
+ return file.forget();
+}
+
+nsXREDirProvider* gDirServiceProvider = nullptr;
+
+nsXREDirProvider::nsXREDirProvider() :
+ mProfileNotified(false)
+{
+ gDirServiceProvider = this;
+}
+
+nsXREDirProvider::~nsXREDirProvider()
+{
+ gDirServiceProvider = nullptr;
+}
+
+nsXREDirProvider*
+nsXREDirProvider::GetSingleton()
+{
+ return gDirServiceProvider;
+}
+
+nsresult
+nsXREDirProvider::Initialize(nsIFile *aXULAppDir,
+ nsIFile *aGREDir,
+ nsIDirectoryServiceProvider* aAppProvider)
+{
+ NS_ENSURE_ARG(aXULAppDir);
+ NS_ENSURE_ARG(aGREDir);
+
+ mAppProvider = aAppProvider;
+ mXULAppDir = aXULAppDir;
+ mGREDir = aGREDir;
+ mGREDir->Clone(getter_AddRefs(mGREBinDir));
+#ifdef XP_MACOSX
+ mGREBinDir->SetNativeLeafName(NS_LITERAL_CSTRING("MacOS"));
+#endif
+
+ if (!mProfileDir) {
+ nsCOMPtr<nsIDirectoryServiceProvider> app(do_QueryInterface(mAppProvider));
+ if (app) {
+ bool per = false;
+ app->GetFile(NS_APP_USER_PROFILE_50_DIR, &per, getter_AddRefs(mProfileDir));
+ NS_ASSERTION(per, "NS_APP_USER_PROFILE_50_DIR must be persistent!");
+ NS_ASSERTION(mProfileDir, "NS_APP_USER_PROFILE_50_DIR not defined! This shouldn't happen!");
+ }
+ }
+
+#ifdef MOZ_B2G
+ LoadAppBundleDirs();
+#endif
+
+ return NS_OK;
+}
+
+nsresult
+nsXREDirProvider::SetProfile(nsIFile* aDir, nsIFile* aLocalDir)
+{
+ NS_ASSERTION(aDir && aLocalDir, "We don't support no-profile apps yet!");
+
+ nsresult rv;
+
+ rv = EnsureDirectoryExists(aDir);
+ if (NS_FAILED(rv))
+ return rv;
+
+ rv = EnsureDirectoryExists(aLocalDir);
+ if (NS_FAILED(rv))
+ return rv;
+
+#ifdef XP_MACOSX
+ bool same;
+ if (NS_SUCCEEDED(aDir->Equals(aLocalDir, &same)) && !same) {
+ // Ensure that the cache directory is not indexed by Spotlight
+ // (bug 718910). At least on OS X, the cache directory (under
+ // ~/Library/Caches/) is always the "local" user profile
+ // directory. This is confusing, since *both* user profile
+ // directories are "local" (they both exist under the user's
+ // home directory). But this usage dates back at least as far
+ // as the patch for bug 291033, where "local" seems to mean
+ // "suitable for temporary storage". Don't hide the cache
+ // directory if by some chance it and the "non-local" profile
+ // directory are the same -- there are bad side effects from
+ // hiding a profile directory under /Library/Application Support/
+ // (see bug 801883).
+ nsAutoCString cacheDir;
+ if (NS_SUCCEEDED(aLocalDir->GetNativePath(cacheDir))) {
+ if (chflags(cacheDir.get(), UF_HIDDEN)) {
+ NS_WARNING("Failed to set Cache directory to HIDDEN.");
+ }
+ }
+ }
+#endif
+
+ mProfileDir = aDir;
+ mProfileLocalDir = aLocalDir;
+ return NS_OK;
+}
+
+NS_IMPL_QUERY_INTERFACE(nsXREDirProvider,
+ nsIDirectoryServiceProvider,
+ nsIDirectoryServiceProvider2,
+ nsIProfileStartup)
+
+NS_IMETHODIMP_(MozExternalRefCountType)
+nsXREDirProvider::AddRef()
+{
+ return 1;
+}
+
+NS_IMETHODIMP_(MozExternalRefCountType)
+nsXREDirProvider::Release()
+{
+ return 0;
+}
+
+nsresult
+nsXREDirProvider::GetUserProfilesRootDir(nsIFile** aResult,
+ const nsACString* aProfileName,
+ const nsACString* aAppName,
+ const nsACString* aVendorName)
+{
+ nsCOMPtr<nsIFile> file;
+ nsresult rv = GetUserDataDirectory(getter_AddRefs(file),
+ false,
+ aProfileName, aAppName, aVendorName);
+
+ if (NS_SUCCEEDED(rv)) {
+#if !defined(XP_UNIX) || defined(XP_MACOSX)
+ rv = file->AppendNative(NS_LITERAL_CSTRING("Profiles"));
+#endif
+ // We must create the profile directory here if it does not exist.
+ nsresult tmp = EnsureDirectoryExists(file);
+ if (NS_FAILED(tmp)) {
+ rv = tmp;
+ }
+ }
+ file.swap(*aResult);
+ return rv;
+}
+
+nsresult
+nsXREDirProvider::GetUserProfilesLocalDir(nsIFile** aResult,
+ const nsACString* aProfileName,
+ const nsACString* aAppName,
+ const nsACString* aVendorName)
+{
+ nsCOMPtr<nsIFile> file;
+ nsresult rv = GetUserDataDirectory(getter_AddRefs(file),
+ true,
+ aProfileName, aAppName, aVendorName);
+
+ if (NS_SUCCEEDED(rv)) {
+#if !defined(XP_UNIX) || defined(XP_MACOSX)
+ rv = file->AppendNative(NS_LITERAL_CSTRING("Profiles"));
+#endif
+ // We must create the profile directory here if it does not exist.
+ nsresult tmp = EnsureDirectoryExists(file);
+ if (NS_FAILED(tmp)) {
+ rv = tmp;
+ }
+ }
+ file.swap(*aResult);
+ return NS_OK;
+}
+
+#if defined(XP_UNIX) || defined(XP_MACOSX)
+/**
+ * Get the directory that is the parent of the system-wide directories
+ * for extensions and native-messaing manifests.
+ *
+ * On OSX this is /Library/Application Support/Mozilla
+ * On Linux this is /usr/{lib,lib64}/mozilla
+ * (for 32- and 64-bit systems respsectively)
+ */
+static nsresult
+GetSystemParentDirectory(nsIFile** aFile)
+{
+ nsresult rv;
+ nsCOMPtr<nsIFile> localDir;
+#if defined(XP_MACOSX)
+ rv = GetOSXFolderType(kOnSystemDisk, kApplicationSupportFolderType, getter_AddRefs(localDir));
+ if (NS_SUCCEEDED(rv)) {
+ rv = localDir->AppendNative(NS_LITERAL_CSTRING("Mozilla"));
+ }
+#else
+ NS_NAMED_LITERAL_CSTRING(dirname,
+#ifdef HAVE_USR_LIB64_DIR
+ "/usr/lib64/mozilla"
+#elif defined(__OpenBSD__) || defined(__FreeBSD__)
+ "/usr/local/lib/mozilla"
+#else
+ "/usr/lib/mozilla"
+#endif
+ );
+ rv = NS_NewNativeLocalFile(dirname, false, getter_AddRefs(localDir));
+#endif
+
+ if (NS_SUCCEEDED(rv)) {
+ localDir.forget(aFile);
+ }
+ return rv;
+}
+#endif
+
+NS_IMETHODIMP
+nsXREDirProvider::GetFile(const char* aProperty, bool* aPersistent,
+ nsIFile** aFile)
+{
+ nsresult rv;
+
+ bool gettingProfile = false;
+
+ if (!strcmp(aProperty, NS_APP_USER_PROFILE_LOCAL_50_DIR)) {
+ // If XRE_NotifyProfile hasn't been called, don't fall through to
+ // mAppProvider on the profile keys.
+ if (!mProfileNotified)
+ return NS_ERROR_FAILURE;
+
+ if (mProfileLocalDir)
+ return mProfileLocalDir->Clone(aFile);
+
+ if (mAppProvider)
+ return mAppProvider->GetFile(aProperty, aPersistent, aFile);
+
+ // This falls through to the case below
+ gettingProfile = true;
+ }
+ if (!strcmp(aProperty, NS_APP_USER_PROFILE_50_DIR) || gettingProfile) {
+ if (!mProfileNotified)
+ return NS_ERROR_FAILURE;
+
+ if (mProfileDir)
+ return mProfileDir->Clone(aFile);
+
+ if (mAppProvider)
+ return mAppProvider->GetFile(aProperty, aPersistent, aFile);
+
+ // If we don't succeed here, bail early so that we aren't reentrant
+ // through the "GetProfileDir" call below.
+ return NS_ERROR_FAILURE;
+ }
+
+ if (mAppProvider) {
+ rv = mAppProvider->GetFile(aProperty, aPersistent, aFile);
+ if (NS_SUCCEEDED(rv) && *aFile)
+ return rv;
+ }
+
+ *aPersistent = true;
+
+ if (!strcmp(aProperty, NS_GRE_DIR)) {
+ return mGREDir->Clone(aFile);
+ }
+ else if (!strcmp(aProperty, NS_GRE_BIN_DIR)) {
+ return mGREBinDir->Clone(aFile);
+ }
+ else if (!strcmp(aProperty, NS_OS_CURRENT_PROCESS_DIR) ||
+ !strcmp(aProperty, NS_APP_INSTALL_CLEANUP_DIR)) {
+ return GetAppDir()->Clone(aFile);
+ }
+
+ rv = NS_ERROR_FAILURE;
+ nsCOMPtr<nsIFile> file;
+
+ if (!strcmp(aProperty, NS_APP_PREF_DEFAULTS_50_DIR))
+ {
+ // return the GRE default prefs directory here, and the app default prefs
+ // directory (if applicable) in NS_APP_PREFS_DEFAULTS_DIR_LIST.
+ rv = mGREDir->Clone(getter_AddRefs(file));
+ if (NS_SUCCEEDED(rv)) {
+ rv = file->AppendNative(NS_LITERAL_CSTRING("defaults"));
+ if (NS_SUCCEEDED(rv))
+ rv = file->AppendNative(NS_LITERAL_CSTRING("pref"));
+ }
+ }
+ else if (!strcmp(aProperty, NS_APP_APPLICATION_REGISTRY_DIR) ||
+ !strcmp(aProperty, XRE_USER_APP_DATA_DIR)) {
+ rv = GetUserAppDataDirectory(getter_AddRefs(file));
+ }
+#if defined(XP_UNIX) || defined(XP_MACOSX)
+ else if (!strcmp(aProperty, XRE_SYS_NATIVE_MESSAGING_MANIFESTS)) {
+ nsCOMPtr<nsIFile> localDir;
+
+ rv = ::GetSystemParentDirectory(getter_AddRefs(localDir));
+ if (NS_SUCCEEDED(rv)) {
+ NS_NAMED_LITERAL_CSTRING(dirname,
+#if defined(XP_MACOSX)
+ "NativeMessagingHosts"
+#else
+ "native-messaging-hosts"
+#endif
+ );
+ rv = localDir->AppendNative(dirname);
+ if (NS_SUCCEEDED(rv)) {
+ localDir.swap(file);
+ }
+ }
+ }
+ else if (!strcmp(aProperty, XRE_USER_NATIVE_MESSAGING_MANIFESTS)) {
+ nsCOMPtr<nsIFile> localDir;
+ rv = GetUserDataDirectoryHome(getter_AddRefs(localDir), false);
+ if (NS_SUCCEEDED(rv)) {
+#if defined(XP_MACOSX)
+ rv = localDir->AppendNative(NS_LITERAL_CSTRING("Mozilla"));
+ if (NS_SUCCEEDED(rv)) {
+ rv = localDir->AppendNative(NS_LITERAL_CSTRING("NativeMessagingHosts"));
+ }
+#else
+ rv = localDir->AppendNative(NS_LITERAL_CSTRING(".mozilla"));
+ if (NS_SUCCEEDED(rv)) {
+ rv = localDir->AppendNative(NS_LITERAL_CSTRING("native-messaging-hosts"));
+ }
+#endif
+ }
+ if (NS_SUCCEEDED(rv)) {
+ localDir.swap(file);
+ }
+ }
+#endif
+ else if (!strcmp(aProperty, XRE_UPDATE_ROOT_DIR)) {
+ rv = GetUpdateRootDir(getter_AddRefs(file));
+ }
+ else if (!strcmp(aProperty, NS_APP_APPLICATION_REGISTRY_FILE)) {
+ rv = GetUserAppDataDirectory(getter_AddRefs(file));
+ if (NS_SUCCEEDED(rv))
+ rv = file->AppendNative(NS_LITERAL_CSTRING(APP_REGISTRY_NAME));
+ }
+ else if (!strcmp(aProperty, NS_APP_USER_PROFILES_ROOT_DIR)) {
+ rv = GetUserProfilesRootDir(getter_AddRefs(file), nullptr, nullptr, nullptr);
+ }
+ else if (!strcmp(aProperty, NS_APP_USER_PROFILES_LOCAL_ROOT_DIR)) {
+ rv = GetUserProfilesLocalDir(getter_AddRefs(file), nullptr, nullptr, nullptr);
+ }
+ else if (!strcmp(aProperty, XRE_EXECUTABLE_FILE) && gArgv[0]) {
+ nsCOMPtr<nsIFile> lf;
+ rv = XRE_GetBinaryPath(gArgv[0], getter_AddRefs(lf));
+ if (NS_SUCCEEDED(rv))
+ file = lf;
+ }
+
+ else if (!strcmp(aProperty, NS_APP_PROFILE_DIR_STARTUP) && mProfileDir) {
+ return mProfileDir->Clone(aFile);
+ }
+ else if (!strcmp(aProperty, NS_APP_PROFILE_LOCAL_DIR_STARTUP)) {
+ if (mProfileLocalDir)
+ return mProfileLocalDir->Clone(aFile);
+
+ if (mProfileDir)
+ return mProfileDir->Clone(aFile);
+
+ if (mAppProvider)
+ return mAppProvider->GetFile(NS_APP_PROFILE_DIR_STARTUP, aPersistent,
+ aFile);
+ }
+#if defined(XP_UNIX) || defined(XP_MACOSX)
+ else if (!strcmp(aProperty, XRE_SYS_LOCAL_EXTENSION_PARENT_DIR)) {
+#ifdef ENABLE_SYSTEM_EXTENSION_DIRS
+ return GetSystemExtensionsDirectory(aFile);
+#else
+ return NS_ERROR_FAILURE;
+#endif
+ }
+#endif
+#if defined(XP_UNIX) && !defined(XP_MACOSX)
+ else if (!strcmp(aProperty, XRE_SYS_SHARE_EXTENSION_PARENT_DIR)) {
+#ifdef ENABLE_SYSTEM_EXTENSION_DIRS
+#if defined(__OpenBSD__) || defined(__FreeBSD__)
+ static const char *const sysLExtDir = "/usr/local/share/mozilla/extensions";
+#else
+ static const char *const sysLExtDir = "/usr/share/mozilla/extensions";
+#endif
+ return NS_NewNativeLocalFile(nsDependentCString(sysLExtDir),
+ false, aFile);
+#else
+ return NS_ERROR_FAILURE;
+#endif
+ }
+#endif
+ else if (!strcmp(aProperty, XRE_USER_SYS_EXTENSION_DIR)) {
+#ifdef ENABLE_SYSTEM_EXTENSION_DIRS
+ return GetSysUserExtensionsDirectory(aFile);
+#else
+ return NS_ERROR_FAILURE;
+#endif
+ }
+ else if (!strcmp(aProperty, XRE_APP_DISTRIBUTION_DIR)) {
+ bool persistent = false;
+ rv = GetFile(NS_GRE_DIR, &persistent, getter_AddRefs(file));
+ if (NS_SUCCEEDED(rv))
+ rv = file->AppendNative(NS_LITERAL_CSTRING("distribution"));
+ }
+ else if (!strcmp(aProperty, XRE_APP_FEATURES_DIR)) {
+ rv = GetAppDir()->Clone(getter_AddRefs(file));
+ if (NS_SUCCEEDED(rv))
+ rv = file->AppendNative(NS_LITERAL_CSTRING("features"));
+ }
+ else if (!strcmp(aProperty, XRE_ADDON_APP_DIR)) {
+ nsCOMPtr<nsIDirectoryServiceProvider> dirsvc(do_GetService("@mozilla.org/file/directory_service;1", &rv));
+ if (NS_FAILED(rv))
+ return rv;
+ bool unused;
+ rv = dirsvc->GetFile("XCurProcD", &unused, getter_AddRefs(file));
+ }
+#if (defined(XP_WIN) || defined(XP_MACOSX)) && defined(MOZ_CONTENT_SANDBOX)
+ else if (!strcmp(aProperty, NS_APP_CONTENT_PROCESS_TEMP_DIR)) {
+ if (!mContentTempDir && NS_FAILED((rv = LoadContentProcessTempDir()))) {
+ return rv;
+ }
+ rv = mContentTempDir->Clone(getter_AddRefs(file));
+ }
+#endif // defined(XP_WIN) && defined(MOZ_CONTENT_SANDBOX)
+ else if (NS_SUCCEEDED(GetProfileStartupDir(getter_AddRefs(file)))) {
+ // We need to allow component, xpt, and chrome registration to
+ // occur prior to the profile-after-change notification.
+ if (!strcmp(aProperty, NS_APP_USER_CHROME_DIR)) {
+ rv = file->AppendNative(NS_LITERAL_CSTRING("chrome"));
+ }
+ }
+
+ if (NS_SUCCEEDED(rv) && file) {
+ file.forget(aFile);
+ return NS_OK;
+ }
+
+ bool ensureFilePermissions = false;
+
+ if (NS_SUCCEEDED(GetProfileDir(getter_AddRefs(file)))) {
+ if (!strcmp(aProperty, NS_APP_PREFS_50_DIR)) {
+ rv = NS_OK;
+ }
+ else if (!strcmp(aProperty, NS_APP_PREFS_50_FILE)) {
+ rv = file->AppendNative(NS_LITERAL_CSTRING("prefs.js"));
+ }
+ else if (!strcmp(aProperty, NS_LOCALSTORE_UNSAFE_FILE)) {
+ rv = file->AppendNative(NS_LITERAL_CSTRING("localstore.rdf"));
+ }
+ else if (!strcmp(aProperty, NS_APP_LOCALSTORE_50_FILE)) {
+ if (gSafeMode) {
+ rv = file->AppendNative(NS_LITERAL_CSTRING("localstore-safe.rdf"));
+ file->Remove(false);
+ }
+ else {
+ rv = file->AppendNative(NS_LITERAL_CSTRING("localstore.rdf"));
+ ensureFilePermissions = true;
+ }
+ }
+ else if (!strcmp(aProperty, NS_APP_USER_MIMETYPES_50_FILE)) {
+ rv = file->AppendNative(NS_LITERAL_CSTRING("mimeTypes.rdf"));
+ ensureFilePermissions = true;
+ }
+ else if (!strcmp(aProperty, NS_APP_DOWNLOADS_50_FILE)) {
+ rv = file->AppendNative(NS_LITERAL_CSTRING("downloads.rdf"));
+ }
+ else if (!strcmp(aProperty, NS_APP_PREFS_OVERRIDE_DIR)) {
+ rv = mProfileDir->Clone(getter_AddRefs(file));
+ nsresult tmp = file->AppendNative(NS_LITERAL_CSTRING(PREF_OVERRIDE_DIRNAME));
+ if (NS_FAILED(tmp)) {
+ rv = tmp;
+ }
+ tmp = EnsureDirectoryExists(file);
+ if (NS_FAILED(tmp)) {
+ rv = tmp;
+ }
+ }
+ }
+ if (NS_FAILED(rv) || !file)
+ return NS_ERROR_FAILURE;
+
+ if (ensureFilePermissions) {
+ bool fileToEnsureExists;
+ bool isWritable;
+ if (NS_SUCCEEDED(file->Exists(&fileToEnsureExists)) && fileToEnsureExists
+ && NS_SUCCEEDED(file->IsWritable(&isWritable)) && !isWritable) {
+ uint32_t permissions;
+ if (NS_SUCCEEDED(file->GetPermissions(&permissions))) {
+ rv = file->SetPermissions(permissions | 0600);
+ NS_ASSERTION(NS_SUCCEEDED(rv), "failed to ensure file permissions");
+ }
+ }
+ }
+
+ file.forget(aFile);
+ return NS_OK;
+}
+
+static void
+LoadDirIntoArray(nsIFile* dir,
+ const char *const *aAppendList,
+ nsCOMArray<nsIFile>& aDirectories)
+{
+ if (!dir)
+ return;
+
+ nsCOMPtr<nsIFile> subdir;
+ dir->Clone(getter_AddRefs(subdir));
+ if (!subdir)
+ return;
+
+ for (const char *const *a = aAppendList; *a; ++a) {
+ subdir->AppendNative(nsDependentCString(*a));
+ }
+
+ bool exists;
+ if (NS_SUCCEEDED(subdir->Exists(&exists)) && exists) {
+ aDirectories.AppendObject(subdir);
+ }
+}
+
+static void
+LoadDirsIntoArray(nsCOMArray<nsIFile>& aSourceDirs,
+ const char *const* aAppendList,
+ nsCOMArray<nsIFile>& aDirectories)
+{
+ nsCOMPtr<nsIFile> appended;
+ bool exists;
+
+ for (int32_t i = 0; i < aSourceDirs.Count(); ++i) {
+ aSourceDirs[i]->Clone(getter_AddRefs(appended));
+ if (!appended)
+ continue;
+
+ nsAutoCString leaf;
+ appended->GetNativeLeafName(leaf);
+ if (!Substring(leaf, leaf.Length() - 4).EqualsLiteral(".xpi")) {
+ LoadDirIntoArray(appended,
+ aAppendList,
+ aDirectories);
+ }
+ else if (NS_SUCCEEDED(appended->Exists(&exists)) && exists)
+ aDirectories.AppendObject(appended);
+ }
+}
+
+NS_IMETHODIMP
+nsXREDirProvider::GetFiles(const char* aProperty, nsISimpleEnumerator** aResult)
+{
+ nsresult rv;
+
+ nsCOMPtr<nsISimpleEnumerator> appEnum;
+ nsCOMPtr<nsIDirectoryServiceProvider2>
+ appP2(do_QueryInterface(mAppProvider));
+ if (appP2) {
+ rv = appP2->GetFiles(aProperty, getter_AddRefs(appEnum));
+ if (NS_FAILED(rv)) {
+ appEnum = nullptr;
+ }
+ else if (rv != NS_SUCCESS_AGGREGATE_RESULT) {
+ appEnum.forget(aResult);
+ return NS_OK;
+ }
+ }
+
+ nsCOMPtr<nsISimpleEnumerator> xreEnum;
+ rv = GetFilesInternal(aProperty, getter_AddRefs(xreEnum));
+ if (NS_FAILED(rv)) {
+ if (appEnum) {
+ appEnum.forget(aResult);
+ return NS_SUCCESS_AGGREGATE_RESULT;
+ }
+
+ return rv;
+ }
+
+ rv = NS_NewUnionEnumerator(aResult, appEnum, xreEnum);
+ if (NS_FAILED(rv))
+ return rv;
+
+ return NS_SUCCESS_AGGREGATE_RESULT;
+}
+
+static void
+RegisterExtensionInterpositions(nsINIParser &parser)
+{
+ if (!mozilla::Preferences::GetBool("extensions.interposition.enabled", false))
+ return;
+
+ nsCOMPtr<nsIAddonInterposition> interposition =
+ do_GetService("@mozilla.org/addons/multiprocess-shims;1");
+
+ nsresult rv;
+ int32_t i = 0;
+ do {
+ nsAutoCString buf("Extension");
+ buf.AppendInt(i++);
+
+ nsAutoCString addonId;
+ rv = parser.GetString("MultiprocessIncompatibleExtensions", buf.get(), addonId);
+ if (NS_FAILED(rv))
+ return;
+
+ if (!xpc::SetAddonInterposition(addonId, interposition))
+ continue;
+
+ if (!xpc::AllowCPOWsInAddon(addonId, true))
+ continue;
+ }
+ while (true);
+}
+
+static void
+LoadExtensionDirectories(nsINIParser &parser,
+ const char *aSection,
+ nsCOMArray<nsIFile> &aDirectories,
+ NSLocationType aType)
+{
+ nsresult rv;
+ int32_t i = 0;
+ do {
+ nsAutoCString buf("Extension");
+ buf.AppendInt(i++);
+
+ nsAutoCString path;
+ rv = parser.GetString(aSection, buf.get(), path);
+ if (NS_FAILED(rv))
+ return;
+
+ nsCOMPtr<nsIFile> dir = do_CreateInstance("@mozilla.org/file/local;1", &rv);
+ if (NS_FAILED(rv))
+ continue;
+
+ rv = dir->SetPersistentDescriptor(path);
+ if (NS_FAILED(rv))
+ continue;
+
+ aDirectories.AppendObject(dir);
+ if (Substring(path, path.Length() - 4).EqualsLiteral(".xpi")) {
+ XRE_AddJarManifestLocation(aType, dir);
+ }
+ else {
+ nsCOMPtr<nsIFile> manifest =
+ CloneAndAppend(dir, "chrome.manifest");
+ XRE_AddManifestLocation(aType, manifest);
+ }
+ }
+ while (true);
+}
+
+#if (defined(XP_WIN) || defined(XP_MACOSX)) && defined(MOZ_CONTENT_SANDBOX)
+
+static const char*
+GetContentProcessTempBaseDirKey()
+{
+#if defined(XP_WIN)
+ return NS_WIN_LOW_INTEGRITY_TEMP_BASE;
+#else
+ return NS_OS_TEMP_DIR;
+#endif
+}
+
+//
+// Sets mContentTempDir so that it refers to the appropriate temp dir.
+// If the sandbox is enabled, NS_APP_CONTENT_PROCESS_TEMP_DIR, otherwise
+// NS_OS_TEMP_DIR is used.
+//
+nsresult
+nsXREDirProvider::LoadContentProcessTempDir()
+{
+ mContentTempDir = GetContentProcessSandboxTempDir();
+ if (mContentTempDir) {
+ return NS_OK;
+ } else {
+ return NS_GetSpecialDirectory(NS_OS_TEMP_DIR,
+ getter_AddRefs(mContentTempDir));
+ }
+}
+
+static bool
+IsContentSandboxDisabled()
+{
+ if (!BrowserTabsRemoteAutostart()) {
+ return false;
+ }
+#if defined(XP_WIN)
+ const bool isSandboxDisabled = !mozilla::IsVistaOrLater() ||
+ (Preferences::GetInt("security.sandbox.content.level") < 1);
+#elif defined(XP_MACOSX)
+ const bool isSandboxDisabled =
+ Preferences::GetInt("security.sandbox.content.level") < 1;
+#endif
+ return isSandboxDisabled;
+}
+
+//
+// If a content process sandbox temp dir is to be used, returns an nsIFile
+// for the directory. Returns null if the content sandbox is disabled or
+// an error occurs.
+//
+static already_AddRefed<nsIFile>
+GetContentProcessSandboxTempDir()
+{
+ if (IsContentSandboxDisabled()) {
+ return nullptr;
+ }
+
+ nsCOMPtr<nsIFile> localFile;
+
+ nsresult rv = NS_GetSpecialDirectory(GetContentProcessTempBaseDirKey(),
+ getter_AddRefs(localFile));
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return nullptr;
+ }
+
+ nsAutoString tempDirSuffix;
+ rv = Preferences::GetString("security.sandbox.content.tempDirSuffix",
+ &tempDirSuffix);
+ if (NS_WARN_IF(NS_FAILED(rv)) || tempDirSuffix.IsEmpty()) {
+ return nullptr;
+ }
+
+ rv = localFile->Append(NS_LITERAL_STRING("Temp-") + tempDirSuffix);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return nullptr;
+ }
+
+ return localFile.forget();
+}
+
+//
+// Create a temporary directory for use from sandboxed content processes.
+// Only called in the parent. The path is derived from a UUID stored in a
+// pref which is available to content processes. Returns null if the
+// content sandbox is disabled or if an error occurs.
+//
+static already_AddRefed<nsIFile>
+CreateContentProcessSandboxTempDir()
+{
+ if (IsContentSandboxDisabled()) {
+ return nullptr;
+ }
+
+ // Get (and create if blank) temp directory suffix pref.
+ nsresult rv;
+ nsAdoptingString tempDirSuffix =
+ Preferences::GetString("security.sandbox.content.tempDirSuffix");
+ if (tempDirSuffix.IsEmpty()) {
+ nsCOMPtr<nsIUUIDGenerator> uuidgen =
+ do_GetService("@mozilla.org/uuid-generator;1", &rv);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return nullptr;
+ }
+
+ nsID uuid;
+ rv = uuidgen->GenerateUUIDInPlace(&uuid);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return nullptr;
+ }
+
+ char uuidChars[NSID_LENGTH];
+ uuid.ToProvidedString(uuidChars);
+ tempDirSuffix.AssignASCII(uuidChars);
+
+ // Save the pref
+ rv = Preferences::SetCString("security.sandbox.content.tempDirSuffix",
+ uuidChars);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ // If we fail to save the pref we don't want to create the temp dir,
+ // because we won't be able to clean it up later.
+ return nullptr;
+ }
+
+ nsCOMPtr<nsIPrefService> prefsvc = Preferences::GetService();
+ if (!prefsvc || NS_FAILED((rv = prefsvc->SavePrefFile(nullptr)))) {
+ // Again, if we fail to save the pref file we might not be able to clean
+ // up the temp directory, so don't create one.
+ NS_WARNING("Failed to save pref file, cannot create temp dir.");
+ return nullptr;
+ }
+ }
+
+ nsCOMPtr<nsIFile> sandboxTempDir = GetContentProcessSandboxTempDir();
+ if (!sandboxTempDir) {
+ NS_WARNING("Failed to determine sandbox temp dir path.");
+ return nullptr;
+ }
+
+ // Remove the directory. It may exist due to a previous crash.
+ if (NS_FAILED(DeleteDirIfExists(sandboxTempDir))) {
+ NS_WARNING("Failed to reset sandbox temp dir.");
+ return nullptr;
+ }
+
+ // Create the directory
+ rv = sandboxTempDir->Create(nsIFile::DIRECTORY_TYPE, 0700);
+ if (NS_FAILED(rv)) {
+ NS_WARNING("Failed to create sandbox temp dir.");
+ return nullptr;
+ }
+
+ return sandboxTempDir.forget();
+}
+
+static nsresult
+DeleteDirIfExists(nsIFile* dir)
+{
+ if (dir) {
+ // Don't return an error if the directory doesn't exist.
+ // Windows Remove() returns NS_ERROR_FILE_NOT_FOUND while
+ // OS X returns NS_ERROR_FILE_TARGET_DOES_NOT_EXIST.
+ nsresult rv = dir->Remove(/* aRecursive */ true);
+ if (NS_FAILED(rv) && rv != NS_ERROR_FILE_NOT_FOUND &&
+ rv != NS_ERROR_FILE_TARGET_DOES_NOT_EXIST) {
+ return rv;
+ }
+ }
+ return NS_OK;
+}
+
+#endif // (defined(XP_WIN) || defined(XP_MACOSX)) &&
+ // defined(MOZ_CONTENT_SANDBOX)
+
+void
+nsXREDirProvider::LoadExtensionBundleDirectories()
+{
+ if (!mozilla::Preferences::GetBool("extensions.defaultProviders.enabled", true))
+ return;
+
+ if (mProfileDir) {
+ if (!gSafeMode) {
+ nsCOMPtr<nsIFile> extensionsINI;
+ mProfileDir->Clone(getter_AddRefs(extensionsINI));
+ if (!extensionsINI)
+ return;
+
+ extensionsINI->AppendNative(NS_LITERAL_CSTRING("extensions.ini"));
+
+ nsCOMPtr<nsIFile> extensionsINILF =
+ do_QueryInterface(extensionsINI);
+ if (!extensionsINILF)
+ return;
+
+ nsINIParser parser;
+ nsresult rv = parser.Init(extensionsINILF);
+ if (NS_FAILED(rv))
+ return;
+
+ RegisterExtensionInterpositions(parser);
+ LoadExtensionDirectories(parser, "ExtensionDirs", mExtensionDirectories,
+ NS_EXTENSION_LOCATION);
+ LoadExtensionDirectories(parser, "ThemeDirs", mThemeDirectories,
+ NS_SKIN_LOCATION);
+/* non-Firefox applications that use overrides in their default theme should
+ * define AC_DEFINE(MOZ_SEPARATE_MANIFEST_FOR_THEME_OVERRIDES) in their
+ * configure.in */
+#if defined(MOZ_BUILD_APP_IS_BROWSER) || defined(MOZ_SEPARATE_MANIFEST_FOR_THEME_OVERRIDES)
+ } else {
+ // In safe mode, still load the default theme directory:
+ nsCOMPtr<nsIFile> themeManifest;
+ mXULAppDir->Clone(getter_AddRefs(themeManifest));
+ themeManifest->AppendNative(NS_LITERAL_CSTRING("extensions"));
+ themeManifest->AppendNative(NS_LITERAL_CSTRING("{972ce4c6-7e08-4474-a285-3208198ce6fd}.xpi"));
+ bool exists = false;
+ if (NS_SUCCEEDED(themeManifest->Exists(&exists)) && exists) {
+ XRE_AddJarManifestLocation(NS_SKIN_LOCATION, themeManifest);
+ } else {
+ themeManifest->SetNativeLeafName(NS_LITERAL_CSTRING("{972ce4c6-7e08-4474-a285-3208198ce6fd}"));
+ themeManifest->AppendNative(NS_LITERAL_CSTRING("chrome.manifest"));
+ XRE_AddManifestLocation(NS_SKIN_LOCATION, themeManifest);
+ }
+#endif
+ }
+ }
+}
+
+#ifdef MOZ_B2G
+void
+nsXREDirProvider::LoadAppBundleDirs()
+{
+ nsCOMPtr<nsIFile> dir;
+ bool persistent = false;
+ nsresult rv = GetFile(XRE_APP_DISTRIBUTION_DIR, &persistent, getter_AddRefs(dir));
+ if (NS_FAILED(rv))
+ return;
+
+ dir->AppendNative(NS_LITERAL_CSTRING("bundles"));
+
+ nsCOMPtr<nsISimpleEnumerator> e;
+ rv = dir->GetDirectoryEntries(getter_AddRefs(e));
+ if (NS_FAILED(rv))
+ return;
+
+ nsCOMPtr<nsIDirectoryEnumerator> files = do_QueryInterface(e);
+ if (!files)
+ return;
+
+ nsCOMPtr<nsIFile> subdir;
+ while (NS_SUCCEEDED(files->GetNextFile(getter_AddRefs(subdir))) && subdir) {
+ mAppBundleDirectories.AppendObject(subdir);
+
+ nsCOMPtr<nsIFile> manifest =
+ CloneAndAppend(subdir, "chrome.manifest");
+ XRE_AddManifestLocation(NS_APP_LOCATION, manifest);
+ }
+}
+#endif
+
+static const char *const kAppendPrefDir[] = { "defaults", "preferences", nullptr };
+
+#ifdef DEBUG_bsmedberg
+static void
+DumpFileArray(const char *key,
+ nsCOMArray<nsIFile> dirs)
+{
+ fprintf(stderr, "nsXREDirProvider::GetFilesInternal(%s)\n", key);
+
+ nsAutoCString path;
+ for (int32_t i = 0; i < dirs.Count(); ++i) {
+ dirs[i]->GetNativePath(path);
+ fprintf(stderr, " %s\n", path.get());
+ }
+}
+#endif // DEBUG_bsmedberg
+
+nsresult
+nsXREDirProvider::GetFilesInternal(const char* aProperty,
+ nsISimpleEnumerator** aResult)
+{
+ nsresult rv = NS_OK;
+ *aResult = nullptr;
+
+ if (!strcmp(aProperty, XRE_EXTENSIONS_DIR_LIST)) {
+ nsCOMArray<nsIFile> directories;
+
+ static const char *const kAppendNothing[] = { nullptr };
+
+ LoadDirsIntoArray(mAppBundleDirectories,
+ kAppendNothing, directories);
+ LoadDirsIntoArray(mExtensionDirectories,
+ kAppendNothing, directories);
+
+ rv = NS_NewArrayEnumerator(aResult, directories);
+ }
+ else if (!strcmp(aProperty, NS_APP_PREFS_DEFAULTS_DIR_LIST)) {
+ nsCOMArray<nsIFile> directories;
+
+ LoadDirIntoArray(mXULAppDir, kAppendPrefDir, directories);
+ LoadDirsIntoArray(mAppBundleDirectories,
+ kAppendPrefDir, directories);
+
+ rv = NS_NewArrayEnumerator(aResult, directories);
+ }
+ else if (!strcmp(aProperty, NS_EXT_PREFS_DEFAULTS_DIR_LIST)) {
+ nsCOMArray<nsIFile> directories;
+
+ LoadDirsIntoArray(mExtensionDirectories,
+ kAppendPrefDir, directories);
+
+ if (mProfileDir) {
+ nsCOMPtr<nsIFile> overrideFile;
+ mProfileDir->Clone(getter_AddRefs(overrideFile));
+ overrideFile->AppendNative(NS_LITERAL_CSTRING(PREF_OVERRIDE_DIRNAME));
+
+ bool exists;
+ if (NS_SUCCEEDED(overrideFile->Exists(&exists)) && exists)
+ directories.AppendObject(overrideFile);
+ }
+
+ rv = NS_NewArrayEnumerator(aResult, directories);
+ }
+ else if (!strcmp(aProperty, NS_APP_CHROME_DIR_LIST)) {
+ // NS_APP_CHROME_DIR_LIST is only used to get default (native) icons
+ // for OS window decoration.
+
+ static const char *const kAppendChromeDir[] = { "chrome", nullptr };
+ nsCOMArray<nsIFile> directories;
+ LoadDirIntoArray(mXULAppDir,
+ kAppendChromeDir,
+ directories);
+ LoadDirsIntoArray(mAppBundleDirectories,
+ kAppendChromeDir,
+ directories);
+ LoadDirsIntoArray(mExtensionDirectories,
+ kAppendChromeDir,
+ directories);
+
+ rv = NS_NewArrayEnumerator(aResult, directories);
+ }
+ else if (!strcmp(aProperty, NS_APP_PLUGINS_DIR_LIST)) {
+ nsCOMArray<nsIFile> directories;
+
+ if (mozilla::Preferences::GetBool("plugins.load_appdir_plugins", false)) {
+ nsCOMPtr<nsIFile> appdir;
+ rv = XRE_GetBinaryPath(gArgv[0], getter_AddRefs(appdir));
+ if (NS_SUCCEEDED(rv)) {
+ appdir->SetNativeLeafName(NS_LITERAL_CSTRING("plugins"));
+ directories.AppendObject(appdir);
+ }
+ }
+
+ static const char *const kAppendPlugins[] = { "plugins", nullptr };
+
+ // The root dirserviceprovider does quite a bit for us: we're mainly
+ // interested in xulapp and extension-provided plugins.
+ LoadDirsIntoArray(mAppBundleDirectories,
+ kAppendPlugins,
+ directories);
+ LoadDirsIntoArray(mExtensionDirectories,
+ kAppendPlugins,
+ directories);
+
+ if (mProfileDir) {
+ nsCOMArray<nsIFile> profileDir;
+ profileDir.AppendObject(mProfileDir);
+ LoadDirsIntoArray(profileDir,
+ kAppendPlugins,
+ directories);
+ }
+
+ rv = NS_NewArrayEnumerator(aResult, directories);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = NS_SUCCESS_AGGREGATE_RESULT;
+ }
+ else
+ rv = NS_ERROR_FAILURE;
+
+ return rv;
+}
+
+NS_IMETHODIMP
+nsXREDirProvider::GetDirectory(nsIFile* *aResult)
+{
+ NS_ENSURE_TRUE(mProfileDir, NS_ERROR_NOT_INITIALIZED);
+
+ return mProfileDir->Clone(aResult);
+}
+
+NS_IMETHODIMP
+nsXREDirProvider::DoStartup()
+{
+ if (!mProfileNotified) {
+ nsCOMPtr<nsIObserverService> obsSvc =
+ mozilla::services::GetObserverService();
+ if (!obsSvc) return NS_ERROR_FAILURE;
+
+ mProfileNotified = true;
+
+ /*
+ Setup prefs before profile-do-change to be able to use them to track
+ crashes and because we want to begin crash tracking before other code run
+ from this notification since they may cause crashes.
+ */
+ nsresult rv = mozilla::Preferences::ResetAndReadUserPrefs();
+ if (NS_FAILED(rv)) NS_WARNING("Failed to setup pref service.");
+
+ bool safeModeNecessary = false;
+ nsCOMPtr<nsIAppStartup> appStartup (do_GetService(NS_APPSTARTUP_CONTRACTID));
+ if (appStartup) {
+ rv = appStartup->TrackStartupCrashBegin(&safeModeNecessary);
+ if (NS_FAILED(rv) && rv != NS_ERROR_NOT_AVAILABLE)
+ NS_WARNING("Error while beginning startup crash tracking");
+
+ if (!gSafeMode && safeModeNecessary) {
+ appStartup->RestartInSafeMode(nsIAppStartup::eForceQuit);
+ return NS_OK;
+ }
+ }
+
+ static const char16_t kStartup[] = {'s','t','a','r','t','u','p','\0'};
+ obsSvc->NotifyObservers(nullptr, "profile-do-change", kStartup);
+
+ // Init the Extension Manager
+ nsCOMPtr<nsIObserver> em = do_GetService("@mozilla.org/addons/integration;1");
+ if (em) {
+ em->Observe(nullptr, "addons-startup", nullptr);
+ } else {
+ NS_WARNING("Failed to create Addons Manager.");
+ }
+
+ LoadExtensionBundleDirectories();
+
+ obsSvc->NotifyObservers(nullptr, "load-extension-defaults", nullptr);
+ obsSvc->NotifyObservers(nullptr, "profile-after-change", kStartup);
+
+ // Any component that has registered for the profile-after-change category
+ // should also be created at this time.
+ (void)NS_CreateServicesFromCategory("profile-after-change", nullptr,
+ "profile-after-change");
+
+ if (gSafeMode && safeModeNecessary) {
+ static const char16_t kCrashed[] = {'c','r','a','s','h','e','d','\0'};
+ obsSvc->NotifyObservers(nullptr, "safemode-forced", kCrashed);
+ }
+
+ // 1 = Regular mode, 2 = Safe mode, 3 = Safe mode forced
+ int mode = 1;
+ if (gSafeMode) {
+ if (safeModeNecessary)
+ mode = 3;
+ else
+ mode = 2;
+ }
+ mozilla::Telemetry::Accumulate(mozilla::Telemetry::SAFE_MODE_USAGE, mode);
+
+ // Telemetry about number of profiles.
+ nsCOMPtr<nsIToolkitProfileService> profileService =
+ do_GetService("@mozilla.org/toolkit/profile-service;1");
+ if (profileService) {
+ nsCOMPtr<nsISimpleEnumerator> profiles;
+ rv = profileService->GetProfiles(getter_AddRefs(profiles));
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ uint32_t count = 0;
+ nsCOMPtr<nsISupports> profile;
+ while (NS_SUCCEEDED(profiles->GetNext(getter_AddRefs(profile)))) {
+ ++count;
+ }
+
+ mozilla::Telemetry::Accumulate(mozilla::Telemetry::NUMBER_OF_PROFILES,
+ count);
+ }
+
+ obsSvc->NotifyObservers(nullptr, "profile-initial-state", nullptr);
+
+#if (defined(XP_WIN) || defined(XP_MACOSX)) && defined(MOZ_CONTENT_SANDBOX)
+ // The parent is responsible for creating the sandbox temp dir
+ if (XRE_IsParentProcess()) {
+ mContentProcessSandboxTempDir = CreateContentProcessSandboxTempDir();
+ mContentTempDir = mContentProcessSandboxTempDir;
+ }
+#endif
+ }
+ return NS_OK;
+}
+
+void
+nsXREDirProvider::DoShutdown()
+{
+ PROFILER_LABEL_FUNC(js::ProfileEntry::Category::OTHER);
+
+ if (mProfileNotified) {
+#if (defined(XP_WIN) || defined(XP_MACOSX)) && defined(MOZ_CONTENT_SANDBOX)
+ if (XRE_IsParentProcess()) {
+ Unused << DeleteDirIfExists(mContentProcessSandboxTempDir);
+ }
+#endif
+
+ nsCOMPtr<nsIObserverService> obsSvc =
+ mozilla::services::GetObserverService();
+ NS_ASSERTION(obsSvc, "No observer service?");
+ if (obsSvc) {
+ static const char16_t kShutdownPersist[] = u"shutdown-persist";
+ obsSvc->NotifyObservers(nullptr, "profile-change-net-teardown", kShutdownPersist);
+ obsSvc->NotifyObservers(nullptr, "profile-change-teardown", kShutdownPersist);
+
+ // Phase 2c: Now that things are torn down, force JS GC so that things which depend on
+ // resources which are about to go away in "profile-before-change" are destroyed first.
+
+ if (JSContext* cx = dom::danger::GetJSContext()) {
+ JS_GC(cx);
+ }
+
+ // Phase 3: Notify observers of a profile change
+ obsSvc->NotifyObservers(nullptr, "profile-before-change", kShutdownPersist);
+ obsSvc->NotifyObservers(nullptr, "profile-before-change-qm", kShutdownPersist);
+ obsSvc->NotifyObservers(nullptr, "profile-before-change-telemetry", kShutdownPersist);
+ }
+ mProfileNotified = false;
+ }
+}
+
+#ifdef XP_WIN
+static nsresult
+GetShellFolderPath(int folder, nsAString& _retval)
+{
+ wchar_t* buf;
+ uint32_t bufLength = _retval.GetMutableData(&buf, MAXPATHLEN + 3);
+ NS_ENSURE_TRUE(bufLength >= (MAXPATHLEN + 3), NS_ERROR_OUT_OF_MEMORY);
+
+ nsresult rv = NS_OK;
+
+ LPITEMIDLIST pItemIDList = nullptr;
+
+ if (SUCCEEDED(SHGetSpecialFolderLocation(nullptr, folder, &pItemIDList)) &&
+ SHGetPathFromIDListW(pItemIDList, buf)) {
+ // We're going to use wcslen (wcsnlen not available in msvc7.1) so make
+ // sure to null terminate.
+ buf[bufLength - 1] = L'\0';
+ _retval.SetLength(wcslen(buf));
+ } else {
+ _retval.SetLength(0);
+ rv = NS_ERROR_NOT_AVAILABLE;
+ }
+
+ CoTaskMemFree(pItemIDList);
+
+ return rv;
+}
+
+/**
+ * Provides a fallback for getting the path to APPDATA or LOCALAPPDATA by
+ * querying the registry when the call to SHGetSpecialFolderLocation or
+ * SHGetPathFromIDListW is unable to provide these paths (Bug 513958).
+ */
+static nsresult
+GetRegWindowsAppDataFolder(bool aLocal, nsAString& _retval)
+{
+ HKEY key;
+ NS_NAMED_LITERAL_STRING(keyName,
+ "Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Shell Folders");
+ DWORD res = ::RegOpenKeyExW(HKEY_CURRENT_USER, keyName.get(), 0, KEY_READ,
+ &key);
+ if (res != ERROR_SUCCESS) {
+ _retval.SetLength(0);
+ return NS_ERROR_NOT_AVAILABLE;
+ }
+
+ DWORD type, size;
+ res = RegQueryValueExW(key, (aLocal ? L"Local AppData" : L"AppData"),
+ nullptr, &type, nullptr, &size);
+ // The call to RegQueryValueExW must succeed, the type must be REG_SZ, the
+ // buffer size must not equal 0, and the buffer size be a multiple of 2.
+ if (res != ERROR_SUCCESS || type != REG_SZ || size == 0 || size % 2 != 0) {
+ ::RegCloseKey(key);
+ _retval.SetLength(0);
+ return NS_ERROR_NOT_AVAILABLE;
+ }
+
+ // |size| may or may not include room for the terminating null character
+ DWORD resultLen = size / 2;
+
+ if (!_retval.SetLength(resultLen, mozilla::fallible)) {
+ ::RegCloseKey(key);
+ _retval.SetLength(0);
+ return NS_ERROR_NOT_AVAILABLE;
+ }
+
+ nsAString::iterator begin;
+ _retval.BeginWriting(begin);
+
+ res = RegQueryValueExW(key, (aLocal ? L"Local AppData" : L"AppData"),
+ nullptr, nullptr, (LPBYTE) begin.get(), &size);
+ ::RegCloseKey(key);
+ if (res != ERROR_SUCCESS) {
+ _retval.SetLength(0);
+ return NS_ERROR_NOT_AVAILABLE;
+ }
+
+ if (!_retval.CharAt(resultLen - 1)) {
+ // It was already null terminated.
+ _retval.Truncate(resultLen - 1);
+ }
+
+ return NS_OK;
+}
+
+static bool
+GetCachedHash(HKEY rootKey, const nsAString &regPath, const nsAString &path,
+ nsAString &cachedHash)
+{
+ HKEY baseKey;
+ if (RegOpenKeyExW(rootKey, reinterpret_cast<const wchar_t*>(regPath.BeginReading()), 0, KEY_READ, &baseKey) !=
+ ERROR_SUCCESS) {
+ return false;
+ }
+
+ wchar_t cachedHashRaw[512];
+ DWORD bufferSize = sizeof(cachedHashRaw);
+ LONG result = RegQueryValueExW(baseKey, reinterpret_cast<const wchar_t*>(path.BeginReading()), 0, nullptr,
+ (LPBYTE)cachedHashRaw, &bufferSize);
+ RegCloseKey(baseKey);
+ if (result == ERROR_SUCCESS) {
+ cachedHash.Assign(cachedHashRaw);
+ }
+ return ERROR_SUCCESS == result;
+}
+
+#endif
+
+nsresult
+nsXREDirProvider::GetUpdateRootDir(nsIFile* *aResult)
+{
+ nsCOMPtr<nsIFile> updRoot;
+#if defined(MOZ_WIDGET_GONK)
+
+ nsresult rv = NS_NewNativeLocalFile(nsDependentCString("/data/local"),
+ true,
+ getter_AddRefs(updRoot));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+#else
+ nsCOMPtr<nsIFile> appFile;
+ bool per = false;
+ nsresult rv = GetFile(XRE_EXECUTABLE_FILE, &per, getter_AddRefs(appFile));
+ NS_ENSURE_SUCCESS(rv, rv);
+ rv = appFile->GetParent(getter_AddRefs(updRoot));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+#ifdef XP_MACOSX
+ nsCOMPtr<nsIFile> appRootDirFile;
+ nsCOMPtr<nsIFile> localDir;
+ nsAutoString appDirPath;
+ if (NS_FAILED(appFile->GetParent(getter_AddRefs(appRootDirFile))) ||
+ NS_FAILED(appRootDirFile->GetPath(appDirPath)) ||
+ NS_FAILED(GetUserDataDirectoryHome(getter_AddRefs(localDir), true))) {
+ return NS_ERROR_FAILURE;
+ }
+
+ int32_t dotIndex = appDirPath.RFind(".app");
+ if (dotIndex == kNotFound) {
+ dotIndex = appDirPath.Length();
+ }
+ appDirPath = Substring(appDirPath, 1, dotIndex - 1);
+
+ bool hasVendor = gAppData->vendor && strlen(gAppData->vendor) != 0;
+ if (hasVendor || gAppData->name) {
+ if (NS_FAILED(localDir->AppendNative(nsDependentCString(hasVendor ?
+ gAppData->vendor :
+ gAppData->name)))) {
+ return NS_ERROR_FAILURE;
+ }
+ } else if (NS_FAILED(localDir->AppendNative(NS_LITERAL_CSTRING("Mozilla")))) {
+ return NS_ERROR_FAILURE;
+ }
+
+ if (NS_FAILED(localDir->Append(NS_LITERAL_STRING("updates"))) ||
+ NS_FAILED(localDir->AppendRelativePath(appDirPath))) {
+ return NS_ERROR_FAILURE;
+ }
+
+ localDir.forget(aResult);
+ return NS_OK;
+
+#elif XP_WIN
+ nsAutoString pathHash;
+ bool pathHashResult = false;
+ bool hasVendor = gAppData->vendor && strlen(gAppData->vendor) != 0;
+
+ nsAutoString appDirPath;
+ if (SUCCEEDED(updRoot->GetPath(appDirPath))) {
+
+ // Figure out where we should check for a cached hash value. If the
+ // application doesn't have the nsXREAppData vendor value defined check
+ // under SOFTWARE\Mozilla.
+ wchar_t regPath[1024] = { L'\0' };
+ swprintf_s(regPath, mozilla::ArrayLength(regPath), L"SOFTWARE\\%S\\%S\\TaskBarIDs",
+ (hasVendor ? gAppData->vendor : "Mozilla"), MOZ_APP_BASENAME);
+
+ // If we pre-computed the hash, grab it from the registry.
+ pathHashResult = GetCachedHash(HKEY_LOCAL_MACHINE,
+ nsDependentString(regPath), appDirPath,
+ pathHash);
+ if (!pathHashResult) {
+ pathHashResult = GetCachedHash(HKEY_CURRENT_USER,
+ nsDependentString(regPath), appDirPath,
+ pathHash);
+ }
+ }
+
+ // Get the local app data directory and if a vendor name exists append it.
+ // If only a product name exists, append it. If neither exist fallback to
+ // old handling. We don't use the product name on purpose because we want a
+ // shared update directory for different apps run from the same path.
+ nsCOMPtr<nsIFile> localDir;
+ if (pathHashResult && (hasVendor || gAppData->name) &&
+ NS_SUCCEEDED(GetUserDataDirectoryHome(getter_AddRefs(localDir), true)) &&
+ NS_SUCCEEDED(localDir->AppendNative(nsDependentCString(hasVendor ?
+ gAppData->vendor : gAppData->name))) &&
+ NS_SUCCEEDED(localDir->Append(NS_LITERAL_STRING("updates"))) &&
+ NS_SUCCEEDED(localDir->Append(pathHash))) {
+ localDir.forget(aResult);
+ return NS_OK;
+ }
+
+ nsAutoString appPath;
+ rv = updRoot->GetPath(appPath);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // AppDir may be a short path. Convert to long path to make sure
+ // the consistency of the update folder location
+ nsString longPath;
+ wchar_t* buf;
+
+ uint32_t bufLength = longPath.GetMutableData(&buf, MAXPATHLEN);
+ NS_ENSURE_TRUE(bufLength >= MAXPATHLEN, NS_ERROR_OUT_OF_MEMORY);
+
+ DWORD len = GetLongPathNameW(appPath.get(), buf, bufLength);
+
+ // Failing GetLongPathName() is not fatal.
+ if (len <= 0 || len >= bufLength)
+ longPath.Assign(appPath);
+ else
+ longPath.SetLength(len);
+
+ // Use <UserLocalDataDir>\updates\<relative path to app dir from
+ // Program Files> if app dir is under Program Files to avoid the
+ // folder virtualization mess on Windows Vista
+ nsAutoString programFiles;
+ rv = GetShellFolderPath(CSIDL_PROGRAM_FILES, programFiles);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ programFiles.Append('\\');
+ uint32_t programFilesLen = programFiles.Length();
+
+ nsAutoString programName;
+ if (_wcsnicmp(programFiles.get(), longPath.get(), programFilesLen) == 0) {
+ programName = Substring(longPath, programFilesLen);
+ } else {
+ // We need the update root directory to live outside of the installation
+ // directory, because otherwise the updater writing the log file can cause
+ // the directory to be locked, which prevents it from being replaced after
+ // background updates.
+ programName.AssignASCII(MOZ_APP_NAME);
+ }
+
+ rv = GetUserLocalDataDirectory(getter_AddRefs(updRoot));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = updRoot->AppendRelativePath(programName);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+#endif // XP_WIN
+#endif
+ updRoot.forget(aResult);
+ return NS_OK;
+}
+
+nsresult
+nsXREDirProvider::GetProfileStartupDir(nsIFile* *aResult)
+{
+ if (mProfileDir)
+ return mProfileDir->Clone(aResult);
+
+ if (mAppProvider) {
+ nsCOMPtr<nsIFile> needsclone;
+ bool dummy;
+ nsresult rv = mAppProvider->GetFile(NS_APP_PROFILE_DIR_STARTUP,
+ &dummy,
+ getter_AddRefs(needsclone));
+ if (NS_SUCCEEDED(rv))
+ return needsclone->Clone(aResult);
+ }
+
+ return NS_ERROR_FAILURE;
+}
+
+nsresult
+nsXREDirProvider::GetProfileDir(nsIFile* *aResult)
+{
+ if (mProfileDir) {
+ if (!mProfileNotified)
+ return NS_ERROR_FAILURE;
+
+ return mProfileDir->Clone(aResult);
+ }
+
+ if (mAppProvider) {
+ nsCOMPtr<nsIFile> needsclone;
+ bool dummy;
+ nsresult rv = mAppProvider->GetFile(NS_APP_USER_PROFILE_50_DIR,
+ &dummy,
+ getter_AddRefs(needsclone));
+ if (NS_SUCCEEDED(rv))
+ return needsclone->Clone(aResult);
+ }
+
+ return NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR, aResult);
+}
+
+nsresult
+nsXREDirProvider::GetUserDataDirectoryHome(nsIFile** aFile, bool aLocal)
+{
+ // Copied from nsAppFileLocationProvider (more or less)
+ nsresult rv;
+ nsCOMPtr<nsIFile> localDir;
+
+#if defined(XP_MACOSX)
+ FSRef fsRef;
+ OSType folderType;
+ if (aLocal) {
+ folderType = kCachedDataFolderType;
+ } else {
+#ifdef MOZ_THUNDERBIRD
+ folderType = kDomainLibraryFolderType;
+#else
+ folderType = kApplicationSupportFolderType;
+#endif
+ }
+ OSErr err = ::FSFindFolder(kUserDomain, folderType, kCreateFolder, &fsRef);
+ NS_ENSURE_FALSE(err, NS_ERROR_FAILURE);
+
+ rv = NS_NewNativeLocalFile(EmptyCString(), true, getter_AddRefs(localDir));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsILocalFileMac> dirFileMac = do_QueryInterface(localDir);
+ NS_ENSURE_TRUE(dirFileMac, NS_ERROR_UNEXPECTED);
+
+ rv = dirFileMac->InitWithFSRef(&fsRef);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ localDir = do_QueryInterface(dirFileMac, &rv);
+#elif defined(XP_IOS)
+ nsAutoCString userDir;
+ if (GetUIKitDirectory(aLocal, userDir)) {
+ rv = NS_NewNativeLocalFile(userDir, true, getter_AddRefs(localDir));
+ } else {
+ rv = NS_ERROR_FAILURE;
+ }
+ NS_ENSURE_SUCCESS(rv, rv);
+#elif defined(XP_WIN)
+ nsString path;
+ if (aLocal) {
+ rv = GetShellFolderPath(CSIDL_LOCAL_APPDATA, path);
+ if (NS_FAILED(rv))
+ rv = GetRegWindowsAppDataFolder(aLocal, path);
+ }
+ if (!aLocal || NS_FAILED(rv)) {
+ rv = GetShellFolderPath(CSIDL_APPDATA, path);
+ if (NS_FAILED(rv)) {
+ if (!aLocal)
+ rv = GetRegWindowsAppDataFolder(aLocal, path);
+ }
+ }
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = NS_NewLocalFile(path, true, getter_AddRefs(localDir));
+#elif defined(MOZ_WIDGET_GONK)
+ rv = NS_NewNativeLocalFile(NS_LITERAL_CSTRING("/data/b2g"), true,
+ getter_AddRefs(localDir));
+#elif defined(XP_UNIX)
+ const char* homeDir = getenv("HOME");
+ if (!homeDir || !*homeDir)
+ return NS_ERROR_FAILURE;
+
+#ifdef ANDROID /* We want (ProfD == ProfLD) on Android. */
+ aLocal = false;
+#endif
+
+ if (aLocal) {
+ // If $XDG_CACHE_HOME is defined use it, otherwise use $HOME/.cache.
+ const char* cacheHome = getenv("XDG_CACHE_HOME");
+ if (cacheHome && *cacheHome) {
+ rv = NS_NewNativeLocalFile(nsDependentCString(cacheHome), true,
+ getter_AddRefs(localDir));
+ } else {
+ rv = NS_NewNativeLocalFile(nsDependentCString(homeDir), true,
+ getter_AddRefs(localDir));
+ if (NS_SUCCEEDED(rv))
+ rv = localDir->AppendNative(NS_LITERAL_CSTRING(".cache"));
+ }
+ } else {
+ rv = NS_NewNativeLocalFile(nsDependentCString(homeDir), true,
+ getter_AddRefs(localDir));
+ }
+#else
+#error "Don't know how to get product dir on your platform"
+#endif
+
+ NS_IF_ADDREF(*aFile = localDir);
+ return rv;
+}
+
+nsresult
+nsXREDirProvider::GetSysUserExtensionsDirectory(nsIFile** aFile)
+{
+ nsCOMPtr<nsIFile> localDir;
+ nsresult rv = GetUserDataDirectoryHome(getter_AddRefs(localDir), false);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = AppendSysUserExtensionPath(localDir);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = EnsureDirectoryExists(localDir);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ localDir.forget(aFile);
+ return NS_OK;
+}
+
+#if defined(XP_UNIX) || defined(XP_MACOSX)
+nsresult
+nsXREDirProvider::GetSystemExtensionsDirectory(nsIFile** aFile)
+{
+ nsresult rv;
+ nsCOMPtr<nsIFile> localDir;
+
+ rv = GetSystemParentDirectory(getter_AddRefs(localDir));
+ if (NS_SUCCEEDED(rv)) {
+ NS_NAMED_LITERAL_CSTRING(sExtensions,
+#if defined(XP_MACOSX)
+ "Extensions"
+#else
+ "extensions"
+#endif
+ );
+
+ rv = localDir->AppendNative(sExtensions);
+ if (NS_SUCCEEDED(rv)) {
+ localDir.forget(aFile);
+ }
+ }
+ return rv;
+}
+#endif
+
+nsresult
+nsXREDirProvider::GetUserDataDirectory(nsIFile** aFile, bool aLocal,
+ const nsACString* aProfileName,
+ const nsACString* aAppName,
+ const nsACString* aVendorName)
+{
+ nsCOMPtr<nsIFile> localDir;
+ nsresult rv = GetUserDataDirectoryHome(getter_AddRefs(localDir), aLocal);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = AppendProfilePath(localDir, aProfileName, aAppName, aVendorName, aLocal);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+#ifdef DEBUG_jungshik
+ nsAutoCString cwd;
+ localDir->GetNativePath(cwd);
+ printf("nsXREDirProvider::GetUserDataDirectory: %s\n", cwd.get());
+#endif
+ rv = EnsureDirectoryExists(localDir);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ localDir.forget(aFile);
+ return NS_OK;
+}
+
+nsresult
+nsXREDirProvider::EnsureDirectoryExists(nsIFile* aDirectory)
+{
+ bool exists;
+ nsresult rv = aDirectory->Exists(&exists);
+ NS_ENSURE_SUCCESS(rv, rv);
+#ifdef DEBUG_jungshik
+ if (!exists) {
+ nsAutoCString cwd;
+ aDirectory->GetNativePath(cwd);
+ printf("nsXREDirProvider::EnsureDirectoryExists: %s does not\n", cwd.get());
+ }
+#endif
+ if (!exists)
+ rv = aDirectory->Create(nsIFile::DIRECTORY_TYPE, 0700);
+#ifdef DEBUG_jungshik
+ if (NS_FAILED(rv))
+ NS_WARNING("nsXREDirProvider::EnsureDirectoryExists: create failed");
+#endif
+
+ return rv;
+}
+
+nsresult
+nsXREDirProvider::AppendSysUserExtensionPath(nsIFile* aFile)
+{
+ NS_ASSERTION(aFile, "Null pointer!");
+
+ nsresult rv;
+
+#if defined (XP_MACOSX) || defined(XP_WIN)
+
+ static const char* const sXR = "Mozilla";
+ rv = aFile->AppendNative(nsDependentCString(sXR));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ static const char* const sExtensions = "Extensions";
+ rv = aFile->AppendNative(nsDependentCString(sExtensions));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+#elif defined(XP_UNIX)
+
+ static const char* const sXR = ".mozilla";
+ rv = aFile->AppendNative(nsDependentCString(sXR));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ static const char* const sExtensions = "extensions";
+ rv = aFile->AppendNative(nsDependentCString(sExtensions));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+#else
+#error "Don't know how to get XRE user extension path on your platform"
+#endif
+ return NS_OK;
+}
+
+
+nsresult
+nsXREDirProvider::AppendProfilePath(nsIFile* aFile,
+ const nsACString* aProfileName,
+ const nsACString* aAppName,
+ const nsACString* aVendorName,
+ bool aLocal)
+{
+ NS_ASSERTION(aFile, "Null pointer!");
+
+ if (!gAppData) {
+ return NS_ERROR_FAILURE;
+ }
+
+ nsAutoCString profile;
+ nsAutoCString appName;
+ nsAutoCString vendor;
+ if (aProfileName && !aProfileName->IsEmpty()) {
+ profile = *aProfileName;
+ } else if (aAppName) {
+ appName = *aAppName;
+ if (aVendorName) {
+ vendor = *aVendorName;
+ }
+ } else if (gAppData->profile) {
+ profile = gAppData->profile;
+ } else {
+ appName = gAppData->name;
+ vendor = gAppData->vendor;
+ }
+
+ nsresult rv;
+
+#if defined (XP_MACOSX)
+ if (!profile.IsEmpty()) {
+ rv = AppendProfileString(aFile, profile.get());
+ }
+ else {
+ // Note that MacOS ignores the vendor when creating the profile hierarchy -
+ // all application preferences directories live alongside one another in
+ // ~/Library/Application Support/
+ rv = aFile->AppendNative(appName);
+ }
+ NS_ENSURE_SUCCESS(rv, rv);
+
+#elif defined(XP_WIN)
+ if (!profile.IsEmpty()) {
+ rv = AppendProfileString(aFile, profile.get());
+ }
+ else {
+ if (!vendor.IsEmpty()) {
+ rv = aFile->AppendNative(vendor);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+ rv = aFile->AppendNative(appName);
+ }
+ NS_ENSURE_SUCCESS(rv, rv);
+
+#elif defined(ANDROID)
+ // The directory used for storing profiles
+ // The parent of this directory is set in GetUserDataDirectoryHome
+ // XXX: handle gAppData->profile properly
+ // XXXsmaug ...and the rest of the profile creation!
+ MOZ_ASSERT(!aAppName,
+ "Profile creation for external applications is not implemented!");
+ rv = aFile->AppendNative(nsDependentCString("mozilla"));
+ NS_ENSURE_SUCCESS(rv, rv);
+#elif defined(XP_UNIX)
+ nsAutoCString folder;
+ // Make it hidden (by starting with "."), except when local (the
+ // profile is already under ~/.cache or XDG_CACHE_HOME).
+ if (!aLocal)
+ folder.Assign('.');
+
+ if (!profile.IsEmpty()) {
+ // Skip any leading path characters
+ const char* profileStart = profile.get();
+ while (*profileStart == '/' || *profileStart == '\\')
+ profileStart++;
+
+ // On the off chance that someone wanted their folder to be hidden don't
+ // let it become ".."
+ if (*profileStart == '.' && !aLocal)
+ profileStart++;
+
+ folder.Append(profileStart);
+ ToLowerCase(folder);
+
+ rv = AppendProfileString(aFile, folder.BeginReading());
+ }
+ else {
+ if (!vendor.IsEmpty()) {
+ folder.Append(vendor);
+ ToLowerCase(folder);
+
+ rv = aFile->AppendNative(folder);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ folder.Truncate();
+ }
+
+ folder.Append(appName);
+ ToLowerCase(folder);
+
+ rv = aFile->AppendNative(folder);
+ }
+ NS_ENSURE_SUCCESS(rv, rv);
+
+#else
+#error "Don't know how to get profile path on your platform"
+#endif
+ return NS_OK;
+}
+
+nsresult
+nsXREDirProvider::AppendProfileString(nsIFile* aFile, const char* aPath)
+{
+ NS_ASSERTION(aFile, "Null file!");
+ NS_ASSERTION(aPath, "Null path!");
+
+ nsAutoCString pathDup(aPath);
+
+ char* path = pathDup.BeginWriting();
+
+ nsresult rv;
+ char* subdir;
+ while ((subdir = NS_strtok("/\\", &path))) {
+ rv = aFile->AppendNative(nsDependentCString(subdir));
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+
+ return NS_OK;
+}
diff --git a/toolkit/xre/nsXREDirProvider.h b/toolkit/xre/nsXREDirProvider.h
new file mode 100644
index 000000000..7ec64da78
--- /dev/null
+++ b/toolkit/xre/nsXREDirProvider.h
@@ -0,0 +1,158 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef _nsXREDirProvider_h__
+#define _nsXREDirProvider_h__
+
+#include "nsIDirectoryService.h"
+#include "nsIProfileMigrator.h"
+#include "nsIFile.h"
+
+#include "nsCOMPtr.h"
+#include "nsCOMArray.h"
+#include "mozilla/Attributes.h"
+
+class nsXREDirProvider final : public nsIDirectoryServiceProvider2,
+ public nsIProfileStartup
+{
+public:
+ // we use a custom isupports implementation (no refcount)
+ NS_IMETHOD QueryInterface(REFNSIID aIID, void** aInstancePtr) override;
+ NS_IMETHOD_(MozExternalRefCountType) AddRef(void) override;
+ NS_IMETHOD_(MozExternalRefCountType) Release(void) override;
+
+ NS_DECL_NSIDIRECTORYSERVICEPROVIDER
+ NS_DECL_NSIDIRECTORYSERVICEPROVIDER2
+ NS_DECL_NSIPROFILESTARTUP
+
+ nsXREDirProvider();
+
+ // if aXULAppDir is null, use gArgv[0]
+ nsresult Initialize(nsIFile *aXULAppDir,
+ nsIFile *aGREDir,
+ nsIDirectoryServiceProvider* aAppProvider = nullptr);
+ ~nsXREDirProvider();
+
+ static nsXREDirProvider* GetSingleton();
+
+ nsresult GetUserProfilesRootDir(nsIFile** aResult,
+ const nsACString* aProfileName,
+ const nsACString* aAppName,
+ const nsACString* aVendorName);
+ nsresult GetUserProfilesLocalDir(nsIFile** aResult,
+ const nsACString* aProfileName,
+ const nsACString* aAppName,
+ const nsACString* aVendorName);
+
+ // We only set the profile dir, we don't ensure that it exists;
+ // that is the responsibility of the toolkit profile service.
+ // We also don't fire profile-changed notifications... that is
+ // the responsibility of the apprunner.
+ nsresult SetProfile(nsIFile* aProfileDir, nsIFile* aProfileLocalDir);
+
+ void DoShutdown();
+
+ static nsresult GetUserAppDataDirectory(nsIFile* *aFile) {
+ return GetUserDataDirectory(aFile, false, nullptr, nullptr, nullptr);
+ }
+ static nsresult GetUserLocalDataDirectory(nsIFile* *aFile) {
+ return GetUserDataDirectory(aFile, true, nullptr, nullptr, nullptr);
+ }
+
+ // By default GetUserDataDirectory gets profile path from gAppData,
+ // but that can be overridden by using aProfileName/aAppName/aVendorName.
+ static nsresult GetUserDataDirectory(nsIFile** aFile, bool aLocal,
+ const nsACString* aProfileName,
+ const nsACString* aAppName,
+ const nsACString* aVendorName);
+
+ /* make sure you clone it, if you need to do stuff to it */
+ nsIFile* GetGREDir() { return mGREDir; }
+ nsIFile* GetGREBinDir() { return mGREBinDir; }
+ nsIFile* GetAppDir() {
+ if (mXULAppDir)
+ return mXULAppDir;
+ return mGREDir;
+ }
+
+ /**
+ * Get the directory under which update directory is created.
+ * This method may be called before XPCOM is started. aResult
+ * is a clone, it may be modified.
+ */
+ nsresult GetUpdateRootDir(nsIFile* *aResult);
+
+ /**
+ * Get the profile startup directory as determined by this class or by
+ * mAppProvider. This method may be called before XPCOM is started. aResult
+ * is a clone, it may be modified.
+ */
+ nsresult GetProfileStartupDir(nsIFile* *aResult);
+
+ /**
+ * Get the profile directory as determined by this class or by an
+ * embedder-provided XPCOM directory provider. Only call this method
+ * when XPCOM is initialized! aResult is a clone, it may be modified.
+ */
+ nsresult GetProfileDir(nsIFile* *aResult);
+
+protected:
+ nsresult GetFilesInternal(const char* aProperty, nsISimpleEnumerator** aResult);
+ static nsresult GetUserDataDirectoryHome(nsIFile* *aFile, bool aLocal);
+ static nsresult GetSysUserExtensionsDirectory(nsIFile* *aFile);
+#if defined(XP_UNIX) || defined(XP_MACOSX)
+ static nsresult GetSystemExtensionsDirectory(nsIFile** aFile);
+#endif
+ static nsresult EnsureDirectoryExists(nsIFile* aDirectory);
+
+ // Determine the profile path within the UAppData directory. This is different
+ // on every major platform.
+ static nsresult AppendProfilePath(nsIFile* aFile,
+ const nsACString* aProfileName,
+ const nsACString* aAppName,
+ const nsACString* aVendorName,
+ bool aLocal);
+
+ static nsresult AppendSysUserExtensionPath(nsIFile* aFile);
+
+ // Internal helper that splits a path into components using the '/' and '\\'
+ // delimiters.
+ static inline nsresult AppendProfileString(nsIFile* aFile, const char* aPath);
+
+#if (defined(XP_WIN) || defined(XP_MACOSX)) && defined(MOZ_CONTENT_SANDBOX)
+ // Load the temp directory for sandboxed content processes
+ nsresult LoadContentProcessTempDir();
+#endif
+
+ // Calculate and register extension and theme bundle directories.
+ void LoadExtensionBundleDirectories();
+
+#ifdef MOZ_B2G
+ // Calculate and register app-bundled extension directories.
+ void LoadAppBundleDirs();
+#endif
+
+ void Append(nsIFile* aDirectory);
+
+ nsCOMPtr<nsIDirectoryServiceProvider> mAppProvider;
+ // On OSX, mGREDir points to .app/Contents/Resources
+ nsCOMPtr<nsIFile> mGREDir;
+ // On OSX, mGREBinDir points to .app/Contents/MacOS
+ nsCOMPtr<nsIFile> mGREBinDir;
+ // On OSX, mXULAppDir points to .app/Contents/Resources/browser
+ nsCOMPtr<nsIFile> mXULAppDir;
+ nsCOMPtr<nsIFile> mProfileDir;
+ nsCOMPtr<nsIFile> mProfileLocalDir;
+ bool mProfileNotified;
+#if (defined(XP_WIN) || defined(XP_MACOSX)) && defined(MOZ_CONTENT_SANDBOX)
+ nsCOMPtr<nsIFile> mContentTempDir;
+ nsCOMPtr<nsIFile> mContentProcessSandboxTempDir;
+#endif
+ nsCOMArray<nsIFile> mAppBundleDirectories;
+ nsCOMArray<nsIFile> mExtensionDirectories;
+ nsCOMArray<nsIFile> mThemeDirectories;
+};
+
+#endif
diff --git a/toolkit/xre/platform.ini b/toolkit/xre/platform.ini
new file mode 100644
index 000000000..01c8b741a
--- /dev/null
+++ b/toolkit/xre/platform.ini
@@ -0,0 +1,17 @@
+#if 0
+; 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/.
+#endif
+#filter substitution
+#include @TOPOBJDIR@/buildid.h
+#include @TOPOBJDIR@/source-repo.h
+[Build]
+BuildID=@MOZ_BUILDID@
+Milestone=@GRE_MILESTONE@
+#ifdef MOZ_SOURCE_REPO
+SourceRepository=@MOZ_SOURCE_REPO@
+#endif
+#ifdef MOZ_SOURCE_STAMP
+SourceStamp=@MOZ_SOURCE_STAMP@
+#endif
diff --git a/toolkit/xre/test/.eslintrc.js b/toolkit/xre/test/.eslintrc.js
new file mode 100644
index 000000000..e3134a291
--- /dev/null
+++ b/toolkit/xre/test/.eslintrc.js
@@ -0,0 +1,7 @@
+"use strict";
+
+module.exports = {
+ "extends": [
+ "../../../testing/mochitest/mochitest.eslintrc.js"
+ ]
+};
diff --git a/toolkit/xre/test/browser.ini b/toolkit/xre/test/browser.ini
new file mode 100644
index 000000000..deadebc46
--- /dev/null
+++ b/toolkit/xre/test/browser.ini
@@ -0,0 +1,4 @@
+[DEFAULT]
+
+[browser_checkdllblockliststate.js]
+skip-if = os != "win"
diff --git a/toolkit/xre/test/browser_checkdllblockliststate.js b/toolkit/xre/test/browser_checkdllblockliststate.js
new file mode 100644
index 000000000..6b989c370
--- /dev/null
+++ b/toolkit/xre/test/browser_checkdllblockliststate.js
@@ -0,0 +1,16 @@
+// Any copyright is dedicated to the Public Domain.
+// http://creativecommons.org/publicdomain/zero/1.0/
+
+// Tests that the dll blocklist initializes correctly during test runs.
+add_task(function* test() {
+ yield BrowserTestUtils.withNewTab({gBrowser, url: "about:blank" }, function* (browser) {
+ ok(Components.classes["@mozilla.org/xre/app-info;1"]
+ .getService(Ci.nsIXULRuntime)
+ .windowsDLLBlocklistStatus,
+ "Windows dll blocklist status should be true, indicating it is " +
+ "running properly. A failure in this test is considered a " +
+ "release blocker.");
+ });
+});
+
+
diff --git a/toolkit/xre/test/mochitest.ini b/toolkit/xre/test/mochitest.ini
new file mode 100644
index 000000000..ccbb08ed9
--- /dev/null
+++ b/toolkit/xre/test/mochitest.ini
@@ -0,0 +1,3 @@
+[DEFAULT]
+
+[test_fpuhandler.html]
diff --git a/toolkit/xre/test/test_fpuhandler.html b/toolkit/xre/test/test_fpuhandler.html
new file mode 100644
index 000000000..afe70ee64
--- /dev/null
+++ b/toolkit/xre/test/test_fpuhandler.html
@@ -0,0 +1,38 @@
+<head>
+ <title>Floating-point exception handler test</title>
+ <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+
+<body onload="runTest()">
+ <embed id="plugin1" type="application/x-test" width="400" height="400"></embed>
+
+ <script class="testbody" type="application/javascript">
+ SimpleTest.waitForExplicitFinish();
+
+ function doDiv(x, y) {
+ var z;
+ z = x / y;
+
+ for (i = 0 + x; i < 1000; ++i)
+ z = y / x;
+
+ z = x / y;
+ return z;
+ }
+
+ function runTest()
+ {
+ ok(isNaN(doDiv(0.0, 0.0)), "Undefined division-by-zero doesn't crash");
+
+ try {
+ document.getElementById('plugin1').enableFPExceptions();
+ }
+ catch (e) {
+ ok(true, "No special code to set the FPU bit in the testplugin.");
+ SimpleTest.finish();
+ return;
+ }
+
+ ok(isNaN(doDiv(0.0, 0.0)), "Undefined division-by-zero doesn't crash again.");
+ SimpleTest.finish();
+ }
+ </script>
diff --git a/toolkit/xre/test/win/Makefile.in b/toolkit/xre/test/win/Makefile.in
new file mode 100644
index 000000000..331a8a4fd
--- /dev/null
+++ b/toolkit/xre/test/win/Makefile.in
@@ -0,0 +1,14 @@
+# 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/.
+
+MOZ_WINCONSOLE = 1
+
+include $(topsrcdir)/config/rules.mk
+
+libs:: TestXREMakeCommandLineWin.ini
+ $(INSTALL) $^ $(FINAL_TARGET)/
+
+check::
+ @echo 'Running TestXREMakeCommandLineWin tests'
+ @$(RUN_TEST_PROGRAM) $(FINAL_TARGET)/TestXREMakeCommandLineWin.exe
diff --git a/toolkit/xre/test/win/TestDllInterceptor.cpp b/toolkit/xre/test/win/TestDllInterceptor.cpp
new file mode 100644
index 000000000..dff71817f
--- /dev/null
+++ b/toolkit/xre/test/win/TestDllInterceptor.cpp
@@ -0,0 +1,237 @@
+/* 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/. */
+
+#if _WIN32_WINNT < 0x0600
+#undef _WIN32_WINNT
+#define _WIN32_WINNT 0x0600
+#endif
+
+#include <shlobj.h>
+#include <stdio.h>
+
+#include "mozilla/WindowsVersion.h"
+#include "nsWindowsDllInterceptor.h"
+#include "nsWindowsHelpers.h"
+
+using namespace mozilla;
+
+struct payload {
+ UINT64 a;
+ UINT64 b;
+ UINT64 c;
+
+ bool operator==(const payload &other) const {
+ return (a == other.a &&
+ b == other.b &&
+ c == other.c);
+ }
+};
+
+extern "C" __declspec(dllexport) __declspec(noinline) payload rotatePayload(payload p) {
+ UINT64 tmp = p.a;
+ p.a = p.b;
+ p.b = p.c;
+ p.c = tmp;
+ return p;
+}
+
+static bool patched_func_called = false;
+
+static payload (*orig_rotatePayload)(payload);
+
+static payload
+patched_rotatePayload(payload p)
+{
+ patched_func_called = true;
+ return orig_rotatePayload(p);
+}
+
+bool TestHook(const char *dll, const char *func)
+{
+ void *orig_func;
+ bool successful = false;
+ {
+ WindowsDllInterceptor TestIntercept;
+ TestIntercept.Init(dll);
+ successful = TestIntercept.AddHook(func, 0, &orig_func);
+ }
+
+ if (successful) {
+ printf("TEST-PASS | WindowsDllInterceptor | Could hook %s from %s\n", func, dll);
+ return true;
+ } else {
+ printf("TEST-UNEXPECTED-FAIL | WindowsDllInterceptor | Failed to hook %s from %s\n", func, dll);
+ return false;
+ }
+}
+
+bool TestDetour(const char *dll, const char *func)
+{
+ void *orig_func;
+ bool successful = false;
+ {
+ WindowsDllInterceptor TestIntercept;
+ TestIntercept.Init(dll);
+ successful = TestIntercept.AddDetour(func, 0, &orig_func);
+ }
+
+ if (successful) {
+ printf("TEST-PASS | WindowsDllInterceptor | Could detour %s from %s\n", func, dll);
+ return true;
+ } else {
+ printf("TEST-UNEXPECTED-FAIL | WindowsDllInterceptor | Failed to detour %s from %s\n", func, dll);
+ return false;
+ }
+}
+
+bool MaybeTestHook(const bool cond, const char* dll, const char* func)
+{
+ if (!cond) {
+ return true;
+ }
+
+ return TestHook(dll, func);
+}
+
+bool ShouldTestTipTsf()
+{
+#if defined(_M_X64)
+ return false;
+#else
+ if (!IsWin8OrLater()) {
+ return false;
+ }
+
+ nsModuleHandle shell32(LoadLibraryW(L"shell32.dll"));
+ if (!shell32) {
+ return true;
+ }
+
+ auto pSHGetKnownFolderPath = reinterpret_cast<decltype(&SHGetKnownFolderPath)>(GetProcAddress(shell32, "SHGetKnownFolderPath"));
+ if (!pSHGetKnownFolderPath) {
+ return true;
+ }
+
+ PWSTR commonFilesPath = nullptr;
+ if (FAILED(pSHGetKnownFolderPath(FOLDERID_ProgramFilesCommon, 0, nullptr,
+ &commonFilesPath))) {
+ return true;
+ }
+
+ wchar_t fullPath[MAX_PATH + 1] = {};
+ wcscpy(fullPath, commonFilesPath);
+ wcscat(fullPath, L"\\Microsoft Shared\\Ink\\tiptsf.dll");
+ CoTaskMemFree(commonFilesPath);
+
+ if (!LoadLibraryW(fullPath)) {
+ return false;
+ }
+
+ // Leak the module so that it's loaded for the interceptor test
+ return true;
+#endif
+}
+
+int main()
+{
+ payload initial = { 0x12345678, 0xfc4e9d31, 0x87654321 };
+ payload p0, p1;
+ ZeroMemory(&p0, sizeof(p0));
+ ZeroMemory(&p1, sizeof(p1));
+
+ p0 = rotatePayload(initial);
+
+ {
+ WindowsDllInterceptor ExeIntercept;
+ ExeIntercept.Init("TestDllInterceptor.exe");
+ if (ExeIntercept.AddHook("rotatePayload", reinterpret_cast<intptr_t>(patched_rotatePayload), (void**) &orig_rotatePayload)) {
+ printf("TEST-PASS | WindowsDllInterceptor | Hook added\n");
+ } else {
+ printf("TEST-UNEXPECTED-FAIL | WindowsDllInterceptor | Failed to add hook\n");
+ return 1;
+ }
+
+ p1 = rotatePayload(initial);
+
+ if (patched_func_called) {
+ printf("TEST-PASS | WindowsDllInterceptor | Hook called\n");
+ } else {
+ printf("TEST-UNEXPECTED-FAIL | WindowsDllInterceptor | Hook was not called\n");
+ return 1;
+ }
+
+ if (p0 == p1) {
+ printf("TEST-PASS | WindowsDllInterceptor | Hook works properly\n");
+ } else {
+ printf("TEST-UNEXPECTED-FAIL | WindowsDllInterceptor | Hook didn't return the right information\n");
+ return 1;
+ }
+ }
+
+ patched_func_called = false;
+ ZeroMemory(&p1, sizeof(p1));
+
+ p1 = rotatePayload(initial);
+
+ if (!patched_func_called) {
+ printf("TEST-PASS | WindowsDllInterceptor | Hook was not called after unregistration\n");
+ } else {
+ printf("TEST-UNEXPECTED-FAIL | WindowsDllInterceptor | Hook was still called after unregistration\n");
+ return 1;
+ }
+
+ if (p0 == p1) {
+ printf("TEST-PASS | WindowsDllInterceptor | Original function worked properly\n");
+ } else {
+ printf("TEST-UNEXPECTED-FAIL | WindowsDllInterceptor | Original function didn't return the right information\n");
+ return 1;
+ }
+
+ if (TestHook("user32.dll", "GetWindowInfo") &&
+#ifdef _WIN64
+ TestHook("user32.dll", "SetWindowLongPtrA") &&
+ TestHook("user32.dll", "SetWindowLongPtrW") &&
+#else
+ TestHook("user32.dll", "SetWindowLongA") &&
+ TestHook("user32.dll", "SetWindowLongW") &&
+#endif
+ TestHook("user32.dll", "TrackPopupMenu") &&
+#ifdef _M_IX86
+ // We keep this test to hook complex code on x86. (Bug 850957)
+ TestHook("ntdll.dll", "NtFlushBuffersFile") &&
+#endif
+ TestHook("ntdll.dll", "NtCreateFile") &&
+ TestHook("ntdll.dll", "NtReadFile") &&
+ TestHook("ntdll.dll", "NtReadFileScatter") &&
+ TestHook("ntdll.dll", "NtWriteFile") &&
+ TestHook("ntdll.dll", "NtWriteFileGather") &&
+ TestHook("ntdll.dll", "NtQueryFullAttributesFile") &&
+ // Bug 733892: toolkit/crashreporter/nsExceptionHandler.cpp
+ TestHook("kernel32.dll", "SetUnhandledExceptionFilter") &&
+#ifdef _M_IX86
+ // Bug 670967: xpcom/base/AvailableMemoryTracker.cpp
+ TestHook("kernel32.dll", "VirtualAlloc") &&
+ TestHook("kernel32.dll", "MapViewOfFile") &&
+ TestHook("gdi32.dll", "CreateDIBSection") &&
+ TestHook("kernel32.dll", "CreateFileW") &&
+#endif
+ TestDetour("user32.dll", "CreateWindowExW") &&
+ TestHook("user32.dll", "InSendMessageEx") &&
+ TestHook("imm32.dll", "ImmGetContext") &&
+ TestHook("imm32.dll", "ImmGetCompositionStringW") &&
+ TestHook("imm32.dll", "ImmSetCandidateWindow") &&
+#ifdef _M_X64
+ TestHook("user32.dll", "GetKeyState") &&
+#endif
+ MaybeTestHook(ShouldTestTipTsf(), "tiptsf.dll", "ProcessCaretEvents") &&
+#ifdef _M_IX86
+ TestHook("user32.dll", "SendMessageTimeoutW") &&
+#endif
+ TestDetour("ntdll.dll", "LdrLoadDll")) {
+ printf("TEST-PASS | WindowsDllInterceptor | all checks passed\n");
+ return 0;
+ }
+
+ return 1;
+}
diff --git a/toolkit/xre/test/win/TestXREMakeCommandLineWin.cpp b/toolkit/xre/test/win/TestXREMakeCommandLineWin.cpp
new file mode 100644
index 000000000..00d786aa2
--- /dev/null
+++ b/toolkit/xre/test/win/TestXREMakeCommandLineWin.cpp
@@ -0,0 +1,265 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* 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 <stdio.h>
+#include <stdlib.h>
+#include <windows.h>
+// Support for _setmode
+#include <fcntl.h>
+#include <io.h>
+
+#include "nsWindowsRestart.cpp"
+
+// CommandLineToArgvW may return different values for argv[0] since it contains
+// the path to the binary that was executed so we prepend an argument that is
+// quoted with a space to prevent argv[1] being appended to argv[0].
+#define DUMMY_ARG1 L"\"arg 1\" "
+
+#ifndef MAXPATHLEN
+# ifdef PATH_MAX
+# define MAXPATHLEN PATH_MAX
+# elif defined(MAX_PATH)
+# define MAXPATHLEN MAX_PATH
+# elif defined(_MAX_PATH)
+# define MAXPATHLEN _MAX_PATH
+# elif defined(CCHMAXPATH)
+# define MAXPATHLEN CCHMAXPATH
+# else
+# define MAXPATHLEN 1024
+# endif
+#endif
+
+#define TEST_NAME L"XRE MakeCommandLine"
+#define MAX_TESTS 100
+
+// Verbose output can be enabled by defining VERBOSE 1
+#define VERBOSE 0
+
+// Compares compareCmdLine with the output of MakeCommandLine. This is
+// accomplished by converting inCmdLine to an argument list with
+// CommandLineToArgvW and converting it back to a command line with
+// MakeCommandLine.
+static int
+verifyCmdLineCreation(wchar_t *inCmdLine,
+ wchar_t *compareCmdLine,
+ bool passes, int testNum)
+{
+ int rv = 0;
+ int i;
+ int inArgc;
+ int outArgc;
+ bool isEqual;
+
+ // When debugging with command lines containing Unicode characters greater
+ // than 255 you can set the mode for stdout to Unicode so the console will
+ // receive the correct characters though it won't display them properly unless
+ // the console's font has been set to one that can display the characters. You
+ // can also redirect the console output to a file that has been saved as Unicode
+ // to view the characters.
+// _setmode(_fileno(stdout), _O_WTEXT);
+
+ // Prepend an additional argument to the command line. CommandLineToArgvW
+ // handles argv[0] differently than other arguments since argv[0] is the path
+ // to the binary being executed and MakeCommandLine only handles argv[1] and
+ // larger.
+ wchar_t *inCmdLineNew = (wchar_t *) malloc((wcslen(DUMMY_ARG1) + wcslen(inCmdLine) + 1) * sizeof(wchar_t));
+ wcscpy(inCmdLineNew, DUMMY_ARG1);
+ wcscat(inCmdLineNew, inCmdLine);
+ LPWSTR *inArgv = CommandLineToArgvW(inCmdLineNew, &inArgc);
+
+ wchar_t *outCmdLine = MakeCommandLine(inArgc - 1, inArgv + 1);
+ wchar_t *outCmdLineNew = (wchar_t *) malloc((wcslen(DUMMY_ARG1) + wcslen(outCmdLine) + 1) * sizeof(wchar_t));
+ wcscpy(outCmdLineNew, DUMMY_ARG1);
+ wcscat(outCmdLineNew, outCmdLine);
+ LPWSTR *outArgv = CommandLineToArgvW(outCmdLineNew, &outArgc);
+
+ if (VERBOSE) {
+ wprintf(L"\n");
+ wprintf(L"Verbose Output\n");
+ wprintf(L"--------------\n");
+ wprintf(L"Input command line : >%s<\n", inCmdLine);
+ wprintf(L"MakeComandLine output: >%s<\n", outCmdLine);
+ wprintf(L"Expected command line: >%s<\n", compareCmdLine);
+
+ wprintf(L"input argc : %d\n", inArgc - 1);
+ wprintf(L"output argc: %d\n", outArgc - 1);
+
+ for (i = 1; i < inArgc; ++i) {
+ wprintf(L"input argv[%d] : >%s<\n", i - 1, inArgv[i]);
+ }
+
+ for (i = 1; i < outArgc; ++i) {
+ wprintf(L"output argv[%d]: >%s<\n", i - 1, outArgv[i]);
+ }
+ wprintf(L"\n");
+ }
+
+ isEqual = (inArgc == outArgc);
+ if (!isEqual) {
+ wprintf(L"TEST-%s-FAIL | %s | ARGC Comparison (check %2d)\n",
+ passes ? L"UNEXPECTED" : L"KNOWN", TEST_NAME, testNum);
+ if (passes) {
+ rv = 1;
+ }
+ LocalFree(inArgv);
+ LocalFree(outArgv);
+ free(inCmdLineNew);
+ free(outCmdLineNew);
+ free(outCmdLine);
+ return rv;
+ }
+
+ for (i = 1; i < inArgc; ++i) {
+ isEqual = (wcscmp(inArgv[i], outArgv[i]) == 0);
+ if (!isEqual) {
+ wprintf(L"TEST-%s-FAIL | %s | ARGV Comparison (check %2d)\n",
+ passes ? L"UNEXPECTED" : L"KNOWN", TEST_NAME, testNum);
+ if (passes) {
+ rv = 1;
+ }
+ LocalFree(inArgv);
+ LocalFree(outArgv);
+ free(inCmdLineNew);
+ free(outCmdLineNew);
+ free(outCmdLine);
+ return rv;
+ }
+ }
+
+ isEqual = (wcscmp(outCmdLine, compareCmdLine) == 0);
+ if (!isEqual) {
+ wprintf(L"TEST-%s-FAIL | %s | Command Line Comparison (check %2d)\n",
+ passes ? L"UNEXPECTED" : L"KNOWN", TEST_NAME, testNum);
+ if (passes) {
+ rv = 1;
+ }
+ LocalFree(inArgv);
+ LocalFree(outArgv);
+ free(inCmdLineNew);
+ free(outCmdLineNew);
+ free(outCmdLine);
+ return rv;
+ }
+
+ if (rv == 0) {
+ if (passes) {
+ wprintf(L"TEST-PASS | %s | check %2d\n", TEST_NAME, testNum);
+ } else {
+ wprintf(L"TEST-UNEXPECTED-PASS | %s | check %2d\n", TEST_NAME, testNum);
+ rv = 1;
+ }
+ }
+
+ LocalFree(inArgv);
+ LocalFree(outArgv);
+ free(inCmdLineNew);
+ free(outCmdLineNew);
+ free(outCmdLine);
+ return rv;
+}
+
+int wmain(int argc, wchar_t *argv[])
+{
+ int i;
+ int rv = 0;
+
+ if (argc > 1 && (_wcsicmp(argv[1], L"-check-one") != 0 || argc != 3)) {
+ fwprintf(stderr, L"Displays and validates output from MakeCommandLine.\n\n");
+ fwprintf(stderr, L"Usage: %s -check-one <test number>\n\n", argv[0]);
+ fwprintf(stderr, L" <test number>\tSpecifies the test number to run from the\n");
+ fwprintf(stderr, L"\t\tTestXREMakeCommandLineWin.ini file.\n");
+ return 255;
+ }
+
+ wchar_t inifile[MAXPATHLEN];
+ if (!::GetModuleFileNameW(0, inifile, MAXPATHLEN)) {
+ wprintf(L"TEST-UNEXPECTED-FAIL | %s | GetModuleFileNameW\n", TEST_NAME);
+ return 2;
+ }
+
+ WCHAR *slash = wcsrchr(inifile, '\\');
+ if (!slash) {
+ wprintf(L"TEST-UNEXPECTED-FAIL | %s | wcsrchr\n", TEST_NAME);
+ return 3;
+ }
+
+ wcscpy(slash + 1, L"TestXREMakeCommandLineWin.ini\0");
+
+ for (i = 0; i < MAX_TESTS; ++i) {
+ wchar_t sInputVal[MAXPATHLEN];
+ wchar_t sOutputVal[MAXPATHLEN];
+ wchar_t sPassesVal[MAXPATHLEN];
+ wchar_t sInputKey[MAXPATHLEN];
+ wchar_t sOutputKey[MAXPATHLEN];
+ wchar_t sPassesKey[MAXPATHLEN];
+
+ if (argc > 2 && _wcsicmp(argv[1], L"-check-one") == 0 && argc == 3) {
+ i = _wtoi(argv[2]);
+ }
+
+ _snwprintf(sInputKey, MAXPATHLEN, L"input_%d", i);
+ _snwprintf(sOutputKey, MAXPATHLEN, L"output_%d", i);
+ _snwprintf(sPassesKey, MAXPATHLEN, L"passes_%d", i);
+
+ if (!GetPrivateProfileStringW(L"MakeCommandLineTests", sInputKey, nullptr,
+ sInputVal, MAXPATHLEN, inifile)) {
+ if (i == 0 || (argc > 2 && _wcsicmp(argv[1], L"-check-one") == 0)) {
+ wprintf(L"TEST-UNEXPECTED-FAIL | %s | see following explanation:\n", TEST_NAME);
+ wprintf(L"ERROR: Either the TestXREMakeCommandLineWin.ini file doesn't exist\n");
+ if (argc > 1 && _wcsicmp(argv[1], L"-check-one") == 0 && argc == 3) {
+ wprintf(L"ERROR: or the test is not defined in the MakeCommandLineTests section.\n");
+ } else {
+ wprintf(L"ERROR: or it has no tests defined in the MakeCommandLineTests section.\n");
+ }
+ wprintf(L"ERROR: File: %s\n", inifile);
+ return 4;
+ }
+ break;
+ }
+
+ GetPrivateProfileStringW(L"MakeCommandLineTests", sOutputKey, nullptr,
+ sOutputVal, MAXPATHLEN, inifile);
+ GetPrivateProfileStringW(L"MakeCommandLineTests", sPassesKey, nullptr,
+ sPassesVal, MAXPATHLEN, inifile);
+
+ rv |= verifyCmdLineCreation(sInputVal, sOutputVal,
+ (_wcsicmp(sPassesVal, L"false") == 0) ? FALSE : TRUE,
+ i);
+
+ if (argc > 2 && _wcsicmp(argv[1], L"-check-one") == 0) {
+ break;
+ }
+ }
+
+ if (rv == 0) {
+ wprintf(L"TEST-PASS | %s | all checks passed\n", TEST_NAME);
+ } else {
+ wprintf(L"TEST-UNEXPECTED-FAIL | %s | some checks failed\n", TEST_NAME);
+ }
+
+ return rv;
+}
+
+#ifdef __MINGW32__
+
+/* MingW currently does not implement a wide version of the
+ startup routines. Workaround is to implement something like
+ it ourselves. See bug 411826 */
+
+#include <shellapi.h>
+
+int main(int argc, char **argv)
+{
+ LPWSTR commandLine = GetCommandLineW();
+ int argcw = 0;
+ LPWSTR *argvw = CommandLineToArgvW(commandLine, &argcw);
+ if (!argvw)
+ return 127;
+
+ int result = wmain(argcw, argvw);
+ LocalFree(argvw);
+ return result;
+}
+#endif /* __MINGW32__ */
diff --git a/toolkit/xre/test/win/TestXREMakeCommandLineWin.ini b/toolkit/xre/test/win/TestXREMakeCommandLineWin.ini
new file mode 100644
index 000000000..dbb529d1b
--- /dev/null
+++ b/toolkit/xre/test/win/TestXREMakeCommandLineWin.ini
@@ -0,0 +1,94 @@
+; A typical MakeCommandLine test will contain an input and an output name value
+; pair. The value for input_xx is the input command line and the value for
+; output_xx is the expected output command line.
+;
+; A test that is known to fail can be added as follows. If the passes_xx name
+; value pair doesn't exist it defaults to true.
+; input_99=yabadaba
+; output_99=doo
+; passes_99=false
+;
+; If a value starts and ends with single or double quotation marks then it must
+; be enclosed in single or double quotation marks due to GetPrivateProfileString
+; discarding the outmost quotation marks. See GetPrivateProfileString on MSDN
+; for more information.
+; http://msdn.microsoft.com/en-us/library/ms724353.aspx
+
+[MakeCommandLineTests]
+input_0=a:\
+output_0=a:\
+
+input_1=""a:\""
+output_1=a:\"
+
+input_2=""a:\b c""
+output_2=""a:\b c""
+
+input_3=""a:\b c\""
+output_3=""a:\b c\"""
+
+input_4=""a:\b c\d e""
+output_4=""a:\b c\d e""
+
+input_5=""a:\b c\d e\""
+output_5=""a:\b c\d e\"""
+
+input_6=""a:\\""
+output_6=a:\
+
+input_7="a:\" "b:\c d"
+output_7=a:\" "b:\c d"
+
+input_8="a "b:\" "c:\d e""
+output_8="a "b:\" c:\d" e"
+
+input_9="abc" d e
+output_9=abc d e
+
+input_10="a b c" d e
+output_10="a b c" d e
+
+input_11=a\\\b d"e f"g h
+output_11=a\\\b "de fg" h
+
+input_12=a b
+output_12=a b
+
+input_13=""a b""
+output_13=""a b""
+
+input_14=a\\\"b c d
+output_14=a\\\"b c d
+
+input_15=a\\\"b c"
+output_15=a\\\"b c
+
+input_16=""a\\\b c"
+output_16=""a\\\b c""
+
+input_17=\"a
+output_17=\"a
+
+input_18=\\"a
+output_18=\a
+
+input_19=\\"\\\\"a
+output_19=\\\a
+
+input_20=\\"\\\\\"a
+output_20=\\\\\\\"a
+
+input_21="a\\\"b c\" d e
+output_21=""a\\\"b c\" d e""
+
+input_22=a\\\\\"b c" d e"
+output_22=a\\\\\"b "c d e"
+
+input_23=a:\b c\アルファ オメガ\d
+output_23=a:\b c\アルファ オメガ\d
+
+input_24=a:\b "c\アルファ オメガ\d"
+output_24=a:\b "c\アルファ オメガ\d"
+
+input_25=アルファ オメガ
+output_25=アルファ オメガ
diff --git a/toolkit/xre/test/win/moz.build b/toolkit/xre/test/win/moz.build
new file mode 100644
index 000000000..51ad0bcb3
--- /dev/null
+++ b/toolkit/xre/test/win/moz.build
@@ -0,0 +1,30 @@
+# -*- 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/.
+
+SimplePrograms([
+ 'TestXREMakeCommandLineWin',
+])
+
+CppUnitTests([
+ 'TestDllInterceptor',
+])
+
+DEFINES['NS_NO_XPCOM'] = True
+
+LOCAL_INCLUDES += [
+ '/config',
+ '/toolkit/xre',
+]
+
+DISABLE_STL_WRAPPING = True
+USE_STATIC_LIBS = True
+
+OS_LIBS += [
+ 'comctl32',
+ 'ole32',
+ 'shell32',
+ 'ws2_32',
+]
diff --git a/toolkit/xre/updaterfileutils_osx.h b/toolkit/xre/updaterfileutils_osx.h
new file mode 100644
index 000000000..75510b2ff
--- /dev/null
+++ b/toolkit/xre/updaterfileutils_osx.h
@@ -0,0 +1,13 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef updaterfileutils_osx_h_
+#define updaterfileutils_osx_h_
+
+extern "C" {
+ bool IsRecursivelyWritable(const char* aPath);
+}
+
+#endif \ No newline at end of file
diff --git a/toolkit/xre/updaterfileutils_osx.mm b/toolkit/xre/updaterfileutils_osx.mm
new file mode 100644
index 000000000..f10b94e1a
--- /dev/null
+++ b/toolkit/xre/updaterfileutils_osx.mm
@@ -0,0 +1,52 @@
+/* -*- 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 "updaterfileutils_osx.h"
+
+#include <Cocoa/Cocoa.h>
+
+bool IsRecursivelyWritable(const char* aPath)
+{
+ NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
+
+ NSString* rootPath = [NSString stringWithUTF8String:aPath];
+ NSFileManager* fileManager = [NSFileManager defaultManager];
+ NSError* error = nil;
+ NSArray* subPaths =
+ [fileManager subpathsOfDirectoryAtPath:rootPath
+ error:&error];
+ NSMutableArray* paths =
+ [NSMutableArray arrayWithCapacity:[subPaths count] + 1];
+ [paths addObject:@""];
+ [paths addObjectsFromArray:subPaths];
+
+ if (error) {
+ [pool drain];
+ return false;
+ }
+
+ for (NSString* currPath in paths) {
+ NSString* child = [rootPath stringByAppendingPathComponent:currPath];
+
+ NSDictionary* attributes =
+ [fileManager attributesOfItemAtPath:child
+ error:&error];
+ if (error) {
+ [pool drain];
+ return false;
+ }
+
+ // Don't check for writability of files pointed to by symlinks, as they may
+ // not be descendants of the root path.
+ if ([attributes fileType] != NSFileTypeSymbolicLink &&
+ [fileManager isWritableFileAtPath:child] == NO) {
+ [pool drain];
+ return false;
+ }
+ }
+
+ [pool drain];
+ return true;
+}