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 "nsMathMLTokenFrame.h"
8 
9 #include "mozilla/PresShell.h"
10 #include "nsPresContext.h"
11 #include "nsContentUtils.h"
12 #include "nsTextFrame.h"
13 #include <algorithm>
14 
15 using namespace mozilla;
16 
NS_NewMathMLTokenFrame(PresShell * aPresShell,ComputedStyle * aStyle)17 nsIFrame* NS_NewMathMLTokenFrame(PresShell* aPresShell, ComputedStyle* aStyle) {
18   return new (aPresShell)
19       nsMathMLTokenFrame(aStyle, aPresShell->GetPresContext());
20 }
21 
22 NS_IMPL_FRAMEARENA_HELPERS(nsMathMLTokenFrame)
23 
24 nsMathMLTokenFrame::~nsMathMLTokenFrame() = default;
25 
26 NS_IMETHODIMP
InheritAutomaticData(nsIFrame * aParent)27 nsMathMLTokenFrame::InheritAutomaticData(nsIFrame* aParent) {
28   // let the base class get the default from our parent
29   nsMathMLContainerFrame::InheritAutomaticData(aParent);
30 
31   return NS_OK;
32 }
33 
GetMathMLFrameType()34 eMathMLFrameType nsMathMLTokenFrame::GetMathMLFrameType() {
35   // treat everything other than <mi> as ordinary...
36   if (!mContent->IsMathMLElement(nsGkAtoms::mi_)) {
37     return eMathMLFrameType_Ordinary;
38   }
39 
40   uint8_t mathVariant = StyleFont()->mMathVariant;
41   if ((mathVariant == NS_MATHML_MATHVARIANT_NONE &&
42        (StyleFont()->mFont.style == FontSlantStyle::Italic() ||
43         HasAnyStateBits(NS_FRAME_IS_IN_SINGLE_CHAR_MI))) ||
44       mathVariant == NS_MATHML_MATHVARIANT_ITALIC ||
45       mathVariant == NS_MATHML_MATHVARIANT_BOLD_ITALIC ||
46       mathVariant == NS_MATHML_MATHVARIANT_SANS_SERIF_ITALIC ||
47       mathVariant == NS_MATHML_MATHVARIANT_SANS_SERIF_BOLD_ITALIC) {
48     return eMathMLFrameType_ItalicIdentifier;
49   }
50   return eMathMLFrameType_UprightIdentifier;
51 }
52 
MarkTextFramesAsTokenMathML()53 void nsMathMLTokenFrame::MarkTextFramesAsTokenMathML() {
54   nsIFrame* child = nullptr;
55   uint32_t childCount = 0;
56 
57   // Set flags on child text frames
58   // - to force them to trim their leading and trailing whitespaces.
59   // - Indicate which frames are suitable for mathvariant
60   // - flag single character <mi> frames for special italic treatment
61   for (nsIFrame* childFrame = PrincipalChildList().FirstChild(); childFrame;
62        childFrame = childFrame->GetNextSibling()) {
63     for (nsIFrame* childFrame2 = childFrame->PrincipalChildList().FirstChild();
64          childFrame2; childFrame2 = childFrame2->GetNextSibling()) {
65       if (childFrame2->IsTextFrame()) {
66         childFrame2->AddStateBits(TEXT_IS_IN_TOKEN_MATHML);
67         child = childFrame2;
68         childCount++;
69       }
70     }
71   }
72   if (mContent->IsMathMLElement(nsGkAtoms::mi_) && childCount == 1) {
73     nsAutoString data;
74     nsContentUtils::GetNodeTextContent(mContent, false, data);
75 
76     data.CompressWhitespace();
77     int32_t length = data.Length();
78 
79     bool isSingleCharacter =
80         length == 1 || (length == 2 && NS_IS_HIGH_SURROGATE(data[0]));
81 
82     if (isSingleCharacter) {
83       child->AddStateBits(NS_FRAME_IS_IN_SINGLE_CHAR_MI);
84       AddStateBits(NS_FRAME_IS_IN_SINGLE_CHAR_MI);
85     }
86   }
87 }
88 
SetInitialChildList(ChildListID aListID,nsFrameList & aChildList)89 void nsMathMLTokenFrame::SetInitialChildList(ChildListID aListID,
90                                              nsFrameList& aChildList) {
91   // First, let the base class do its work
92   nsMathMLContainerFrame::SetInitialChildList(aListID, aChildList);
93   MarkTextFramesAsTokenMathML();
94 }
95 
AppendFrames(ChildListID aListID,nsFrameList & aChildList)96 void nsMathMLTokenFrame::AppendFrames(ChildListID aListID,
97                                       nsFrameList& aChildList) {
98   nsMathMLContainerFrame::AppendFrames(aListID, aChildList);
99   MarkTextFramesAsTokenMathML();
100 }
101 
InsertFrames(ChildListID aListID,nsIFrame * aPrevFrame,const nsLineList::iterator * aPrevFrameLine,nsFrameList & aChildList)102 void nsMathMLTokenFrame::InsertFrames(
103     ChildListID aListID, nsIFrame* aPrevFrame,
104     const nsLineList::iterator* aPrevFrameLine, nsFrameList& aChildList) {
105   nsMathMLContainerFrame::InsertFrames(aListID, aPrevFrame, aPrevFrameLine,
106                                        aChildList);
107   MarkTextFramesAsTokenMathML();
108 }
109 
Reflow(nsPresContext * aPresContext,ReflowOutput & aDesiredSize,const ReflowInput & aReflowInput,nsReflowStatus & aStatus)110 void nsMathMLTokenFrame::Reflow(nsPresContext* aPresContext,
111                                 ReflowOutput& aDesiredSize,
112                                 const ReflowInput& aReflowInput,
113                                 nsReflowStatus& aStatus) {
114   MarkInReflow();
115   MOZ_ASSERT(aStatus.IsEmpty(), "Caller should pass a fresh reflow status!");
116 
117   mPresentationData.flags &= ~NS_MATHML_ERROR;
118 
119   // initializations needed for empty markup like <mtag></mtag>
120   aDesiredSize.ClearSize();
121   aDesiredSize.SetBlockStartAscent(0);
122   aDesiredSize.mBoundingMetrics = nsBoundingMetrics();
123 
124   for (nsIFrame* childFrame : PrincipalChildList()) {
125     // ask our children to compute their bounding metrics
126     ReflowOutput childDesiredSize(aReflowInput.GetWritingMode());
127     WritingMode wm = childFrame->GetWritingMode();
128     LogicalSize availSize = aReflowInput.ComputedSize(wm);
129     availSize.BSize(wm) = NS_UNCONSTRAINEDSIZE;
130     ReflowInput childReflowInput(aPresContext, aReflowInput, childFrame,
131                                  availSize);
132     ReflowChild(childFrame, aPresContext, childDesiredSize, childReflowInput,
133                 aStatus);
134     // NS_ASSERTION(aStatus.IsComplete(), "bad status");
135     SaveReflowAndBoundingMetricsFor(childFrame, childDesiredSize,
136                                     childDesiredSize.mBoundingMetrics);
137   }
138 
139   // place and size children
140   FinalizeReflow(aReflowInput.mRenderingContext->GetDrawTarget(), aDesiredSize);
141 
142   aStatus.Reset();  // This type of frame can't be split.
143   NS_FRAME_SET_TRUNCATION(aStatus, aReflowInput, aDesiredSize);
144 }
145 
146 // For token elements, mBoundingMetrics is computed at the ReflowToken
147 // pass, it is not computed here because our children may be text frames
148 // that do not implement the GetBoundingMetrics() interface.
149 /* virtual */
Place(DrawTarget * aDrawTarget,bool aPlaceOrigin,ReflowOutput & aDesiredSize)150 nsresult nsMathMLTokenFrame::Place(DrawTarget* aDrawTarget, bool aPlaceOrigin,
151                                    ReflowOutput& aDesiredSize) {
152   mBoundingMetrics = nsBoundingMetrics();
153   for (nsIFrame* childFrame : PrincipalChildList()) {
154     ReflowOutput childSize(aDesiredSize.GetWritingMode());
155     GetReflowAndBoundingMetricsFor(childFrame, childSize,
156                                    childSize.mBoundingMetrics, nullptr);
157     // compute and cache the bounding metrics
158     mBoundingMetrics += childSize.mBoundingMetrics;
159   }
160 
161   RefPtr<nsFontMetrics> fm =
162       nsLayoutUtils::GetInflatedFontMetricsForFrame(this);
163   nscoord ascent = fm->MaxAscent();
164   nscoord descent = fm->MaxDescent();
165 
166   aDesiredSize.mBoundingMetrics = mBoundingMetrics;
167   aDesiredSize.Width() = mBoundingMetrics.width;
168   aDesiredSize.SetBlockStartAscent(std::max(mBoundingMetrics.ascent, ascent));
169   aDesiredSize.Height() = aDesiredSize.BlockStartAscent() +
170                           std::max(mBoundingMetrics.descent, descent);
171 
172   if (aPlaceOrigin) {
173     nscoord dy, dx = 0;
174     for (nsIFrame* childFrame : PrincipalChildList()) {
175       ReflowOutput childSize(aDesiredSize.GetWritingMode());
176       GetReflowAndBoundingMetricsFor(childFrame, childSize,
177                                      childSize.mBoundingMetrics);
178 
179       // place and size the child; (dx,0) makes the caret happy - bug 188146
180       dy = childSize.Height() == 0
181                ? 0
182                : aDesiredSize.BlockStartAscent() - childSize.BlockStartAscent();
183       FinishReflowChild(childFrame, PresContext(), childSize, nullptr, dx, dy,
184                         ReflowChildFlags::Default);
185       dx += childSize.Width();
186     }
187   }
188 
189   SetReference(nsPoint(0, aDesiredSize.BlockStartAscent()));
190 
191   return NS_OK;
192 }
193