Bug 1769547 - Do not MOZ_CRASH() on missing process r=nika
[gecko.git] / widget / RemoteLookAndFeel.cpp
blob03f2036c86588fa1f241e183e0b11e1031084c64
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2 * vim: sw=2 ts=8 et :
3 */
4 /* This Source Code Form is subject to the terms of the Mozilla Public
5 * License, v. 2.0. If a copy of the MPL was not distributed with this
6 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
8 #include "RemoteLookAndFeel.h"
10 #include "gfxFont.h"
11 #include "MainThreadUtils.h"
12 #include "mozilla/Assertions.h"
13 #include "mozilla/ClearOnShutdown.h"
14 #include "mozilla/Result.h"
15 #include "mozilla/ResultExtensions.h"
16 #include "mozilla/StaticPrefs_widget.h"
17 #include "nsLookAndFeel.h"
18 #include "nsXULAppAPI.h"
20 #include <limits>
21 #include <type_traits>
22 #include <utility>
24 namespace mozilla::widget {
26 // A cached copy of the data extracted by ExtractData.
28 // Storing this lets us avoid doing most of the work of ExtractData each
29 // time we create a new content process.
31 // Only used in the parent process.
32 static StaticAutoPtr<FullLookAndFeel> sCachedLookAndFeelData;
34 RemoteLookAndFeel::RemoteLookAndFeel(FullLookAndFeel&& aData)
35 : mTables(std::move(aData.tables())) {
36 MOZ_ASSERT(XRE_IsContentProcess(),
37 "Only content processes should be using a RemoteLookAndFeel");
39 #ifdef MOZ_WIDGET_GTK
40 if (!StaticPrefs::widget_non_native_theme_enabled()) {
41 // Configure the theme in this content process with the Gtk theme that was
42 // chosen by WithThemeConfiguredForContent in the parent process.
43 nsLookAndFeel::ConfigureTheme(aData.theme());
45 #endif
48 RemoteLookAndFeel::~RemoteLookAndFeel() = default;
50 void RemoteLookAndFeel::SetDataImpl(FullLookAndFeel&& aData) {
51 MOZ_ASSERT(XRE_IsContentProcess(),
52 "Only content processes should be using a RemoteLookAndFeel");
53 MOZ_ASSERT(NS_IsMainThread());
54 mTables = std::move(aData.tables());
56 #ifdef MOZ_WIDGET_GTK
57 if (!StaticPrefs::widget_non_native_theme_enabled()) {
58 // Configure the theme in this content process with the Gtk theme that was
59 // chosen by WithThemeConfiguredForContent in the parent process.
60 nsLookAndFeel::ConfigureTheme(aData.theme());
62 #endif
65 namespace {
67 // Some lnf values are somewhat expensive to get, and are not needed in child
68 // processes, so we can avoid querying them.
69 bool IsNeededInChildProcess(LookAndFeel::IntID aId) {
70 switch (aId) {
71 case LookAndFeel::IntID::AlertNotificationOrigin:
72 return false; // see bug 1703205
73 default:
74 return true;
78 template <typename Item, typename UInt, typename ID>
79 Result<const Item*, nsresult> MapLookup(const nsTArray<Item>& aItems,
80 const nsTArray<UInt>& aMap, ID aID) {
81 UInt mapped = aMap[static_cast<size_t>(aID)];
83 if (mapped == std::numeric_limits<UInt>::max()) {
84 return Err(NS_ERROR_NOT_IMPLEMENTED);
87 return &aItems[static_cast<size_t>(mapped)];
90 template <typename Item, typename UInt, typename Id>
91 void AddToMap(nsTArray<Item>& aItems, nsTArray<UInt>& aMap, Id aId,
92 Maybe<Item>&& aNewItem) {
93 auto mapIndex = size_t(aId);
94 aMap.EnsureLengthAtLeast(mapIndex + 1);
95 if (aNewItem.isNothing()) {
96 aMap[mapIndex] = std::numeric_limits<UInt>::max();
97 return;
100 size_t newIndex = aItems.Length();
101 MOZ_ASSERT(newIndex < std::numeric_limits<UInt>::max());
103 // Check if there is an existing value in aItems that we can point to.
105 // The arrays should be small enough and contain few enough unique
106 // values that sequential search here is reasonable.
107 for (size_t i = 0; i < newIndex; ++i) {
108 if (aItems[i] == aNewItem.ref()) {
109 aMap[mapIndex] = static_cast<UInt>(i);
110 return;
114 aItems.AppendElement(aNewItem.extract());
115 aMap[mapIndex] = static_cast<UInt>(newIndex);
118 } // namespace
120 nsresult RemoteLookAndFeel::NativeGetColor(ColorID aID, ColorScheme aScheme,
121 nscolor& aResult) {
122 const nscolor* result;
123 const bool dark = aScheme == ColorScheme::Dark;
124 MOZ_TRY_VAR(
125 result,
126 MapLookup(dark ? mTables.darkColors() : mTables.lightColors(),
127 dark ? mTables.darkColorMap() : mTables.lightColorMap(), aID));
128 aResult = *result;
129 return NS_OK;
132 nsresult RemoteLookAndFeel::NativeGetInt(IntID aID, int32_t& aResult) {
133 MOZ_DIAGNOSTIC_ASSERT(
134 IsNeededInChildProcess(aID),
135 "Querying value that we didn't bother getting from the parent process!");
137 const int32_t* result;
138 MOZ_TRY_VAR(result, MapLookup(mTables.ints(), mTables.intMap(), aID));
139 aResult = *result;
140 return NS_OK;
143 nsresult RemoteLookAndFeel::NativeGetFloat(FloatID aID, float& aResult) {
144 const float* result;
145 MOZ_TRY_VAR(result, MapLookup(mTables.floats(), mTables.floatMap(), aID));
146 aResult = *result;
147 return NS_OK;
150 bool RemoteLookAndFeel::NativeGetFont(FontID aID, nsString& aFontName,
151 gfxFontStyle& aFontStyle) {
152 auto result = MapLookup(mTables.fonts(), mTables.fontMap(), aID);
153 if (result.isErr()) {
154 return false;
156 const LookAndFeelFont& font = *result.unwrap();
157 return LookAndFeelFontToStyle(font, aFontName, aFontStyle);
160 char16_t RemoteLookAndFeel::GetPasswordCharacterImpl() {
161 return static_cast<char16_t>(mTables.passwordChar());
164 bool RemoteLookAndFeel::GetEchoPasswordImpl() { return mTables.passwordEcho(); }
166 static bool AddIDsToMap(nsXPLookAndFeel* aImpl, FullLookAndFeel* aLf) {
167 using IntID = LookAndFeel::IntID;
168 using FontID = LookAndFeel::FontID;
169 using FloatID = LookAndFeel::FloatID;
170 using ColorID = LookAndFeel::ColorID;
171 using ColorScheme = LookAndFeel::ColorScheme;
173 bool anyFromOtherTheme = false;
174 for (auto id : MakeEnumeratedRange(IntID::End)) {
175 if (!IsNeededInChildProcess(id)) {
176 continue;
178 int32_t theInt;
179 nsresult rv = aImpl->NativeGetInt(id, theInt);
180 AddToMap(aLf->tables().ints(), aLf->tables().intMap(), id,
181 NS_SUCCEEDED(rv) ? Some(theInt) : Nothing{});
184 for (auto id : MakeEnumeratedRange(ColorID::End)) {
185 nscolor theColor;
186 nsresult rv = aImpl->NativeGetColor(id, ColorScheme::Light, theColor);
187 AddToMap(aLf->tables().lightColors(), aLf->tables().lightColorMap(), id,
188 NS_SUCCEEDED(rv) ? Some(theColor) : Nothing{});
189 rv = aImpl->NativeGetColor(id, ColorScheme::Dark, theColor);
190 AddToMap(aLf->tables().darkColors(), aLf->tables().darkColorMap(), id,
191 NS_SUCCEEDED(rv) ? Some(theColor) : Nothing{});
194 for (auto id : MakeEnumeratedRange(FloatID::End)) {
195 float theFloat;
196 nsresult rv = aImpl->NativeGetFloat(id, theFloat);
197 AddToMap(aLf->tables().floats(), aLf->tables().floatMap(), id,
198 NS_SUCCEEDED(rv) ? Some(theFloat) : Nothing{});
201 for (auto id : MakeEnumeratedRange(FontID::End)) {
202 gfxFontStyle fontStyle{};
204 nsString name;
205 bool rv = aImpl->NativeGetFont(id, name, fontStyle);
206 Maybe<LookAndFeelFont> maybeFont;
207 if (rv) {
208 maybeFont.emplace(
209 nsXPLookAndFeel::StyleToLookAndFeelFont(name, fontStyle));
211 AddToMap(aLf->tables().fonts(), aLf->tables().fontMap(), id,
212 std::move(maybeFont));
215 return anyFromOtherTheme;
218 // static
219 const FullLookAndFeel* RemoteLookAndFeel::ExtractData() {
220 MOZ_ASSERT(XRE_IsParentProcess(),
221 "Only parent processes should be extracting LookAndFeel data");
223 if (sCachedLookAndFeelData) {
224 return sCachedLookAndFeelData;
227 static bool sInitialized = false;
228 if (!sInitialized) {
229 sInitialized = true;
230 ClearOnShutdown(&sCachedLookAndFeelData);
233 FullLookAndFeel* lf = new FullLookAndFeel{};
234 nsXPLookAndFeel* impl = nsXPLookAndFeel::GetInstance();
236 #ifdef MOZ_WIDGET_GTK
237 impl->GetGtkContentTheme(lf->theme());
238 #endif
240 lf->tables().passwordChar() = impl->GetPasswordCharacterImpl();
241 lf->tables().passwordEcho() = impl->GetEchoPasswordImpl();
243 AddIDsToMap(impl, lf);
245 // This assignment to sCachedLookAndFeelData must be done after the
246 // WithThemeConfiguredForContent call, since it can end up calling RefreshImpl
247 // on the LookAndFeel, which will clear out sCachedTables.
248 sCachedLookAndFeelData = lf;
249 return sCachedLookAndFeelData;
252 void RemoteLookAndFeel::ClearCachedData() {
253 MOZ_ASSERT(XRE_IsParentProcess());
254 sCachedLookAndFeelData = nullptr;
257 } // namespace mozilla::widget