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/. */
6 
7 #include "nsFontFaceUtils.h"
8 
9 #include "gfxUserFontSet.h"
10 #include "nsFontMetrics.h"
11 #include "nsIFrame.h"
12 #include "nsLayoutUtils.h"
13 #include "nsPlaceholderFrame.h"
14 #include "nsTArray.h"
15 #include "SVGTextFrame.h"
16 
StyleContextContainsFont(nsStyleContext * aStyleContext,const gfxUserFontSet * aUserFontSet,const gfxUserFontEntry * aFont)17 static bool StyleContextContainsFont(nsStyleContext* aStyleContext,
18                                      const gfxUserFontSet* aUserFontSet,
19                                      const gfxUserFontEntry* aFont) {
20   // if the font is null, simply check to see whether fontlist includes
21   // downloadable fonts
22   if (!aFont) {
23     const mozilla::FontFamilyList& fontlist =
24         aStyleContext->StyleFont()->mFont.fontlist;
25     return aUserFontSet->ContainsUserFontSetFonts(fontlist);
26   }
27 
28   // first, check if the family name is in the fontlist
29   const nsString& familyName = aFont->FamilyName();
30   if (!aStyleContext->StyleFont()->mFont.fontlist.Contains(familyName)) {
31     return false;
32   }
33 
34   // family name is in the fontlist, check to see if the font group
35   // associated with the frame includes the specific userfont
36   RefPtr<nsFontMetrics> fm =
37       nsLayoutUtils::GetFontMetricsForStyleContext(aStyleContext, 1.0f);
38 
39   if (fm->GetThebesFontGroup()->ContainsUserFont(aFont)) {
40     return true;
41   }
42 
43   return false;
44 }
45 
FrameUsesFont(nsIFrame * aFrame,const gfxUserFontEntry * aFont)46 static bool FrameUsesFont(nsIFrame* aFrame, const gfxUserFontEntry* aFont) {
47   // check the style context of the frame
48   gfxUserFontSet* ufs = aFrame->PresContext()->GetUserFontSet();
49   if (StyleContextContainsFont(aFrame->StyleContext(), ufs, aFont)) {
50     return true;
51   }
52 
53   // check additional style contexts
54   int32_t contextIndex = 0;
55   for (nsStyleContext* extraContext;
56        (extraContext = aFrame->GetAdditionalStyleContext(contextIndex));
57        ++contextIndex) {
58     if (StyleContextContainsFont(extraContext, ufs, aFont)) {
59       return true;
60     }
61   }
62 
63   return false;
64 }
65 
ScheduleReflow(nsIPresShell * aShell,nsIFrame * aFrame)66 static void ScheduleReflow(nsIPresShell* aShell, nsIFrame* aFrame) {
67   nsIFrame* f = aFrame;
68   if (f->IsFrameOfType(nsIFrame::eSVG) || nsSVGUtils::IsInSVGTextSubtree(f)) {
69     // SVG frames (and the non-SVG descendants of an SVGTextFrame) need special
70     // reflow handling.  We need to search upwards for the first displayed
71     // nsSVGOuterSVGFrame or non-SVG frame, which is the frame we can call
72     // FrameNeedsReflow on.  (This logic is based on
73     // nsSVGUtils::ScheduleReflowSVG and
74     // SVGTextFrame::ScheduleReflowSVGNonDisplayText.)
75     if (f->GetStateBits() & NS_FRAME_IS_NONDISPLAY) {
76       while (f) {
77         if (!(f->GetStateBits() & NS_FRAME_IS_NONDISPLAY)) {
78           if (NS_SUBTREE_DIRTY(f)) {
79             // This is a displayed frame, so if it is already dirty, we
80             // will be reflowed soon anyway.  No need to call
81             // FrameNeedsReflow again, then.
82             return;
83           }
84           if (f->GetStateBits() & NS_STATE_IS_OUTER_SVG ||
85               !(f->IsFrameOfType(nsIFrame::eSVG) ||
86                 nsSVGUtils::IsInSVGTextSubtree(f))) {
87             break;
88           }
89           f->AddStateBits(NS_FRAME_HAS_DIRTY_CHILDREN);
90         }
91         f = f->GetParent();
92       }
93       MOZ_ASSERT(f, "should have found an ancestor frame to reflow");
94     }
95   }
96 
97   aShell->FrameNeedsReflow(f, nsIPresShell::eStyleChange, NS_FRAME_IS_DIRTY);
98 }
99 
MarkDirtyForFontChange(nsIFrame * aSubtreeRoot,const gfxUserFontEntry * aFont)100 /* static */ void nsFontFaceUtils::MarkDirtyForFontChange(
101     nsIFrame* aSubtreeRoot, const gfxUserFontEntry* aFont) {
102   AutoTArray<nsIFrame*, 4> subtrees;
103   subtrees.AppendElement(aSubtreeRoot);
104 
105   nsIPresShell* ps = aSubtreeRoot->PresShell();
106 
107   // check descendants, iterating over subtrees that may include
108   // additional subtrees associated with placeholders
109   do {
110     nsIFrame* subtreeRoot = subtrees.ElementAt(subtrees.Length() - 1);
111     subtrees.RemoveElementAt(subtrees.Length() - 1);
112 
113     // Check all descendants to see if they use the font
114     AutoTArray<nsIFrame*, 32> stack;
115     stack.AppendElement(subtreeRoot);
116 
117     do {
118       nsIFrame* f = stack.ElementAt(stack.Length() - 1);
119       stack.RemoveElementAt(stack.Length() - 1);
120 
121       // if this frame uses the font, mark its descendants dirty
122       // and skip checking its children
123       if (FrameUsesFont(f, aFont)) {
124         ScheduleReflow(ps, f);
125       } else {
126         if (f->IsPlaceholderFrame()) {
127           nsIFrame* oof = nsPlaceholderFrame::GetRealFrameForPlaceholder(f);
128           if (!nsLayoutUtils::IsProperAncestorFrame(subtreeRoot, oof)) {
129             // We have another distinct subtree we need to mark.
130             subtrees.AppendElement(oof);
131           }
132         }
133 
134         nsIFrame::ChildListIterator lists(f);
135         for (; !lists.IsDone(); lists.Next()) {
136           nsFrameList::Enumerator childFrames(lists.CurrentList());
137           for (; !childFrames.AtEnd(); childFrames.Next()) {
138             nsIFrame* kid = childFrames.get();
139             stack.AppendElement(kid);
140           }
141         }
142       }
143     } while (!stack.IsEmpty());
144   } while (!subtrees.IsEmpty());
145 }
146