Bug 1700051: part 28) Refactor `WordSplitState<T>::GetDOMWordSeparatorOffset` to...
[gecko.git] / widget / RemoteLookAndFeel.cpp
blobf0eca1b97308430399dc52a794fd8ccac8467952
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 bool aDifferentTheme, bool aFromParentTheme) {
168 using IntID = LookAndFeel::IntID;
169 using FontID = LookAndFeel::FontID;
170 using FloatID = LookAndFeel::FloatID;
171 using ColorID = LookAndFeel::ColorID;
172 using ColorScheme = LookAndFeel::ColorScheme;
174 bool anyFromOtherTheme = false;
175 for (auto id : MakeEnumeratedRange(IntID::End)) {
176 if (!IsNeededInChildProcess(id)) {
177 continue;
179 if (aDifferentTheme && aImpl->FromParentTheme(id) != aFromParentTheme) {
180 anyFromOtherTheme = true;
181 continue;
183 int32_t theInt;
184 nsresult rv = aImpl->NativeGetInt(id, theInt);
185 AddToMap(aLf->tables().ints(), aLf->tables().intMap(), id,
186 NS_SUCCEEDED(rv) ? Some(theInt) : Nothing{});
189 for (auto id : MakeEnumeratedRange(ColorID::End)) {
190 if (aDifferentTheme && aImpl->FromParentTheme(id) != aFromParentTheme) {
191 anyFromOtherTheme = true;
192 continue;
194 nscolor theColor;
195 nsresult rv = aImpl->NativeGetColor(id, ColorScheme::Light, theColor);
196 AddToMap(aLf->tables().lightColors(), aLf->tables().lightColorMap(), id,
197 NS_SUCCEEDED(rv) ? Some(theColor) : Nothing{});
198 rv = aImpl->NativeGetColor(id, ColorScheme::Dark, theColor);
199 AddToMap(aLf->tables().darkColors(), aLf->tables().darkColorMap(), id,
200 NS_SUCCEEDED(rv) ? Some(theColor) : Nothing{});
203 // The rest of IDs only come from the child content theme.
204 if (aFromParentTheme) {
205 return anyFromOtherTheme;
208 for (auto id : MakeEnumeratedRange(FloatID::End)) {
209 float theFloat;
210 nsresult rv = aImpl->NativeGetFloat(id, theFloat);
211 AddToMap(aLf->tables().floats(), aLf->tables().floatMap(), id,
212 NS_SUCCEEDED(rv) ? Some(theFloat) : Nothing{});
215 for (auto id : MakeEnumeratedRange(FontID::End)) {
216 gfxFontStyle fontStyle{};
218 nsString name;
219 bool rv = aImpl->NativeGetFont(id, name, fontStyle);
220 Maybe<LookAndFeelFont> maybeFont;
221 if (rv) {
222 maybeFont.emplace(
223 nsXPLookAndFeel::StyleToLookAndFeelFont(name, fontStyle));
225 AddToMap(aLf->tables().fonts(), aLf->tables().fontMap(), id,
226 std::move(maybeFont));
229 return anyFromOtherTheme;
232 // static
233 const FullLookAndFeel* RemoteLookAndFeel::ExtractData() {
234 MOZ_ASSERT(XRE_IsParentProcess(),
235 "Only parent processes should be extracting LookAndFeel data");
237 if (sCachedLookAndFeelData) {
238 return sCachedLookAndFeelData;
241 static bool sInitialized = false;
242 if (!sInitialized) {
243 sInitialized = true;
244 ClearOnShutdown(&sCachedLookAndFeelData);
247 FullLookAndFeel* lf = new FullLookAndFeel{};
248 nsXPLookAndFeel* impl = nsXPLookAndFeel::GetInstance();
250 bool anyFromParent = false;
251 impl->WithThemeConfiguredForContent([&](const LookAndFeelTheme& aTheme,
252 bool aDifferentTheme) {
253 anyFromParent =
254 AddIDsToMap(impl, lf, aDifferentTheme, /* aFromParentTheme = */ false);
255 MOZ_ASSERT_IF(anyFromParent, aDifferentTheme);
256 lf->tables().passwordChar() = impl->GetPasswordCharacterImpl();
257 lf->tables().passwordEcho() = impl->GetEchoPasswordImpl();
258 #ifdef MOZ_WIDGET_GTK
259 lf->theme() = aTheme;
260 #endif
263 if (anyFromParent) {
264 AddIDsToMap(impl, lf, /* aDifferentTheme = */ true,
265 /* aFromParentTheme = */ true);
268 // This assignment to sCachedLookAndFeelData must be done after the
269 // WithThemeConfiguredForContent call, since it can end up calling RefreshImpl
270 // on the LookAndFeel, which will clear out sCachedTables.
271 sCachedLookAndFeelData = lf;
272 return sCachedLookAndFeelData;
275 void RemoteLookAndFeel::ClearCachedData() {
276 MOZ_ASSERT(XRE_IsParentProcess());
277 sCachedLookAndFeelData = nullptr;
280 } // namespace mozilla::widget