summaryrefslogtreecommitdiffstats
path: root/widget/RemoteLookAndFeel.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'widget/RemoteLookAndFeel.cpp')
-rw-r--r--widget/RemoteLookAndFeel.cpp265
1 files changed, 265 insertions, 0 deletions
diff --git a/widget/RemoteLookAndFeel.cpp b/widget/RemoteLookAndFeel.cpp
new file mode 100644
index 0000000000..8ca87d9631
--- /dev/null
+++ b/widget/RemoteLookAndFeel.cpp
@@ -0,0 +1,265 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: sw=2 ts=8 et :
+ */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "RemoteLookAndFeel.h"
+
+#include "gfxFont.h"
+#include "MainThreadUtils.h"
+#include "mozilla/Assertions.h"
+#include "mozilla/Result.h"
+#include "mozilla/ResultExtensions.h"
+#include "mozilla/StaticPrefs_widget.h"
+#include "nsLookAndFeel.h"
+#include "nsXULAppAPI.h"
+
+#include <limits>
+#include <type_traits>
+#include <utility>
+
+namespace mozilla::widget {
+
+RemoteLookAndFeel::RemoteLookAndFeel(FullLookAndFeel&& aData)
+ : mTables(std::move(aData.tables())) {
+ MOZ_ASSERT(XRE_IsContentProcess(),
+ "Only content processes should be using a RemoteLookAndFeel");
+
+#ifdef MOZ_WIDGET_GTK
+ if (!StaticPrefs::widget_disable_native_theme_for_content()) {
+ // Configure the theme in this content process with the Gtk theme that was
+ // chosen by WithThemeConfiguredForContent in the parent process.
+ nsLookAndFeel::ConfigureTheme(aData.theme());
+ }
+#endif
+}
+
+RemoteLookAndFeel::~RemoteLookAndFeel() = default;
+
+void RemoteLookAndFeel::SetDataImpl(FullLookAndFeel&& aData) {
+ MOZ_ASSERT(XRE_IsContentProcess(),
+ "Only content processes should be using a RemoteLookAndFeel");
+ MOZ_ASSERT(NS_IsMainThread());
+ mTables = std::move(aData.tables());
+
+#ifdef MOZ_WIDGET_GTK
+ if (!StaticPrefs::widget_disable_native_theme_for_content()) {
+ // Configure the theme in this content process with the Gtk theme that was
+ // chosen by WithThemeConfiguredForContent in the parent process.
+ nsLookAndFeel::ConfigureTheme(aData.theme());
+ }
+#endif
+}
+
+namespace {
+
+template <typename Item, typename UInt, typename ID>
+Result<const Item*, nsresult> MapLookup(const nsTArray<Item>& aItems,
+ const nsTArray<UInt>& aMap, ID aID,
+ ID aMinimum = ID(0)) {
+ UInt mapped = aMap[static_cast<size_t>(aID) - static_cast<size_t>(aMinimum)];
+
+ if (mapped == std::numeric_limits<UInt>::max()) {
+ return Err(NS_ERROR_NOT_IMPLEMENTED);
+ }
+
+ return &aItems[static_cast<size_t>(mapped)];
+}
+
+template <typename Item, typename UInt>
+void AddToMap(nsTArray<Item>* aItems, nsTArray<UInt>* aMap,
+ Maybe<Item>&& aNewItem) {
+ if (aNewItem.isNothing()) {
+ aMap->AppendElement(std::numeric_limits<UInt>::max());
+ return;
+ }
+
+ size_t newIndex = aItems->Length();
+ MOZ_ASSERT(newIndex < std::numeric_limits<UInt>::max());
+
+ // Check if there is an existing value in aItems that we can point to.
+ //
+ // The arrays should be small enough and contain few enough unique
+ // values that sequential search here is reasonable.
+ for (size_t i = 0; i < newIndex; ++i) {
+ if ((*aItems)[i] == aNewItem.ref()) {
+ aMap->AppendElement(static_cast<UInt>(i));
+ return;
+ }
+ }
+
+ aItems->AppendElement(aNewItem.extract());
+ aMap->AppendElement(static_cast<UInt>(newIndex));
+}
+
+} // namespace
+
+nsresult RemoteLookAndFeel::NativeGetColor(ColorID aID, nscolor& aResult) {
+ const nscolor* result;
+ MOZ_TRY_VAR(result, MapLookup(mTables.colors(), mTables.colorMap(), aID));
+ aResult = *result;
+ return NS_OK;
+}
+
+nsresult RemoteLookAndFeel::NativeGetInt(IntID aID, int32_t& aResult) {
+ const int32_t* result;
+ MOZ_TRY_VAR(result, MapLookup(mTables.ints(), mTables.intMap(), aID));
+ aResult = *result;
+ return NS_OK;
+}
+
+nsresult RemoteLookAndFeel::NativeGetFloat(FloatID aID, float& aResult) {
+ const float* result;
+ MOZ_TRY_VAR(result, MapLookup(mTables.floats(), mTables.floatMap(), aID));
+ aResult = *result;
+ return NS_OK;
+}
+
+bool RemoteLookAndFeel::NativeGetFont(FontID aID, nsString& aFontName,
+ gfxFontStyle& aFontStyle) {
+ auto result =
+ MapLookup(mTables.fonts(), mTables.fontMap(), aID, FontID::MINIMUM);
+ if (result.isErr()) {
+ return false;
+ }
+
+ const LookAndFeelFont& font = *result.unwrap();
+ MOZ_ASSERT(font.haveFont());
+ aFontName = font.name();
+ aFontStyle = gfxFontStyle();
+ aFontStyle.size = font.size();
+ aFontStyle.weight = FontWeight(font.weight());
+ aFontStyle.style =
+ font.italic() ? FontSlantStyle::Italic() : FontSlantStyle::Normal();
+
+ return true;
+}
+
+char16_t RemoteLookAndFeel::GetPasswordCharacterImpl() {
+ return static_cast<char16_t>(mTables.passwordChar());
+}
+
+bool RemoteLookAndFeel::GetEchoPasswordImpl() { return mTables.passwordEcho(); }
+
+// static
+const FullLookAndFeel* RemoteLookAndFeel::ExtractData() {
+ MOZ_ASSERT(XRE_IsParentProcess(),
+ "Only parent processes should be extracting LookAndFeel data");
+
+ if (sCachedLookAndFeelData) {
+ return sCachedLookAndFeelData;
+ }
+
+ static bool sInitialized = false;
+ if (!sInitialized) {
+ sInitialized = true;
+ ClearOnShutdown(&sCachedLookAndFeelData);
+ }
+
+ FullLookAndFeel* lf = new FullLookAndFeel{};
+ nsXPLookAndFeel* impl = nsXPLookAndFeel::GetInstance();
+
+ int32_t darkTheme = 0;
+ int32_t accessibilityTheme = 0;
+ impl->NativeGetInt(IntID::SystemUsesDarkTheme, darkTheme);
+ impl->NativeGetInt(IntID::UseAccessibilityTheme, accessibilityTheme);
+
+ impl->WithThemeConfiguredForContent([&](const LookAndFeelTheme& aTheme) {
+ for (auto id : MakeEnumeratedRange(IntID::End)) {
+ int32_t theInt;
+ nsresult rv;
+ // We want to take SystemUsesDarkTheme and UseAccessibilityTheme from
+ // the parent process theme rather than the content configured theme.
+ // This ensures that media queries like (prefers-color-scheme: dark) will
+ // match correctly in content processes.
+ //
+ // (When the RemoteLookAndFeel is not in use, the LookAndFeelCache
+ // ensures we get these values from the parent process theme.)
+ switch (id) {
+ case IntID::SystemUsesDarkTheme:
+ theInt = darkTheme;
+ rv = NS_OK;
+ break;
+ case IntID::UseAccessibilityTheme:
+ theInt = accessibilityTheme;
+ rv = NS_OK;
+ break;
+ default:
+ rv = impl->NativeGetInt(id, theInt);
+ break;
+ }
+ AddToMap(&lf->tables().ints(), &lf->tables().intMap(),
+ NS_SUCCEEDED(rv) ? Some(theInt) : Nothing{});
+ }
+
+ for (auto id : MakeEnumeratedRange(FloatID::End)) {
+ float theFloat;
+ nsresult rv = impl->NativeGetFloat(id, theFloat);
+ AddToMap(&lf->tables().floats(), &lf->tables().floatMap(),
+ NS_SUCCEEDED(rv) ? Some(theFloat) : Nothing{});
+ }
+
+ for (auto id : MakeEnumeratedRange(ColorID::End)) {
+ nscolor theColor;
+ nsresult rv = impl->NativeGetColor(id, theColor);
+ AddToMap(&lf->tables().colors(), &lf->tables().colorMap(),
+ NS_SUCCEEDED(rv) ? Some(theColor) : Nothing{});
+ }
+
+ for (auto id :
+ MakeInclusiveEnumeratedRange(FontID::MINIMUM, FontID::MAXIMUM)) {
+ LookAndFeelFont font{};
+ gfxFontStyle fontStyle{};
+
+ bool rv = impl->NativeGetFont(id, font.name(), fontStyle);
+ Maybe<LookAndFeelFont> maybeFont;
+ if (rv) {
+ font.haveFont() = true;
+ font.size() = fontStyle.size;
+ font.weight() = fontStyle.weight.ToFloat();
+ font.italic() = fontStyle.style.IsItalic();
+ MOZ_ASSERT(fontStyle.style.IsNormal() || fontStyle.style.IsItalic(),
+ "Cannot handle oblique font style");
+#ifdef DEBUG
+ {
+ // Assert that all the remaining font style properties have their
+ // default values.
+ gfxFontStyle candidate = fontStyle;
+ gfxFontStyle defaults{};
+ candidate.size = defaults.size;
+ candidate.weight = defaults.weight;
+ candidate.style = defaults.style;
+ MOZ_ASSERT(candidate.Equals(defaults),
+ "Some font style properties not supported");
+ }
+#endif
+ maybeFont = Some(std::move(font));
+ }
+ AddToMap(&lf->tables().fonts(), &lf->tables().fontMap(),
+ std::move(maybeFont));
+ }
+
+ lf->tables().passwordChar() = impl->GetPasswordCharacterImpl();
+ lf->tables().passwordEcho() = impl->GetEchoPasswordImpl();
+#ifdef MOZ_WIDGET_GTK
+ lf->theme() = aTheme;
+#endif
+ });
+
+ // This assignment to sCachedLookAndFeelData must be done after the
+ // WithThemeConfiguredForContent call, since it can end up calling RefreshImpl
+ // on the LookAndFeel, which will clear out sCachedTables.
+ sCachedLookAndFeelData = lf;
+ return sCachedLookAndFeelData;
+}
+
+void RemoteLookAndFeel::ClearCachedData() {
+ MOZ_ASSERT(XRE_IsParentProcess());
+ sCachedLookAndFeelData = nullptr;
+}
+
+StaticAutoPtr<FullLookAndFeel> RemoteLookAndFeel::sCachedLookAndFeelData;
+
+} // namespace mozilla::widget