forked from mirrors/gecko-dev
		
	
		
			
				
	
	
		
			236 lines
		
	
	
	
		
			7.4 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			236 lines
		
	
	
	
		
			7.4 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| /* -*- 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/ClearOnShutdown.h"
 | |
| #include "mozilla/ResultExtensions.h"
 | |
| #include "mozilla/StaticPrefs_widget.h"
 | |
| #include "mozilla/Try.h"
 | |
| #include "nsXULAppAPI.h"
 | |
| 
 | |
| #include <limits>
 | |
| #include <type_traits>
 | |
| #include <utility>
 | |
| 
 | |
| namespace mozilla::widget {
 | |
| 
 | |
| // A cached copy of the data extracted by ExtractData.
 | |
| //
 | |
| // Storing this lets us avoid doing most of the work of ExtractData each
 | |
| // time we create a new content process.
 | |
| //
 | |
| // Only used in the parent process.
 | |
| static StaticAutoPtr<FullLookAndFeel> sCachedLookAndFeelData;
 | |
| 
 | |
| RemoteLookAndFeel::RemoteLookAndFeel(FullLookAndFeel&& aData)
 | |
|     : mTables(std::move(aData.tables())) {
 | |
|   MOZ_ASSERT(XRE_IsContentProcess(),
 | |
|              "Only content processes should be using a RemoteLookAndFeel");
 | |
| }
 | |
| 
 | |
| 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());
 | |
| }
 | |
| 
 | |
| namespace {
 | |
| 
 | |
| // Some lnf values are somewhat expensive to get, and are not needed in child
 | |
| // processes, so we can avoid querying them.
 | |
| bool IsNeededInChildProcess(LookAndFeel::IntID aId) {
 | |
|   switch (aId) {
 | |
|     case LookAndFeel::IntID::AlertNotificationOrigin:
 | |
|       return false;  // see bug 1703205
 | |
|     default:
 | |
|       return true;
 | |
|   }
 | |
| }
 | |
| 
 | |
| template <typename Item, typename UInt, typename ID>
 | |
| Result<const Item*, nsresult> MapLookup(const nsTArray<Item>& aItems,
 | |
|                                         const nsTArray<UInt>& aMap, ID aID) {
 | |
|   UInt mapped = aMap[static_cast<size_t>(aID)];
 | |
| 
 | |
|   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, typename Id>
 | |
| void AddToMap(nsTArray<Item>& aItems, nsTArray<UInt>& aMap, Id aId,
 | |
|               Maybe<Item>&& aNewItem) {
 | |
|   auto mapIndex = size_t(aId);
 | |
|   aMap.EnsureLengthAtLeast(mapIndex + 1);
 | |
|   if (aNewItem.isNothing()) {
 | |
|     aMap[mapIndex] = 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[mapIndex] = static_cast<UInt>(i);
 | |
|       return;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   aItems.AppendElement(aNewItem.extract());
 | |
|   aMap[mapIndex] = static_cast<UInt>(newIndex);
 | |
| }
 | |
| 
 | |
| }  // namespace
 | |
| 
 | |
| nsresult RemoteLookAndFeel::NativeGetColor(ColorID aID, ColorScheme aScheme,
 | |
|                                            nscolor& aResult) {
 | |
|   const nscolor* result;
 | |
|   const bool dark = aScheme == ColorScheme::Dark;
 | |
|   MOZ_TRY_VAR(
 | |
|       result,
 | |
|       MapLookup(dark ? mTables.darkColors() : mTables.lightColors(),
 | |
|                 dark ? mTables.darkColorMap() : mTables.lightColorMap(), aID));
 | |
|   aResult = *result;
 | |
|   return NS_OK;
 | |
| }
 | |
| 
 | |
| nsresult RemoteLookAndFeel::NativeGetInt(IntID aID, int32_t& aResult) {
 | |
|   MOZ_DIAGNOSTIC_ASSERT(
 | |
|       IsNeededInChildProcess(aID),
 | |
|       "Querying value that we didn't bother getting from the parent process!");
 | |
| 
 | |
|   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);
 | |
|   if (result.isErr()) {
 | |
|     return false;
 | |
|   }
 | |
|   const LookAndFeelFont& font = *result.unwrap();
 | |
|   return LookAndFeelFontToStyle(font, aFontName, aFontStyle);
 | |
| }
 | |
| 
 | |
| char16_t RemoteLookAndFeel::GetPasswordCharacterImpl() {
 | |
|   return static_cast<char16_t>(mTables.passwordChar());
 | |
| }
 | |
| 
 | |
| bool RemoteLookAndFeel::GetEchoPasswordImpl() { return mTables.passwordEcho(); }
 | |
| 
 | |
| static bool AddIDsToMap(nsXPLookAndFeel* aImpl, FullLookAndFeel* aLf) {
 | |
|   using IntID = LookAndFeel::IntID;
 | |
|   using FontID = LookAndFeel::FontID;
 | |
|   using FloatID = LookAndFeel::FloatID;
 | |
|   using ColorID = LookAndFeel::ColorID;
 | |
|   using ColorScheme = LookAndFeel::ColorScheme;
 | |
| 
 | |
|   bool anyFromOtherTheme = false;
 | |
|   for (auto id : MakeEnumeratedRange(IntID::End)) {
 | |
|     if (!IsNeededInChildProcess(id)) {
 | |
|       continue;
 | |
|     }
 | |
|     int32_t theInt;
 | |
|     nsresult rv = aImpl->NativeGetInt(id, theInt);
 | |
|     AddToMap(aLf->tables().ints(), aLf->tables().intMap(), id,
 | |
|              NS_SUCCEEDED(rv) ? Some(theInt) : Nothing{});
 | |
|   }
 | |
| 
 | |
|   for (auto id : MakeEnumeratedRange(ColorID::End)) {
 | |
|     nscolor theColor;
 | |
|     nsresult rv = aImpl->NativeGetColor(id, ColorScheme::Light, theColor);
 | |
|     AddToMap(aLf->tables().lightColors(), aLf->tables().lightColorMap(), id,
 | |
|              NS_SUCCEEDED(rv) ? Some(theColor) : Nothing{});
 | |
|     rv = aImpl->NativeGetColor(id, ColorScheme::Dark, theColor);
 | |
|     AddToMap(aLf->tables().darkColors(), aLf->tables().darkColorMap(), id,
 | |
|              NS_SUCCEEDED(rv) ? Some(theColor) : Nothing{});
 | |
|   }
 | |
| 
 | |
|   for (auto id : MakeEnumeratedRange(FloatID::End)) {
 | |
|     float theFloat;
 | |
|     nsresult rv = aImpl->NativeGetFloat(id, theFloat);
 | |
|     AddToMap(aLf->tables().floats(), aLf->tables().floatMap(), id,
 | |
|              NS_SUCCEEDED(rv) ? Some(theFloat) : Nothing{});
 | |
|   }
 | |
| 
 | |
|   for (auto id : MakeEnumeratedRange(FontID::End)) {
 | |
|     gfxFontStyle fontStyle{};
 | |
| 
 | |
|     nsString name;
 | |
|     bool rv = aImpl->NativeGetFont(id, name, fontStyle);
 | |
|     Maybe<LookAndFeelFont> maybeFont;
 | |
|     if (rv) {
 | |
|       maybeFont.emplace(
 | |
|           nsXPLookAndFeel::StyleToLookAndFeelFont(name, fontStyle));
 | |
|     }
 | |
|     AddToMap(aLf->tables().fonts(), aLf->tables().fontMap(), id,
 | |
|              std::move(maybeFont));
 | |
|   }
 | |
| 
 | |
|   return anyFromOtherTheme;
 | |
| }
 | |
| 
 | |
| // 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();
 | |
| 
 | |
|   lf->tables().passwordChar() = impl->GetPasswordCharacterImpl();
 | |
|   lf->tables().passwordEcho() = impl->GetEchoPasswordImpl();
 | |
| 
 | |
|   AddIDsToMap(impl, lf);
 | |
| 
 | |
|   // 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;
 | |
| }
 | |
| 
 | |
| }  // namespace mozilla::widget
 | 
