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/Result.h"
15 #include "mozilla/ResultExtensions.h"
16 #include "mozilla/StaticPrefs_widget.h"
17 #include "nsLookAndFeel.h"
18 #include "nsXULAppAPI.h"
21 #include <type_traits>
24 namespace mozilla::widget
{
26 RemoteLookAndFeel::RemoteLookAndFeel(FullLookAndFeel
&& aData
)
27 : mTables(std::move(aData
.tables())) {
28 MOZ_ASSERT(XRE_IsContentProcess(),
29 "Only content processes should be using a RemoteLookAndFeel");
32 if (!StaticPrefs::widget_non_native_theme_enabled()) {
33 // Configure the theme in this content process with the Gtk theme that was
34 // chosen by WithThemeConfiguredForContent in the parent process.
35 nsLookAndFeel::ConfigureTheme(aData
.theme());
40 RemoteLookAndFeel::~RemoteLookAndFeel() = default;
42 void RemoteLookAndFeel::SetDataImpl(FullLookAndFeel
&& aData
) {
43 MOZ_ASSERT(XRE_IsContentProcess(),
44 "Only content processes should be using a RemoteLookAndFeel");
45 MOZ_ASSERT(NS_IsMainThread());
46 mTables
= std::move(aData
.tables());
49 if (!StaticPrefs::widget_non_native_theme_enabled()) {
50 // Configure the theme in this content process with the Gtk theme that was
51 // chosen by WithThemeConfiguredForContent in the parent process.
52 nsLookAndFeel::ConfigureTheme(aData
.theme());
59 template <typename Item
, typename UInt
, typename ID
>
60 Result
<const Item
*, nsresult
> MapLookup(const nsTArray
<Item
>& aItems
,
61 const nsTArray
<UInt
>& aMap
, ID aID
,
62 ID aMinimum
= ID(0)) {
63 UInt mapped
= aMap
[static_cast<size_t>(aID
) - static_cast<size_t>(aMinimum
)];
65 if (mapped
== std::numeric_limits
<UInt
>::max()) {
66 return Err(NS_ERROR_NOT_IMPLEMENTED
);
69 return &aItems
[static_cast<size_t>(mapped
)];
72 template <typename Item
, typename UInt
>
73 void AddToMap(nsTArray
<Item
>* aItems
, nsTArray
<UInt
>* aMap
,
74 Maybe
<Item
>&& aNewItem
) {
75 if (aNewItem
.isNothing()) {
76 aMap
->AppendElement(std::numeric_limits
<UInt
>::max());
80 size_t newIndex
= aItems
->Length();
81 MOZ_ASSERT(newIndex
< std::numeric_limits
<UInt
>::max());
83 // Check if there is an existing value in aItems that we can point to.
85 // The arrays should be small enough and contain few enough unique
86 // values that sequential search here is reasonable.
87 for (size_t i
= 0; i
< newIndex
; ++i
) {
88 if ((*aItems
)[i
] == aNewItem
.ref()) {
89 aMap
->AppendElement(static_cast<UInt
>(i
));
94 aItems
->AppendElement(aNewItem
.extract());
95 aMap
->AppendElement(static_cast<UInt
>(newIndex
));
100 nsresult
RemoteLookAndFeel::NativeGetColor(ColorID aID
, nscolor
& aResult
) {
101 const nscolor
* result
;
102 MOZ_TRY_VAR(result
, MapLookup(mTables
.colors(), mTables
.colorMap(), aID
));
107 nsresult
RemoteLookAndFeel::NativeGetInt(IntID aID
, int32_t& aResult
) {
108 const int32_t* result
;
109 MOZ_TRY_VAR(result
, MapLookup(mTables
.ints(), mTables
.intMap(), aID
));
114 nsresult
RemoteLookAndFeel::NativeGetFloat(FloatID aID
, float& aResult
) {
116 MOZ_TRY_VAR(result
, MapLookup(mTables
.floats(), mTables
.floatMap(), aID
));
121 bool RemoteLookAndFeel::NativeGetFont(FontID aID
, nsString
& aFontName
,
122 gfxFontStyle
& aFontStyle
) {
124 MapLookup(mTables
.fonts(), mTables
.fontMap(), aID
, FontID::MINIMUM
);
125 if (result
.isErr()) {
129 const LookAndFeelFont
& font
= *result
.unwrap();
130 MOZ_ASSERT(font
.haveFont());
131 aFontName
= font
.name();
132 aFontStyle
= gfxFontStyle();
133 aFontStyle
.size
= font
.size();
134 aFontStyle
.weight
= FontWeight(font
.weight());
136 font
.italic() ? FontSlantStyle::Italic() : FontSlantStyle::Normal();
141 char16_t
RemoteLookAndFeel::GetPasswordCharacterImpl() {
142 return static_cast<char16_t
>(mTables
.passwordChar());
145 bool RemoteLookAndFeel::GetEchoPasswordImpl() { return mTables
.passwordEcho(); }
148 const FullLookAndFeel
* RemoteLookAndFeel::ExtractData() {
149 MOZ_ASSERT(XRE_IsParentProcess(),
150 "Only parent processes should be extracting LookAndFeel data");
152 if (sCachedLookAndFeelData
) {
153 return sCachedLookAndFeelData
;
156 static bool sInitialized
= false;
159 ClearOnShutdown(&sCachedLookAndFeelData
);
162 FullLookAndFeel
* lf
= new FullLookAndFeel
{};
163 nsXPLookAndFeel
* impl
= nsXPLookAndFeel::GetInstance();
165 int32_t darkTheme
= 0;
166 int32_t accessibilityTheme
= 0;
167 impl
->NativeGetInt(IntID::SystemUsesDarkTheme
, darkTheme
);
168 impl
->NativeGetInt(IntID::UseAccessibilityTheme
, accessibilityTheme
);
170 impl
->WithThemeConfiguredForContent([&](const LookAndFeelTheme
& aTheme
) {
171 for (auto id
: MakeEnumeratedRange(IntID::End
)) {
174 // We want to take SystemUsesDarkTheme and UseAccessibilityTheme from
175 // the parent process theme rather than the content configured theme.
176 // This ensures that media queries like (prefers-color-scheme: dark) will
177 // match correctly in content processes.
179 // (When the RemoteLookAndFeel is not in use, the LookAndFeelCache
180 // ensures we get these values from the parent process theme.)
182 case IntID::SystemUsesDarkTheme
:
186 case IntID::UseAccessibilityTheme
:
187 theInt
= accessibilityTheme
;
191 rv
= impl
->NativeGetInt(id
, theInt
);
194 AddToMap(&lf
->tables().ints(), &lf
->tables().intMap(),
195 NS_SUCCEEDED(rv
) ? Some(theInt
) : Nothing
{});
198 for (auto id
: MakeEnumeratedRange(FloatID::End
)) {
200 nsresult rv
= impl
->NativeGetFloat(id
, theFloat
);
201 AddToMap(&lf
->tables().floats(), &lf
->tables().floatMap(),
202 NS_SUCCEEDED(rv
) ? Some(theFloat
) : Nothing
{});
205 for (auto id
: MakeEnumeratedRange(ColorID::End
)) {
207 nsresult rv
= impl
->NativeGetColor(id
, theColor
);
208 AddToMap(&lf
->tables().colors(), &lf
->tables().colorMap(),
209 NS_SUCCEEDED(rv
) ? Some(theColor
) : Nothing
{});
213 MakeInclusiveEnumeratedRange(FontID::MINIMUM
, FontID::MAXIMUM
)) {
214 LookAndFeelFont font
{};
215 gfxFontStyle fontStyle
{};
217 bool rv
= impl
->NativeGetFont(id
, font
.name(), fontStyle
);
218 Maybe
<LookAndFeelFont
> maybeFont
;
220 font
.haveFont() = true;
221 font
.size() = fontStyle
.size
;
222 font
.weight() = fontStyle
.weight
.ToFloat();
223 font
.italic() = fontStyle
.style
.IsItalic();
224 MOZ_ASSERT(fontStyle
.style
.IsNormal() || fontStyle
.style
.IsItalic(),
225 "Cannot handle oblique font style");
228 // Assert that all the remaining font style properties have their
230 gfxFontStyle candidate
= fontStyle
;
231 gfxFontStyle defaults
{};
232 candidate
.size
= defaults
.size
;
233 candidate
.weight
= defaults
.weight
;
234 candidate
.style
= defaults
.style
;
235 MOZ_ASSERT(candidate
.Equals(defaults
),
236 "Some font style properties not supported");
239 maybeFont
= Some(std::move(font
));
241 AddToMap(&lf
->tables().fonts(), &lf
->tables().fontMap(),
242 std::move(maybeFont
));
245 lf
->tables().passwordChar() = impl
->GetPasswordCharacterImpl();
246 lf
->tables().passwordEcho() = impl
->GetEchoPasswordImpl();
247 #ifdef MOZ_WIDGET_GTK
248 lf
->theme() = aTheme
;
252 // This assignment to sCachedLookAndFeelData must be done after the
253 // WithThemeConfiguredForContent call, since it can end up calling RefreshImpl
254 // on the LookAndFeel, which will clear out sCachedTables.
255 sCachedLookAndFeelData
= lf
;
256 return sCachedLookAndFeelData
;
259 void RemoteLookAndFeel::ClearCachedData() {
260 MOZ_ASSERT(XRE_IsParentProcess());
261 sCachedLookAndFeelData
= nullptr;
264 StaticAutoPtr
<FullLookAndFeel
> RemoteLookAndFeel::sCachedLookAndFeelData
;
266 } // namespace mozilla::widget