1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
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"
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"
20 #include <type_traits>
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());
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
) {
54 case LookAndFeel::IntID::AlertNotificationOrigin
:
55 return false; // see bug 1703205
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();
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
);
97 aItems
.AppendElement(aNewItem
.extract());
98 aMap
[mapIndex
] = static_cast<UInt
>(newIndex
);
103 nsresult
RemoteLookAndFeel::NativeGetColor(ColorID aID
, ColorScheme aScheme
,
105 const nscolor
* result
;
106 const bool dark
= aScheme
== ColorScheme::Dark
;
109 MapLookup(dark
? mTables
.darkColors() : mTables
.lightColors(),
110 dark
? mTables
.darkColorMap() : mTables
.lightColorMap(), aID
));
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
));
126 nsresult
RemoteLookAndFeel::NativeGetFloat(FloatID aID
, float& aResult
) {
128 MOZ_TRY_VAR(result
, MapLookup(mTables
.floats(), mTables
.floatMap(), aID
));
133 bool RemoteLookAndFeel::NativeGetFont(FontID aID
, nsString
& aFontName
,
134 gfxFontStyle
& aFontStyle
) {
135 auto result
= MapLookup(mTables
.fonts(), mTables
.fontMap(), aID
);
136 if (result
.isErr()) {
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
)) {
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
)) {
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
)) {
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
{};
188 bool rv
= aImpl
->NativeGetFont(id
, name
, fontStyle
);
189 Maybe
<LookAndFeelFont
> maybeFont
;
192 nsXPLookAndFeel::StyleToLookAndFeelFont(name
, fontStyle
));
194 AddToMap(aLf
->tables().fonts(), aLf
->tables().fontMap(), id
,
195 std::move(maybeFont
));
198 return anyFromOtherTheme
;
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;
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