summaryrefslogtreecommitdiffstats
path: root/modules/libpref/Preferences.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'modules/libpref/Preferences.cpp')
-rw-r--r--modules/libpref/Preferences.cpp185
1 files changed, 170 insertions, 15 deletions
diff --git a/modules/libpref/Preferences.cpp b/modules/libpref/Preferences.cpp
index 47759ffadc..5b7df133cd 100644
--- a/modules/libpref/Preferences.cpp
+++ b/modules/libpref/Preferences.cpp
@@ -21,6 +21,7 @@
#include "mozilla/Attributes.h"
#include "mozilla/Components.h"
#include "mozilla/dom/PContent.h"
+#include "mozilla/dom/Promise.h"
#include "mozilla/dom/RemoteType.h"
#include "mozilla/HashFunctions.h"
#include "mozilla/HashTable.h"
@@ -74,6 +75,7 @@
#include "nsIZipReader.h"
#include "nsNetUtil.h"
#include "nsPrintfCString.h"
+#include "nsProxyRelease.h"
#include "nsReadableUtils.h"
#include "nsRefPtrHashtable.h"
#include "nsRelativeFilePref.h"
@@ -112,8 +114,13 @@
# include "mozilla/WidgetUtilsGtk.h"
#endif // defined(MOZ_WIDGET_GTK)
+#ifdef MOZ_WIDGET_COCOA
+# include "ChannelPrefsUtil.h"
+#endif
+
using namespace mozilla;
+using dom::Promise;
using ipc::FileDescriptor;
#ifdef DEBUG
@@ -3280,7 +3287,13 @@ StaticMutex PreferencesWriter::sWritingToFile;
class PWRunnable : public Runnable {
public:
- explicit PWRunnable(nsIFile* aFile) : Runnable("PWRunnable"), mFile(aFile) {}
+ explicit PWRunnable(
+ nsIFile* aFile,
+ UniquePtr<MozPromiseHolder<Preferences::WritePrefFilePromise>>
+ aPromiseHolder)
+ : Runnable("PWRunnable"),
+ mFile(aFile),
+ mPromiseHolder(std::move(aPromiseHolder)) {}
NS_IMETHOD Run() override {
// Preference writes are handled a bit strangely, in that a "newer"
@@ -3332,11 +3345,15 @@ class PWRunnable : public Runnable {
nsresult rvCopy = rv;
nsCOMPtr<nsIFile> fileCopy(mFile);
SchedulerGroup::Dispatch(NS_NewRunnableFunction(
- "Preferences::WriterRunnable", [fileCopy, rvCopy] {
+ "Preferences::WriterRunnable",
+ [fileCopy, rvCopy, promiseHolder = std::move(mPromiseHolder)] {
MOZ_RELEASE_ASSERT(NS_IsMainThread());
if (NS_FAILED(rvCopy)) {
Preferences::HandleDirty();
}
+ if (promiseHolder) {
+ promiseHolder->ResolveIfExists(true, __func__);
+ }
}));
}
}
@@ -3349,8 +3366,16 @@ class PWRunnable : public Runnable {
return rv;
}
+ private:
+ ~PWRunnable() {
+ if (mPromiseHolder) {
+ mPromiseHolder->RejectIfExists(NS_ERROR_ABORT, __func__);
+ }
+ }
+
protected:
nsCOMPtr<nsIFile> mFile;
+ UniquePtr<MozPromiseHolder<Preferences::WritePrefFilePromise>> mPromiseHolder;
};
// Although this is a member of Preferences, it measures sPreferences and
@@ -4059,6 +4084,67 @@ Preferences::SavePrefFile(nsIFile* aFile) {
return SavePrefFileInternal(aFile, SaveMethod::Asynchronous);
}
+NS_IMETHODIMP
+Preferences::BackupPrefFile(nsIFile* aFile, JSContext* aCx,
+ Promise** aPromise) {
+ MOZ_ASSERT(NS_IsMainThread());
+
+ if (!aFile) {
+ return NS_ERROR_INVALID_ARG;
+ }
+
+ if (mCurrentFile) {
+ bool equalsCurrent = false;
+ nsresult rv = aFile->Equals(mCurrentFile, &equalsCurrent);
+
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ if (equalsCurrent) {
+ return NS_ERROR_INVALID_ARG;
+ }
+ }
+
+ ErrorResult result;
+ RefPtr<Promise> promise =
+ Promise::Create(xpc::CurrentNativeGlobal(aCx), result);
+
+ if (MOZ_UNLIKELY(result.Failed())) {
+ return result.StealNSResult();
+ }
+
+ nsMainThreadPtrHandle<Promise> domPromiseHolder(
+ new nsMainThreadPtrHolder<Promise>("Preferences::BackupPrefFile promise",
+ promise));
+
+ auto mozPromiseHolder = MakeUnique<MozPromiseHolder<WritePrefFilePromise>>();
+ RefPtr<WritePrefFilePromise> writePrefPromise =
+ mozPromiseHolder->Ensure(__func__);
+
+ nsresult rv = WritePrefFile(aFile, SaveMethod::Asynchronous,
+ std::move(mozPromiseHolder));
+ if (NS_FAILED(rv)) {
+ // WritePrefFile is responsible for rejecting the underlying MozPromise in
+ // the event that it the method failed somewhere.
+ return rv;
+ }
+
+ writePrefPromise->Then(
+ GetMainThreadSerialEventTarget(), __func__,
+ [domPromiseHolder](bool) {
+ MOZ_ASSERT(NS_IsMainThread());
+ domPromiseHolder.get()->MaybeResolveWithUndefined();
+ },
+ [domPromiseHolder](nsresult rv) {
+ MOZ_ASSERT(NS_IsMainThread());
+ domPromiseHolder.get()->MaybeReject(rv);
+ });
+
+ promise.forget(aPromise);
+ return NS_OK;
+}
+
/* static */
void Preferences::SetPreference(const dom::Pref& aDomPref) {
MOZ_ASSERT(!XRE_IsParentProcess());
@@ -4389,31 +4475,53 @@ nsresult Preferences::SavePrefFileInternal(nsIFile* aFile,
return rv;
} else {
- // We only allow off main thread writes on mCurrentFile.
+ // We only allow off main thread writes on mCurrentFile using this method.
+ // If you want to write asynchronously, use BackupPrefFile instead.
return WritePrefFile(aFile, SaveMethod::Blocking);
}
}
-nsresult Preferences::WritePrefFile(nsIFile* aFile, SaveMethod aSaveMethod) {
+nsresult Preferences::WritePrefFile(
+ nsIFile* aFile, SaveMethod aSaveMethod,
+ UniquePtr<MozPromiseHolder<WritePrefFilePromise>>
+ aPromiseHolder /* = nullptr */) {
MOZ_ASSERT(XRE_IsParentProcess());
+#define REJECT_IF_PROMISE_HOLDER_EXISTS(rv) \
+ if (aPromiseHolder) { \
+ aPromiseHolder->RejectIfExists(rv, __func__); \
+ } \
+ return rv;
+
if (!HashTable()) {
- return NS_ERROR_NOT_INITIALIZED;
+ REJECT_IF_PROMISE_HOLDER_EXISTS(NS_ERROR_NOT_INITIALIZED);
}
AUTO_PROFILER_LABEL("Preferences::WritePrefFile", OTHER);
if (AllowOffMainThreadSave()) {
- nsresult rv = NS_OK;
UniquePtr<PrefSaveData> prefs = MakeUnique<PrefSaveData>(pref_savePrefs());
+ nsresult rv = NS_OK;
+ bool writingToCurrent = false;
+
+ if (mCurrentFile) {
+ rv = mCurrentFile->Equals(aFile, &writingToCurrent);
+ if (NS_FAILED(rv)) {
+ REJECT_IF_PROMISE_HOLDER_EXISTS(rv);
+ }
+ }
+
// Put the newly constructed preference data into sPendingWriteData
// for the next request to pick up
prefs.reset(PreferencesWriter::sPendingWriteData.exchange(prefs.release()));
- if (prefs) {
- // There was a previous request that hasn't been processed,
- // and this is the data it had.
- return rv;
+ if (prefs && !writingToCurrent) {
+ MOZ_ASSERT(!aPromiseHolder,
+ "Shouldn't be able to enter here if aPromiseHolder is set");
+ // There was a previous request writing to the default location that
+ // hasn't been processed. It will do the work of eventually writing this
+ // latest batch of data to disk.
+ return NS_OK;
}
// There were no previous requests. Dispatch one since sPendingWriteData has
@@ -4432,20 +4540,27 @@ nsresult Preferences::WritePrefFile(nsIFile* aFile, SaveMethod aSaveMethod) {
// PreferencesWriter::Flush. Better that in future code we miss an
// increment of sPendingWriteCount and cause a simple crash due to it
// ending up negative.
+ //
+ // If aPromiseHolder is not null, ownership is transferred to PWRunnable.
+ // The PWRunnable will automatically reject the MozPromise if it is
+ // destroyed before being resolved or rejected by the Run method.
PreferencesWriter::sPendingWriteCount++;
if (async) {
- rv = target->Dispatch(new PWRunnable(aFile),
+ rv = target->Dispatch(new PWRunnable(aFile, std::move(aPromiseHolder)),
nsIEventTarget::DISPATCH_NORMAL);
} else {
- rv =
- SyncRunnable::DispatchToThread(target, new PWRunnable(aFile), true);
+ rv = SyncRunnable::DispatchToThread(
+ target, new PWRunnable(aFile, std::move(aPromiseHolder)), true);
}
if (NS_FAILED(rv)) {
// If our dispatch failed, we should correct our bookkeeping to
// avoid shutdown hangs.
PreferencesWriter::sPendingWriteCount--;
+ // No need to reject the aPromiseHolder here, as the PWRunnable will
+ // have already done so.
+ return rv;
}
- return rv;
+ return NS_OK;
}
// If we can't get the thread for writing, for whatever reason, do the main
@@ -4457,7 +4572,26 @@ nsresult Preferences::WritePrefFile(nsIFile* aFile, SaveMethod aSaveMethod) {
// AllowOffMainThreadSave() returns a consistent value for the lifetime of
// the parent process.
PrefSaveData prefsData = pref_savePrefs();
- return PreferencesWriter::Write(aFile, prefsData);
+
+ // If we were given a MozPromiseHolder, this means the caller is attempting
+ // to write prefs asynchronously to the disk - but if we get here, it means
+ // that AllowOffMainThreadSave() return false, and that we will be forced
+ // to write on the main thread instead. We still have to resolve or reject
+ // that MozPromise regardless.
+ nsresult rv = PreferencesWriter::Write(aFile, prefsData);
+ if (aPromiseHolder) {
+ NS_WARNING(
+ "Cannot write to prefs asynchronously, as AllowOffMainThreadSave() "
+ "returned false.");
+ if (NS_SUCCEEDED(rv)) {
+ aPromiseHolder->ResolveIfExists(true, __func__);
+ } else {
+ aPromiseHolder->RejectIfExists(rv, __func__);
+ }
+ }
+ return rv;
+
+#undef REJECT_IF_PROMISE_HOLDER_EXISTS
}
static nsresult openPrefFile(nsIFile* aFile, PrefValueKind aKind) {
@@ -4844,6 +4978,27 @@ nsresult Preferences::InitInitialObjects(bool aIsStartup) {
NS_WARNING("Error parsing application default preferences.");
}
+#ifdef MOZ_WIDGET_COCOA
+ // On macOS, channel-prefs.js is no longer bundled with the application and
+ // the "app.update.channel" pref is now read from a Framework instead.
+ // Previously, channel-prefs.js was read as one of the files in
+ // NS_APP_PREF_DEFAULTS_50_DIR (see just above). See bug 1799332 for more
+ // info.
+ nsAutoCString appUpdatePrefKey;
+ appUpdatePrefKey.Assign(kChannelPref);
+ nsAutoCString appUpdatePrefValue;
+ PrefValue channelPrefValue;
+ channelPrefValue.mStringVal = MOZ_STRINGIFY(MOZ_UPDATE_CHANNEL);
+ if (ChannelPrefsUtil::GetChannelPrefValue(appUpdatePrefValue)) {
+ channelPrefValue.mStringVal = appUpdatePrefValue.get();
+ }
+ pref_SetPref(appUpdatePrefKey, PrefType::String, PrefValueKind::Default,
+ channelPrefValue,
+ /* isSticky */ false,
+ /* isLocked */ true,
+ /* fromInit */ true);
+#endif
+
// Load jar:$app/omni.jar!/defaults/preferences/*.js
// or jar:$gre/omni.jar!/defaults/preferences/*.js.
RefPtr<nsZipArchive> appJarReader = Omnijar::GetReader(Omnijar::APP);