Fri, 16 Jan 2015 18:13:44 +0100
Integrate suggestion from review to improve consistency with existing code.
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 #include "nsMathMLmtableFrame.h"
7 #include "nsPresContext.h"
8 #include "nsStyleContext.h"
9 #include "nsStyleConsts.h"
10 #include "nsNameSpaceManager.h"
11 #include "nsRenderingContext.h"
12 #include "nsCSSRendering.h"
14 #include "nsTArray.h"
15 #include "nsTableFrame.h"
16 #include "celldata.h"
18 #include "RestyleManager.h"
19 #include <algorithm>
21 #include "nsIScriptError.h"
22 #include "nsContentUtils.h"
24 using namespace mozilla;
26 //
27 // <mtable> -- table or matrix - implementation
28 //
30 static int8_t
31 ParseStyleValue(nsIAtom* aAttribute, const nsAString& aAttributeValue)
32 {
33 if (aAttribute == nsGkAtoms::rowalign_) {
34 if (aAttributeValue.EqualsLiteral("top"))
35 return NS_STYLE_VERTICAL_ALIGN_TOP;
36 else if (aAttributeValue.EqualsLiteral("bottom"))
37 return NS_STYLE_VERTICAL_ALIGN_BOTTOM;
38 else if (aAttributeValue.EqualsLiteral("center"))
39 return NS_STYLE_VERTICAL_ALIGN_MIDDLE;
40 else
41 return NS_STYLE_VERTICAL_ALIGN_BASELINE;
42 } else if (aAttribute == nsGkAtoms::columnalign_) {
43 if (aAttributeValue.EqualsLiteral("left"))
44 return NS_STYLE_TEXT_ALIGN_LEFT;
45 else if (aAttributeValue.EqualsLiteral("right"))
46 return NS_STYLE_TEXT_ALIGN_RIGHT;
47 else
48 return NS_STYLE_TEXT_ALIGN_CENTER;
49 } else if (aAttribute == nsGkAtoms::rowlines_ ||
50 aAttribute == nsGkAtoms::columnlines_) {
51 if (aAttributeValue.EqualsLiteral("solid"))
52 return NS_STYLE_BORDER_STYLE_SOLID;
53 else if (aAttributeValue.EqualsLiteral("dashed"))
54 return NS_STYLE_BORDER_STYLE_DASHED;
55 else
56 return NS_STYLE_BORDER_STYLE_NONE;
57 } else {
58 MOZ_CRASH("Unrecognized attribute.");
59 }
61 return -1;
62 }
64 static nsTArray<int8_t>*
65 ExtractStyleValues(const nsAString& aString, nsIAtom* aAttribute,
66 bool aAllowMultiValues)
67 {
68 nsTArray<int8_t>* styleArray = nullptr;
70 const char16_t* start = aString.BeginReading();
71 const char16_t* end = aString.EndReading();
73 int32_t startIndex = 0;
74 int32_t count = 0;
76 while (start < end) {
77 // Skip leading spaces.
78 while ((start < end) && nsCRT::IsAsciiSpace(*start)) {
79 start++;
80 startIndex++;
81 }
83 // Look for the end of the string, or another space.
84 while ((start < end) && !nsCRT::IsAsciiSpace(*start)) {
85 start++;
86 count++;
87 }
89 // Grab the value found and process it.
90 if (count > 0) {
91 if (!styleArray)
92 styleArray = new nsTArray<int8_t>();
94 // We want to return a null array if an attribute gives multiple values,
95 // but multiple values aren't allowed.
96 if (styleArray->Length() > 1 && !aAllowMultiValues) {
97 delete styleArray;
98 return nullptr;
99 }
101 nsDependentSubstring valueString(aString, startIndex, count);
102 int8_t styleValue = ParseStyleValue(aAttribute, valueString);
103 styleArray->AppendElement(styleValue);
105 startIndex += count;
106 count = 0;
107 }
108 }
109 return styleArray;
110 }
112 static nsresult ReportParseError(nsIFrame* aFrame, const char16_t* aAttribute,
113 const char16_t* aValue)
114 {
115 nsIContent* content = aFrame->GetContent();
117 const char16_t* params[] =
118 { aValue, aAttribute, content->Tag()->GetUTF16String() };
120 return nsContentUtils::ReportToConsole(nsIScriptError::errorFlag,
121 NS_LITERAL_CSTRING("MathML"),
122 content->OwnerDoc(),
123 nsContentUtils::eMATHML_PROPERTIES,
124 "AttributeParsingError", params, 3);
125 }
127 // Each rowalign='top bottom' or columnalign='left right center' (from
128 // <mtable> or <mtr>) is split once into an nsTArray<int8_t> which is
129 // stored in the property table. Row/Cell frames query the property table
130 // to see what values apply to them.
132 static void
133 DestroyStylePropertyList(void* aPropertyValue)
134 {
135 delete static_cast<nsTArray<int8_t>*>(aPropertyValue);
136 }
138 NS_DECLARE_FRAME_PROPERTY(RowAlignProperty, DestroyStylePropertyList)
139 NS_DECLARE_FRAME_PROPERTY(RowLinesProperty, DestroyStylePropertyList)
140 NS_DECLARE_FRAME_PROPERTY(ColumnAlignProperty, DestroyStylePropertyList)
141 NS_DECLARE_FRAME_PROPERTY(ColumnLinesProperty, DestroyStylePropertyList)
143 static const FramePropertyDescriptor*
144 AttributeToProperty(nsIAtom* aAttribute)
145 {
146 if (aAttribute == nsGkAtoms::rowalign_)
147 return RowAlignProperty();
148 if (aAttribute == nsGkAtoms::rowlines_)
149 return RowLinesProperty();
150 if (aAttribute == nsGkAtoms::columnalign_)
151 return ColumnAlignProperty();
152 NS_ASSERTION(aAttribute == nsGkAtoms::columnlines_, "Invalid attribute");
153 return ColumnLinesProperty();
154 }
156 /* This method looks for a property that applies to a cell, but it looks
157 * recursively because some cell properties can come from the cell, a row,
158 * a table, etc. This function searches through the heirarchy for a property
159 * and returns its value. The function stops searching after checking a <mtable>
160 * frame.
161 */
162 static nsTArray<int8_t>*
163 FindCellProperty(const nsIFrame* aCellFrame,
164 const FramePropertyDescriptor* aFrameProperty)
165 {
166 const nsIFrame* currentFrame = aCellFrame;
167 nsTArray<int8_t>* propertyData = nullptr;
169 while (currentFrame) {
170 FrameProperties props = currentFrame->Properties();
171 propertyData = static_cast<nsTArray<int8_t>*>(props.Get(aFrameProperty));
172 bool frameIsTable = (currentFrame->GetType() == nsGkAtoms::tableFrame);
174 if (propertyData || frameIsTable)
175 currentFrame = nullptr; // A null frame pointer exits the loop
176 else
177 currentFrame = currentFrame->GetParent(); // Go to the parent frame
178 }
180 return propertyData;
181 }
183 static void
184 ApplyBorderToStyle(const nsMathMLmtdFrame* aFrame,
185 nsStyleBorder& aStyleBorder)
186 {
187 int32_t rowIndex;
188 int32_t columnIndex;
189 aFrame->GetRowIndex(rowIndex);
190 aFrame->GetColIndex(columnIndex);
192 nscoord borderWidth =
193 aFrame->PresContext()->GetBorderWidthTable()[NS_STYLE_BORDER_WIDTH_THIN];
195 nsTArray<int8_t>* rowLinesList =
196 FindCellProperty(aFrame, RowLinesProperty());
198 nsTArray<int8_t>* columnLinesList =
199 FindCellProperty(aFrame, ColumnLinesProperty());
201 // We don't place a row line on top of the first row
202 if (rowIndex > 0 && rowLinesList) {
203 // If the row number is greater than the number of provided rowline
204 // values, we simply repeat the last value.
205 int32_t listLength = rowLinesList->Length();
206 if (rowIndex < listLength) {
207 aStyleBorder.SetBorderStyle(NS_SIDE_TOP,
208 rowLinesList->ElementAt(rowIndex - 1));
209 } else {
210 aStyleBorder.SetBorderStyle(NS_SIDE_TOP,
211 rowLinesList->ElementAt(listLength - 1));
212 }
213 aStyleBorder.SetBorderWidth(NS_SIDE_TOP, borderWidth);
214 }
216 // We don't place a column line on the left of the first column.
217 if (columnIndex > 0 && columnLinesList) {
218 // If the column number is greater than the number of provided columline
219 // values, we simply repeat the last value.
220 int32_t listLength = columnLinesList->Length();
221 if (columnIndex < listLength) {
222 aStyleBorder.SetBorderStyle(NS_SIDE_LEFT,
223 columnLinesList->ElementAt(columnIndex - 1));
224 } else {
225 aStyleBorder.SetBorderStyle(NS_SIDE_LEFT,
226 columnLinesList->ElementAt(listLength - 1));
227 }
228 aStyleBorder.SetBorderWidth(NS_SIDE_LEFT, borderWidth);
229 }
230 }
232 /*
233 * A variant of the nsDisplayBorder contains special code to render a border
234 * around a nsMathMLmtdFrame based on the rowline and columnline properties
235 * set on the cell frame.
236 */
237 class nsDisplaymtdBorder : public nsDisplayBorder {
238 public:
239 nsDisplaymtdBorder(nsDisplayListBuilder* aBuilder, nsMathMLmtdFrame* aFrame)
240 : nsDisplayBorder(aBuilder, aFrame)
241 {
242 }
244 virtual nsRect GetBounds(nsDisplayListBuilder* aBuilder, bool* aSnap) MOZ_OVERRIDE
245 {
246 nsStyleBorder styleBorder = *mFrame->StyleBorder();
247 ApplyBorderToStyle(static_cast<nsMathMLmtdFrame*>(mFrame), styleBorder);
248 return CalculateBounds(styleBorder);
249 }
251 virtual void Paint(nsDisplayListBuilder* aBuilder, nsRenderingContext* aCtx) MOZ_OVERRIDE
252 {
253 nsStyleBorder styleBorder = *mFrame->StyleBorder();
254 ApplyBorderToStyle(static_cast<nsMathMLmtdFrame*>(mFrame), styleBorder);
256 nsPoint offset = ToReferenceFrame();
257 nsCSSRendering::PaintBorderWithStyleBorder(mFrame->PresContext(), *aCtx,
258 mFrame, mVisibleRect,
259 nsRect(offset,
260 mFrame->GetSize()),
261 styleBorder,
262 mFrame->StyleContext(),
263 mFrame->GetSkipSides());
264 }
265 };
267 #ifdef DEBUG
268 #define DEBUG_VERIFY_THAT_FRAME_IS(_frame, _expected) \
269 NS_ASSERTION(NS_STYLE_DISPLAY_##_expected == _frame->StyleDisplay()->mDisplay, "internal error");
270 #else
271 #define DEBUG_VERIFY_THAT_FRAME_IS(_frame, _expected)
272 #endif
274 static void
275 ParseFrameAttribute(nsIFrame* aFrame, nsIAtom* aAttribute,
276 bool aAllowMultiValues)
277 {
278 nsAutoString attrValue;
280 nsIContent* frameContent = aFrame->GetContent();
281 frameContent->GetAttr(kNameSpaceID_None, aAttribute, attrValue);
283 if (!attrValue.IsEmpty()) {
284 nsTArray<int8_t>* valueList =
285 ExtractStyleValues(attrValue, aAttribute, aAllowMultiValues);
287 // If valueList is null, that indicates a problem with the attribute value.
288 // Only set properties on a valid attribute value.
289 if (valueList) {
290 // The code reading the property assumes that this list is nonempty.
291 NS_ASSERTION(valueList->Length() >= 1, "valueList should not be empty!");
292 FrameProperties props = aFrame->Properties();
293 props.Set(AttributeToProperty(aAttribute), valueList);
294 } else {
295 ReportParseError(aFrame, aAttribute->GetUTF16String(), attrValue.get());
296 }
297 }
298 }
300 // map all attribues within a table -- requires the indices of rows and cells.
301 // so it can only happen after they are made ready by the table base class.
302 static void
303 MapAllAttributesIntoCSS(nsIFrame* aTableFrame)
304 {
305 // Map mtable rowalign & rowlines.
306 ParseFrameAttribute(aTableFrame, nsGkAtoms::rowalign_, true);
307 ParseFrameAttribute(aTableFrame, nsGkAtoms::rowlines_, true);
309 // Map mtable columnalign & columnlines.
310 ParseFrameAttribute(aTableFrame, nsGkAtoms::columnalign_, true);
311 ParseFrameAttribute(aTableFrame, nsGkAtoms::columnlines_, true);
313 // mtable is simple and only has one (pseudo) row-group
314 nsIFrame* rgFrame = aTableFrame->GetFirstPrincipalChild();
315 if (!rgFrame || rgFrame->GetType() != nsGkAtoms::tableRowGroupFrame)
316 return;
318 nsIFrame* rowFrame = rgFrame->GetFirstPrincipalChild();
319 for ( ; rowFrame; rowFrame = rowFrame->GetNextSibling()) {
320 DEBUG_VERIFY_THAT_FRAME_IS(rowFrame, TABLE_ROW);
321 if (rowFrame->GetType() == nsGkAtoms::tableRowFrame) {
322 // Map row rowalign.
323 ParseFrameAttribute(rowFrame, nsGkAtoms::rowalign_, false);
324 // Map row columnalign.
325 ParseFrameAttribute(rowFrame, nsGkAtoms::columnalign_, true);
327 nsIFrame* cellFrame = rowFrame->GetFirstPrincipalChild();
328 for ( ; cellFrame; cellFrame = cellFrame->GetNextSibling()) {
329 DEBUG_VERIFY_THAT_FRAME_IS(cellFrame, TABLE_CELL);
330 if (IS_TABLE_CELL(cellFrame->GetType())) {
331 // Map cell rowalign.
332 ParseFrameAttribute(cellFrame, nsGkAtoms::rowalign_, false);
333 // Map row columnalign.
334 ParseFrameAttribute(cellFrame, nsGkAtoms::columnalign_, false);
335 }
336 }
337 }
338 }
339 }
341 // the align attribute of mtable can have a row number which indicates
342 // from where to anchor the table, e.g., top 5 means anchor the table at
343 // the top of the 5th row, axis -1 means anchor the table on the axis of
344 // the last row
346 // The REC says that the syntax is
347 // '\s*(top|bottom|center|baseline|axis)(\s+-?[0-9]+)?\s*'
348 // the parsing could have been simpler with that syntax
349 // but for backward compatibility we make optional
350 // the whitespaces between the alignment name and the row number
352 enum eAlign {
353 eAlign_top,
354 eAlign_bottom,
355 eAlign_center,
356 eAlign_baseline,
357 eAlign_axis
358 };
360 static void
361 ParseAlignAttribute(nsString& aValue, eAlign& aAlign, int32_t& aRowIndex)
362 {
363 // by default, the table is centered about the axis
364 aRowIndex = 0;
365 aAlign = eAlign_axis;
366 int32_t len = 0;
368 // we only have to remove the leading spaces because
369 // ToInteger ignores the whitespaces around the number
370 aValue.CompressWhitespace(true, false);
372 if (0 == aValue.Find("top")) {
373 len = 3; // 3 is the length of 'top'
374 aAlign = eAlign_top;
375 }
376 else if (0 == aValue.Find("bottom")) {
377 len = 6; // 6 is the length of 'bottom'
378 aAlign = eAlign_bottom;
379 }
380 else if (0 == aValue.Find("center")) {
381 len = 6; // 6 is the length of 'center'
382 aAlign = eAlign_center;
383 }
384 else if (0 == aValue.Find("baseline")) {
385 len = 8; // 8 is the length of 'baseline'
386 aAlign = eAlign_baseline;
387 }
388 else if (0 == aValue.Find("axis")) {
389 len = 4; // 4 is the length of 'axis'
390 aAlign = eAlign_axis;
391 }
392 if (len) {
393 nsresult error;
394 aValue.Cut(0, len); // aValue is not a const here
395 aRowIndex = aValue.ToInteger(&error);
396 if (NS_FAILED(error))
397 aRowIndex = 0;
398 }
399 }
401 #ifdef DEBUG_rbs_off
402 // call ListMathMLTree(mParent) to get the big picture
403 static void
404 ListMathMLTree(nsIFrame* atLeast)
405 {
406 // climb up to <math> or <body> if <math> isn't there
407 nsIFrame* f = atLeast;
408 for ( ; f; f = f->GetParent()) {
409 nsIContent* c = f->GetContent();
410 if (!c || c->Tag() == nsGkAtoms::math || c->Tag() == nsGkAtoms::body)
411 break;
412 }
413 if (!f) f = atLeast;
414 f->List(stdout, 0);
415 }
416 #endif
418 // --------
419 // implementation of nsMathMLmtableOuterFrame
421 NS_QUERYFRAME_HEAD(nsMathMLmtableOuterFrame)
422 NS_QUERYFRAME_ENTRY(nsIMathMLFrame)
423 NS_QUERYFRAME_TAIL_INHERITING(nsTableOuterFrame)
425 nsIFrame*
426 NS_NewMathMLmtableOuterFrame (nsIPresShell* aPresShell, nsStyleContext* aContext)
427 {
428 return new (aPresShell) nsMathMLmtableOuterFrame(aContext);
429 }
431 NS_IMPL_FRAMEARENA_HELPERS(nsMathMLmtableOuterFrame)
433 nsMathMLmtableOuterFrame::~nsMathMLmtableOuterFrame()
434 {
435 }
437 nsresult
438 nsMathMLmtableOuterFrame::AttributeChanged(int32_t aNameSpaceID,
439 nsIAtom* aAttribute,
440 int32_t aModType)
441 {
442 // Attributes specific to <mtable>:
443 // frame : in mathml.css
444 // framespacing : not yet supported
445 // groupalign : not yet supported
446 // equalrows : not yet supported
447 // equalcolumns : not yet supported
448 // displaystyle : here and in mathml.css
449 // align : in reflow
450 // rowalign : here
451 // rowlines : here
452 // rowspacing : not yet supported
453 // columnalign : here
454 // columnlines : here
455 // columnspacing : not yet supported
457 // mtable is simple and only has one (pseudo) row-group inside our inner-table
458 nsIFrame* tableFrame = mFrames.FirstChild();
459 NS_ASSERTION(tableFrame && tableFrame->GetType() == nsGkAtoms::tableFrame,
460 "should always have an inner table frame");
461 nsIFrame* rgFrame = tableFrame->GetFirstPrincipalChild();
462 if (!rgFrame || rgFrame->GetType() != nsGkAtoms::tableRowGroupFrame)
463 return NS_OK;
465 // align - just need to issue a dirty (resize) reflow command
466 if (aAttribute == nsGkAtoms::align) {
467 PresContext()->PresShell()->
468 FrameNeedsReflow(this, nsIPresShell::eResize, NS_FRAME_IS_DIRTY);
469 return NS_OK;
470 }
472 // displaystyle - may seem innocuous, but it is actually very harsh --
473 // like changing an unit. Blow away and recompute all our automatic
474 // presentational data, and issue a style-changed reflow request
475 if (aAttribute == nsGkAtoms::displaystyle_) {
476 nsMathMLContainerFrame::RebuildAutomaticDataForChildren(mParent);
477 // Need to reflow the parent, not us, because this can actually
478 // affect siblings.
479 PresContext()->PresShell()->
480 FrameNeedsReflow(mParent, nsIPresShell::eStyleChange, NS_FRAME_IS_DIRTY);
481 return NS_OK;
482 }
484 // ...and the other attributes affect rows or columns in one way or another
486 // Ignore attributes that do not affect layout.
487 if (aAttribute != nsGkAtoms::rowalign_ &&
488 aAttribute != nsGkAtoms::rowlines_ &&
489 aAttribute != nsGkAtoms::columnalign_ &&
490 aAttribute != nsGkAtoms::columnlines_) {
491 return NS_OK;
492 }
494 nsPresContext* presContext = tableFrame->PresContext();
496 // clear any cached property list for this table
497 presContext->PropertyTable()->
498 Delete(tableFrame, AttributeToProperty(aAttribute));
500 // Reparse the new attribute on the table.
501 ParseFrameAttribute(tableFrame, aAttribute, true);
503 // Explicitly request a reflow in our subtree to pick up any changes
504 presContext->PresShell()->
505 FrameNeedsReflow(this, nsIPresShell::eStyleChange, NS_FRAME_IS_DIRTY);
507 return NS_OK;
508 }
510 nsIFrame*
511 nsMathMLmtableOuterFrame::GetRowFrameAt(nsPresContext* aPresContext,
512 int32_t aRowIndex)
513 {
514 int32_t rowCount = GetRowCount();
516 // Negative indices mean to find upwards from the end.
517 if (aRowIndex < 0) {
518 aRowIndex = rowCount + aRowIndex;
519 } else {
520 // aRowIndex is 1-based, so convert it to a 0-based index
521 --aRowIndex;
522 }
524 // if our inner table says that the index is valid, find the row now
525 if (0 <= aRowIndex && aRowIndex <= rowCount) {
526 nsIFrame* tableFrame = mFrames.FirstChild();
527 NS_ASSERTION(tableFrame && tableFrame->GetType() == nsGkAtoms::tableFrame,
528 "should always have an inner table frame");
529 nsIFrame* rgFrame = tableFrame->GetFirstPrincipalChild();
530 if (!rgFrame || rgFrame->GetType() != nsGkAtoms::tableRowGroupFrame)
531 return nullptr;
532 nsTableIterator rowIter(*rgFrame);
533 nsIFrame* rowFrame = rowIter.First();
534 for ( ; rowFrame; rowFrame = rowIter.Next()) {
535 if (aRowIndex == 0) {
536 DEBUG_VERIFY_THAT_FRAME_IS(rowFrame, TABLE_ROW);
537 if (rowFrame->GetType() != nsGkAtoms::tableRowFrame)
538 return nullptr;
540 return rowFrame;
541 }
542 --aRowIndex;
543 }
544 }
545 return nullptr;
546 }
548 nsresult
549 nsMathMLmtableOuterFrame::Reflow(nsPresContext* aPresContext,
550 nsHTMLReflowMetrics& aDesiredSize,
551 const nsHTMLReflowState& aReflowState,
552 nsReflowStatus& aStatus)
553 {
554 nsresult rv;
555 nsAutoString value;
556 // we want to return a table that is anchored according to the align attribute
558 rv = nsTableOuterFrame::Reflow(aPresContext, aDesiredSize, aReflowState,
559 aStatus);
560 NS_ASSERTION(aDesiredSize.Height() >= 0, "illegal height for mtable");
561 NS_ASSERTION(aDesiredSize.Width() >= 0, "illegal width for mtable");
563 // see if the user has set the align attribute on the <mtable>
564 int32_t rowIndex = 0;
565 eAlign tableAlign = eAlign_axis;
566 mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::align, value);
567 if (!value.IsEmpty()) {
568 ParseAlignAttribute(value, tableAlign, rowIndex);
569 }
571 // adjustments if there is a specified row from where to anchor the table
572 // (conceptually: when there is no row of reference, picture the table as if
573 // it is wrapped in a single big fictional row at dy = 0, this way of
574 // doing so allows us to have a single code path for all cases).
575 nscoord dy = 0;
576 nscoord height = aDesiredSize.Height();
577 nsIFrame* rowFrame = nullptr;
578 if (rowIndex) {
579 rowFrame = GetRowFrameAt(aPresContext, rowIndex);
580 if (rowFrame) {
581 // translate the coordinates to be relative to us
582 nsIFrame* frame = rowFrame;
583 height = frame->GetSize().height;
584 do {
585 dy += frame->GetPosition().y;
586 frame = frame->GetParent();
587 } while (frame != this);
588 }
589 }
590 switch (tableAlign) {
591 case eAlign_top:
592 aDesiredSize.SetTopAscent(dy);
593 break;
594 case eAlign_bottom:
595 aDesiredSize.SetTopAscent(dy + height);
596 break;
597 case eAlign_center:
598 aDesiredSize.SetTopAscent(dy + height / 2);
599 break;
600 case eAlign_baseline:
601 if (rowFrame) {
602 // anchor the table on the baseline of the row of reference
603 nscoord rowAscent = ((nsTableRowFrame*)rowFrame)->GetMaxCellAscent();
604 if (rowAscent) { // the row has at least one cell with 'vertical-align: baseline'
605 aDesiredSize.SetTopAscent(dy + rowAscent);
606 break;
607 }
608 }
609 // in other situations, fallback to center
610 aDesiredSize.SetTopAscent(dy + height / 2);
611 break;
612 case eAlign_axis:
613 default: {
614 // XXX should instead use style data from the row of reference here ?
615 nsRefPtr<nsFontMetrics> fm;
616 nsLayoutUtils::GetFontMetricsForFrame(this, getter_AddRefs(fm));
617 aReflowState.rendContext->SetFont(fm);
618 nscoord axisHeight;
619 GetAxisHeight(*aReflowState.rendContext,
620 aReflowState.rendContext->FontMetrics(),
621 axisHeight);
622 if (rowFrame) {
623 // anchor the table on the axis of the row of reference
624 // XXX fallback to baseline because it is a hard problem
625 // XXX need to fetch the axis of the row; would need rowalign=axis to work better
626 nscoord rowAscent = ((nsTableRowFrame*)rowFrame)->GetMaxCellAscent();
627 if (rowAscent) { // the row has at least one cell with 'vertical-align: baseline'
628 aDesiredSize.SetTopAscent(dy + rowAscent);
629 break;
630 }
631 }
632 // in other situations, fallback to using half of the height
633 aDesiredSize.SetTopAscent(dy + height / 2 + axisHeight);
634 }
635 }
637 mReference.x = 0;
638 mReference.y = aDesiredSize.TopAscent();
640 // just make-up a bounding metrics
641 mBoundingMetrics = nsBoundingMetrics();
642 mBoundingMetrics.ascent = aDesiredSize.TopAscent();
643 mBoundingMetrics.descent = aDesiredSize.Height() - aDesiredSize.TopAscent();
644 mBoundingMetrics.width = aDesiredSize.Width();
645 mBoundingMetrics.leftBearing = 0;
646 mBoundingMetrics.rightBearing = aDesiredSize.Width();
648 aDesiredSize.mBoundingMetrics = mBoundingMetrics;
649 NS_FRAME_SET_TRUNCATION(aStatus, aReflowState, aDesiredSize);
651 return rv;
652 }
654 nsIFrame*
655 NS_NewMathMLmtableFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
656 {
657 return new (aPresShell) nsMathMLmtableFrame(aContext);
658 }
660 NS_IMPL_FRAMEARENA_HELPERS(nsMathMLmtableFrame)
662 nsMathMLmtableFrame::~nsMathMLmtableFrame()
663 {
664 }
666 nsresult
667 nsMathMLmtableFrame::SetInitialChildList(ChildListID aListID,
668 nsFrameList& aChildList)
669 {
670 nsresult rv = nsTableFrame::SetInitialChildList(aListID, aChildList);
671 if (NS_FAILED(rv)) return rv;
672 MapAllAttributesIntoCSS(this);
673 return rv;
674 }
676 void
677 nsMathMLmtableFrame::RestyleTable()
678 {
679 // re-sync MathML specific style data that may have changed
680 MapAllAttributesIntoCSS(this);
682 // Explicitly request a re-resolve and reflow in our subtree to pick up any changes
683 PresContext()->RestyleManager()->
684 PostRestyleEvent(mContent->AsElement(), eRestyle_Subtree,
685 nsChangeHint_AllReflowHints);
686 }
688 // --------
689 // implementation of nsMathMLmtrFrame
691 nsIFrame*
692 NS_NewMathMLmtrFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
693 {
694 return new (aPresShell) nsMathMLmtrFrame(aContext);
695 }
697 NS_IMPL_FRAMEARENA_HELPERS(nsMathMLmtrFrame)
699 nsMathMLmtrFrame::~nsMathMLmtrFrame()
700 {
701 }
703 nsresult
704 nsMathMLmtrFrame::AttributeChanged(int32_t aNameSpaceID,
705 nsIAtom* aAttribute,
706 int32_t aModType)
707 {
708 // Attributes specific to <mtr>:
709 // groupalign : Not yet supported.
710 // rowalign : Here
711 // columnalign : Here
713 nsPresContext* presContext = PresContext();
715 if (aAttribute != nsGkAtoms::rowalign_ &&
716 aAttribute != nsGkAtoms::columnalign_) {
717 return NS_OK;
718 }
720 presContext->PropertyTable()->Delete(this, AttributeToProperty(aAttribute));
722 bool allowMultiValues = (aAttribute == nsGkAtoms::columnalign_);
724 // Reparse the new attribute.
725 ParseFrameAttribute(this, aAttribute, allowMultiValues);
727 // Explicitly request a reflow in our subtree to pick up any changes
728 presContext->PresShell()->
729 FrameNeedsReflow(this, nsIPresShell::eStyleChange, NS_FRAME_IS_DIRTY);
731 return NS_OK;
732 }
734 // --------
735 // implementation of nsMathMLmtdFrame
737 nsIFrame*
738 NS_NewMathMLmtdFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
739 {
740 return new (aPresShell) nsMathMLmtdFrame(aContext);
741 }
743 NS_IMPL_FRAMEARENA_HELPERS(nsMathMLmtdFrame)
745 nsMathMLmtdFrame::~nsMathMLmtdFrame()
746 {
747 }
749 int32_t
750 nsMathMLmtdFrame::GetRowSpan()
751 {
752 int32_t rowspan = 1;
754 // Don't look at the content's rowspan if we're not an mtd or a pseudo cell.
755 if ((mContent->Tag() == nsGkAtoms::mtd_) && !StyleContext()->GetPseudo()) {
756 nsAutoString value;
757 mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::rowspan, value);
758 if (!value.IsEmpty()) {
759 nsresult error;
760 rowspan = value.ToInteger(&error);
761 if (NS_FAILED(error) || rowspan < 0)
762 rowspan = 1;
763 rowspan = std::min(rowspan, MAX_ROWSPAN);
764 }
765 }
766 return rowspan;
767 }
769 int32_t
770 nsMathMLmtdFrame::GetColSpan()
771 {
772 int32_t colspan = 1;
774 // Don't look at the content's colspan if we're not an mtd or a pseudo cell.
775 if ((mContent->Tag() == nsGkAtoms::mtd_) && !StyleContext()->GetPseudo()) {
776 nsAutoString value;
777 mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::columnspan_, value);
778 if (!value.IsEmpty()) {
779 nsresult error;
780 colspan = value.ToInteger(&error);
781 if (NS_FAILED(error) || colspan < 0 || colspan > MAX_COLSPAN)
782 colspan = 1;
783 }
784 }
785 return colspan;
786 }
788 nsresult
789 nsMathMLmtdFrame::AttributeChanged(int32_t aNameSpaceID,
790 nsIAtom* aAttribute,
791 int32_t aModType)
792 {
793 // Attributes specific to <mtd>:
794 // groupalign : Not yet supported
795 // rowalign : here
796 // columnalign : here
797 // rowspan : here
798 // columnspan : here
800 if (aAttribute == nsGkAtoms::rowalign_ ||
801 aAttribute == nsGkAtoms::columnalign_) {
803 nsPresContext* presContext = PresContext();
804 presContext->PropertyTable()->Delete(this, AttributeToProperty(aAttribute));
806 // Reparse the attribute.
807 ParseFrameAttribute(this, aAttribute, false);
808 return NS_OK;
809 }
811 if (aAttribute == nsGkAtoms::rowspan ||
812 aAttribute == nsGkAtoms::columnspan_) {
813 // use the naming expected by the base class
814 if (aAttribute == nsGkAtoms::columnspan_)
815 aAttribute = nsGkAtoms::colspan;
816 return nsTableCellFrame::AttributeChanged(aNameSpaceID, aAttribute, aModType);
817 }
819 return NS_OK;
820 }
822 uint8_t
823 nsMathMLmtdFrame::GetVerticalAlign() const
824 {
825 // Set the default alignment in case no alignment was specified
826 uint8_t alignment = nsTableCellFrame::GetVerticalAlign();
828 nsTArray<int8_t>* alignmentList = FindCellProperty(this, RowAlignProperty());
830 if (alignmentList) {
831 int32_t rowIndex;
832 GetRowIndex(rowIndex);
834 // If the row number is greater than the number of provided rowalign values,
835 // we simply repeat the last value.
836 if (rowIndex < (int32_t)alignmentList->Length())
837 alignment = alignmentList->ElementAt(rowIndex);
838 else
839 alignment = alignmentList->ElementAt(alignmentList->Length() - 1);
840 }
842 return alignment;
843 }
845 nsresult
846 nsMathMLmtdFrame::ProcessBorders(nsTableFrame* aFrame,
847 nsDisplayListBuilder* aBuilder,
848 const nsDisplayListSet& aLists)
849 {
850 aLists.BorderBackground()->AppendNewToTop(new (aBuilder)
851 nsDisplaymtdBorder(aBuilder, this));
852 return NS_OK;
853 }
855 nsMargin*
856 nsMathMLmtdFrame::GetBorderWidth(nsMargin& aBorder) const
857 {
858 nsStyleBorder styleBorder = *StyleBorder();
859 ApplyBorderToStyle(this, styleBorder);
860 aBorder = styleBorder.GetComputedBorder();
861 return &aBorder;
862 }
864 // --------
865 // implementation of nsMathMLmtdInnerFrame
867 NS_QUERYFRAME_HEAD(nsMathMLmtdInnerFrame)
868 NS_QUERYFRAME_ENTRY(nsIMathMLFrame)
869 NS_QUERYFRAME_TAIL_INHERITING(nsBlockFrame)
871 nsIFrame*
872 NS_NewMathMLmtdInnerFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
873 {
874 return new (aPresShell) nsMathMLmtdInnerFrame(aContext);
875 }
877 NS_IMPL_FRAMEARENA_HELPERS(nsMathMLmtdInnerFrame)
879 nsMathMLmtdInnerFrame::nsMathMLmtdInnerFrame(nsStyleContext* aContext)
880 : nsBlockFrame(aContext)
881 {
882 // Make a copy of the parent nsStyleText for later modificaiton.
883 mUniqueStyleText = new (PresContext()) nsStyleText(*StyleText());
884 }
886 nsMathMLmtdInnerFrame::~nsMathMLmtdInnerFrame()
887 {
888 mUniqueStyleText->Destroy(PresContext());
889 }
891 nsresult
892 nsMathMLmtdInnerFrame::Reflow(nsPresContext* aPresContext,
893 nsHTMLReflowMetrics& aDesiredSize,
894 const nsHTMLReflowState& aReflowState,
895 nsReflowStatus& aStatus)
896 {
897 // Let the base class do the reflow
898 nsresult rv = nsBlockFrame::Reflow(aPresContext, aDesiredSize, aReflowState, aStatus);
900 // more about <maligngroup/> and <malignmark/> later
901 // ...
902 return rv;
903 }
905 const
906 nsStyleText* nsMathMLmtdInnerFrame::StyleTextForLineLayout()
907 {
908 // Set the default alignment in case nothing was specified
909 uint8_t alignment = StyleText()->mTextAlign;
911 nsTArray<int8_t>* alignmentList =
912 FindCellProperty(this, ColumnAlignProperty());
914 if (alignmentList) {
915 nsMathMLmtdFrame* cellFrame = (nsMathMLmtdFrame*)GetParent();
916 int32_t columnIndex;
917 cellFrame->GetColIndex(columnIndex);
919 // If the column number is greater than the number of provided columalign
920 // values, we simply repeat the last value.
921 if (columnIndex < (int32_t)alignmentList->Length())
922 alignment = alignmentList->ElementAt(columnIndex);
923 else
924 alignment = alignmentList->ElementAt(alignmentList->Length() - 1);
925 }
927 mUniqueStyleText->mTextAlign = alignment;
928 return mUniqueStyleText;
929 }
931 /* virtual */ void
932 nsMathMLmtdInnerFrame::DidSetStyleContext(nsStyleContext* aOldStyleContext)
933 {
934 nsBlockFrame::DidSetStyleContext(aOldStyleContext);
935 mUniqueStyleText->Destroy(PresContext());
936 mUniqueStyleText = new (PresContext()) nsStyleText(*StyleText());
937 }