layout/mathml/nsMathMLmoFrame.cpp

Wed, 31 Dec 2014 13:27:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 13:27:57 +0100
branch
TOR_BUG_3246
changeset 6
8bccb770b82d
permissions
-rw-r--r--

Ignore runtime configuration files generated during quality assurance.

michael@0 1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
michael@0 2 /* This Source Code Form is subject to the terms of the Mozilla Public
michael@0 3 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 5
michael@0 6 #include "nsMathMLmoFrame.h"
michael@0 7 #include "nsPresContext.h"
michael@0 8 #include "nsRenderingContext.h"
michael@0 9 #include "nsContentUtils.h"
michael@0 10 #include "nsFrameSelection.h"
michael@0 11 #include "nsMathMLElement.h"
michael@0 12 #include <algorithm>
michael@0 13
michael@0 14 //
michael@0 15 // <mo> -- operator, fence, or separator - implementation
michael@0 16 //
michael@0 17
michael@0 18 // additional style context to be used by our MathMLChar.
michael@0 19 #define NS_MATHML_CHAR_STYLE_CONTEXT_INDEX 0
michael@0 20
michael@0 21 nsIFrame*
michael@0 22 NS_NewMathMLmoFrame(nsIPresShell* aPresShell, nsStyleContext *aContext)
michael@0 23 {
michael@0 24 return new (aPresShell) nsMathMLmoFrame(aContext);
michael@0 25 }
michael@0 26
michael@0 27 NS_IMPL_FRAMEARENA_HELPERS(nsMathMLmoFrame)
michael@0 28
michael@0 29 nsMathMLmoFrame::~nsMathMLmoFrame()
michael@0 30 {
michael@0 31 }
michael@0 32
michael@0 33 static const char16_t kApplyFunction = char16_t(0x2061);
michael@0 34 static const char16_t kInvisibleTimes = char16_t(0x2062);
michael@0 35 static const char16_t kInvisibleSeparator = char16_t(0x2063);
michael@0 36 static const char16_t kInvisiblePlus = char16_t(0x2064);
michael@0 37
michael@0 38 eMathMLFrameType
michael@0 39 nsMathMLmoFrame::GetMathMLFrameType()
michael@0 40 {
michael@0 41 return NS_MATHML_OPERATOR_IS_INVISIBLE(mFlags)
michael@0 42 ? eMathMLFrameType_OperatorInvisible
michael@0 43 : eMathMLFrameType_OperatorOrdinary;
michael@0 44 }
michael@0 45
michael@0 46 // since a mouse click implies selection, we cannot just rely on the
michael@0 47 // frame's state bit in our child text frame. So we will first check
michael@0 48 // its selected state bit, and use this little helper to double check.
michael@0 49 bool
michael@0 50 nsMathMLmoFrame::IsFrameInSelection(nsIFrame* aFrame)
michael@0 51 {
michael@0 52 NS_ASSERTION(aFrame, "null arg");
michael@0 53 if (!aFrame || !aFrame->IsSelected())
michael@0 54 return false;
michael@0 55
michael@0 56 const nsFrameSelection* frameSelection = aFrame->GetConstFrameSelection();
michael@0 57 SelectionDetails* details =
michael@0 58 frameSelection->LookUpSelection(aFrame->GetContent(), 0, 1, true);
michael@0 59
michael@0 60 if (!details)
michael@0 61 return false;
michael@0 62
michael@0 63 while (details) {
michael@0 64 SelectionDetails* next = details->mNext;
michael@0 65 delete details;
michael@0 66 details = next;
michael@0 67 }
michael@0 68 return true;
michael@0 69 }
michael@0 70
michael@0 71 bool
michael@0 72 nsMathMLmoFrame::UseMathMLChar()
michael@0 73 {
michael@0 74 return (NS_MATHML_OPERATOR_GET_FORM(mFlags) &&
michael@0 75 NS_MATHML_OPERATOR_IS_MUTABLE(mFlags)) ||
michael@0 76 NS_MATHML_OPERATOR_IS_CENTERED(mFlags);
michael@0 77 }
michael@0 78
michael@0 79 void
michael@0 80 nsMathMLmoFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder,
michael@0 81 const nsRect& aDirtyRect,
michael@0 82 const nsDisplayListSet& aLists)
michael@0 83 {
michael@0 84 bool useMathMLChar = UseMathMLChar();
michael@0 85
michael@0 86 if (!useMathMLChar) {
michael@0 87 // let the base class do everything
michael@0 88 nsMathMLTokenFrame::BuildDisplayList(aBuilder, aDirtyRect, aLists);
michael@0 89 } else {
michael@0 90 DisplayBorderBackgroundOutline(aBuilder, aLists);
michael@0 91
michael@0 92 // make our char selected if our inner child text frame is selected
michael@0 93 bool isSelected = false;
michael@0 94 nsRect selectedRect;
michael@0 95 nsIFrame* firstChild = mFrames.FirstChild();
michael@0 96 if (IsFrameInSelection(firstChild)) {
michael@0 97 mMathMLChar.GetRect(selectedRect);
michael@0 98 // add a one pixel border (it renders better for operators like minus)
michael@0 99 selectedRect.Inflate(nsPresContext::CSSPixelsToAppUnits(1));
michael@0 100 isSelected = true;
michael@0 101 }
michael@0 102 mMathMLChar.Display(aBuilder, this, aLists, 0, isSelected ? &selectedRect : nullptr);
michael@0 103
michael@0 104 #if defined(DEBUG) && defined(SHOW_BOUNDING_BOX)
michael@0 105 // for visual debug
michael@0 106 DisplayBoundingMetrics(aBuilder, this, mReference, mBoundingMetrics, aLists);
michael@0 107 #endif
michael@0 108 }
michael@0 109 }
michael@0 110
michael@0 111 // get the text that we enclose and setup our nsMathMLChar
michael@0 112 void
michael@0 113 nsMathMLmoFrame::ProcessTextData()
michael@0 114 {
michael@0 115 mFlags = 0;
michael@0 116
michael@0 117 nsAutoString data;
michael@0 118 if (!nsContentUtils::GetNodeTextContent(mContent, false, data)) {
michael@0 119 NS_RUNTIMEABORT("OOM");
michael@0 120 }
michael@0 121
michael@0 122 data.CompressWhitespace();
michael@0 123 int32_t length = data.Length();
michael@0 124 char16_t ch = (length == 0) ? char16_t('\0') : data[0];
michael@0 125
michael@0 126 if ((length == 1) &&
michael@0 127 (ch == kApplyFunction ||
michael@0 128 ch == kInvisibleSeparator ||
michael@0 129 ch == kInvisiblePlus ||
michael@0 130 ch == kInvisibleTimes)) {
michael@0 131 mFlags |= NS_MATHML_OPERATOR_INVISIBLE;
michael@0 132 }
michael@0 133
michael@0 134 // don't bother doing anything special if we don't have a single child
michael@0 135 nsPresContext* presContext = PresContext();
michael@0 136 if (mFrames.GetLength() != 1) {
michael@0 137 data.Truncate(); // empty data to reset the char
michael@0 138 mMathMLChar.SetData(presContext, data);
michael@0 139 ResolveMathMLCharStyle(presContext, mContent, mStyleContext, &mMathMLChar);
michael@0 140 return;
michael@0 141 }
michael@0 142
michael@0 143 // special... in math mode, the usual minus sign '-' looks too short, so
michael@0 144 // what we do here is to remap <mo>-</mo> to the official Unicode minus
michael@0 145 // sign (U+2212) which looks much better. For background on this, see
michael@0 146 // http://groups.google.com/groups?hl=en&th=66488daf1ade7635&rnum=1
michael@0 147 if (1 == length && ch == '-') {
michael@0 148 ch = 0x2212;
michael@0 149 data = ch;
michael@0 150 }
michael@0 151
michael@0 152 // cache the special bits: mutable, accent, movablelimits, centered.
michael@0 153 // we need to do this in anticipation of other requirements, and these
michael@0 154 // bits don't change. Do not reset these bits unless the text gets changed.
michael@0 155
michael@0 156 // lookup all the forms under which the operator is listed in the dictionary,
michael@0 157 // and record whether the operator has accent="true" or movablelimits="true"
michael@0 158 nsOperatorFlags flags[4];
michael@0 159 float lspace[4], rspace[4];
michael@0 160 nsMathMLOperators::LookupOperators(data, flags, lspace, rspace);
michael@0 161 nsOperatorFlags allFlags =
michael@0 162 flags[NS_MATHML_OPERATOR_FORM_INFIX] |
michael@0 163 flags[NS_MATHML_OPERATOR_FORM_POSTFIX] |
michael@0 164 flags[NS_MATHML_OPERATOR_FORM_PREFIX];
michael@0 165
michael@0 166 mFlags |= allFlags & NS_MATHML_OPERATOR_ACCENT;
michael@0 167 mFlags |= allFlags & NS_MATHML_OPERATOR_MOVABLELIMITS;
michael@0 168
michael@0 169 // see if this is an operator that should be centered to cater for
michael@0 170 // fonts that are not math-aware
michael@0 171 if (1 == length) {
michael@0 172 if ((ch == '+') || (ch == '=') || (ch == '*') ||
michael@0 173 (ch == 0x2212) || // &minus;
michael@0 174 (ch == 0x2264) || // &le;
michael@0 175 (ch == 0x2265) || // &ge;
michael@0 176 (ch == 0x00D7)) { // &times;
michael@0 177 mFlags |= NS_MATHML_OPERATOR_CENTERED;
michael@0 178 }
michael@0 179 }
michael@0 180
michael@0 181 // cache the operator
michael@0 182 mMathMLChar.SetData(presContext, data);
michael@0 183
michael@0 184 // cache the native direction -- beware of bug 133429...
michael@0 185 // mEmbellishData.direction must always retain our native direction, whereas
michael@0 186 // mMathMLChar.GetStretchDirection() may change later, when Stretch() is called
michael@0 187 mEmbellishData.direction = mMathMLChar.GetStretchDirection();
michael@0 188
michael@0 189 bool isMutable =
michael@0 190 NS_MATHML_OPERATOR_IS_LARGEOP(allFlags) ||
michael@0 191 (mEmbellishData.direction != NS_STRETCH_DIRECTION_UNSUPPORTED);
michael@0 192 if (isMutable)
michael@0 193 mFlags |= NS_MATHML_OPERATOR_MUTABLE;
michael@0 194
michael@0 195 ResolveMathMLCharStyle(presContext, mContent, mStyleContext, &mMathMLChar);
michael@0 196 }
michael@0 197
michael@0 198 // get our 'form' and lookup in the Operator Dictionary to fetch
michael@0 199 // our default data that may come from there. Then complete our setup
michael@0 200 // using attributes that we may have. To stay in sync, this function is
michael@0 201 // called very often. We depend on many things that may change around us.
michael@0 202 // However, we re-use unchanged values.
michael@0 203 void
michael@0 204 nsMathMLmoFrame::ProcessOperatorData()
michael@0 205 {
michael@0 206 // if we have been here before, we will just use our cached form
michael@0 207 nsOperatorFlags form = NS_MATHML_OPERATOR_GET_FORM(mFlags);
michael@0 208 nsAutoString value;
michael@0 209
michael@0 210 // special bits are always kept in mFlags.
michael@0 211 // remember the mutable bit from ProcessTextData().
michael@0 212 // Some chars are listed under different forms in the dictionary,
michael@0 213 // and there could be a form under which the char is mutable.
michael@0 214 // If the char is the core of an embellished container, we will keep
michael@0 215 // it mutable irrespective of the form of the embellished container.
michael@0 216 // Also remember the other special bits that we want to carry forward.
michael@0 217 mFlags &= NS_MATHML_OPERATOR_MUTABLE |
michael@0 218 NS_MATHML_OPERATOR_ACCENT |
michael@0 219 NS_MATHML_OPERATOR_MOVABLELIMITS |
michael@0 220 NS_MATHML_OPERATOR_CENTERED |
michael@0 221 NS_MATHML_OPERATOR_INVISIBLE;
michael@0 222
michael@0 223 if (!mEmbellishData.coreFrame) {
michael@0 224 // i.e., we haven't been here before, the default form is infix
michael@0 225 form = NS_MATHML_OPERATOR_FORM_INFIX;
michael@0 226
michael@0 227 // reset everything so that we don't keep outdated values around
michael@0 228 // in case of dynamic changes
michael@0 229 mEmbellishData.flags = 0;
michael@0 230 mEmbellishData.coreFrame = nullptr;
michael@0 231 mEmbellishData.leadingSpace = 0;
michael@0 232 mEmbellishData.trailingSpace = 0;
michael@0 233 if (mMathMLChar.Length() != 1)
michael@0 234 mEmbellishData.direction = NS_STRETCH_DIRECTION_UNSUPPORTED;
michael@0 235 // else... retain the native direction obtained in ProcessTextData()
michael@0 236
michael@0 237 if (!mFrames.FirstChild()) {
michael@0 238 return;
michael@0 239 }
michael@0 240
michael@0 241 mEmbellishData.flags |= NS_MATHML_EMBELLISH_OPERATOR;
michael@0 242 mEmbellishData.coreFrame = this;
michael@0 243
michael@0 244 // there are two particular things that we also need to record so that if our
michael@0 245 // parent is <mover>, <munder>, or <munderover>, they will treat us properly:
michael@0 246 // 1) do we have accent="true"
michael@0 247 // 2) do we have movablelimits="true"
michael@0 248
michael@0 249 // they need the extra information to decide how to treat their scripts/limits
michael@0 250 // (note: <mover>, <munder>, or <munderover> need not necessarily be our
michael@0 251 // direct parent -- case of embellished operators)
michael@0 252
michael@0 253 // default values from the Operator Dictionary were obtained in ProcessTextData()
michael@0 254 // and these special bits are always kept in mFlags
michael@0 255 if (NS_MATHML_OPERATOR_IS_ACCENT(mFlags))
michael@0 256 mEmbellishData.flags |= NS_MATHML_EMBELLISH_ACCENT;
michael@0 257 if (NS_MATHML_OPERATOR_IS_MOVABLELIMITS(mFlags))
michael@0 258 mEmbellishData.flags |= NS_MATHML_EMBELLISH_MOVABLELIMITS;
michael@0 259
michael@0 260 // see if the accent attribute is there
michael@0 261 mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::accent_, value);
michael@0 262 if (value.EqualsLiteral("true"))
michael@0 263 mEmbellishData.flags |= NS_MATHML_EMBELLISH_ACCENT;
michael@0 264 else if (value.EqualsLiteral("false"))
michael@0 265 mEmbellishData.flags &= ~NS_MATHML_EMBELLISH_ACCENT;
michael@0 266
michael@0 267 // see if the movablelimits attribute is there
michael@0 268 mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::movablelimits_, value);
michael@0 269 if (value.EqualsLiteral("true"))
michael@0 270 mEmbellishData.flags |= NS_MATHML_EMBELLISH_MOVABLELIMITS;
michael@0 271 else if (value.EqualsLiteral("false"))
michael@0 272 mEmbellishData.flags &= ~NS_MATHML_EMBELLISH_MOVABLELIMITS;
michael@0 273
michael@0 274 // ---------------------------------------------------------------------
michael@0 275 // we will be called again to re-sync the rest of our state next time...
michael@0 276 // (nobody needs the other values below at this stage)
michael@0 277 mFlags |= form;
michael@0 278 return;
michael@0 279 }
michael@0 280
michael@0 281 nsPresContext* presContext = PresContext();
michael@0 282
michael@0 283 // beware of bug 133814 - there is a two-way dependency in the
michael@0 284 // embellished hierarchy: our embellished ancestors need to set
michael@0 285 // their flags based on some of our state (set above), and here we
michael@0 286 // need to re-sync our 'form' depending on our outermost embellished
michael@0 287 // container. A null form here means that an earlier attempt to stretch
michael@0 288 // our mMathMLChar failed, in which case we don't bother re-stretching again
michael@0 289 if (form) {
michael@0 290 // get our outermost embellished container and its parent.
michael@0 291 // (we ensure that we are the core, not just a sibling of the core)
michael@0 292 nsIFrame* embellishAncestor = this;
michael@0 293 nsEmbellishData embellishData;
michael@0 294 nsIFrame* parentAncestor = this;
michael@0 295 do {
michael@0 296 embellishAncestor = parentAncestor;
michael@0 297 parentAncestor = embellishAncestor->GetParent();
michael@0 298 GetEmbellishDataFrom(parentAncestor, embellishData);
michael@0 299 } while (embellishData.coreFrame == this);
michael@0 300
michael@0 301 // flag if we have an embellished ancestor
michael@0 302 if (embellishAncestor != this)
michael@0 303 mFlags |= NS_MATHML_OPERATOR_EMBELLISH_ANCESTOR;
michael@0 304 else
michael@0 305 mFlags &= ~NS_MATHML_OPERATOR_EMBELLISH_ANCESTOR;
michael@0 306
michael@0 307 // find the position of our outermost embellished container w.r.t
michael@0 308 // its siblings.
michael@0 309
michael@0 310 nsIFrame* nextSibling = embellishAncestor->GetNextSibling();
michael@0 311 nsIFrame* prevSibling = embellishAncestor->GetPrevSibling();
michael@0 312
michael@0 313 // flag to distinguish from a real infix. Set for (embellished) operators
michael@0 314 // that live in (inferred) mrows.
michael@0 315 nsIMathMLFrame* mathAncestor = do_QueryFrame(parentAncestor);
michael@0 316 bool zeroSpacing = false;
michael@0 317 if (mathAncestor) {
michael@0 318 zeroSpacing = !mathAncestor->IsMrowLike();
michael@0 319 } else {
michael@0 320 nsMathMLmathBlockFrame* blockFrame = do_QueryFrame(parentAncestor);
michael@0 321 if (blockFrame) {
michael@0 322 zeroSpacing = !blockFrame->IsMrowLike();
michael@0 323 }
michael@0 324 }
michael@0 325 if (zeroSpacing) {
michael@0 326 mFlags |= NS_MATHML_OPERATOR_EMBELLISH_ISOLATED;
michael@0 327 } else {
michael@0 328 mFlags &= ~NS_MATHML_OPERATOR_EMBELLISH_ISOLATED;
michael@0 329 }
michael@0 330
michael@0 331 // find our form
michael@0 332 form = NS_MATHML_OPERATOR_FORM_INFIX;
michael@0 333 mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::form, value);
michael@0 334 if (!value.IsEmpty()) {
michael@0 335 if (value.EqualsLiteral("prefix"))
michael@0 336 form = NS_MATHML_OPERATOR_FORM_PREFIX;
michael@0 337 else if (value.EqualsLiteral("postfix"))
michael@0 338 form = NS_MATHML_OPERATOR_FORM_POSTFIX;
michael@0 339 }
michael@0 340 else {
michael@0 341 // set our form flag depending on the position
michael@0 342 if (!prevSibling && nextSibling)
michael@0 343 form = NS_MATHML_OPERATOR_FORM_PREFIX;
michael@0 344 else if (prevSibling && !nextSibling)
michael@0 345 form = NS_MATHML_OPERATOR_FORM_POSTFIX;
michael@0 346 }
michael@0 347 mFlags &= ~NS_MATHML_OPERATOR_FORM; // clear the old form bits
michael@0 348 mFlags |= form;
michael@0 349
michael@0 350 // Use the default value suggested by the MathML REC.
michael@0 351 // http://www.w3.org/TR/MathML/chapter3.html#presm.mo.attrs
michael@0 352 // thickmathspace = 5/18em
michael@0 353 float lspace = 5.0f/18.0f;
michael@0 354 float rspace = 5.0f/18.0f;
michael@0 355 // lookup the operator dictionary
michael@0 356 nsAutoString data;
michael@0 357 mMathMLChar.GetData(data);
michael@0 358 nsMathMLOperators::LookupOperator(data, form, &mFlags, &lspace, &rspace);
michael@0 359 // Spacing is zero if our outermost embellished operator is not in an
michael@0 360 // inferred mrow.
michael@0 361 if (!NS_MATHML_OPERATOR_EMBELLISH_IS_ISOLATED(mFlags) &&
michael@0 362 (lspace || rspace)) {
michael@0 363 // Cache the default values of lspace and rspace.
michael@0 364 // since these values are relative to the 'em' unit, convert to twips now
michael@0 365 nscoord em;
michael@0 366 nsRefPtr<nsFontMetrics> fm;
michael@0 367 nsLayoutUtils::GetFontMetricsForFrame(this, getter_AddRefs(fm));
michael@0 368 GetEmHeight(fm, em);
michael@0 369
michael@0 370 mEmbellishData.leadingSpace = NSToCoordRound(lspace * em);
michael@0 371 mEmbellishData.trailingSpace = NSToCoordRound(rspace * em);
michael@0 372
michael@0 373 // tuning if we don't want too much extra space when we are a script.
michael@0 374 // (with its fonts, TeX sets lspace=0 & rspace=0 as soon as scriptlevel>0.
michael@0 375 // Our fonts can be anything, so...)
michael@0 376 if (StyleFont()->mScriptLevel > 0 &&
michael@0 377 !NS_MATHML_OPERATOR_HAS_EMBELLISH_ANCESTOR(mFlags)) {
michael@0 378 mEmbellishData.leadingSpace /= 2;
michael@0 379 mEmbellishData.trailingSpace /= 2;
michael@0 380 }
michael@0 381 }
michael@0 382 }
michael@0 383
michael@0 384 // If we are an accent without explicit lspace="." or rspace=".",
michael@0 385 // we will ignore our default leading/trailing space
michael@0 386
michael@0 387 // lspace
michael@0 388 //
michael@0 389 // "Specifies the leading space appearing before the operator"
michael@0 390 //
michael@0 391 // values: length
michael@0 392 // default: set by dictionary (thickmathspace)
michael@0 393 //
michael@0 394 // XXXfredw Support for negative and relative values is not implemented
michael@0 395 // (bug 805926).
michael@0 396 // Relative values will give a multiple of the current leading space,
michael@0 397 // which is not necessarily the default one.
michael@0 398 //
michael@0 399 nscoord leadingSpace = mEmbellishData.leadingSpace;
michael@0 400 mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::lspace_, value);
michael@0 401 if (!value.IsEmpty()) {
michael@0 402 nsCSSValue cssValue;
michael@0 403 if (nsMathMLElement::ParseNumericValue(value, cssValue, 0,
michael@0 404 mContent->OwnerDoc())) {
michael@0 405 if ((eCSSUnit_Number == cssValue.GetUnit()) && !cssValue.GetFloatValue())
michael@0 406 leadingSpace = 0;
michael@0 407 else if (cssValue.IsLengthUnit())
michael@0 408 leadingSpace = CalcLength(presContext, mStyleContext, cssValue);
michael@0 409 mFlags |= NS_MATHML_OPERATOR_LSPACE_ATTR;
michael@0 410 }
michael@0 411 }
michael@0 412
michael@0 413 // rspace
michael@0 414 //
michael@0 415 // "Specifies the trailing space appearing after the operator"
michael@0 416 //
michael@0 417 // values: length
michael@0 418 // default: set by dictionary (thickmathspace)
michael@0 419 //
michael@0 420 // XXXfredw Support for negative and relative values is not implemented
michael@0 421 // (bug 805926).
michael@0 422 // Relative values will give a multiple of the current leading space,
michael@0 423 // which is not necessarily the default one.
michael@0 424 //
michael@0 425 nscoord trailingSpace = mEmbellishData.trailingSpace;
michael@0 426 mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::rspace_, value);
michael@0 427 if (!value.IsEmpty()) {
michael@0 428 nsCSSValue cssValue;
michael@0 429 if (nsMathMLElement::ParseNumericValue(value, cssValue, 0,
michael@0 430 mContent->OwnerDoc())) {
michael@0 431 if ((eCSSUnit_Number == cssValue.GetUnit()) && !cssValue.GetFloatValue())
michael@0 432 trailingSpace = 0;
michael@0 433 else if (cssValue.IsLengthUnit())
michael@0 434 trailingSpace = CalcLength(presContext, mStyleContext, cssValue);
michael@0 435 mFlags |= NS_MATHML_OPERATOR_RSPACE_ATTR;
michael@0 436 }
michael@0 437 }
michael@0 438
michael@0 439 // little extra tuning to round lspace & rspace to at least a pixel so that
michael@0 440 // operators don't look as if they are colliding with their operands
michael@0 441 if (leadingSpace || trailingSpace) {
michael@0 442 nscoord onePixel = nsPresContext::CSSPixelsToAppUnits(1);
michael@0 443 if (leadingSpace && leadingSpace < onePixel)
michael@0 444 leadingSpace = onePixel;
michael@0 445 if (trailingSpace && trailingSpace < onePixel)
michael@0 446 trailingSpace = onePixel;
michael@0 447 }
michael@0 448
michael@0 449 // the values that we get from our attributes override the dictionary
michael@0 450 mEmbellishData.leadingSpace = leadingSpace;
michael@0 451 mEmbellishData.trailingSpace = trailingSpace;
michael@0 452
michael@0 453 // Now see if there are user-defined attributes that override the dictionary.
michael@0 454 // XXX If an attribute can be forced to be true when it is false in the
michael@0 455 // dictionary, then the following code has to change...
michael@0 456
michael@0 457 // For each attribute overriden by the user, turn off its bit flag.
michael@0 458 // symmetric|movablelimits|separator|largeop|accent|fence|stretchy|form
michael@0 459 // special: accent and movablelimits are handled above,
michael@0 460 // don't process them here
michael@0 461
michael@0 462 mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::stretchy_, value);
michael@0 463 if (value.EqualsLiteral("false")) {
michael@0 464 mFlags &= ~NS_MATHML_OPERATOR_STRETCHY;
michael@0 465 } else if (value.EqualsLiteral("true")) {
michael@0 466 mFlags |= NS_MATHML_OPERATOR_STRETCHY;
michael@0 467 }
michael@0 468 if (NS_MATHML_OPERATOR_IS_FENCE(mFlags)) {
michael@0 469 mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::fence_, value);
michael@0 470 if (value.EqualsLiteral("false"))
michael@0 471 mFlags &= ~NS_MATHML_OPERATOR_FENCE;
michael@0 472 }
michael@0 473 mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::largeop_, value);
michael@0 474 if (value.EqualsLiteral("false")) {
michael@0 475 mFlags &= ~NS_MATHML_OPERATOR_LARGEOP;
michael@0 476 } else if (value.EqualsLiteral("true")) {
michael@0 477 mFlags |= NS_MATHML_OPERATOR_LARGEOP;
michael@0 478 }
michael@0 479 if (NS_MATHML_OPERATOR_IS_SEPARATOR(mFlags)) {
michael@0 480 mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::separator_, value);
michael@0 481 if (value.EqualsLiteral("false"))
michael@0 482 mFlags &= ~NS_MATHML_OPERATOR_SEPARATOR;
michael@0 483 }
michael@0 484 mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::symmetric_, value);
michael@0 485 if (value.EqualsLiteral("false"))
michael@0 486 mFlags &= ~NS_MATHML_OPERATOR_SYMMETRIC;
michael@0 487 else if (value.EqualsLiteral("true"))
michael@0 488 mFlags |= NS_MATHML_OPERATOR_SYMMETRIC;
michael@0 489
michael@0 490
michael@0 491 // minsize
michael@0 492 //
michael@0 493 // "Specifies the minimum size of the operator when stretchy"
michael@0 494 //
michael@0 495 // values: length
michael@0 496 // default: set by dictionary (1em)
michael@0 497 //
michael@0 498 // We don't allow negative values.
michael@0 499 // Note: Contrary to other "length" values, unitless and percentage do not
michael@0 500 // give a multiple of the defaut value but a multiple of the operator at
michael@0 501 // normal size.
michael@0 502 //
michael@0 503 mMinSize = 0;
michael@0 504 mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::minsize_, value);
michael@0 505 if (!value.IsEmpty()) {
michael@0 506 nsCSSValue cssValue;
michael@0 507 if (nsMathMLElement::ParseNumericValue(value, cssValue,
michael@0 508 nsMathMLElement::
michael@0 509 PARSE_ALLOW_UNITLESS,
michael@0 510 mContent->OwnerDoc())) {
michael@0 511 nsCSSUnit unit = cssValue.GetUnit();
michael@0 512 if (eCSSUnit_Number == unit)
michael@0 513 mMinSize = cssValue.GetFloatValue();
michael@0 514 else if (eCSSUnit_Percent == unit)
michael@0 515 mMinSize = cssValue.GetPercentValue();
michael@0 516 else if (eCSSUnit_Null != unit) {
michael@0 517 mMinSize = float(CalcLength(presContext, mStyleContext, cssValue));
michael@0 518 mFlags |= NS_MATHML_OPERATOR_MINSIZE_ABSOLUTE;
michael@0 519 }
michael@0 520 }
michael@0 521 }
michael@0 522
michael@0 523 // maxsize
michael@0 524 //
michael@0 525 // "Specifies the maximum size of the operator when stretchy"
michael@0 526 //
michael@0 527 // values: length | "infinity"
michael@0 528 // default: set by dictionary (infinity)
michael@0 529 //
michael@0 530 // We don't allow negative values.
michael@0 531 // Note: Contrary to other "length" values, unitless and percentage do not
michael@0 532 // give a multiple of the defaut value but a multiple of the operator at
michael@0 533 // normal size.
michael@0 534 //
michael@0 535 mMaxSize = NS_MATHML_OPERATOR_SIZE_INFINITY;
michael@0 536 mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::maxsize_, value);
michael@0 537 if (!value.IsEmpty()) {
michael@0 538 nsCSSValue cssValue;
michael@0 539 if (nsMathMLElement::ParseNumericValue(value, cssValue,
michael@0 540 nsMathMLElement::
michael@0 541 PARSE_ALLOW_UNITLESS,
michael@0 542 mContent->OwnerDoc())) {
michael@0 543 nsCSSUnit unit = cssValue.GetUnit();
michael@0 544 if (eCSSUnit_Number == unit)
michael@0 545 mMaxSize = cssValue.GetFloatValue();
michael@0 546 else if (eCSSUnit_Percent == unit)
michael@0 547 mMaxSize = cssValue.GetPercentValue();
michael@0 548 else if (eCSSUnit_Null != unit) {
michael@0 549 mMaxSize = float(CalcLength(presContext, mStyleContext, cssValue));
michael@0 550 mFlags |= NS_MATHML_OPERATOR_MAXSIZE_ABSOLUTE;
michael@0 551 }
michael@0 552 }
michael@0 553 }
michael@0 554 }
michael@0 555
michael@0 556 static uint32_t
michael@0 557 GetStretchHint(nsOperatorFlags aFlags, nsPresentationData aPresentationData,
michael@0 558 bool aIsVertical, const nsStyleFont* aStyleFont)
michael@0 559 {
michael@0 560 uint32_t stretchHint = NS_STRETCH_NONE;
michael@0 561 // See if it is okay to stretch,
michael@0 562 // starting from what the Operator Dictionary said
michael@0 563 if (NS_MATHML_OPERATOR_IS_MUTABLE(aFlags)) {
michael@0 564 // set the largeop or largeopOnly flags to suitably cover all the
michael@0 565 // 8 possible cases depending on whether displaystyle, largeop,
michael@0 566 // stretchy are true or false (see bug 69325).
michael@0 567 // . largeopOnly is taken if largeop=true and stretchy=false
michael@0 568 // . largeop is taken if largeop=true and stretchy=true
michael@0 569 if (aStyleFont->mMathDisplay == NS_MATHML_DISPLAYSTYLE_BLOCK &&
michael@0 570 NS_MATHML_OPERATOR_IS_LARGEOP(aFlags)) {
michael@0 571 stretchHint = NS_STRETCH_LARGEOP; // (largeopOnly, not mask!)
michael@0 572 if (NS_MATHML_OPERATOR_IS_INTEGRAL(aFlags)) {
michael@0 573 stretchHint |= NS_STRETCH_INTEGRAL;
michael@0 574 }
michael@0 575 if (NS_MATHML_OPERATOR_IS_STRETCHY(aFlags)) {
michael@0 576 stretchHint |= NS_STRETCH_NEARER | NS_STRETCH_LARGER;
michael@0 577 }
michael@0 578 }
michael@0 579 else if(NS_MATHML_OPERATOR_IS_STRETCHY(aFlags)) {
michael@0 580 if (aIsVertical) {
michael@0 581 // TeX hint. Can impact some sloppy markups missing <mrow></mrow>
michael@0 582 stretchHint = NS_STRETCH_NEARER;
michael@0 583 }
michael@0 584 else {
michael@0 585 stretchHint = NS_STRETCH_NORMAL;
michael@0 586 }
michael@0 587 }
michael@0 588 // else if the stretchy and largeop attributes have been disabled,
michael@0 589 // the operator is not mutable
michael@0 590 }
michael@0 591 return stretchHint;
michael@0 592 }
michael@0 593
michael@0 594 // NOTE: aDesiredStretchSize is an IN/OUT parameter
michael@0 595 // On input - it contains our current size
michael@0 596 // On output - the same size or the new size that we want
michael@0 597 NS_IMETHODIMP
michael@0 598 nsMathMLmoFrame::Stretch(nsRenderingContext& aRenderingContext,
michael@0 599 nsStretchDirection aStretchDirection,
michael@0 600 nsBoundingMetrics& aContainerSize,
michael@0 601 nsHTMLReflowMetrics& aDesiredStretchSize)
michael@0 602 {
michael@0 603 if (NS_MATHML_STRETCH_WAS_DONE(mPresentationData.flags)) {
michael@0 604 NS_WARNING("it is wrong to fire stretch more than once on a frame");
michael@0 605 return NS_OK;
michael@0 606 }
michael@0 607 mPresentationData.flags |= NS_MATHML_STRETCH_DONE;
michael@0 608
michael@0 609 nsIFrame* firstChild = mFrames.FirstChild();
michael@0 610
michael@0 611 // get the axis height;
michael@0 612 nsRefPtr<nsFontMetrics> fm;
michael@0 613 nsLayoutUtils::GetFontMetricsForFrame(this, getter_AddRefs(fm));
michael@0 614 aRenderingContext.SetFont(fm);
michael@0 615 nscoord axisHeight, height;
michael@0 616 GetAxisHeight(aRenderingContext, fm, axisHeight);
michael@0 617
michael@0 618 // get the leading to be left at the top and the bottom of the stretched char
michael@0 619 // this seems more reliable than using fm->GetLeading() on suspicious fonts
michael@0 620 nscoord em;
michael@0 621 GetEmHeight(fm, em);
michael@0 622 nscoord leading = NSToCoordRound(0.2f * em);
michael@0 623
michael@0 624 // Operators that are stretchy, or those that are to be centered
michael@0 625 // to cater for fonts that are not math-aware, are handled by the MathMLChar
michael@0 626 // ('form' is reset if stretch fails -- i.e., we don't bother to stretch next time)
michael@0 627 bool useMathMLChar = UseMathMLChar();
michael@0 628
michael@0 629 nsBoundingMetrics charSize;
michael@0 630 nsBoundingMetrics container = aDesiredStretchSize.mBoundingMetrics;
michael@0 631 bool isVertical = false;
michael@0 632
michael@0 633 if (((aStretchDirection == NS_STRETCH_DIRECTION_VERTICAL) ||
michael@0 634 (aStretchDirection == NS_STRETCH_DIRECTION_DEFAULT)) &&
michael@0 635 (mEmbellishData.direction == NS_STRETCH_DIRECTION_VERTICAL)) {
michael@0 636 isVertical = true;
michael@0 637 }
michael@0 638
michael@0 639 uint32_t stretchHint =
michael@0 640 GetStretchHint(mFlags, mPresentationData, isVertical, StyleFont());
michael@0 641
michael@0 642 if (useMathMLChar) {
michael@0 643 nsBoundingMetrics initialSize = aDesiredStretchSize.mBoundingMetrics;
michael@0 644
michael@0 645 if (stretchHint != NS_STRETCH_NONE) {
michael@0 646
michael@0 647 container = aContainerSize;
michael@0 648
michael@0 649 // some adjustments if the operator is symmetric and vertical
michael@0 650
michael@0 651 if (isVertical && NS_MATHML_OPERATOR_IS_SYMMETRIC(mFlags)) {
michael@0 652 // we need to center about the axis
michael@0 653 nscoord delta = std::max(container.ascent - axisHeight,
michael@0 654 container.descent + axisHeight);
michael@0 655 container.ascent = delta + axisHeight;
michael@0 656 container.descent = delta - axisHeight;
michael@0 657
michael@0 658 // get ready in case we encounter user-desired min-max size
michael@0 659 delta = std::max(initialSize.ascent - axisHeight,
michael@0 660 initialSize.descent + axisHeight);
michael@0 661 initialSize.ascent = delta + axisHeight;
michael@0 662 initialSize.descent = delta - axisHeight;
michael@0 663 }
michael@0 664
michael@0 665 // check for user-desired min-max size
michael@0 666
michael@0 667 if (mMaxSize != NS_MATHML_OPERATOR_SIZE_INFINITY && mMaxSize > 0.0f) {
michael@0 668 // if we are here, there is a user defined maxsize ...
michael@0 669 //XXX Set stretchHint = NS_STRETCH_NORMAL? to honor the maxsize as close as possible?
michael@0 670 if (NS_MATHML_OPERATOR_MAXSIZE_IS_ABSOLUTE(mFlags)) {
michael@0 671 // there is an explicit value like maxsize="20pt"
michael@0 672 // try to maintain the aspect ratio of the char
michael@0 673 float aspect = mMaxSize / float(initialSize.ascent + initialSize.descent);
michael@0 674 container.ascent =
michael@0 675 std::min(container.ascent, nscoord(initialSize.ascent * aspect));
michael@0 676 container.descent =
michael@0 677 std::min(container.descent, nscoord(initialSize.descent * aspect));
michael@0 678 // below we use a type cast instead of a conversion to avoid a VC++ bug
michael@0 679 // see http://support.microsoft.com/support/kb/articles/Q115/7/05.ASP
michael@0 680 container.width =
michael@0 681 std::min(container.width, (nscoord)mMaxSize);
michael@0 682 }
michael@0 683 else { // multiplicative value
michael@0 684 container.ascent =
michael@0 685 std::min(container.ascent, nscoord(initialSize.ascent * mMaxSize));
michael@0 686 container.descent =
michael@0 687 std::min(container.descent, nscoord(initialSize.descent * mMaxSize));
michael@0 688 container.width =
michael@0 689 std::min(container.width, nscoord(initialSize.width * mMaxSize));
michael@0 690 }
michael@0 691
michael@0 692 if (isVertical && !NS_MATHML_OPERATOR_IS_SYMMETRIC(mFlags)) {
michael@0 693 // re-adjust to align the char with the bottom of the initial container
michael@0 694 height = container.ascent + container.descent;
michael@0 695 container.descent = aContainerSize.descent;
michael@0 696 container.ascent = height - container.descent;
michael@0 697 }
michael@0 698 }
michael@0 699
michael@0 700 if (mMinSize > 0.0f) {
michael@0 701 // if we are here, there is a user defined minsize ...
michael@0 702 // always allow the char to stretch in its natural direction,
michael@0 703 // even if it is different from the caller's direction
michael@0 704 if (aStretchDirection != NS_STRETCH_DIRECTION_DEFAULT &&
michael@0 705 aStretchDirection != mEmbellishData.direction) {
michael@0 706 aStretchDirection = NS_STRETCH_DIRECTION_DEFAULT;
michael@0 707 // but when we are not honoring the requested direction
michael@0 708 // we should not use the caller's container size either
michael@0 709 container = initialSize;
michael@0 710 }
michael@0 711 if (NS_MATHML_OPERATOR_MINSIZE_IS_ABSOLUTE(mFlags)) {
michael@0 712 // there is an explicit value like minsize="20pt"
michael@0 713 // try to maintain the aspect ratio of the char
michael@0 714 float aspect = mMinSize / float(initialSize.ascent + initialSize.descent);
michael@0 715 container.ascent =
michael@0 716 std::max(container.ascent, nscoord(initialSize.ascent * aspect));
michael@0 717 container.descent =
michael@0 718 std::max(container.descent, nscoord(initialSize.descent * aspect));
michael@0 719 container.width =
michael@0 720 std::max(container.width, (nscoord)mMinSize);
michael@0 721 }
michael@0 722 else { // multiplicative value
michael@0 723 container.ascent =
michael@0 724 std::max(container.ascent, nscoord(initialSize.ascent * mMinSize));
michael@0 725 container.descent =
michael@0 726 std::max(container.descent, nscoord(initialSize.descent * mMinSize));
michael@0 727 container.width =
michael@0 728 std::max(container.width, nscoord(initialSize.width * mMinSize));
michael@0 729 }
michael@0 730
michael@0 731 if (isVertical && !NS_MATHML_OPERATOR_IS_SYMMETRIC(mFlags)) {
michael@0 732 // re-adjust to align the char with the bottom of the initial container
michael@0 733 height = container.ascent + container.descent;
michael@0 734 container.descent = aContainerSize.descent;
michael@0 735 container.ascent = height - container.descent;
michael@0 736 }
michael@0 737 }
michael@0 738 }
michael@0 739
michael@0 740 // let the MathMLChar stretch itself...
michael@0 741 nsresult res = mMathMLChar.Stretch(PresContext(), aRenderingContext,
michael@0 742 aStretchDirection, container, charSize,
michael@0 743 stretchHint,
michael@0 744 StyleVisibility()->mDirection);
michael@0 745 if (NS_FAILED(res)) {
michael@0 746 // gracefully handle cases where stretching the char failed (i.e., GetBoundingMetrics failed)
michael@0 747 // clear our 'form' to behave as if the operator wasn't in the dictionary
michael@0 748 mFlags &= ~NS_MATHML_OPERATOR_FORM;
michael@0 749 useMathMLChar = false;
michael@0 750 }
michael@0 751 }
michael@0 752
michael@0 753 // Place our children using the default method
michael@0 754 // This will allow our child text frame to get its DidReflow()
michael@0 755 nsresult rv = Place(aRenderingContext, true, aDesiredStretchSize);
michael@0 756 if (NS_MATHML_HAS_ERROR(mPresentationData.flags) || NS_FAILED(rv)) {
michael@0 757 // Make sure the child frames get their DidReflow() calls.
michael@0 758 DidReflowChildren(mFrames.FirstChild());
michael@0 759 }
michael@0 760
michael@0 761 if (useMathMLChar) {
michael@0 762 // update our bounding metrics... it becomes that of our MathML char
michael@0 763 mBoundingMetrics = charSize;
michael@0 764
michael@0 765 // if the returned direction is 'unsupported', the char didn't actually change.
michael@0 766 // So we do the centering only if necessary
michael@0 767 if (mMathMLChar.GetStretchDirection() != NS_STRETCH_DIRECTION_UNSUPPORTED ||
michael@0 768 NS_MATHML_OPERATOR_IS_CENTERED(mFlags)) {
michael@0 769
michael@0 770 bool largeopOnly =
michael@0 771 (NS_STRETCH_LARGEOP & stretchHint) != 0 &&
michael@0 772 (NS_STRETCH_VARIABLE_MASK & stretchHint) == 0;
michael@0 773
michael@0 774 if (isVertical || NS_MATHML_OPERATOR_IS_CENTERED(mFlags)) {
michael@0 775 // the desired size returned by mMathMLChar maybe different
michael@0 776 // from the size of the container.
michael@0 777 // the mMathMLChar.mRect.y calculation is subtle, watch out!!!
michael@0 778
michael@0 779 height = mBoundingMetrics.ascent + mBoundingMetrics.descent;
michael@0 780 if (NS_MATHML_OPERATOR_IS_SYMMETRIC(mFlags) ||
michael@0 781 NS_MATHML_OPERATOR_IS_CENTERED(mFlags)) {
michael@0 782 // For symmetric and vertical operators, or for operators that are always
michael@0 783 // centered ('+', '*', etc) we want to center about the axis of the container
michael@0 784 mBoundingMetrics.descent = height/2 - axisHeight;
michael@0 785 } else if (!largeopOnly) {
michael@0 786 // Align the center of the char with the center of the container
michael@0 787 mBoundingMetrics.descent = height/2 +
michael@0 788 (container.ascent + container.descent)/2 - container.ascent;
michael@0 789 } // else align the baselines
michael@0 790 mBoundingMetrics.ascent = height - mBoundingMetrics.descent;
michael@0 791 }
michael@0 792 }
michael@0 793 }
michael@0 794
michael@0 795 // Fixup for the final height.
michael@0 796 // On one hand, our stretchy height can sometimes be shorter than surrounding
michael@0 797 // ASCII chars, e.g., arrow symbols have |mBoundingMetrics.ascent + leading|
michael@0 798 // that is smaller than the ASCII's ascent, hence when painting the background
michael@0 799 // later, it won't look uniform along the line.
michael@0 800 // On the other hand, sometimes we may leave too much gap when our glyph happens
michael@0 801 // to come from a font with tall glyphs. For example, since CMEX10 has very tall
michael@0 802 // glyphs, its natural font metrics are large, even if we pick a small glyph
michael@0 803 // whose size is comparable to the size of a normal ASCII glyph.
michael@0 804 // So to avoid uneven spacing in either of these two cases, we use the height
michael@0 805 // of the ASCII font as a reference and try to match it if possible.
michael@0 806
michael@0 807 // special case for accents... keep them short to improve mouse operations...
michael@0 808 // an accent can only be the non-first child of <mover>, <munder>, <munderover>
michael@0 809 bool isAccent =
michael@0 810 NS_MATHML_EMBELLISH_IS_ACCENT(mEmbellishData.flags);
michael@0 811 if (isAccent) {
michael@0 812 nsEmbellishData parentData;
michael@0 813 GetEmbellishDataFrom(mParent, parentData);
michael@0 814 isAccent =
michael@0 815 (NS_MATHML_EMBELLISH_IS_ACCENTOVER(parentData.flags) ||
michael@0 816 NS_MATHML_EMBELLISH_IS_ACCENTUNDER(parentData.flags)) &&
michael@0 817 parentData.coreFrame != this;
michael@0 818 }
michael@0 819 if (isAccent && firstChild) {
michael@0 820 // see bug 188467 for what is going on here
michael@0 821 nscoord dy = aDesiredStretchSize.TopAscent() - (mBoundingMetrics.ascent + leading);
michael@0 822 aDesiredStretchSize.SetTopAscent(mBoundingMetrics.ascent + leading);
michael@0 823 aDesiredStretchSize.Height() = aDesiredStretchSize.TopAscent() + mBoundingMetrics.descent;
michael@0 824
michael@0 825 firstChild->SetPosition(firstChild->GetPosition() - nsPoint(0, dy));
michael@0 826 }
michael@0 827 else if (useMathMLChar) {
michael@0 828 nscoord ascent = fm->MaxAscent();
michael@0 829 nscoord descent = fm->MaxDescent();
michael@0 830 aDesiredStretchSize.SetTopAscent(std::max(mBoundingMetrics.ascent + leading, ascent));
michael@0 831 aDesiredStretchSize.Height() = aDesiredStretchSize.TopAscent() +
michael@0 832 std::max(mBoundingMetrics.descent + leading, descent);
michael@0 833 }
michael@0 834 aDesiredStretchSize.Width() = mBoundingMetrics.width;
michael@0 835 aDesiredStretchSize.mBoundingMetrics = mBoundingMetrics;
michael@0 836 mReference.x = 0;
michael@0 837 mReference.y = aDesiredStretchSize.TopAscent();
michael@0 838 // Place our mMathMLChar, its origin is in our coordinate system
michael@0 839 if (useMathMLChar) {
michael@0 840 nscoord dy = aDesiredStretchSize.TopAscent() - mBoundingMetrics.ascent;
michael@0 841 mMathMLChar.SetRect(nsRect(0, dy, charSize.width, charSize.ascent + charSize.descent));
michael@0 842 }
michael@0 843
michael@0 844 // Before we leave... there is a last item in the check-list:
michael@0 845 // If our parent is not embellished, it means we are the outermost embellished
michael@0 846 // container and so we put the spacing, otherwise we don't include the spacing,
michael@0 847 // the outermost embellished container will take care of it.
michael@0 848
michael@0 849 if (!NS_MATHML_OPERATOR_HAS_EMBELLISH_ANCESTOR(mFlags)) {
michael@0 850
michael@0 851 // Account the spacing if we are not an accent with explicit attributes
michael@0 852 nscoord leadingSpace = mEmbellishData.leadingSpace;
michael@0 853 if (isAccent && !NS_MATHML_OPERATOR_HAS_LSPACE_ATTR(mFlags)) {
michael@0 854 leadingSpace = 0;
michael@0 855 }
michael@0 856 nscoord trailingSpace = mEmbellishData.trailingSpace;
michael@0 857 if (isAccent && !NS_MATHML_OPERATOR_HAS_RSPACE_ATTR(mFlags)) {
michael@0 858 trailingSpace = 0;
michael@0 859 }
michael@0 860
michael@0 861 mBoundingMetrics.width += leadingSpace + trailingSpace;
michael@0 862 aDesiredStretchSize.Width() = mBoundingMetrics.width;
michael@0 863 aDesiredStretchSize.mBoundingMetrics.width = mBoundingMetrics.width;
michael@0 864
michael@0 865 nscoord dx = (StyleVisibility()->mDirection ?
michael@0 866 trailingSpace : leadingSpace);
michael@0 867 if (dx) {
michael@0 868 // adjust the offsets
michael@0 869 mBoundingMetrics.leftBearing += dx;
michael@0 870 mBoundingMetrics.rightBearing += dx;
michael@0 871 aDesiredStretchSize.mBoundingMetrics.leftBearing += dx;
michael@0 872 aDesiredStretchSize.mBoundingMetrics.rightBearing += dx;
michael@0 873
michael@0 874 if (useMathMLChar) {
michael@0 875 nsRect rect;
michael@0 876 mMathMLChar.GetRect(rect);
michael@0 877 mMathMLChar.SetRect(nsRect(rect.x + dx, rect.y,
michael@0 878 rect.width, rect.height));
michael@0 879 }
michael@0 880 else {
michael@0 881 nsIFrame* childFrame = firstChild;
michael@0 882 while (childFrame) {
michael@0 883 childFrame->SetPosition(childFrame->GetPosition() +
michael@0 884 nsPoint(dx, 0));
michael@0 885 childFrame = childFrame->GetNextSibling();
michael@0 886 }
michael@0 887 }
michael@0 888 }
michael@0 889 }
michael@0 890
michael@0 891 // Finished with these:
michael@0 892 ClearSavedChildMetrics();
michael@0 893 // Set our overflow area
michael@0 894 GatherAndStoreOverflow(&aDesiredStretchSize);
michael@0 895
michael@0 896 // There used to be code here to change the height of the child frame to
michael@0 897 // change the caret height, but the text frame that manages the caret is now
michael@0 898 // not a direct child but wrapped in a block frame. See also bug 412033.
michael@0 899
michael@0 900 return NS_OK;
michael@0 901 }
michael@0 902
michael@0 903 NS_IMETHODIMP
michael@0 904 nsMathMLmoFrame::InheritAutomaticData(nsIFrame* aParent)
michael@0 905 {
michael@0 906 // retain our native direction, it only changes if our text content changes
michael@0 907 nsStretchDirection direction = mEmbellishData.direction;
michael@0 908 nsMathMLTokenFrame::InheritAutomaticData(aParent);
michael@0 909 ProcessTextData();
michael@0 910 mEmbellishData.direction = direction;
michael@0 911 return NS_OK;
michael@0 912 }
michael@0 913
michael@0 914 NS_IMETHODIMP
michael@0 915 nsMathMLmoFrame::TransmitAutomaticData()
michael@0 916 {
michael@0 917 // this will cause us to re-sync our flags from scratch
michael@0 918 // but our returned 'form' is still not final (bug 133429), it will
michael@0 919 // be recomputed to its final value during the next call in Reflow()
michael@0 920 mEmbellishData.coreFrame = nullptr;
michael@0 921 ProcessOperatorData();
michael@0 922 return NS_OK;
michael@0 923 }
michael@0 924
michael@0 925 nsresult
michael@0 926 nsMathMLmoFrame::SetInitialChildList(ChildListID aListID,
michael@0 927 nsFrameList& aChildList)
michael@0 928 {
michael@0 929 // First, let the parent class do its work
michael@0 930 nsresult rv = nsMathMLTokenFrame::SetInitialChildList(aListID, aChildList);
michael@0 931 if (NS_FAILED(rv))
michael@0 932 return rv;
michael@0 933
michael@0 934 ProcessTextData();
michael@0 935 return rv;
michael@0 936 }
michael@0 937
michael@0 938 nsresult
michael@0 939 nsMathMLmoFrame::Reflow(nsPresContext* aPresContext,
michael@0 940 nsHTMLReflowMetrics& aDesiredSize,
michael@0 941 const nsHTMLReflowState& aReflowState,
michael@0 942 nsReflowStatus& aStatus)
michael@0 943 {
michael@0 944 // certain values use units that depend on our style context, so
michael@0 945 // it is safer to just process the whole lot here
michael@0 946 ProcessOperatorData();
michael@0 947
michael@0 948 return nsMathMLTokenFrame::Reflow(aPresContext, aDesiredSize,
michael@0 949 aReflowState, aStatus);
michael@0 950 }
michael@0 951
michael@0 952 /* virtual */ void
michael@0 953 nsMathMLmoFrame::MarkIntrinsicWidthsDirty()
michael@0 954 {
michael@0 955 // if we get this, it may mean that something changed in the text
michael@0 956 // content. So blow away everything an re-build the automatic data
michael@0 957 // from the parent of our outermost embellished container (we ensure
michael@0 958 // that we are the core, not just a sibling of the core)
michael@0 959
michael@0 960 ProcessTextData();
michael@0 961
michael@0 962 nsIFrame* target = this;
michael@0 963 nsEmbellishData embellishData;
michael@0 964 do {
michael@0 965 target = target->GetParent();
michael@0 966 GetEmbellishDataFrom(target, embellishData);
michael@0 967 } while (embellishData.coreFrame == this);
michael@0 968
michael@0 969 // we have automatic data to update in the children of the target frame
michael@0 970 // XXXldb This should really be marking dirty rather than rebuilding
michael@0 971 // so that we don't rebuild multiple times for the same change.
michael@0 972 RebuildAutomaticDataForChildren(target);
michael@0 973
michael@0 974 nsMathMLContainerFrame::MarkIntrinsicWidthsDirty();
michael@0 975 }
michael@0 976
michael@0 977 /* virtual */ void
michael@0 978 nsMathMLmoFrame::GetIntrinsicWidthMetrics(nsRenderingContext *aRenderingContext, nsHTMLReflowMetrics& aDesiredSize)
michael@0 979 {
michael@0 980 ProcessOperatorData();
michael@0 981 if (UseMathMLChar()) {
michael@0 982 uint32_t stretchHint = GetStretchHint(mFlags, mPresentationData, true,
michael@0 983 StyleFont());
michael@0 984 aDesiredSize.Width() = mMathMLChar.
michael@0 985 GetMaxWidth(PresContext(), *aRenderingContext,
michael@0 986 stretchHint, mMaxSize,
michael@0 987 NS_MATHML_OPERATOR_MAXSIZE_IS_ABSOLUTE(mFlags));
michael@0 988 }
michael@0 989 else {
michael@0 990 nsMathMLTokenFrame::GetIntrinsicWidthMetrics(aRenderingContext,
michael@0 991 aDesiredSize);
michael@0 992 }
michael@0 993
michael@0 994 // leadingSpace and trailingSpace are actually applied to the outermost
michael@0 995 // embellished container but for determining total intrinsic width it should
michael@0 996 // be safe to include it for the core here instead.
michael@0 997 bool isRTL = StyleVisibility()->mDirection;
michael@0 998 aDesiredSize.Width() +=
michael@0 999 mEmbellishData.leadingSpace + mEmbellishData.trailingSpace;
michael@0 1000 aDesiredSize.mBoundingMetrics.width = aDesiredSize.Width();
michael@0 1001 if (isRTL) {
michael@0 1002 aDesiredSize.mBoundingMetrics.leftBearing += mEmbellishData.trailingSpace;
michael@0 1003 aDesiredSize.mBoundingMetrics.rightBearing += mEmbellishData.trailingSpace;
michael@0 1004 } else {
michael@0 1005 aDesiredSize.mBoundingMetrics.leftBearing += mEmbellishData.leadingSpace;
michael@0 1006 aDesiredSize.mBoundingMetrics.rightBearing += mEmbellishData.leadingSpace;
michael@0 1007 }
michael@0 1008 }
michael@0 1009
michael@0 1010 nsresult
michael@0 1011 nsMathMLmoFrame::AttributeChanged(int32_t aNameSpaceID,
michael@0 1012 nsIAtom* aAttribute,
michael@0 1013 int32_t aModType)
michael@0 1014 {
michael@0 1015 // check if this is an attribute that can affect the embellished hierarchy
michael@0 1016 // in a significant way and re-layout the entire hierarchy.
michael@0 1017 if (nsGkAtoms::accent_ == aAttribute ||
michael@0 1018 nsGkAtoms::movablelimits_ == aAttribute) {
michael@0 1019
michael@0 1020 // set the target as the parent of our outermost embellished container
michael@0 1021 // (we ensure that we are the core, not just a sibling of the core)
michael@0 1022 nsIFrame* target = this;
michael@0 1023 nsEmbellishData embellishData;
michael@0 1024 do {
michael@0 1025 target = target->GetParent();
michael@0 1026 GetEmbellishDataFrom(target, embellishData);
michael@0 1027 } while (embellishData.coreFrame == this);
michael@0 1028
michael@0 1029 // we have automatic data to update in the children of the target frame
michael@0 1030 return ReLayoutChildren(target);
michael@0 1031 }
michael@0 1032
michael@0 1033 return nsMathMLTokenFrame::
michael@0 1034 AttributeChanged(aNameSpaceID, aAttribute, aModType);
michael@0 1035 }
michael@0 1036
michael@0 1037 // ----------------------
michael@0 1038 // No need to track the style context given to our MathML char.
michael@0 1039 // the Style System will use these to pass the proper style context to our MathMLChar
michael@0 1040 nsStyleContext*
michael@0 1041 nsMathMLmoFrame::GetAdditionalStyleContext(int32_t aIndex) const
michael@0 1042 {
michael@0 1043 switch (aIndex) {
michael@0 1044 case NS_MATHML_CHAR_STYLE_CONTEXT_INDEX:
michael@0 1045 return mMathMLChar.GetStyleContext();
michael@0 1046 default:
michael@0 1047 return nullptr;
michael@0 1048 }
michael@0 1049 }
michael@0 1050
michael@0 1051 void
michael@0 1052 nsMathMLmoFrame::SetAdditionalStyleContext(int32_t aIndex,
michael@0 1053 nsStyleContext* aStyleContext)
michael@0 1054 {
michael@0 1055 switch (aIndex) {
michael@0 1056 case NS_MATHML_CHAR_STYLE_CONTEXT_INDEX:
michael@0 1057 mMathMLChar.SetStyleContext(aStyleContext);
michael@0 1058 break;
michael@0 1059 }
michael@0 1060 }

mercurial