Firefox · DOM
CVE-2026-117877
Logic Error in DOM
Overview
High
Severity
—
CVSS
No
Exploited ITW
Fixed
Fix Status
Changed Functions
| Function | Change | Notes |
|---|---|---|
ifgfx/thebes/FontPaletteCache.cpp |
modified | |
gfxFontEntrygfx/thebes/FontPaletteCache.h |
modified | |
FontPaletteValueSetgfx/thebes/FontPaletteCache.h |
modified | |
PaletteCachegfx/thebes/FontPaletteCache.h |
modified | |
ifgfx/thebes/gfxFont.cpp |
modified | |
ifgfx/thebes/gfxFontEntry.cpp |
modified | |
FontPalettegfx/thebes/gfxFontEntry.h |
modified | |
PaletteCachegfx/thebes/gfxFontEntry.h |
modified | |
ifgfx/thebes/gfxFontEntry.h |
modified |
Files Changed
dom/canvas/CanvasRenderingContext2D.cppdom/canvas/CanvasRenderingContext2D.hgfx/src/nsFontMetrics.cppgfx/thebes/FontPaletteCache.cppgfx/thebes/FontPaletteCache.hgfx/thebes/gfxFont.cppgfx/thebes/gfxFont.hgfx/thebes/gfxFontEntry.cppgfx/thebes/gfxFontEntry.hgfx/thebes/gfxTextRun.cppgfx/thebes/gfxTextRun.hlayout/base/nsPresContext.cpplayout/base/nsPresContext.hlayout/generic/TextOverflow.cpplayout/generic/nsTextFrame.cpplayout/mathml/nsMathMLChar.cpp
Patch
diff --git a/dom/canvas/CanvasRenderingContext2D.cpp b/dom/canvas/CanvasRenderingContext2D.cpp
index 8cab5d392cd..bf131b51274 100644
--- a/dom/canvas/CanvasRenderingContext2D.cpp
+++ b/dom/canvas/CanvasRenderingContext2D.cpp
@@ -4809,8 +4809,8 @@ struct MOZ_STACK_CLASS CanvasBidiProcessor final
: public nsBidiPresUtils::BidiProcessor {
using Style = CanvasRenderingContext2D::Style;
- explicit CanvasBidiProcessor(mozilla::gfx::PaletteCache& aPaletteCache)
- : mPaletteCache(aPaletteCache) {
+ explicit CanvasBidiProcessor(gfx::FontPaletteValueSet* aPaletteValueSet)
+ : mPaletteValueSet(aPaletteValueSet) {
if (StaticPrefs::gfx_missing_fonts_notify()) {
mMissingFonts = MakeUnique<gfxMissingFontRecorder>();
}
@@ -5060,7 +5060,7 @@ struct MOZ_STACK_CLASS CanvasBidiProcessor final
}
gfxContext thebes(target, /* aPreserveTransform */ true);
- gfxTextRun::DrawParams params(&thebes, mPaletteCache);
+ gfxTextRun::DrawParams params(&thebes, mPaletteValueSet);
params.allowGDI = false;
@@ -5125,15 +5125,15 @@ struct MOZ_STACK_CLASS CanvasBidiProcessor final
// Pointer to the draw target we should fill our text to
CanvasRenderingContext2D* mCtx = nullptr;
+ // palette-values set from the presContext, if any.
+ gfx::FontPaletteValueSet* mPaletteValueSet = nullptr;
+
// position of the left side of the string, alphabetic baseline
gfx::Point mPt;
// current font
gfxFontGroup* mFontgrp = nullptr;
- // palette cache for COLR font rendering
- mozilla::gfx::PaletteCache& mPaletteCache;
-
// spacing adjustments to be applied
gfx::Float mLetterSpacing = 0.0f;
gfx::Float mWordSpacing = 0.0f;
@@ -5256,7 +5256,9 @@ UniquePtr<TextMetrics> CanvasRenderingContext2D::DrawOrMeasureText(
return nullptr;
}
- CanvasBidiProcessor processor(mPaletteCache);
+ auto* paletteValueSet =
+ presContext ? presContext->GetFontPaletteValueSet() : nullptr;
+ CanvasBidiProcessor processor(paletteValueSet);
// If we don't have a ComputedStyle, we can't set up vertical-text flags
// (for now, at least; perhaps we need new Canvas API to control this).
diff --git a/dom/canvas/CanvasRenderingContext2D.h b/dom/canvas/CanvasRenderingContext2D.h
index ae6b540f1b3..25a7b7921ca 100644
--- a/dom/canvas/CanvasRenderingContext2D.h
+++ b/dom/canvas/CanvasRenderingContext2D.h
@@ -1322,8 +1322,6 @@ class CanvasRenderingContext2D : public nsICanvasRenderingContextInternal,
ColorStyleCacheEntry ParseColorSlow(const nsACString&);
- mozilla::gfx::PaletteCache mPaletteCache;
-
friend class CanvasGeneralPattern;
friend class AdjustedTarget;
friend class AdjustedTargetForShadow;
diff --git a/gfx/src/nsFontMetrics.cpp b/gfx/src/nsFontMetrics.cpp
index 3f329cfab6a..981df9465f4 100644
--- a/gfx/src/nsFontMetrics.cpp
+++ b/gfx/src/nsFontMetrics.cpp
@@ -402,8 +402,7 @@ void nsFontMetrics::DrawString(const char* aString, uint32_t aLength,
pt.x += textRun->GetAdvanceWidth(range, &provider);
}
}
- mozilla::gfx::PaletteCache paletteCache;
- gfxTextRun::DrawParams params(aContext, paletteCache);
+ gfxTextRun::DrawParams params(aContext, /* aPaletteValueSet */ nullptr);
params.provider = &provider;
mozilla::image::imgDrawingParams dummy;
textRun->Draw(range, pt, params, dummy);
@@ -429,8 +428,7 @@ void nsFontMetrics::DrawString(
pt.x += textRun->GetAdvanceWidth(range, &provider);
}
}
- mozilla::gfx::PaletteCache paletteCache;
- gfxTextRun::DrawParams params(aContext, paletteCache);
+ gfxTextRun::DrawParams params(aContext, /* aPaletteValueSet */ nullptr);
params.provider = &provider;
mozilla::image::imgDrawingParams dummy;
textRun->Draw(range, pt, params, dummy);
diff --git a/gfx/thebes/FontPaletteCache.cpp b/gfx/thebes/FontPaletteCache.cpp
index 1686fbf5905..66cb45770a5 100644
--- a/gfx/thebes/FontPaletteCache.cpp
+++ b/gfx/thebes/FontPaletteCache.cpp
@@ -4,30 +4,38 @@
#include "FontPaletteCache.h"
-#include "COLRFonts.h"
#include "gfxFontEntry.h"
using namespace mozilla;
-void gfx::PaletteCache::SetPaletteValueSet(
- const gfx::FontPaletteValueSet* aSet) {
- mPaletteValueSet = aSet;
- Clear();
-}
+// Return a cached palette, if present; the caller is holding a read lock.
+already_AddRefed<gfx::FontPalette> gfx::PaletteCache::GetCachedPalette(
+ gfx::FontPaletteValueSet* aPaletteValueSet, nsAtom* aPaletteName) {
+ if (aPaletteValueSet != mPaletteValueSet) {
+ return nullptr;
+ }
-already_AddRefed<gfx::FontPalette> gfx::PaletteCache::GetPaletteFor(
- gfxFontEntry* aFontEntry, nsAtom* aPaletteName) {
- auto entry = Lookup(std::pair(aFontEntry, aPaletteName));
- if (!entry) {
- CacheData newData;
- newData.mKey = std::pair(aFontEntry, aPaletteName);
+ auto entry = Lookup(aPaletteName);
+ if (entry) {
+ RefPtr result = entry.Data().mPalette;
+ return result.forget();
+ }
- gfxFontEntry::AutoHBFace face = aFontEntry->GetHBFace();
- newData.mPalette = new FontPalette(gfx::COLRFonts::CreateColorPalette(
- face, mPaletteValueSet, aPaletteName, aFontEntry->FamilyName()));
+ return nullptr;
+}
- entry.Set(std::move(newData));
+// Add a palette to the cache, checking that the palette value set matches.
+void gfx::PaletteCache::AddPalette(gfx::FontPaletteValueSet* aPaletteValueSet,
+ nsAtom* aPaletteName,
+ gfx::FontPalette* aPalette) {
+ if (aPaletteValueSet != mPaletteValueSet) {
+ // Discard any palettes that were created for a different value set.
+ Clear();
+ mPaletteValueSet = aPaletteValueSet;
}
- RefPtr result = entry.Data().mPalette;
- return result.forget();
+
+ CacheData newData;
+ newData.mKey = aPaletteName;
+ newData.mPalette = aPalette;
+ Put(aPaletteName, std::move(newData));
}
diff --git a/gfx/thebes/FontPaletteCache.h b/gfx/thebes/FontPaletteCache.h
index fd082514ed6..bcbbf161919 100644
--- a/gfx/thebes/FontPaletteCache.h
+++ b/gfx/thebes/FontPaletteCache.h
@@ -7,7 +7,6 @@
#include <utility>
-#include "gfxFontEntry.h"
#include "harfbuzz/hb.h"
#include "mozilla/HashFunctions.h"
#include "mozilla/MruCache.h"
@@ -16,6 +15,8 @@
#include "nsAtom.h"
#include "nsTArray.h"
+class gfxFontEntry;
+
namespace mozilla::gfx {
class FontPaletteValueSet;
@@ -39,33 +40,37 @@ class FontPalette {
// MRU cache used for resolved color-font palettes, to avoid reconstructing
// the palette for each glyph rendered with a given font.
-using CacheKey = std::pair<RefPtr<gfxFontEntry>, RefPtr<nsAtom>>;
+using CacheKey = RefPtr<nsAtom>;
struct CacheData {
CacheKey mKey;
RefPtr<FontPalette> mPalette;
};
+// PaletteCache is attached to a specific gfxFontEntry and holds palettes found
+// in that font, with lookup by palette name.
class PaletteCache
- : public mozilla::MruCache<CacheKey, CacheData, PaletteCache> {
+ : public mozilla::MruCache<CacheKey, CacheData, PaletteCache, 8> {
public:
- explicit PaletteCache(const FontPaletteValueSet* aPaletteValueSet = nullptr)
- : mPaletteValueSet(aPaletteValueSet) {}
+ PaletteCache() = default;
- void SetPaletteValueSet(const FontPaletteValueSet* aSet);
+ // Get the palette named aPaletteName, if present.
+ already_AddRefed<FontPalette> GetCachedPalette(
+ mozilla::gfx::FontPaletteValueSet* aPaletteValueSet,
+ nsAtom* aPaletteName);
- already_AddRefed<FontPalette> GetPaletteFor(gfxFontEntry* aFontEntry,
- nsAtom* aPaletteName);
+ // Store the given palette in the cache.
+ void AddPalette(mozilla::gfx::FontPaletteValueSet* aPaletteValueSet,
+ nsAtom* aPaletteName, mozilla::gfx::FontPalette* aPalette);
- static bool IsEmpty(const CacheData& aVal) { return !aVal.mKey.first; }
- static mozilla::HashNumber Hash(const CacheKey& aKey) {
- return mozilla::HashGeneric(aKey.first.get(), aKey.second.get());
- }
+ static bool IsEmpty(const CacheData& aVal) { return !aVal.mKey; }
+ static mozilla::HashNumber Hash(const CacheKey& aKey) { return aKey->hash(); }
static bool Match(const CacheKey& aKey, const CacheData& aVal) {
return aVal.mKey == aKey;
}
protected:
- const FontPaletteValueSet* mPaletteValueSet = nullptr;
+ // The palette value set for which cached entries were created.
+ RefPtr<FontPaletteValueSet> mPaletteValueSet;
};
} // namespace mozilla::gfx
diff --git a/gfx/thebes/gfxFont.cpp b/gfx/thebes/gfxFont.cpp
index e5bf4a858a5..54991a24738 100644
--- a/gfx/thebes/gfxFont.cpp
+++ b/gfx/thebes/gfxFont.cpp
@@ -1789,8 +1789,7 @@ bool gfxFont::HasFeatureSet(uint32_t aFeature, bool& aFeatureOn) {
already_AddRefed<mozilla::gfx::ScaledFont> gfxFont::GetScaledFont(
mozilla::gfx::DrawTarget* aDrawTarget) {
- mozilla::gfx::PaletteCache dummy;
- TextRunDrawParams params(dummy);
+ TextRunDrawParams params(nullptr);
return GetScaledFont(params);
}
@@ -2380,7 +2379,7 @@ void gfxFont::DrawEmphasisMarks(const gfxTextRun* aShapedText, gfx::Point* aPt,
imgDrawingParams& aImgParams) {
float& inlineCoord = aParams.isVertical ? aPt->y.value : aPt->x.value;
gfxTextRun::Range markRange(aParams.mark);
- gfxTextRun::DrawParams params(aParams.context, aParams.paletteCache);
+ gfxTextRun::DrawParams params(aParams.context, aParams.paletteValueSet);
float clusterStart = -std::numeric_limits<float>::infinity();
bool shouldDrawEmphasisMark = false;
@@ -2448,8 +2447,8 @@ void gfxFont::Draw(const gfxTextRun* aTextRun, uint32_t aStart, uint32_t aEnd,
fontParams.currentColor = aRunParams.context->GetDeviceColor(ctxColor)
? sRGBColor::FromABGR(ctxColor.ToABGR())
: sRGBColor::OpaqueBlack();
- fontParams.palette = aRunParams.paletteCache.GetPaletteFor(
- GetFontEntry(), aRunParams.fontPalette);
+ fontParams.palette = GetFontEntry()->GetPalette(aRunParams.fontPalette,
+ aRunParams.paletteValueSet);
}
if (textDrawer) {
diff --git a/gfx/thebes/gfxFont.h b/gfx/thebes/gfxFont.h
index b1539da49de..9b32f858780 100644
--- a/gfx/thebes/gfxFont.h
+++ b/gfx/thebes/gfxFont.h
@@ -28,7 +28,6 @@
#include "mozilla/TypedEnumBits.h"
#include "mozilla/UniquePtr.h"
#include "mozilla/gfx/2D.h"
-#include "mozilla/gfx/FontPaletteCache.h"
#include "mozilla/gfx/MatrixFwd.h"
#include "mozilla/gfx/Point.h"
#include "mozilla/intl/UnicodeScriptCodes.h"
@@ -2479,10 +2478,10 @@ class gfxFont {
// are dependent on the specific font, so they are set per GlyphRun.
struct MOZ_STACK_CLASS TextRunDrawParams {
- explicit TextRunDrawParams(mozilla::gfx::PaletteCache& aPaletteCache)
- : paletteCache(aPaletteCache) {}
+ explicit TextRunDrawParams(
+ mozilla::gfx::FontPaletteValueSet* aPaletteValueSet)
+ : paletteValueSet(aPaletteValueSet) {}
- mozilla::gfx::PaletteCache& paletteCache;
RefPtr<mozilla::gfx::DrawTarget> dt;
gfxContext* context = nullptr;
gfxFont::Spacing* spacing = nullptr;
@@ -2498,6 +2497,7 @@ struct MOZ_STACK_CLASS TextRunDrawParams {
const mozilla::gfx::StrokeOptions* strokeOpts = nullptr;
const mozilla::gfx::DrawOptions* drawOpts = nullptr;
nsAtom* fontPalette = nullptr;
+ mozilla::gfx::FontPaletteValueSet* paletteValueSet = nullptr;
// The source text for the range passed to gfxFont::Draw, as provided by
// gfxTextRun::PropertyProvider::GetSourceText, but converted to UTF-8.
// Only set if needsSourceText is true and the provider returned the text.
@@ -2537,10 +2537,10 @@ struct MOZ_STACK_CLASS FontDrawParams {
struct MOZ_STACK_CLASS EmphasisMarkDrawParams {
EmphasisMarkDrawParams(gfxContext* aContext,
- mozilla::gfx::PaletteCache& aPaletteCache)
- : context(aContext), paletteCache(aPaletteCache) {}
+ mozilla::gfx::FontPaletteValueSet* aPaletteValueSet)
+ : context(aContext), paletteValueSet(aPaletteValueSet) {}
gfxContext* context;
- mozilla::gfx::PaletteCache& paletteCache;
+ mozilla::gfx::FontPaletteValueSet* paletteValueSet;
gfxFont::Spacing* spacing;
gfxTextRun* mark;
gfxFloat advance;
diff --git a/gfx/thebes/gfxFontEntry.cpp b/gfx/thebes/gfxFontEntry.cpp
index c3b7212671f..ec3f7b9145a 100644
--- a/gfx/thebes/gfxFontEntry.cpp
+++ b/gfx/thebes/gfxFontEntry.cpp
@@ -74,6 +74,8 @@ gfxFontEntry::~gfxFontEntry() {
hb_blob_destroy(mCOLR.exchange(nullptr));
hb_blob_destroy(mCPAL.exchange(nullptr));
+ delete mPaletteCache.exchange(nullptr);
+
if (TrakTableInitialized()) {
// Only if it was initialized, so that we don't try to call hb_blob_destroy
// on the kTrakTableUninitialized flag value!
@@ -441,6 +443,46 @@ bool gfxFontEntry::TryGetColorGlyphs() {
return mCOLR && mCPAL;
}
+already_AddRefed<gfx::FontPalette> gfxFontEntry::GetPalette(
+ nsAtom* aPaletteName, gfx::FontPaletteValueSet* aPaletteValueSet) {
+ // Get the palette cache, creating it if necessary. Note that once the
+ // mPaletteCache has been assigned, it is never modified until font entry
+ // destruction, so there is no risk of the local `cache` pointer here
+ // becoming invalid during this function.
+ gfx::PaletteCache* cache = mPaletteCache;
+ if (!cache) {
+ cache = new gfx::PaletteCache();
+ if (!mPaletteCache.compareExchange(nullptr, cache)) {
+ delete cache; // Lost a race: use the cache created by the winner.
+ cache = mPaletteCache;
+ }
+ }
+
+ // Reading and modifying the cache is guarded by our RWLock.
+ {
+ // First try to return an existing cached palette.
+ AutoReadLock lock(mLock);
+ RefPtr palette = cache->GetCachedPalette(aPaletteValueSet, aPaletteName);
+ if (palette) {
+ return palette.forget();
+ }
+ }
+
+ // Create a new resolved palette.
+ gfxFontEntry::AutoHBFace face = GetHBFace();
+ RefPtr palette = MakeRefPtr<FontPalette>(gfx::COLRFonts::CreateColorPalette(
+ face, aPaletteValueSet, aPaletteName, FamilyName()));
+
+ // Store it in the cache for future use. This may evict an existing cache
+ // entry, but any caller that retrieved the old entry was given its own
+ // strong reference to the palette it contained, which will remain valid
+ // as long as the caller holds its ref.
+ AutoWriteLock lock(mLock);
+ cache->AddPalette(aPaletteValueSet, aPaletteName, palette);
+
+ return palette.forget();
+}
+
already_AddRefed<gfxCharacterMap> gfxFontEntry::GetCMAPFromFontInfo(
FontInfoData* aFontInfoData, uint32_t& aUVSOffset) {
if (!aFontInfoData || !aFontInfoData->mLoadCmaps) {
diff --git a/gfx/thebes/gfxFontEntry.h b/gfx/thebes/gfxFontEntry.h
index 74c9556c87b..b4bb5867fdf 100644
--- a/gfx/thebes/gfxFontEntry.h
+++ b/gfx/thebes/gfxFontEntry.h
@@ -64,6 +64,10 @@ namespace fontlist {
struct Face;
struct Family;
} // namespace fontlist
+namespace gfx {
+class FontPalette;
+class PaletteCache;
+} // namespace gfx
} // namespace mozilla
typedef struct gr_face gr_face;
@@ -366,6 +370,10 @@ class gfxFontEntry {
bool TryGetColorGlyphs();
+ already_AddRefed<mozilla::gfx::FontPalette> GetPalette(
+ nsAtom* aPaletteName,
+ mozilla::gfx::FontPaletteValueSet* aPaletteValueSet);
+
bool HasColorBitmapTable() {
LazyFlag flag = mHasColorBitmapTable;
if (flag == LazyFlag::Uninitialized) {
@@ -639,6 +647,8 @@ class gfxFontEntry {
hb_blob_t* GetCOLR() const { return mCOLR; }
hb_blob_t* GetCPAL() const { return mCPAL; }
+ mozilla::Atomic<mozilla::gfx::PaletteCache*> mPaletteCache;
+
// bitvector of substitution space features per script, one each
// for default and non-default features
uint32_t mDefaultSubSpaceFeatures[(int(Script::NUM_SCRIPT_CODES) + 31) /
Loading diff…
References
On This Page