Source code
Revision control
Copy as Markdown
Other Tools
/* -*- mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* 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
#include "mozilla/ArrayUtils.h"
#include "mozilla/LookAndFeel.h"
#include "mozilla/RWLock.h"
#include "nscore.h"
#include "nsXPLookAndFeel.h"
#include "nsLookAndFeel.h"
#include "HeadlessLookAndFeel.h"
#include "RemoteLookAndFeel.h"
#include "nsContentUtils.h"
#include "nsCRT.h"
#include "nsFont.h"
#include "nsIFrame.h"
#include "nsIXULRuntime.h"
#include "nsLayoutUtils.h"
#include "Theme.h"
#include "SurfaceCacheUtils.h"
#include "mozilla/dom/ContentParent.h"
#include "mozilla/dom/ContentChild.h"
#include "mozilla/glean/WidgetMetrics.h"
#include "mozilla/Preferences.h"
#include "mozilla/Services.h"
#include "mozilla/ServoStyleSet.h"
#include "mozilla/ServoCSSParser.h"
#include "mozilla/StaticPrefs_browser.h"
#include "mozilla/StaticPrefs_editor.h"
#include "mozilla/StaticPrefs_layout.h"
#include "mozilla/StaticPrefs_ui.h"
#include "mozilla/StaticPrefs_widget.h"
#include "mozilla/dom/Document.h"
#include "mozilla/PreferenceSheet.h"
#include "mozilla/gfx/2D.h"
#include "mozilla/widget/WidgetMessageUtils.h"
#include "mozilla/dom/KeyboardEventBinding.h"
#include "mozilla/RelativeLuminanceUtils.h"
#include "mozilla/glean/GleanMetrics.h"
#include "mozilla/TelemetryScalarEnums.h"
#include "mozilla/Try.h"
#include "gfxPlatform.h"
#include "gfxFont.h"
#include "qcms.h"
#include <bitset>
using namespace mozilla;
using IntID = mozilla::LookAndFeel::IntID;
using FloatID = mozilla::LookAndFeel::FloatID;
using ColorID = mozilla::LookAndFeel::ColorID;
using FontID = mozilla::LookAndFeel::FontID;
// Fully transparent red seems unlikely enough.
constexpr nscolor kNoColor = NS_RGBA(0xff, 0, 0, 0);
using ColorStore =
EnumeratedArray<ColorID, RelaxedAtomicUint32, size_t(ColorID::End)>;
struct ColorStores {
using UseStandins = LookAndFeel::UseStandins;
ColorStore mStores[2][2];
constexpr ColorStores() = default;
ColorStore& Get(ColorScheme aScheme, UseStandins aUseStandins) {
return mStores[aScheme == ColorScheme::Dark]
[aUseStandins == UseStandins::Yes];
}
};
static ColorStores sColorStores;
constexpr uint32_t kNoFloat = 0xffffff;
static EnumeratedArray<FloatID, RelaxedAtomicUint32, size_t(FloatID::End)>
sFloatStore;
constexpr int32_t kNoInt = INT32_MIN;
static EnumeratedArray<IntID, RelaxedAtomicInt32, size_t(IntID::End)> sIntStore;
StaticRWLock sFontStoreLock;
MOZ_RUNINIT static EnumeratedArray<FontID, widget::LookAndFeelFont,
size_t(FontID::End)>
sFontStore MOZ_GUARDED_BY(sFontStoreLock);
// To make one of these prefs toggleable from a reftest add a user
// pref in testing/profiles/reftest/user.js. For example, to make
// ui.useAccessibilityTheme toggleable, add:
//
// user_pref("ui.useAccessibilityTheme", 0);
//
// This needs to be of the same length and in the same order as
// LookAndFeel::IntID values.
static const char sIntPrefs[][45] = {
"ui.caretBlinkTime",
"ui.caretBlinkCount",
"ui.caretWidth",
"ui.selectTextfieldsOnKeyFocus",
"ui.submenuDelay",
"ui.menusCanOverlapOSBar",
"ui.useOverlayScrollbars",
"ui.allowOverlayScrollbarsOverlap",
"ui.skipNavigatingDisabledMenuItem",
"ui.dragThresholdX",
"ui.dragThresholdY",
"ui.useAccessibilityTheme",
"ui.scrollArrowStyle",
"ui.scrollButtonLeftMouseButtonAction",
"ui.scrollButtonMiddleMouseButtonAction",
"ui.scrollButtonRightMouseButtonAction",
"ui.treeOpenDelay",
"ui.treeCloseDelay",
"ui.treeLazyScrollDelay",
"ui.treeScrollDelay",
"ui.treeScrollLinesMax",
"ui.chosenMenuItemsShouldBlink",
"ui.windowsAccentColorInTitlebar",
"ui.windowsMica",
"ui.windowsMicaPopups",
"ui.macBigSurTheme",
"ui.macRTL",
"ui.macTitlebarHeight",
"ui.alertNotificationOrigin",
"ui.scrollToClick",
"ui.IMERawInputUnderlineStyle",
"ui.IMESelectedRawTextUnderlineStyle",
"ui.IMEConvertedTextUnderlineStyle",
"ui.IMESelectedConvertedTextUnderlineStyle",
"ui.SpellCheckerUnderlineStyle",
"ui.menuBarDrag",
"ui.scrollbarButtonAutoRepeatBehavior",
"ui.swipeAnimationEnabled",
"ui.scrollbarDisplayOnMouseMove",
"ui.scrollbarFadeBeginDelay",
"ui.scrollbarFadeDuration",
"ui.contextMenuOffsetVertical",
"ui.contextMenuOffsetHorizontal",
"ui.tooltipOffsetVertical",
"ui.GtkCSDAvailable",
"ui.GtkCSDTransparencyAvailable",
"ui.GtkCSDMinimizeButton",
"ui.GtkCSDMaximizeButton",
"ui.GtkCSDCloseButton",
"ui.GtkCSDMinimizeButtonPosition",
"ui.GtkCSDMaximizeButtonPosition",
"ui.GtkCSDCloseButtonPosition",
"ui.GtkCSDReversedPlacement",
"ui.systemUsesDarkTheme",
"ui.prefersReducedMotion",
"ui.prefersReducedTransparency",
"ui.invertedColors",
"ui.primaryPointerCapabilities",
"ui.allPointerCapabilities",
"ui.systemScrollbarSize",
"ui.touchDeviceSupportPresent",
"ui.titlebarRadius",
"ui.titlebarButtonSpacing",
"ui.tooltipRadius",
"ui.dynamicRange",
"ui.panelAnimations",
"ui.hideCursorWhileTyping",
"ui.gtkThemeFamily",
"ui.fullKeyboardAccess",
"ui.pointingDeviceKinds",
"ui.nativeMenubar",
};
static_assert(std::size(sIntPrefs) == size_t(LookAndFeel::IntID::End),
"Should have a pref for each int value");
// This array MUST be kept in the same order as the float id list in
// LookAndFeel.h
// clang-format off
static const char sFloatPrefs[][37] = {
"ui.IMEUnderlineRelativeSize",
"ui.SpellCheckerUnderlineRelativeSize",
"ui.caretAspectRatio",
"ui.textScaleFactor",
"ui.cursorScale",
};
// clang-format on
static_assert(std::size(sFloatPrefs) == size_t(LookAndFeel::FloatID::End),
"Should have a pref for each float value");
// This array MUST be kept in the same order as the color list in
// specified/color.rs
static const char sColorPrefs[][41] = {
"ui.activeborder",
"ui.activecaption",
"ui.appworkspace",
"ui.background",
"ui.buttonface",
"ui.buttonhighlight",
"ui.buttonshadow",
"ui.buttontext",
"ui.buttonborder",
"ui.captiontext",
"ui.-moz-field",
"ui.-moz-disabledfield",
"ui.-moz-fieldtext",
"ui.mark",
"ui.marktext",
"ui.-moz-comboboxtext",
"ui.-moz-combobox",
"ui.graytext",
"ui.highlight",
"ui.highlighttext",
"ui.inactiveborder",
"ui.inactivecaption",
"ui.inactivecaptiontext",
"ui.infobackground",
"ui.infotext",
"ui.menu",
"ui.menutext",
"ui.scrollbar",
"ui.threeddarkshadow",
"ui.threedface",
"ui.threedhighlight",
"ui.threedlightshadow",
"ui.threedshadow",
"ui.window",
"ui.windowframe",
"ui.windowtext",
"ui.-moz-default-color",
"ui.-moz-default-background-color",
"ui.-moz-dialog",
"ui.-moz-dialogtext",
"ui.-moz-cellhighlight",
"ui.-moz_cellhighlighttext",
"ui.selecteditem",
"ui.selecteditemtext",
"ui.-moz_menuhover",
"ui.-moz_menuhoverdisabled",
"ui.-moz_menuhovertext",
"ui.-moz_menubarhovertext",
"ui.-moz_oddtreerow",
"ui.-moz-buttonhoverface",
"ui.-moz_buttonhovertext",
"ui.-moz_buttonhoverborder",
"ui.-moz-buttonactiveface",
"ui.-moz-buttonactivetext",
"ui.-moz-buttonactiveborder",
"ui.-moz-buttondisabledface",
"ui.-moz-buttondisabledborder",
"ui.-moz-headerbar",
"ui.-moz-headerbartext",
"ui.-moz-headerbarinactive",
"ui.-moz-headerbarinactivetext",
"ui.-moz-mac-defaultbuttontext",
"ui.-moz-mac-focusring",
"ui.-moz_mac_disabledtoolbartext",
"ui.-moz-sidebar",
"ui.-moz-sidebartext",
"ui.-moz-sidebarborder",
"ui.accentcolor",
"ui.accentcolortext",
"ui.-moz-autofill-background",
"ui.-moz-hyperlinktext",
"ui.-moz-activehyperlinktext",
"ui.-moz-visitedhyperlinktext",
"ui.-moz-colheader",
"ui.-moz-colheadertext",
"ui.-moz-colheaderhover",
"ui.-moz-colheaderhovertext",
"ui.-moz-colheaderactive",
"ui.-moz-colheaderactivetext",
"ui.textSelectDisabledBackground",
"ui.textSelectAttentionBackground",
"ui.textSelectAttentionForeground",
"ui.textHighlightBackground",
"ui.textHighlightForeground",
"ui.targetTextBackground",
"ui.targetTextForeground",
"ui.IMERawInputBackground",
"ui.IMERawInputForeground",
"ui.IMERawInputUnderline",
"ui.IMESelectedRawTextBackground",
"ui.IMESelectedRawTextForeground",
"ui.IMESelectedRawTextUnderline",
"ui.IMEConvertedTextBackground",
"ui.IMEConvertedTextForeground",
"ui.IMEConvertedTextUnderline",
"ui.IMESelectedConvertedTextBackground",
"ui.IMESelectedConvertedTextForeground",
"ui.IMESelectedConvertedTextUnderline",
"ui.SpellCheckerUnderline",
"ui.themedScrollbar",
"ui.themedScrollbarThumb",
"ui.themedScrollbarThumbHover",
"ui.themedScrollbarThumbActive",
};
static_assert(std::size(sColorPrefs) == size_t(LookAndFeel::ColorID::End),
"Should have a pref for each color value");
// This array MUST be kept in the same order as the SystemFont enum.
static const char sFontPrefs[][41] = {
"ui.font.caption",
"ui.font.icon",
"ui.font.menu",
"ui.font.message-box",
"ui.font.small-caption",
"ui.font.status-bar",
"ui.font.-moz-pull-down-menu",
"ui.font.-moz-button",
"ui.font.-moz-list",
"ui.font.-moz-field",
};
static_assert(std::size(sFontPrefs) == size_t(LookAndFeel::FontID::End),
"Should have a pref for each font value");
const char* nsXPLookAndFeel::GetColorPrefName(ColorID aId) {
return sColorPrefs[size_t(aId)];
}
nsXPLookAndFeel* nsXPLookAndFeel::sInstance = nullptr;
bool nsXPLookAndFeel::sShutdown = false;
auto LookAndFeel::SystemZoomSettings() -> ZoomSettings {
ZoomSettings settings;
switch (StaticPrefs::browser_display_os_zoom_behavior()) {
case 0:
default:
break;
case 1:
settings.mFullZoom = GetTextScaleFactor();
break;
case 2:
settings.mTextZoom = GetTextScaleFactor();
break;
}
return settings;
}
// static
nsXPLookAndFeel* nsXPLookAndFeel::GetInstance() {
if (sInstance) {
return sInstance;
}
NS_ENSURE_TRUE(!sShutdown, nullptr);
// If we're in a content process, then the parent process will have supplied
// us with an initial FullLookAndFeel object.
// We grab this data from the ContentChild,
// where it's been temporarily stashed, and initialize our new LookAndFeel
// object with it.
FullLookAndFeel* lnf = nullptr;
if (auto* cc = mozilla::dom::ContentChild::GetSingleton()) {
lnf = &cc->BorrowLookAndFeelData();
}
if (lnf) {
sInstance = new widget::RemoteLookAndFeel(std::move(*lnf));
} else if (gfxPlatform::IsHeadless()) {
sInstance = new widget::HeadlessLookAndFeel();
} else {
sInstance = new nsLookAndFeel();
}
// This is only ever used once during initialization, and can be cleared now.
if (lnf) {
*lnf = {};
}
sInstance->Init();
sInstance->NativeInit();
FillStores(sInstance);
widget::Theme::Init();
if (XRE_IsParentProcess()) {
nsLayoutUtils::RecomputeSmoothScrollDefault();
}
PreferenceSheet::Refresh();
return sInstance;
}
void nsXPLookAndFeel::FillStores(nsXPLookAndFeel* aInst) {
MOZ_RELEASE_ASSERT(NS_IsMainThread());
for (IntID id : MakeEnumeratedRange(IntID(0), IntID::End)) {
int32_t value = 0;
nsresult rv = aInst->GetIntValue(id, value);
MOZ_ASSERT_IF(NS_SUCCEEDED(rv), value != kNoInt);
sIntStore[id] = NS_SUCCEEDED(rv) ? value : kNoInt;
}
for (FloatID id : MakeEnumeratedRange(FloatID(0), FloatID::End)) {
float value = 0;
nsresult rv = aInst->GetFloatValue(id, value);
auto repr = BitwiseCast<uint32_t>(value);
MOZ_ASSERT_IF(NS_SUCCEEDED(rv), repr != kNoFloat);
sFloatStore[id] = NS_SUCCEEDED(rv) ? repr : kNoFloat;
}
for (auto scheme : {ColorScheme::Light, ColorScheme::Dark}) {
for (auto standins : {UseStandins::Yes, UseStandins::No}) {
auto& store = sColorStores.Get(scheme, standins);
for (ColorID id : MakeEnumeratedRange(ColorID(0), ColorID::End)) {
auto uncached = aInst->GetUncachedColor(id, scheme, standins);
MOZ_ASSERT_IF(uncached, uncached.value() != kNoColor);
store[id] = uncached.valueOr(kNoColor);
}
}
}
// NOTE(emilio): As of right now we depend on this being last, as fonts
// depend on things like GetTextScaleFactor(). This is not great but it's
// tested in test_textScaleFactor_system_font.html.
StaticAutoWriteLock guard(sFontStoreLock);
for (FontID id : MakeEnumeratedRange(FontID(0), FontID::End)) {
sFontStore[id] = aInst->GetFontValue(id);
}
}
// static
void nsXPLookAndFeel::Shutdown() {
if (sShutdown) {
return;
}
sShutdown = true;
delete sInstance;
sInstance = nullptr;
// This keeps strings alive, so need to clear to make leak checking happy.
{
StaticAutoWriteLock guard(sFontStoreLock);
for (auto& f : sFontStore) {
f = {};
}
}
widget::Theme::Shutdown();
}
static void IntPrefChanged(const nsACString& aPref) {
// Most Int prefs can't change our system colors or fonts, but
// ui.systemUsesDarkTheme can, since it affects the effective color-scheme
// (affecting system colors).
auto changeKind = aPref.EqualsLiteral("ui.systemUsesDarkTheme")
? widget::ThemeChangeKind::Style
: widget::ThemeChangeKind::MediaQueriesOnly;
LookAndFeel::NotifyChangedAllWindows(changeKind);
}
static void FloatPrefChanged(const nsACString& aPref) {
// Most float prefs can't change our system colors or fonts, but
// textScaleFactor affects layout.
auto changeKind = aPref.EqualsLiteral("ui.textScaleFactor")
? widget::ThemeChangeKind::StyleAndLayout
: widget::ThemeChangeKind::MediaQueriesOnly;
LookAndFeel::NotifyChangedAllWindows(changeKind);
}
static void ColorPrefChanged() {
// Color prefs affect style, because they by definition change system colors.
LookAndFeel::NotifyChangedAllWindows(widget::ThemeChangeKind::Style);
}
static void FontPrefChanged() {
// Color prefs affect style, because they by definition change system fonts.
LookAndFeel::NotifyChangedAllWindows(widget::ThemeChangeKind::Style);
}
// static
void nsXPLookAndFeel::OnPrefChanged(const char* aPref, void* aClosure) {
nsDependentCString prefName(aPref);
for (const char* pref : sIntPrefs) {
if (prefName.Equals(pref)) {
IntPrefChanged(prefName);
return;
}
}
for (const char* pref : sFloatPrefs) {
if (prefName.Equals(pref)) {
FloatPrefChanged(prefName);
return;
}
}
for (const char* pref : sColorPrefs) {
// We use StringBeginsWith to handle .dark prefs too.
if (StringBeginsWith(prefName, nsDependentCString(pref))) {
ColorPrefChanged();
return;
}
}
for (const char* pref : sFontPrefs) {
if (StringBeginsWith(prefName, nsDependentCString(pref))) {
FontPrefChanged();
return;
}
}
}
static constexpr struct {
nsLiteralCString mName;
widget::ThemeChangeKind mChangeKind =
widget::ThemeChangeKind::MediaQueriesOnly;
} kMediaQueryPrefs[] = {
// Affects whether standins are used for the accent color.
{"widget.non-native-theme.use-theme-accent"_ns,
widget::ThemeChangeKind::Style},
// These three affect system colors on Windows.
{"widget.windows.uwp-system-colors.enabled"_ns,
widget::ThemeChangeKind::Style},
{"widget.windows.uwp-system-colors.highlight-accent"_ns,
widget::ThemeChangeKind::Style},
// Affects env().
{"layout.css.prefers-color-scheme.content-override"_ns,
widget::ThemeChangeKind::Style},
// Affects media queries and scrollbar sizes, so gotta relayout.
{"widget.gtk.overlay-scrollbars.enabled"_ns,
widget::ThemeChangeKind::StyleAndLayout},
// Affects zoom settings which includes text and full zoom.
{"browser.display.os-zoom-behavior"_ns,
widget::ThemeChangeKind::StyleAndLayout},
// This affects system colors on Linux.
{"widget.gtk.libadwaita-colors.enabled"_ns, widget::ThemeChangeKind::Style},
// This affects not only the media query, but also the native theme, so we
// need to re-layout.
{"browser.theme.toolbar-theme"_ns, widget::ThemeChangeKind::AllBits},
{"browser.theme.content-theme"_ns},
// Affects PreferenceSheet, and thus styling.
{"browser.anchor_color"_ns, widget::ThemeChangeKind::Style},
{"browser.anchor_color.dark"_ns, widget::ThemeChangeKind::Style},
{"browser.active_color"_ns, widget::ThemeChangeKind::Style},
{"browser.active_color.dark"_ns, widget::ThemeChangeKind::Style},
{"browser.visited_color"_ns, widget::ThemeChangeKind::Style},
{"browser.visited_color.dark"_ns, widget::ThemeChangeKind::Style},
{"browser.display.background_color"_ns, widget::ThemeChangeKind::Style},
{"browser.display.background_color.dark"_ns,
widget::ThemeChangeKind::Style},
{"browser.display.foreground_color"_ns, widget::ThemeChangeKind::Style},
{"browser.display.foreground_color.dark"_ns,
widget::ThemeChangeKind::Style},
{"browser.display.document_color_use"_ns, widget::ThemeChangeKind::Style},
{"browser.display.use_document_fonts"_ns, widget::ThemeChangeKind::Style},
{"browser.display.permit_backplate"_ns, widget::ThemeChangeKind::Style},
{"ui.use_standins_for_native_colors"_ns, widget::ThemeChangeKind::Style},
{"privacy.resistFingerprinting"_ns, widget::ThemeChangeKind::Style},
// End of PreferenceSheet prefs.
};
// Read values from the user's preferences.
// This is done once at startup, but since the user's preferences
// haven't actually been read yet at that time, we also have to
// set a callback to inform us of changes to each pref.
void nsXPLookAndFeel::Init() {
MOZ_RELEASE_ASSERT(NS_IsMainThread());
// XXX If we could reorganize the pref names, we should separate the branch
// for each types. Then, we could reduce the unnecessary loop from
// nsXPLookAndFeel::OnPrefChanged().
Preferences::RegisterPrefixCallback(OnPrefChanged, "ui.");
for (const auto& pref : kMediaQueryPrefs) {
Preferences::RegisterCallback(
[](const char*, void* aChangeKind) {
auto changeKind =
widget::ThemeChangeKind(reinterpret_cast<uintptr_t>(aChangeKind));
LookAndFeel::NotifyChangedAllWindows(changeKind);
},
pref.mName, reinterpret_cast<void*>(uintptr_t(pref.mChangeKind)));
}
}
nsXPLookAndFeel::~nsXPLookAndFeel() {
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(sInstance == this,
"This destroying instance isn't the singleton instance");
sInstance = nullptr;
}
nscolor nsXPLookAndFeel::GetStandinForNativeColor(ColorID aID,
ColorScheme aScheme) {
if (aScheme == ColorScheme::Dark) {
if (auto color = GenericDarkColor(aID)) {
return *color;
}
}
// The stand-in colors are taken from what the non-native theme needs (for
// field/button colors), the Windows 7 Aero theme except Mac-specific colors
// which are taken from Mac OS 10.7.
#define COLOR(name_, r, g, b) \
case ColorID::name_: \
return NS_RGB(r, g, b);
#define COLORA(name_, r, g, b, a) \
case ColorID::name_: \
return NS_RGBA(r, g, b, a);
switch (aID) {
// These are here for the purposes of headless mode.
case ColorID::IMESelectedRawTextBackground:
case ColorID::IMESelectedConvertedTextBackground:
case ColorID::IMERawInputBackground:
case ColorID::IMEConvertedTextBackground:
return NS_TRANSPARENT;
case ColorID::IMESelectedRawTextForeground:
case ColorID::IMESelectedConvertedTextForeground:
case ColorID::IMERawInputForeground:
case ColorID::IMEConvertedTextForeground:
return NS_SAME_AS_FOREGROUND_COLOR;
case ColorID::IMERawInputUnderline:
case ColorID::IMEConvertedTextUnderline:
return NS_40PERCENT_FOREGROUND_COLOR;
case ColorID::Accentcolor:
return widget::sDefaultAccent.ToABGR();
case ColorID::Accentcolortext:
return widget::sDefaultAccentText.ToABGR();
COLOR(SpellCheckerUnderline, 0xff, 0x00, 0x00)
COLOR(TextSelectDisabledBackground, 0xAA, 0xAA, 0xAA)
// Titlebar colors
// deprecated in CSS Color Level 4, same as Canvas/Window:
COLOR(Activecaption, 0xFF, 0xFF, 0xFF)
// deprecated in CSS Color Level 4, same as Canvas/Window:
COLOR(Inactivecaption, 0xFF, 0xFF, 0xFF)
// deprecated in CSS Color Level 4, same as Canvastext/Windowtext:
COLOR(Captiontext, 0x00, 0x00, 0x00)
// deprecated in CSS Color Level 4, same as Graytext:
COLOR(Inactivecaptiontext, 0x6D, 0x6D, 0x6D)
// CSS 2 colors:
// deprecated in CSS Color Level 4, same as Canvas/Window:
COLOR(Appworkspace, 0xFF, 0xFF, 0xFF)
// deprecated in CSS Color Level 4, same as Canvas/Window:
COLOR(Background, 0xFF, 0xFF, 0xFF)
// deprecated in CSS Color Level 4, same as Buttonface
case ColorID::Buttonhighlight:
case ColorID::Buttonshadow:
case ColorID::Threedface:
// Buttons and comboboxes should be kept in sync since they are drawn with
// the same colors by the non-native theme.
case ColorID::MozCombobox:
case ColorID::Buttonface:
return NS_RGB(0xE9, 0xE9, 0xED);
COLOR(MozButtonhoverface, 0xd0, 0xd0, 0xd7)
COLOR(MozButtonactiveface, 0xb1, 0xb1, 0xb9)
COLORA(MozButtondisabledface, 0xE9, 0xE9, 0xED, 128)
case ColorID::MozComboboxtext:
case ColorID::MozButtonhovertext:
case ColorID::MozButtonactivetext:
COLOR(Buttontext, 0x00, 0x00, 0x00)
// deprecated in CSS Color Level 4, same as Buttonborder:
case ColorID::Threedhighlight:
case ColorID::Threedlightshadow:
case ColorID::Threedshadow:
case ColorID::Threeddarkshadow:
case ColorID::Windowframe:
case ColorID::Activeborder:
case ColorID::Inactiveborder:
case ColorID::Buttonborder:
return NS_RGB(0x8f, 0x8f, 0x9d);
COLOR(MozButtonhoverborder, 0x67, 0x67, 0x74)
COLOR(MozButtonactiveborder, 0x48, 0x48, 0x51)
COLORA(MozButtondisabledborder, 0x8f, 0x8f, 0x9d, 0x7f)
COLOR(Graytext, 0x6D, 0x6D, 0x6D)
COLOR(Highlight, 0x33, 0x99, 0xFF)
COLOR(Highlighttext, 0xFF, 0xFF, 0xFF)
// deprecated in CSS Color Level 4, same as Canvas/Window:
COLOR(Infobackground, 0xFF, 0xFF, 0xFF)
// deprecated in CSS Color Level 4, same as Canvastext/Windowtext:
COLOR(Infotext, 0x00, 0x00, 0x00)
// deprecated in CSS Color Level 4, same as Canvas/Window:
COLOR(Menu, 0xFF, 0xFF, 0xFF)
// deprecated in CSS Color Level 4, same as Canvastext/Windowtext:
COLOR(Menutext, 0x00, 0x00, 0x00)
// deprecated in CSS Color Level 4, same as Canvas/Window:
COLOR(Scrollbar, 0xFF, 0xFF, 0xFF)
COLOR(Mark, 0xFF, 0xFF, 0x00)
COLOR(Marktext, 0x00, 0x00, 0x00)
COLOR(Window, 0xFF, 0xFF, 0xFF)
COLOR(Windowtext, 0x00, 0x00, 0x00)
COLOR(Field, 0xFF, 0xFF, 0xFF)
COLORA(MozDisabledfield, 0xFF, 0xFF, 0xFF, 128)
COLOR(Fieldtext, 0x00, 0x00, 0x00)
COLOR(MozDialog, 0xF0, 0xF0, 0xF0)
COLOR(MozDialogtext, 0x00, 0x00, 0x00)
COLOR(MozColheadertext, 0x00, 0x00, 0x00)
COLOR(MozColheaderhovertext, 0x00, 0x00, 0x00)
COLOR(MozCellhighlight, 0xF0, 0xF0, 0xF0)
COLOR(MozCellhighlighttext, 0x00, 0x00, 0x00)
COLOR(Selecteditem, 0x33, 0x99, 0xFF)
COLOR(Selecteditemtext, 0xFF, 0xFF, 0xFF)
COLOR(MozMenuhover, 0x33, 0x99, 0xFF)
COLOR(MozMenuhovertext, 0x00, 0x00, 0x00)
COLOR(MozMenubarhovertext, 0x00, 0x00, 0x00)
COLOR(MozMenuhoverdisabled, 0xF0, 0xF0, 0xF0)
COLOR(MozOddtreerow, 0xFF, 0xFF, 0xFF)
COLOR(MozMacFocusring, 0x60, 0x9D, 0xD7)
COLOR(MozMacDisabledtoolbartext, 0x3F, 0x3F, 0x3F)
COLOR(Linktext, 0x00, 0x00, 0xee)
COLOR(Activetext, 0xee, 0x00, 0x00)
COLOR(Visitedtext, 0x55, 0x1A, 0x8B)
COLOR(MozAutofillBackground, 0xff, 0xfc, 0xc8)
COLOR(TargetTextBackground, 0xff, 0xeb, 0xcd)
COLOR(TargetTextForeground, 0x00, 0x00, 0x00)
default:
break;
}
return NS_RGB(0xFF, 0xFF, 0xFF);
}
#undef COLOR
#undef COLORA
// Taken from in-content/common.inc.css's dark theme.
Maybe<nscolor> nsXPLookAndFeel::GenericDarkColor(ColorID aID) {
nscolor color = NS_RGB(0, 0, 0);
static constexpr nscolor kWindowBackground = NS_RGB(28, 27, 34);
static constexpr nscolor kWindowText = NS_RGB(251, 251, 254);
switch (aID) {
case ColorID::Window: // --in-content-page-background
case ColorID::Background:
case ColorID::Appworkspace:
case ColorID::Scrollbar:
case ColorID::Infobackground:
color = kWindowBackground;
break;
case ColorID::Menu:
color = NS_RGB(0x2b, 0x2a, 0x33);
break;
case ColorID::MozMenuhovertext:
case ColorID::MozMenubarhovertext:
case ColorID::Menutext:
color = NS_RGB(0xfb, 0xfb, 0xfe);
break;
case ColorID::MozMenuhover:
color = NS_RGB(0x52, 0x52, 0x5e);
break;
case ColorID::MozMenuhoverdisabled:
color = NS_RGB(0x3a, 0x39, 0x44);
break;
case ColorID::MozOddtreerow:
case ColorID::MozDialog: // --background-color-box
color = NS_RGB(35, 34, 43);
break;
case ColorID::Windowtext: // --in-content-page-color
case ColorID::MozDialogtext:
case ColorID::MozSidebartext:
case ColorID::Fieldtext:
case ColorID::Infotext:
case ColorID::Buttontext: // --in-content-button-text-color (via
// --in-content-page-color)
case ColorID::MozComboboxtext:
case ColorID::MozButtonhovertext:
case ColorID::MozButtonactivetext:
case ColorID::MozHeaderbartext:
case ColorID::MozHeaderbarinactivetext:
case ColorID::Captiontext:
case ColorID::Inactivecaptiontext: // TODO(emilio): Maybe make
// Inactivecaptiontext Graytext?
case ColorID::MozColheadertext:
case ColorID::MozColheaderhovertext:
case ColorID::MozColheaderactivetext:
color = kWindowText;
break;
case ColorID::MozSidebarborder:
case ColorID::Windowframe: // --in-content-box-border-color computed
// with kWindowText above
// kWindowBackground.
case ColorID::Graytext: // opacity: 0.4 of kWindowText blended over the
// "Window" background color, which happens to be
// the same :-)
color = NS_ComposeColors(kWindowBackground, NS_RGBA(251, 251, 254, 102));
break;
case ColorID::Threedshadow:
case ColorID::Threedlightshadow:
case ColorID::Threedhighlight:
case ColorID::Buttonborder:
case ColorID::MozButtondisabledborder:
color = NS_RGB(0x8f, 0x8f, 0x9d);
break;
case ColorID::MozButtonactiveborder:
color = NS_RGB(0xd0, 0xd0, 0xd7);
break;
case ColorID::MozButtonhoverborder:
color = NS_RGB(0xb1, 0xb1, 0xb1);
break;
case ColorID::MozCellhighlight:
case ColorID::Selecteditem: // --in-content-primary-button-background /
// --in-content-item-selected
color = NS_RGB(0, 221, 255);
break;
case ColorID::MozSidebar:
case ColorID::Field:
case ColorID::Buttonface: // --in-content-button-background
case ColorID::Buttonshadow:
case ColorID::Buttonhighlight:
case ColorID::MozColheader:
case ColorID::Threedface:
case ColorID::MozCombobox:
case ColorID::MozCellhighlighttext:
case ColorID::Selecteditemtext: // --in-content-primary-button-text-color /
// --in-content-item-selected-text
color = NS_RGB(43, 42, 51);
break;
case ColorID::Threeddarkshadow: // Same as Threedlightshadow but with the
// background.
case ColorID::MozDisabledfield: // opacity: 0.4 of the face above blended
// over the "Window" background color.
case ColorID::MozButtondisabledface:
color = NS_ComposeColors(kWindowBackground, NS_RGBA(43, 42, 51, 102));
break;
case ColorID::MozButtonhoverface: // --in-content-button-background-hover
case ColorID::MozColheaderhover:
color = NS_RGB(82, 82, 94);
break;
case ColorID::MozButtonactiveface: // --in-content-button-background-active
case ColorID::MozColheaderactive:
color = NS_RGB(91, 91, 102);
break;
case ColorID::Highlight:
color = NS_RGBA(0, 221, 255, 78);
break;
case ColorID::Highlighttext:
color = NS_SAME_AS_FOREGROUND_COLOR;
break;
case ColorID::Linktext:
// If you change this color, you probably also want to change the default
// value of browser.anchor_color.dark.
color = NS_RGB(0x8c, 0x8c, 0xff);
break;
case ColorID::Activetext:
case ColorID::SpellCheckerUnderline:
// This is the default for active links in dark mode as well
// other options too.
color = NS_RGB(0xff, 0x66, 0x66);
break;
case ColorID::Visitedtext:
// If you change this color, you probably also want to change the default
// value of browser.visited_color.dark.
color = NS_RGB(0xff, 0xad, 0xff);
break;
case ColorID::Activeborder:
case ColorID::Inactiveborder:
color = NS_RGB(57, 57, 57);
break;
case ColorID::MozHeaderbar:
case ColorID::MozHeaderbarinactive:
case ColorID::Activecaption:
case ColorID::Inactivecaption:
color = NS_RGB(28, 27, 34);
break;
case ColorID::MozAutofillBackground:
// This is the light version of this color, but darkened to have good
// contrast with our white-ish FieldText.
color = NS_RGB(0x72, 0x6c, 0x00);
break;
default:
return Nothing();
}
return Some(color);
}
// Uncomment the #define below if you want to debug system color use in a skin
// that uses them. When set, it will make all system color pairs that are
// appropriate for foreground/background pairing the same. This means if the
// skin is using system colors correctly you will not be able to see *any* text.
//
// #define DEBUG_SYSTEM_COLOR_USE
#ifdef DEBUG_SYSTEM_COLOR_USE
static nsresult SystemColorUseDebuggingColor(LookAndFeel::ColorID aID,
nscolor& aResult) {
using ColorID = LookAndFeel::ColorID;
switch (aID) {
case ColorID::Activecaption:
// active window caption background
case ColorID::Captiontext:
// text in active window caption
aResult = NS_RGB(0xff, 0x00, 0x00);
break;
case ColorID::Highlight:
// background of selected item
case ColorID::Highlighttext:
// text of selected item
aResult = NS_RGB(0xff, 0xff, 0x00);
break;
case ColorID::Inactivecaption:
// inactive window caption
case ColorID::Inactivecaptiontext:
// text in inactive window caption
aResult = NS_RGB(0x66, 0x66, 0x00);
break;
case ColorID::Infobackground:
// tooltip background color
case ColorID::Infotext:
// tooltip text color
aResult = NS_RGB(0x00, 0xff, 0x00);
break;
case ColorID::Menu:
// menu background
case ColorID::Menutext:
// menu text
aResult = NS_RGB(0x00, 0xff, 0xff);
break;
case ColorID::Threedface:
case ColorID::Buttonface:
// 3-D face color
case ColorID::Buttontext:
// text on push buttons
aResult = NS_RGB(0x00, 0x66, 0x66);
break;
case ColorID::Window:
case ColorID::Windowtext:
aResult = NS_RGB(0x00, 0x00, 0xff);
break;
// from the CSS3 working draft (not yet finalized)
case ColorID::Field:
case ColorID::Fieldtext:
aResult = NS_RGB(0xff, 0x00, 0xff);
break;
case ColorID::MozDialog:
case ColorID::MozDialogtext:
aResult = NS_RGB(0x66, 0x00, 0x66);
break;
default:
return NS_ERROR_NOT_AVAILABLE;
}
return NS_OK;
}
#endif
static nsresult GetPrefColor(const char* aPref, nscolor& aResult) {
nsAutoCString colorStr;
MOZ_TRY(Preferences::GetCString(aPref, colorStr));
if (!ServoCSSParser::ComputeColor(nullptr, NS_RGB(0, 0, 0), colorStr,
&aResult)) {
return NS_ERROR_FAILURE;
}
return NS_OK;
}
static nsresult GetColorFromPref(LookAndFeel::ColorID aID, ColorScheme aScheme,
nscolor& aResult) {
const char* prefName = sColorPrefs[size_t(aID)];
if (aScheme == ColorScheme::Dark) {
nsAutoCString darkPrefName(prefName);
darkPrefName.Append(".dark");
if (NS_SUCCEEDED(GetPrefColor(darkPrefName.get(), aResult))) {
return NS_OK;
}
}
return GetPrefColor(prefName, aResult);
}
// All these routines will return NS_OK if they have a value,
// in which case the nsLookAndFeel should use that value;
// otherwise we'll return NS_ERROR_NOT_AVAILABLE, in which case, the
// platform-specific nsLookAndFeel should use its own values instead.
nsresult nsXPLookAndFeel::GetColorValue(ColorID aID, ColorScheme aScheme,
UseStandins aUseStandins,
nscolor& aResult) {
#ifdef DEBUG_SYSTEM_COLOR_USE
if (NS_SUCCEEDED(SystemColorUseDebuggingColor(aID, aResult))) {
return NS_OK;
}
#endif
auto result = GetUncachedColor(aID, aScheme, aUseStandins);
if (!result) {
return NS_ERROR_FAILURE;
}
aResult = *result;
return NS_OK;
}
Maybe<nscolor> nsXPLookAndFeel::GetUncachedColor(ColorID aID,
ColorScheme aScheme,
UseStandins aUseStandins) {
if (aUseStandins == UseStandins::Yes) {
return Some(GetStandinForNativeColor(aID, aScheme));
}
nscolor r;
if (NS_SUCCEEDED(GetColorFromPref(aID, aScheme, r))) {
return Some(r);
}
if (NS_SUCCEEDED(NativeGetColor(aID, aScheme, r))) {
return Some(r);
}
return Nothing();
}
nsresult nsXPLookAndFeel::GetIntValue(IntID aID, int32_t& aResult) {
if (NS_SUCCEEDED(Preferences::GetInt(sIntPrefs[size_t(aID)], &aResult))) {
return NS_OK;
}
if (NS_FAILED(NativeGetInt(aID, aResult))) {
return NS_ERROR_FAILURE;
}
return NS_OK;
}
nsresult nsXPLookAndFeel::GetFloatValue(FloatID aID, float& aResult) {
int32_t pref = 0;
if (NS_SUCCEEDED(Preferences::GetInt(sFloatPrefs[size_t(aID)], &pref))) {
aResult = float(pref) / 100.0f;
return NS_OK;
}
return NativeGetFloat(aID, aResult);
}
bool nsXPLookAndFeel::LookAndFeelFontToStyle(const LookAndFeelFont& aFont,
nsString& aName,
gfxFontStyle& aStyle) {
if (!aFont.haveFont()) {
return false;
}
aName = aFont.name();
aStyle = gfxFontStyle();
aStyle.size = aFont.size();
aStyle.weight = FontWeight::FromInt(aFont.weight());
aStyle.style =
aFont.italic() ? FontSlantStyle::ITALIC : FontSlantStyle::NORMAL;
aStyle.systemFont = true;
return true;
}
widget::LookAndFeelFont nsXPLookAndFeel::StyleToLookAndFeelFont(
const nsAString& aName, const gfxFontStyle& aStyle) {
LookAndFeelFont font;
font.haveFont() = true;
font.name() = aName;
font.size() = aStyle.size;
font.weight() = aStyle.weight.ToFloat();
font.italic() = aStyle.style.IsItalic();
MOZ_ASSERT(aStyle.style.IsNormal() || aStyle.style.IsItalic(),
"Cannot handle oblique font style");
#ifdef DEBUG
{
// Assert that all the remaining font style properties have their
// default values.
gfxFontStyle candidate = aStyle;
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
return font;
}
widget::LookAndFeelFont nsXPLookAndFeel::GetFontValue(FontID aID) {
LookAndFeelFont font;
auto GetFontsFromPrefs = [&]() -> bool {
nsDependentCString pref(sFontPrefs[size_t(aID)]);
if (NS_FAILED(Preferences::GetString(pref.get(), font.name()))) {
return false;
}
font.haveFont() = true;
font.size() = Preferences::GetFloat(nsAutoCString(pref + ".size"_ns).get());
// This is written this way rather than using the fallback so that an empty
// pref (such like the one about:config creates) doesn't cause system fonts
// to have zero-size.
if (font.size() < 1.0f) {
font.size() = StyleFONT_MEDIUM_PX;
}
font.weight() = Preferences::GetFloat(
nsAutoCString(pref + ".weight"_ns).get(), FontWeight::NORMAL.ToFloat());
font.italic() =
Preferences::GetBool(nsAutoCString(pref + ".italic"_ns).get());
return true;
};
if (!GetFontsFromPrefs()) {
nsAutoString name;
gfxFontStyle style;
if (NativeGetFont(aID, name, style)) {
font = StyleToLookAndFeelFont(name, style);
} else {
MOZ_ASSERT(!font.haveFont());
}
}
return font;
}
void nsXPLookAndFeel::RefreshImpl() {}
static bool sRecordedLookAndFeelTelemetry = false;
void nsXPLookAndFeel::RecordTelemetry() {
if (!XRE_IsParentProcess()) {
return;
}
if (sRecordedLookAndFeelTelemetry) {
return;
}
sRecordedLookAndFeelTelemetry = true;
int32_t i;
glean::widget::dark_mode.Set(
NS_SUCCEEDED(GetIntValue(IntID::SystemUsesDarkTheme, i)) && i != 0);
auto devices =
static_cast<PointingDeviceKinds>(GetInt(IntID::PointingDeviceKinds, 0));
glean::widget::pointing_devices
.EnumGet(glean::widget::PointingDevicesLabel::eMouse)
.Set(!!(devices & PointingDeviceKinds::Mouse));
glean::widget::pointing_devices
.EnumGet(glean::widget::PointingDevicesLabel::eTouch)
.Set(!!(devices & PointingDeviceKinds::Touch));
glean::widget::pointing_devices
.EnumGet(glean::widget::PointingDevicesLabel::ePen)
.Set(!!(devices & PointingDeviceKinds::Pen));
RecordLookAndFeelSpecificTelemetry();
}
namespace mozilla {
bool LookAndFeel::sGlobalThemeChanged;
static widget::ThemeChangeKind sGlobalThemeChangeKind{0};
void LookAndFeel::NotifyChangedAllWindows(widget::ThemeChangeKind aKind) {
sGlobalThemeChanged = true;
sGlobalThemeChangeKind |= aKind;
if (nsCOMPtr<nsIObserverService> obs = services::GetObserverService()) {
const char16_t kind[] = {char16_t(aKind), 0};
obs->NotifyObservers(nullptr, "internal-look-and-feel-changed", kind);
}
}
void LookAndFeel::DoHandleGlobalThemeChange() {
MOZ_ASSERT(sGlobalThemeChanged);
sGlobalThemeChanged = false;
auto kind = std::exchange(sGlobalThemeChangeKind, widget::ThemeChangeKind(0));
// Tell the theme that it changed, so it can flush any handles to stale theme
// data.
//
// We can use the *DoNotUseDirectly functions directly here, because we want
// to notify all possible themes in a given process (but just once).
if (XRE_IsParentProcess()) {
if (nsCOMPtr<nsITheme> theme = do_GetNativeThemeDoNotUseDirectly()) {
theme->ThemeChanged();
}
}
if (nsCOMPtr<nsITheme> theme = do_GetBasicNativeThemeDoNotUseDirectly()) {
theme->ThemeChanged();
}
// Clear all cached LookAndFeel colors.
LookAndFeel::Refresh();
// Vector images (SVG) may be using theme colors so we discard all cached
// surfaces. (We could add a vector image only version of DiscardAll, but
image::SurfaceCacheUtils::DiscardAll();
if (XRE_IsParentProcess()) {
dom::ContentParent::BroadcastThemeUpdate(kind);
}
nsContentUtils::AddScriptRunner(
NS_NewRunnableFunction("HandleGlobalThemeChange", [] {
if (nsCOMPtr<nsIObserverService> obs = services::GetObserverService()) {
obs->NotifyObservers(nullptr, "look-and-feel-changed", nullptr);
}
}));
}
#define BIT_FOR(_c) (1ull << size_t(ColorID::_c))
// We want to use a non-native color scheme for the non-native theme (except in
// high-contrast mode), so spoof some of the colors with stand-ins to prevent
// lack of contrast.
static constexpr std::bitset<size_t(ColorID::End)> sNonNativeThemeStandinColors{
// Used by default button styles.
BIT_FOR(Buttonface) | BIT_FOR(Buttontext) | BIT_FOR(Buttonborder) |
BIT_FOR(MozButtonhoverface) | BIT_FOR(MozButtonhovertext) |
BIT_FOR(MozButtonhoverborder) | BIT_FOR(MozButtonactiveface) |
BIT_FOR(MozButtonactivetext) | BIT_FOR(MozButtonactiveborder) |
BIT_FOR(MozButtondisabledface) | BIT_FOR(MozButtondisabledborder) |
// Used by select elements.
BIT_FOR(MozCombobox) | BIT_FOR(MozComboboxtext) |
BIT_FOR(Threedlightshadow) |
// For symmetry with the above.
BIT_FOR(Threeddarkshadow) |
// Used by input / textarea.
BIT_FOR(Field) | BIT_FOR(Fieldtext) |
// Used by disabled form controls.
BIT_FOR(MozDisabledfield) | BIT_FOR(Graytext) |
// Per spec, the following colors are deprecated, see
// should match ButtonFace:
BIT_FOR(Buttonhighlight) | BIT_FOR(Buttonshadow) | BIT_FOR(Threedface) |
// should match ButtonBorder:
BIT_FOR(Activeborder) | BIT_FOR(Inactiveborder) |
BIT_FOR(Threeddarkshadow) | BIT_FOR(Threedhighlight) |
BIT_FOR(Threedshadow) | BIT_FOR(Windowframe) |
// should match GrayText:
BIT_FOR(Inactivecaptiontext) |
// should match Canvas/Window:
BIT_FOR(Appworkspace) | BIT_FOR(Background) | BIT_FOR(Inactivecaption) |
BIT_FOR(Infobackground) | BIT_FOR(Menu) | BIT_FOR(Scrollbar) |
// should match CanvasText/WindowText:
BIT_FOR(Activecaption) | BIT_FOR(Captiontext) | BIT_FOR(Infotext) |
BIT_FOR(Menutext) |
// Some pages expect these to return windows-like colors, see bug
// 1773795.
// Also, per spec, these should match Canvas/CanvasText, see
BIT_FOR(Window) | BIT_FOR(Windowtext)};
#undef BIT_FOR
static bool ShouldUseStandinsForNativeColorForNonNativeTheme(
const dom::Document& aDoc, LookAndFeel::ColorID aColor,
const PreferenceSheet::Prefs& aPrefs) {
const bool shouldUseStandinsForColor = [&] {
if (sNonNativeThemeStandinColors[size_t(aColor)]) {
return true;
}
// There are platforms where we want the content-exposed accent color to be
// the windows blue rather than the system accent color, for now.
return !StaticPrefs::widget_non_native_theme_use_theme_accent() &&
(aColor == LookAndFeel::ColorID::Accentcolor ||
aColor == LookAndFeel::ColorID::Accentcolortext);
}();
return shouldUseStandinsForColor && aDoc.ShouldAvoidNativeTheme() &&
aPrefs.mUseDocumentColors &&
!StaticPrefs::widget_non_native_theme_always_high_contrast();
}
bool LookAndFeel::IsDarkColor(nscolor aColor) {
// threshold that tells us whether contrast is better against white or black.
//
// Contrast ratio against black is: (L + 0.05) / 0.05
// Contrast ratio against white is: 1.05 / (L + 0.05)
//
// So the intersection is:
//
// (L + 0.05) / 0.05 = 1.05 / (L + 0.05)
//
// And the solution to that equation is:
//
// sqrt(1.05 * 0.05) - 0.05
//
// So we consider a color dark if the contrast is below this threshold, and
// it's at least half-opaque.
constexpr float kThreshold = 0.179129;
return NS_GET_A(aColor) > 127 &&
RelativeLuminanceUtils::Compute(aColor) < kThreshold;
}
ColorScheme LookAndFeel::ColorSchemeForStyle(
const dom::Document& aDoc, const StyleColorSchemeFlags& aFlags,
ColorSchemeMode aMode) {
const auto& prefs = PreferenceSheet::PrefsFor(aDoc);
StyleColorSchemeFlags style(aFlags);
if (!style) {
style._0 = aDoc.GetColorSchemeBits();
}
const bool supportsDark = bool(style & StyleColorSchemeFlags::DARK);
const bool supportsLight = bool(style & StyleColorSchemeFlags::LIGHT);
if (supportsLight && supportsDark) {
// Both color-schemes are explicitly supported, use the preferred one.
return aDoc.PreferredColorScheme();
}
if (supportsDark || supportsLight) {
// One color-scheme is explicitly supported and one isn't, so use the one
// the content supports.
return supportsDark ? ColorScheme::Dark : ColorScheme::Light;
}
// No value specified. Chrome docs, and forced-colors mode always supports
// both, so use the preferred color-scheme.
if (aMode == ColorSchemeMode::Preferred || aDoc.ChromeRulesEnabled() ||
!prefs.mUseDocumentColors) {
return aDoc.PreferredColorScheme();
}
// Otherwise default content to light.
return ColorScheme::Light;
}
LookAndFeel::ColorScheme LookAndFeel::ColorSchemeForFrame(
const nsIFrame* aFrame, ColorSchemeMode aMode) {
return ColorSchemeForStyle(*aFrame->PresContext()->Document(),
aFrame->StyleUI()->mColorScheme.bits, aMode);
}
// static
Maybe<nscolor> LookAndFeel::GetColor(ColorID aId, ColorScheme aScheme,
UseStandins aUseStandins) {
MOZ_ASSERT(nsXPLookAndFeel::sInstance, "Not initialized");
nscolor color = sColorStores.Get(aScheme, aUseStandins)[aId];
if (color == kNoColor) {
return Nothing();
}
return Some(color);
}
// Returns whether there is a CSS color name for this color.
static bool ColorIsCSSAccessible(LookAndFeel::ColorID aId) {
using ColorID = LookAndFeel::ColorID;
switch (aId) {
case ColorID::TextSelectDisabledBackground:
case ColorID::TextSelectAttentionBackground:
case ColorID::TextSelectAttentionForeground:
case ColorID::TextHighlightBackground:
case ColorID::TextHighlightForeground:
case ColorID::ThemedScrollbar:
case ColorID::ThemedScrollbarThumb:
case ColorID::ThemedScrollbarThumbActive:
case ColorID::ThemedScrollbarThumbHover:
case ColorID::IMERawInputBackground:
case ColorID::IMERawInputForeground:
case ColorID::IMERawInputUnderline:
case ColorID::IMESelectedRawTextBackground:
case ColorID::IMESelectedRawTextForeground:
case ColorID::IMESelectedRawTextUnderline:
case ColorID::IMEConvertedTextBackground:
case ColorID::IMEConvertedTextForeground:
case ColorID::IMEConvertedTextUnderline:
case ColorID::IMESelectedConvertedTextBackground:
case ColorID::IMESelectedConvertedTextForeground:
case ColorID::IMESelectedConvertedTextUnderline:
case ColorID::SpellCheckerUnderline:
return false;
default:
break;
}
return true;
}
LookAndFeel::UseStandins LookAndFeel::ShouldUseStandins(
const dom::Document& aDoc, ColorID aId) {
const auto& prefs = PreferenceSheet::PrefsFor(aDoc);
if (ShouldUseStandinsForNativeColorForNonNativeTheme(aDoc, aId, prefs)) {
return UseStandins::Yes;
}
if (prefs.mUseStandins && ColorIsCSSAccessible(aId)) {
return UseStandins::Yes;
}
return UseStandins::No;
}
Maybe<nscolor> LookAndFeel::GetColor(ColorID aId, const nsIFrame* aFrame) {
const auto* doc = aFrame->PresContext()->Document();
return GetColor(aId, ColorSchemeForFrame(aFrame),
ShouldUseStandins(*doc, aId));
}
// static
nsresult LookAndFeel::GetInt(IntID aID, int32_t* aResult) {
MOZ_ASSERT(nsXPLookAndFeel::sInstance, "Not initialized?");
int32_t result = sIntStore[aID];
if (result == kNoInt) {
return NS_ERROR_FAILURE;
}
*aResult = result;
return NS_OK;
}
// static
nsresult LookAndFeel::GetFloat(FloatID aID, float* aResult) {
uint32_t result = sFloatStore[aID];
if (result == kNoFloat) {
return NS_ERROR_FAILURE;
}
*aResult = BitwiseCast<float>(result);
return NS_OK;
}
// static
void LookAndFeel::GetFont(FontID aID, widget::LookAndFeelFont& aFont) {
MOZ_ASSERT(nsXPLookAndFeel::sInstance, "Not initialized?");
StaticAutoReadLock guard(sFontStoreLock);
aFont = sFontStore[aID];
}
bool LookAndFeel::GetFont(FontID aID, nsString& aName, gfxFontStyle& aStyle) {
MOZ_ASSERT(nsXPLookAndFeel::sInstance, "Not initialized?");
StaticAutoReadLock guard(sFontStoreLock);
return nsXPLookAndFeel::LookAndFeelFontToStyle(sFontStore[aID], aName,
aStyle);
}
// static
char16_t LookAndFeel::GetPasswordCharacter() {
return nsLookAndFeel::GetInstance()->GetPasswordCharacterImpl();
}
// static
bool LookAndFeel::GetEchoPassword() {
if (StaticPrefs::editor_password_mask_delay() >= 0) {
return StaticPrefs::editor_password_mask_delay() > 0;
}
return nsLookAndFeel::GetInstance()->GetEchoPasswordImpl();
}
// static
uint32_t LookAndFeel::GetPasswordMaskDelay() {
int32_t delay = StaticPrefs::editor_password_mask_delay();
if (delay < 0) {
return nsLookAndFeel::GetInstance()->GetPasswordMaskDelayImpl();
}
return delay;
}
bool LookAndFeel::DrawInTitlebar() {
switch (StaticPrefs::browser_tabs_inTitlebar()) {
case 0:
return false;
case 1:
return true;
default:
break;
}
return nsLookAndFeel::GetInstance()->GetDefaultDrawInTitlebar();
}
LookAndFeel::TitlebarAction LookAndFeel::GetTitlebarAction(
TitlebarEvent aEvent) {
return nsLookAndFeel::GetInstance()->GetTitlebarAction(aEvent);
}
void LookAndFeel::GetThemeInfo(nsACString& aOut) {
nsLookAndFeel::GetInstance()->GetThemeInfo(aOut);
}
uint32_t LookAndFeel::GetMenuAccessKey() {
return StaticPrefs::ui_key_menuAccessKey();
}
Modifiers LookAndFeel::GetMenuAccessKeyModifiers() {
switch (GetMenuAccessKey()) {
case dom::KeyboardEvent_Binding::DOM_VK_SHIFT:
return MODIFIER_SHIFT;
case dom::KeyboardEvent_Binding::DOM_VK_CONTROL:
return MODIFIER_CONTROL;
case dom::KeyboardEvent_Binding::DOM_VK_ALT:
return MODIFIER_ALT;
case dom::KeyboardEvent_Binding::DOM_VK_META:
case dom::KeyboardEvent_Binding::DOM_VK_WIN:
return MODIFIER_META;
default:
return 0;
}
}
void LookAndFeel::EnsureInit() { Unused << nsXPLookAndFeel::GetInstance(); }
// static
void LookAndFeel::Refresh() {
auto* inst = nsLookAndFeel::GetInstance();
inst->RefreshImpl();
inst->NativeInit();
nsXPLookAndFeel::FillStores(inst);
if (XRE_IsParentProcess()) {
nsLayoutUtils::RecomputeSmoothScrollDefault();
// Clear any cached FullLookAndFeel data, which is now invalid.
widget::RemoteLookAndFeel::ClearCachedData();
}
widget::Theme::LookAndFeelChanged();
// Reset default background and foreground colors for the document since they
// may be using system colors, color scheme, etc.
PreferenceSheet::Refresh();
}
// static
void LookAndFeel::SetData(widget::FullLookAndFeel&& aTables) {
nsLookAndFeel::GetInstance()->SetDataImpl(std::move(aTables));
}
// static
nsresult LookAndFeel::GetKeyboardLayout(nsACString& aLayout) {
return nsLookAndFeel::GetInstance()->GetKeyboardLayoutImpl(aLayout);
}
} // namespace mozilla