Bug 1852149 Part 1 - Add margin-rule pref r=firefox-style-system-reviewers,emilio
[gecko.git] / widget / RemoteLookAndFeel.cpp
blob5eea54496b92679ccd3821fa7db882a786cbe99a
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/ResultExtensions.h"
15 #include "mozilla/StaticPrefs_widget.h"
16 #include "mozilla/Try.h"
17 #include "nsXULAppAPI.h"
19 #include <limits>
20 #include <type_traits>
21 #include <utility>
23 namespace mozilla::widget {
25 // A cached copy of the data extracted by ExtractData.
27 // Storing this lets us avoid doing most of the work of ExtractData each
28 // time we create a new content process.
30 // Only used in the parent process.
31 static StaticAutoPtr<FullLookAndFeel> sCachedLookAndFeelData;
33 RemoteLookAndFeel::RemoteLookAndFeel(FullLookAndFeel&& aData)
34 : mTables(std::move(aData.tables())) {
35 MOZ_ASSERT(XRE_IsContentProcess(),
36 "Only content processes should be using a RemoteLookAndFeel");
39 RemoteLookAndFeel::~RemoteLookAndFeel() = default;
41 void RemoteLookAndFeel::SetDataImpl(FullLookAndFeel&& aData) {
42 MOZ_ASSERT(XRE_IsContentProcess(),
43 "Only content processes should be using a RemoteLookAndFeel");
44 MOZ_ASSERT(NS_IsMainThread());
45 mTables = std::move(aData.tables());
48 namespace {
50 // Some lnf values are somewhat expensive to get, and are not needed in child
51 // processes, so we can avoid querying them.
52 bool IsNeededInChildProcess(LookAndFeel::IntID aId) {
53 switch (aId) {
54 case LookAndFeel::IntID::AlertNotificationOrigin:
55 return false; // see bug 1703205
56 default:
57 return true;
61 template <typename Item, typename UInt, typename ID>
62 Result<const Item*, nsresult> MapLookup(const nsTArray<Item>& aItems,
63 const nsTArray<UInt>& aMap, ID aID) {
64 UInt mapped = aMap[static_cast<size_t>(aID)];
66 if (mapped == std::numeric_limits<UInt>::max()) {
67 return Err(NS_ERROR_NOT_IMPLEMENTED);
70 return &aItems[static_cast<size_t>(mapped)];
73 template <typename Item, typename UInt, typename Id>
74 void AddToMap(nsTArray<Item>& aItems, nsTArray<UInt>& aMap, Id aId,
75 Maybe<Item>&& aNewItem) {
76 auto mapIndex = size_t(aId);
77 aMap.EnsureLengthAtLeast(mapIndex + 1);
78 if (aNewItem.isNothing()) {
79 aMap[mapIndex] = std::numeric_limits<UInt>::max();
80 return;
83 size_t newIndex = aItems.Length();
84 MOZ_ASSERT(newIndex < std::numeric_limits<UInt>::max());
86 // Check if there is an existing value in aItems that we can point to.
88 // The arrays should be small enough and contain few enough unique
89 // values that sequential search here is reasonable.
90 for (size_t i = 0; i < newIndex; ++i) {
91 if (aItems[i] == aNewItem.ref()) {
92 aMap[mapIndex] = static_cast<UInt>(i);
93 return;
97 aItems.AppendElement(aNewItem.extract());
98 aMap[mapIndex] = static_cast<UInt>(newIndex);
101 } // namespace
103 nsresult RemoteLookAndFeel::NativeGetColor(ColorID aID, ColorScheme aScheme,
104 nscolor& aResult) {
105 const nscolor* result;
106 const bool dark = aScheme == ColorScheme::Dark;
107 MOZ_TRY_VAR(
108 result,
109 MapLookup(dark ? mTables.darkColors() : mTables.lightColors(),
110 dark ? mTables.darkColorMap() : mTables.lightColorMap(), aID));
111 aResult = *result;
112 return NS_OK;
115 nsresult RemoteLookAndFeel::NativeGetInt(IntID aID, int32_t& aResult) {
116 MOZ_DIAGNOSTIC_ASSERT(
117 IsNeededInChildProcess(aID),
118 "Querying value that we didn't bother getting from the parent process!");
120 const int32_t* result;
121 MOZ_TRY_VAR(result, MapLookup(mTables.ints(), mTables.intMap(), aID));
122 aResult = *result;
123 return NS_OK;
126 nsresult RemoteLookAndFeel::NativeGetFloat(FloatID aID, float& aResult) {
127 const float* result;
128 MOZ_TRY_VAR(result, MapLookup(mTables.floats(), mTables.floatMap(), aID));
129 aResult = *result;
130 return NS_OK;
133 bool RemoteLookAndFeel::NativeGetFont(FontID aID, nsString& aFontName,
134 gfxFontStyle& aFontStyle) {
135 auto result = MapLookup(mTables.fonts(), mTables.fontMap(), aID);
136 if (result.isErr()) {
137 return false;
139 const LookAndFeelFont& font = *result.unwrap();
140 return LookAndFeelFontToStyle(font, aFontName, aFontStyle);
143 char16_t RemoteLookAndFeel::GetPasswordCharacterImpl() {
144 return static_cast<char16_t>(mTables.passwordChar());
147 bool RemoteLookAndFeel::GetEchoPasswordImpl() { return mTables.passwordEcho(); }
149 static bool AddIDsToMap(nsXPLookAndFeel* aImpl, FullLookAndFeel* aLf) {
150 using IntID = LookAndFeel::IntID;
151 using FontID = LookAndFeel::FontID;
152 using FloatID = LookAndFeel::FloatID;
153 using ColorID = LookAndFeel::ColorID;
154 using ColorScheme = LookAndFeel::ColorScheme;
156 bool anyFromOtherTheme = false;
157 for (auto id : MakeEnumeratedRange(IntID::End)) {
158 if (!IsNeededInChildProcess(id)) {
159 continue;
161 int32_t theInt;
162 nsresult rv = aImpl->NativeGetInt(id, theInt);
163 AddToMap(aLf->tables().ints(), aLf->tables().intMap(), id,
164 NS_SUCCEEDED(rv) ? Some(theInt) : Nothing{});
167 for (auto id : MakeEnumeratedRange(ColorID::End)) {
168 nscolor theColor;
169 nsresult rv = aImpl->NativeGetColor(id, ColorScheme::Light, theColor);
170 AddToMap(aLf->tables().lightColors(), aLf->tables().lightColorMap(), id,
171 NS_SUCCEEDED(rv) ? Some(theColor) : Nothing{});
172 rv = aImpl->NativeGetColor(id, ColorScheme::Dark, theColor);
173 AddToMap(aLf->tables().darkColors(), aLf->tables().darkColorMap(), id,
174 NS_SUCCEEDED(rv) ? Some(theColor) : Nothing{});
177 for (auto id : MakeEnumeratedRange(FloatID::End)) {
178 float theFloat;
179 nsresult rv = aImpl->NativeGetFloat(id, theFloat);
180 AddToMap(aLf->tables().floats(), aLf->tables().floatMap(), id,
181 NS_SUCCEEDED(rv) ? Some(theFloat) : Nothing{});
184 for (auto id : MakeEnumeratedRange(FontID::End)) {
185 gfxFontStyle fontStyle{};
187 nsString name;
188 bool rv = aImpl->NativeGetFont(id, name, fontStyle);
189 Maybe<LookAndFeelFont> maybeFont;
190 if (rv) {
191 maybeFont.emplace(
192 nsXPLookAndFeel::StyleToLookAndFeelFont(name, fontStyle));
194 AddToMap(aLf->tables().fonts(), aLf->tables().fontMap(), id,
195 std::move(maybeFont));
198 return anyFromOtherTheme;
201 // static
202 const FullLookAndFeel* RemoteLookAndFeel::ExtractData() {
203 MOZ_ASSERT(XRE_IsParentProcess(),
204 "Only parent processes should be extracting LookAndFeel data");
206 if (sCachedLookAndFeelData) {
207 return sCachedLookAndFeelData;
210 static bool sInitialized = false;
211 if (!sInitialized) {
212 sInitialized = true;
213 ClearOnShutdown(&sCachedLookAndFeelData);
216 FullLookAndFeel* lf = new FullLookAndFeel{};
217 nsXPLookAndFeel* impl = nsXPLookAndFeel::GetInstance();
219 lf->tables().passwordChar() = impl->GetPasswordCharacterImpl();
220 lf->tables().passwordEcho() = impl->GetEchoPasswordImpl();
222 AddIDsToMap(impl, lf);
224 // This assignment to sCachedLookAndFeelData must be done after the
225 // WithThemeConfiguredForContent call, since it can end up calling RefreshImpl
226 // on the LookAndFeel, which will clear out sCachedTables.
227 sCachedLookAndFeelData = lf;
228 return sCachedLookAndFeelData;
231 void RemoteLookAndFeel::ClearCachedData() {
232 MOZ_ASSERT(XRE_IsParentProcess());
233 sCachedLookAndFeelData = nullptr;
236 } // namespace mozilla::widget