1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "nsFontFaceUtils.h"
9 #include "gfxTextRun.h"
10 #include "gfxUserFontSet.h"
11 #include "mozilla/PresShell.h"
12 #include "mozilla/RestyleManager.h"
13 #include "nsFontMetrics.h"
15 #include "nsLayoutUtils.h"
16 #include "nsPlaceholderFrame.h"
19 using namespace mozilla
;
21 enum class FontUsageKind
{
22 // The frame did not use the given font.
24 // The frame uses the given font, but doesn't use font-metric-dependent units,
25 // which means that its style doesn't depend on this font.
27 // The frame uses has some font-metric-dependent units on this font.
28 // This means that its style depends on this font, and we need to restyle the
29 // element the frame came from.
32 Max
= Frame
| FontMetrics
,
35 MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(FontUsageKind
);
37 static bool IsFontReferenced(const ComputedStyle
& aStyle
,
38 const nsAString
& aFamilyName
) {
39 for (const auto& family
:
40 aStyle
.StyleFont()->mFont
.family
.families
.list
.AsSpan()) {
41 if (family
.IsNamedFamily(aFamilyName
)) {
48 static FontUsageKind
StyleFontUsage(nsIFrame
* aFrame
, ComputedStyle
* aStyle
,
49 nsPresContext
* aPresContext
,
50 const gfxUserFontEntry
* aFont
,
51 const nsAString
& aFamilyName
,
53 MOZ_ASSERT(NS_ConvertUTF8toUTF16(aFont
->FamilyName()) == aFamilyName
);
55 auto FontIsUsed
= [&aFont
, &aPresContext
,
56 &aFamilyName
](ComputedStyle
* aStyle
) {
57 if (!IsFontReferenced(*aStyle
, aFamilyName
)) {
61 // family name is in the fontlist, check to see if the font group
62 // associated with the frame includes the specific userfont.
64 // TODO(emilio): Is this check really useful? I guess it's useful for
65 // different font faces of the same font?
66 RefPtr
<nsFontMetrics
> fm
= nsLayoutUtils::GetFontMetricsForComputedStyle(
67 aStyle
, aPresContext
, 1.0f
);
68 return fm
->GetThebesFontGroup()->ContainsUserFont(aFont
);
71 auto usage
= FontUsageKind::None
;
73 if (FontIsUsed(aStyle
)) {
74 usage
|= FontUsageKind::Frame
;
75 if (aStyle
->DependsOnSelfFontMetrics()) {
76 usage
|= FontUsageKind::FontMetrics
;
80 if (aStyle
->DependsOnInheritedFontMetrics() &&
81 !(usage
& FontUsageKind::FontMetrics
)) {
82 ComputedStyle
* parentStyle
= nullptr;
84 parentStyle
= aFrame
->Style();
86 nsIFrame
* provider
= nullptr;
87 parentStyle
= aFrame
->GetParentComputedStyle(&provider
);
90 if (parentStyle
&& FontIsUsed(parentStyle
)) {
91 usage
|= FontUsageKind::FontMetrics
;
98 static FontUsageKind
FrameFontUsage(nsIFrame
* aFrame
,
99 nsPresContext
* aPresContext
,
100 const gfxUserFontEntry
* aFont
,
101 const nsAString
& aFamilyName
) {
102 // check the style of the frame
103 FontUsageKind kind
= StyleFontUsage(aFrame
, aFrame
->Style(), aPresContext
,
104 aFont
, aFamilyName
, /* extra = */ false);
105 if (kind
== FontUsageKind::Max
) {
109 // check additional styles
110 int32_t contextIndex
= 0;
111 for (ComputedStyle
* extraContext
;
112 (extraContext
= aFrame
->GetAdditionalComputedStyle(contextIndex
));
114 kind
|= StyleFontUsage(aFrame
, extraContext
, aPresContext
, aFont
,
115 aFamilyName
, /* extra = */ true);
116 if (kind
== FontUsageKind::Max
) {
124 // TODO(emilio): Can we use the restyle-hint machinery instead of this?
125 static void ScheduleReflow(PresShell
* aPresShell
, nsIFrame
* aFrame
) {
126 nsIFrame
* f
= aFrame
;
127 if (f
->IsSVGFrame() || f
->IsInSVGTextSubtree()) {
128 // SVG frames (and the non-SVG descendants of an SVGTextFrame) need special
129 // reflow handling. We need to search upwards for the first displayed
130 // SVGOuterSVGFrame or non-SVG frame, which is the frame we can call
131 // FrameNeedsReflow on. (This logic is based on
132 // SVGUtils::ScheduleReflowSVG and
133 // SVGTextFrame::ScheduleReflowSVGNonDisplayText.)
134 if (f
->HasAnyStateBits(NS_FRAME_IS_NONDISPLAY
)) {
136 if (!f
->HasAnyStateBits(NS_FRAME_IS_NONDISPLAY
)) {
137 if (f
->IsSubtreeDirty()) {
138 // This is a displayed frame, so if it is already dirty, we
139 // will be reflowed soon anyway. No need to call
140 // FrameNeedsReflow again, then.
143 if (!f
->HasAnyStateBits(NS_FRAME_SVG_LAYOUT
) ||
144 !f
->IsInSVGTextSubtree()) {
147 f
->AddStateBits(NS_FRAME_HAS_DIRTY_CHILDREN
);
151 MOZ_ASSERT(f
, "should have found an ancestor frame to reflow");
155 aPresShell
->FrameNeedsReflow(f
, IntrinsicDirty::FrameAncestorsAndDescendants
,
159 enum class ReflowAlreadyScheduled
{
165 void nsFontFaceUtils::MarkDirtyForFontChange(nsIFrame
* aSubtreeRoot
,
166 const gfxUserFontEntry
* aFont
) {
168 AutoTArray
<nsIFrame
*, 4> subtrees
;
169 subtrees
.AppendElement(aSubtreeRoot
);
171 nsPresContext
* pc
= aSubtreeRoot
->PresContext();
172 PresShell
* presShell
= pc
->PresShell();
174 const bool usesMetricsFromStyle
= pc
->StyleSet()->UsesFontMetrics();
176 // StyleSingleFontFamily::IsNamedFamily expects a UTF-16 string. Convert it
177 // once here rather than on each call.
178 NS_ConvertUTF8toUTF16
familyName(aFont
->FamilyName());
180 // check descendants, iterating over subtrees that may include
181 // additional subtrees associated with placeholders
183 nsIFrame
* subtreeRoot
= subtrees
.PopLastElement();
185 // Check all descendants to see if they use the font
186 AutoTArray
<std::pair
<nsIFrame
*, ReflowAlreadyScheduled
>, 32> stack
;
188 std::make_pair(subtreeRoot
, ReflowAlreadyScheduled::No
));
191 auto pair
= stack
.PopLastElement();
192 nsIFrame
* f
= pair
.first
;
193 ReflowAlreadyScheduled alreadyScheduled
= pair
.second
;
195 // if this frame uses the font, mark its descendants dirty
196 // and skip checking its children
197 FontUsageKind kind
= FrameFontUsage(f
, pc
, aFont
, familyName
);
198 if (kind
!= FontUsageKind::None
) {
199 if ((kind
& FontUsageKind::Frame
) &&
200 alreadyScheduled
== ReflowAlreadyScheduled::No
) {
201 ScheduleReflow(presShell
, f
);
202 alreadyScheduled
= ReflowAlreadyScheduled::Yes
;
204 if (kind
& FontUsageKind::FontMetrics
) {
205 MOZ_ASSERT(f
->GetContent() && f
->GetContent()->IsElement(),
206 "How could we target a non-element with selectors?");
207 f
->PresContext()->RestyleManager()->PostRestyleEvent(
208 dom::Element::FromNode(f
->GetContent()),
209 RestyleHint::RECASCADE_SELF
, nsChangeHint(0));
213 if (alreadyScheduled
== ReflowAlreadyScheduled::No
||
214 usesMetricsFromStyle
) {
215 if (f
->IsPlaceholderFrame()) {
216 nsIFrame
* oof
= nsPlaceholderFrame::GetRealFrameForPlaceholder(f
);
217 if (!nsLayoutUtils::IsProperAncestorFrame(subtreeRoot
, oof
)) {
218 // We have another distinct subtree we need to mark.
219 subtrees
.AppendElement(oof
);
223 for (const auto& childList
: f
->ChildLists()) {
224 for (nsIFrame
* kid
: childList
.mList
) {
225 stack
.AppendElement(std::make_pair(kid
, alreadyScheduled
));
229 } while (!stack
.IsEmpty());
230 } while (!subtrees
.IsEmpty());