Skip to content

Commit 938bd0b

Browse files
committed
Bug 1943070 - Support the 'lang' attribute in CanvasTextDrawingStyles. r=gfx-reviewers,webidl,lsalzman,smaug
Differential Revision: https://phabricator.services.mozilla.com/D289096
1 parent ee00bd5 commit 938bd0b

28 files changed

Lines changed: 121 additions & 141 deletions

dom/canvas/CanvasRenderingContext2D.cpp

Lines changed: 79 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -4169,6 +4169,10 @@ void CanvasRenderingContext2D::SetFont(const nsACString& aFont,
41694169
ErrorResult& aError) {
41704170
mFeatureUsage |= CanvasFeatureUsage::SetFont;
41714171

4172+
if (ResolveFontLang()) {
4173+
CurrentState().fontGroup = nullptr;
4174+
}
4175+
41724176
SetFontInternal(aFont, aError);
41734177
if (aError.Failed()) {
41744178
return;
@@ -4205,7 +4209,8 @@ bool CanvasRenderingContext2D::SetFontInternal(const nsACString& aFont,
42054209
}
42064210

42074211
nsPresContext* c = presShell->GetPresContext();
4208-
FontStyleCacheKey key{aFont, c->RestyleManager()->GetRestyleGeneration()};
4212+
FontStyleCacheKey key{aFont, CurrentState().resolvedFontLang,
4213+
c->RestyleManager()->GetRestyleGeneration()};
42094214
auto entry = mFontStyleCache.Lookup(key);
42104215
if (!entry) {
42114216
FontStyleData newData;
@@ -4319,8 +4324,8 @@ bool CanvasRenderingContext2D::SetFontInternal(const nsACString& aFont,
43194324
c->Document()->FlushUserFontSet();
43204325

43214326
nsFontMetrics::Params params;
4322-
params.language = fontStyle->mLanguage;
4323-
params.explicitLanguage = fontStyle->mExplicitLanguage;
4327+
params.language = CurrentState().resolvedFontLang;
4328+
params.explicitLanguage = CurrentState().explicitLang;
43244329
params.userFontSet = c->GetUserFontSet();
43254330
params.textPerf = c->GetTextPerfMetrics();
43264331
#ifdef XP_WIN
@@ -4510,33 +4515,16 @@ bool CanvasRenderingContext2D::SetFontInternalDisconnected(
45104515
break;
45114516
}
45124517

4513-
// If we have a canvas element, get its lang (if known).
4514-
RefPtr<nsAtom> language;
4515-
bool explicitLanguage = false;
4516-
if (mCanvasElement) {
4517-
language = mCanvasElement->FragmentOrElement::GetLang();
4518-
if (language) {
4519-
explicitLanguage = true;
4520-
} else {
4521-
language = mCanvasElement->OwnerDoc()->GetLanguageForStyle();
4522-
}
4523-
} else {
4524-
// Pass the OS default language, to behave similarly to HTML or canvas-
4525-
// element content with no language tag.
4526-
language = nsLanguageAtomService::GetService()->GetLocaleLanguage();
4527-
}
4528-
45294518
// TODO: Cache fontGroups in the Worker (use an nsFontCache?)
4530-
gfxFontGroup* fontGroup =
4531-
new gfxFontGroup(mOffscreenCanvas, // aFontVisibilityProvider
4532-
list, // aFontFamilyList
4533-
&fontStyle, // aStyle
4534-
language, // aLanguage
4535-
explicitLanguage, // aExplicitLanguage
4536-
nullptr, // aTextPerf
4537-
fontFaceSetImpl, // aUserFontSet
4538-
1.0, // aDevToCssSize
4539-
StyleFontVariantEmoji::Normal);
4519+
gfxFontGroup* fontGroup = new gfxFontGroup(
4520+
mOffscreenCanvas, // aFontVisibilityProvider
4521+
list, // aFontFamilyList
4522+
&fontStyle, // aStyle
4523+
CurrentState().resolvedFontLang, CurrentState().explicitLang,
4524+
nullptr, // aTextPerf
4525+
fontFaceSetImpl, // aUserFontSet
4526+
1.0, // aDevToCssSize
4527+
StyleFontVariantEmoji::Normal);
45404528
auto& state = CurrentState();
45414529
state.fontGroup = fontGroup;
45424530
SerializeFontForCanvas(list, fontStyle, state.font);
@@ -5353,6 +5341,65 @@ UniquePtr<TextMetrics> CanvasRenderingContext2D::DrawOrMeasureText(
53535341
return nullptr;
53545342
}
53555343

5344+
// Resolve CurrentState().lang to .resolvedFontLang, returning true if the
5345+
// resolved value changed.
5346+
bool CanvasRenderingContext2D::ResolveFontLang() {
5347+
bool explicitLang = false;
5348+
RefPtr resolvedLang = [&]() {
5349+
nsAtom* lang = CurrentState().lang;
5350+
if (!lang->IsEmpty() && lang != nsGkAtoms::inherit) {
5351+
explicitLang = true;
5352+
return do_AddRef(lang);
5353+
}
5354+
5355+
if (mCanvasElement) {
5356+
// If we have a canvas element, get its lang (if known).
5357+
if (nsAtom* lang = mCanvasElement->FragmentOrElement::GetLang()) {
5358+
explicitLang = true;
5359+
return do_AddRef(lang);
5360+
}
5361+
return do_AddRef(mCanvasElement->OwnerDoc()->GetLanguageForStyle());
5362+
}
5363+
5364+
if (RefPtr presShell = GetPresShell()) {
5365+
// Try to inherit 'lang' from the presShell's document, if any.
5366+
return do_AddRef(presShell->GetDocument()->GetLanguageForStyle());
5367+
}
5368+
5369+
if (mOffscreenCanvas) {
5370+
// If the offscreen canvas has a value transferred from a canvas element,
5371+
// we use that.
5372+
if (nsAtom* lang = mOffscreenCanvas->GetLang()) {
5373+
explicitLang = true;
5374+
return do_AddRef(lang);
5375+
}
5376+
if (auto* window = mOffscreenCanvas->GetOwnerWindow()) {
5377+
if (auto* doc = window->GetExtantDoc()) {
5378+
// Why doesn't doc->GetLanguageForStyle() work here? Get 'lang'
5379+
// from the root element by hand.
5380+
if (auto* root = doc->GetRootElement()) {
5381+
nsAutoString lang;
5382+
root->GetLang(lang);
5383+
if (!lang.IsEmpty()) {
5384+
return NS_Atomize(lang);
5385+
}
5386+
}
5387+
}
5388+
}
5389+
}
5390+
// Fall back to the OS default language, to behave similarly to HTML or
5391+
// canvas-element content with no language tag.
5392+
return do_AddRef(nsLanguageAtomService::GetService()->GetLocaleLanguage());
5393+
}();
5394+
5395+
CurrentState().explicitLang = explicitLang;
5396+
if (resolvedLang == CurrentState().resolvedFontLang) {
5397+
return false;
5398+
}
5399+
CurrentState().resolvedFontLang = resolvedLang;
5400+
return true;
5401+
}
5402+
53565403
gfxFontGroup* CanvasRenderingContext2D::GetCurrentFontStyle() {
53575404
// Use lazy (re)initialization for the fontGroup since it's rather expensive.
53585405

@@ -5370,6 +5417,9 @@ gfxFontGroup* CanvasRenderingContext2D::GetCurrentFontStyle() {
53705417
// If we have a cached fontGroup, check that it is valid for the current
53715418
// prescontext or canvas; if not, we need to discard and re-create it.
53725419
RefPtr<gfxFontGroup>& fontGroup = CurrentState().fontGroup;
5420+
if (ResolveFontLang()) {
5421+
fontGroup = nullptr;
5422+
}
53735423
if (fontGroup) {
53745424
if (fontGroup->GetFontVisibilityProvider() != visProvider) {
53755425
fontGroup = nullptr;

dom/canvas/CanvasRenderingContext2D.h

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -350,6 +350,14 @@ class CanvasRenderingContext2D : public nsICanvasRenderingContextInternal,
350350
CurrentState().textRendering = aTextRendering;
351351
}
352352

353+
void GetLang(nsAString& aLang) { CurrentState().lang->ToString(aLang); }
354+
void SetLang(const nsAString& aLang) {
355+
if (!CurrentState().lang->Equals(aLang)) {
356+
CurrentState().lang = NS_Atomize(aLang);
357+
CurrentState().fontGroup = nullptr;
358+
}
359+
}
360+
353361
void GetLetterSpacing(nsACString& aLetterSpacing);
354362
void SetLetterSpacing(const nsACString& aLetterSpacing);
355363
void GetWordSpacing(nsACString& aWordSpacing);
@@ -1017,6 +1025,10 @@ class CanvasRenderingContext2D : public nsICanvasRenderingContextInternal,
10171025

10181026
// text
10191027

1028+
// Resolve the `lang` property if it is `inherit` or empty, returning true
1029+
// if the resolved value has changed.
1030+
bool ResolveFontLang();
1031+
10201032
public:
10211033
gfxFontGroup* GetCurrentFontStyle();
10221034

@@ -1102,6 +1114,8 @@ class CanvasRenderingContext2D : public nsICanvasRenderingContextInternal,
11021114
gfx::Float wordSpacing = 0.0f;
11031115
nsCString letterSpacingStr;
11041116
nsCString wordSpacingStr;
1117+
RefPtr<nsAtom> lang = nsGkAtoms::inherit;
1118+
RefPtr<nsAtom> resolvedFontLang;
11051119

11061120
nscolor shadowColor = 0;
11071121

@@ -1141,6 +1155,9 @@ class CanvasRenderingContext2D : public nsICanvasRenderingContextInternal,
11411155
// tainted state of the canvas itself, we update our filters accordingly.
11421156
bool filterSourceGraphicTainted = false;
11431157
bool imageSmoothingEnabled = true;
1158+
1159+
// Whether resolvedFontLang was an explicitly-specified lang or inferred.
1160+
bool explicitLang = false;
11441161
};
11451162

11461163
AutoTArray<ContextState, 3> mStyleStack;
@@ -1159,9 +1176,11 @@ class CanvasRenderingContext2D : public nsICanvasRenderingContextInternal,
11591176

11601177
struct FontStyleCacheKey {
11611178
FontStyleCacheKey() = default;
1162-
FontStyleCacheKey(const nsACString& aFont, uint64_t aGeneration)
1163-
: mFont(aFont), mGeneration(aGeneration) {}
1179+
FontStyleCacheKey(const nsACString& aFont, nsAtom* aLang,
1180+
uint64_t aGeneration)
1181+
: mFont(aFont), mLang(aLang), mGeneration(aGeneration) {}
11641182
nsCString mFont;
1183+
RefPtr<nsAtom> mLang;
11651184
uint64_t mGeneration = 0;
11661185
};
11671186

@@ -1176,12 +1195,13 @@ class CanvasRenderingContext2D : public nsICanvasRenderingContextInternal,
11761195
public:
11771196
static HashNumber Hash(const FontStyleCacheKey& aKey) {
11781197
HashNumber hash = HashString(aKey.mFont);
1198+
hash = AddToHash(hash, aKey.mLang->hash());
11791199
return AddToHash(hash, aKey.mGeneration);
11801200
}
11811201
static bool Match(const FontStyleCacheKey& aKey,
11821202
const FontStyleData& aVal) {
11831203
return aVal.mKey.mGeneration == aKey.mGeneration &&
1184-
aVal.mKey.mFont == aKey.mFont;
1204+
aVal.mKey.mLang == aKey.mLang && aVal.mKey.mFont == aKey.mFont;
11851205
}
11861206
};
11871207

dom/canvas/OffscreenCanvas.cpp

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -35,10 +35,11 @@ namespace mozilla::dom {
3535
static mozilla::LazyLogModule gFingerprinterDetection("FingerprinterDetection");
3636

3737
OffscreenCanvasCloneData::OffscreenCanvasCloneData(
38-
OffscreenCanvasDisplayHelper* aDisplay, uint32_t aWidth, uint32_t aHeight,
39-
layers::LayersBackend aCompositorBackend, bool aNeutered, bool aIsWriteOnly,
40-
nsIPrincipal* aExpandedReader)
38+
OffscreenCanvasDisplayHelper* aDisplay, nsAtom* aLang, uint32_t aWidth,
39+
uint32_t aHeight, layers::LayersBackend aCompositorBackend, bool aNeutered,
40+
bool aIsWriteOnly, nsIPrincipal* aExpandedReader)
4141
: mDisplay(aDisplay),
42+
mLang(aLang),
4243
mWidth(aWidth),
4344
mHeight(aHeight),
4445
mCompositorBackendType(aCompositorBackend),
@@ -61,12 +62,13 @@ OffscreenCanvas::OffscreenCanvas(nsIGlobalObject* aGlobal, uint32_t aWidth,
6162
OffscreenCanvas::OffscreenCanvas(
6263
nsIGlobalObject* aGlobal, uint32_t aWidth, uint32_t aHeight,
6364
layers::LayersBackend aCompositorBackend,
64-
already_AddRefed<OffscreenCanvasDisplayHelper> aDisplay)
65+
already_AddRefed<OffscreenCanvasDisplayHelper> aDisplay, nsAtom* aLang)
6566
: DOMEventTargetHelper(aGlobal),
6667
mWidth(aWidth),
6768
mHeight(aHeight),
6869
mCompositorBackendType(aCompositorBackend),
6970
mDisplay(aDisplay),
71+
mLang(aLang),
7072
mFontVisibility(ComputeFontVisibility()) {}
7173

7274
OffscreenCanvas::~OffscreenCanvas() {
@@ -378,7 +380,7 @@ UniquePtr<OffscreenCanvasCloneData> OffscreenCanvas::ToCloneData(
378380
}
379381

380382
auto cloneData = MakeUnique<OffscreenCanvasCloneData>(
381-
mDisplay, mWidth, mHeight, mCompositorBackendType, mNeutered,
383+
mDisplay, mLang, mWidth, mHeight, mCompositorBackendType, mNeutered,
382384
mIsWriteOnly, mExpandedReader);
383385
SetNeutered();
384386
return cloneData;
@@ -626,7 +628,7 @@ already_AddRefed<OffscreenCanvas> OffscreenCanvas::CreateFromCloneData(
626628
MOZ_ASSERT(aData);
627629
RefPtr<OffscreenCanvas> wc = new OffscreenCanvas(
628630
aGlobal, aData->mWidth, aData->mHeight, aData->mCompositorBackendType,
629-
aData->mDisplay.forget());
631+
aData->mDisplay.forget(), aData->mLang);
630632
if (aData->mNeutered) {
631633
wc->SetNeutered();
632634
}

dom/canvas/OffscreenCanvas.h

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,13 +45,14 @@ using OwningOffscreenRenderingContext = class
4545
// store necessary data in it then pass it to worker thread.
4646
struct OffscreenCanvasCloneData final {
4747
OffscreenCanvasCloneData(OffscreenCanvasDisplayHelper* aDisplay,
48-
uint32_t aWidth, uint32_t aHeight,
48+
nsAtom* aLang, uint32_t aWidth, uint32_t aHeight,
4949
layers::LayersBackend aCompositorBackend,
5050
bool aNeutered, bool aIsWriteOnly,
5151
nsIPrincipal* aExpandedReader);
5252
~OffscreenCanvasCloneData();
5353

5454
RefPtr<OffscreenCanvasDisplayHelper> mDisplay;
55+
RefPtr<nsAtom> mLang;
5556
uint32_t mWidth;
5657
uint32_t mHeight;
5758
layers::LayersBackend mCompositorBackendType;
@@ -77,7 +78,8 @@ class OffscreenCanvas final : public DOMEventTargetHelper,
7778

7879
OffscreenCanvas(nsIGlobalObject* aGlobal, uint32_t aWidth, uint32_t aHeight,
7980
layers::LayersBackend aCompositorBackend,
80-
already_AddRefed<OffscreenCanvasDisplayHelper> aDisplay);
81+
already_AddRefed<OffscreenCanvasDisplayHelper> aDisplay,
82+
nsAtom* aLang);
8183

8284
void Destroy();
8385

@@ -170,6 +172,8 @@ class OffscreenCanvas final : public DOMEventTargetHelper,
170172

171173
bool IsTransferredFromElement() const { return !!mDisplay; }
172174

175+
nsAtom* GetLang() const { return mLang; }
176+
173177
private:
174178
~OffscreenCanvas();
175179

@@ -194,6 +198,7 @@ class OffscreenCanvas final : public DOMEventTargetHelper,
194198
layers::LayersBackend::LAYERS_NONE;
195199

196200
RefPtr<OffscreenCanvasDisplayHelper> mDisplay;
201+
RefPtr<nsAtom> mLang;
197202
RefPtr<CancelableRunnable> mPendingCommit;
198203
RefPtr<nsIPrincipal> mExpandedReader;
199204
Maybe<OffscreenCanvasDisplayData> mPendingUpdate;

dom/html/HTMLCanvasElement.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1125,7 +1125,8 @@ OffscreenCanvas* HTMLCanvasElement::TransferControlToOffscreen(
11251125
mOffscreenDisplay =
11261126
MakeRefPtr<OffscreenCanvasDisplayHelper>(this, sz.width, sz.height);
11271127
mOffscreenCanvas = new OffscreenCanvas(win->AsGlobal(), sz.width, sz.height,
1128-
backend, do_AddRef(mOffscreenDisplay));
1128+
backend, do_AddRef(mOffscreenDisplay),
1129+
FragmentOrElement::GetLang());
11291130
if (mWriteOnly) {
11301131
mOffscreenCanvas->SetWriteOnly(mExpandedReader);
11311132
}

dom/webidl/CanvasRenderingContext2D.webidl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -321,6 +321,7 @@ interface mixin CanvasPathDrawingStyles {
321321

322322
interface mixin CanvasTextDrawingStyles {
323323
// text
324+
attribute DOMString lang; // default: "inherit"
324325
[SetterThrows]
325326
attribute UTF8String font; // (default 10px sans-serif)
326327
attribute CanvasTextAlign textAlign; // (default: "start")

testing/web-platform/meta/html/canvas/element/manual/text/canvas.2d.lang.dynamic.html.ini

Lines changed: 0 additions & 9 deletions
This file was deleted.

testing/web-platform/meta/html/canvas/element/manual/text/canvas.2d.lang.empty.canvas.html.ini

Lines changed: 0 additions & 4 deletions
This file was deleted.

testing/web-platform/meta/html/canvas/element/manual/text/canvas.2d.lang.html.ini

Lines changed: 0 additions & 4 deletions
This file was deleted.

testing/web-platform/meta/html/canvas/element/manual/text/canvas.2d.lang.inherit.disconnected.canvas.html.ini

Lines changed: 0 additions & 2 deletions
This file was deleted.

0 commit comments

Comments
 (0)