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