diff options
Diffstat (limited to 'modules/libpref/Preferences.cpp')
-rw-r--r-- | modules/libpref/Preferences.cpp | 185 |
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); |