Merge mozilla-central and tracemonkey. (a=blockers)
[mozilla-central.git] / layout / mathml / nsMathMLmtableFrame.cpp
blob51053b8763706cb227ebd72d137d7a8acae96391
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* ***** BEGIN LICENSE BLOCK *****
3 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
5 * The contents of this file are subject to the Mozilla Public License Version
6 * 1.1 (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 * http://www.mozilla.org/MPL/
10 * Software distributed under the License is distributed on an "AS IS" basis,
11 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 * for the specific language governing rights and limitations under the
13 * License.
15 * The Original Code is Mozilla MathML Project.
17 * The Initial Developer of the Original Code is
18 * The University Of Queensland.
19 * Portions created by the Initial Developer are Copyright (C) 1999
20 * the Initial Developer. All Rights Reserved.
22 * Contributor(s):
23 * Roger B. Sidje <rbs@maths.uq.edu.au>
25 * Alternatively, the contents of this file may be used under the terms of
26 * either of the GNU General Public License Version 2 or later (the "GPL"),
27 * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
28 * in which case the provisions of the GPL or the LGPL are applicable instead
29 * of those above. If you wish to allow use of your version of this file only
30 * under the terms of either the GPL or the LGPL, and not to allow others to
31 * use your version of this file under the terms of the MPL, indicate your
32 * decision by deleting the provisions above and replace them with the notice
33 * and other provisions required by the GPL or the LGPL. If you do not delete
34 * the provisions above, a recipient may use your version of this file under
35 * the terms of any one of the MPL, the GPL or the LGPL.
37 * ***** END LICENSE BLOCK ***** */
39 #include "nsCOMPtr.h"
40 #include "nsFrame.h"
41 #include "nsBlockFrame.h"
42 #include "nsPresContext.h"
43 #include "nsStyleContext.h"
44 #include "nsStyleConsts.h"
45 #include "nsINameSpaceManager.h"
46 #include "nsIRenderingContext.h"
47 #include "nsIFontMetrics.h"
49 #include "nsTArray.h"
50 #include "nsCSSFrameConstructor.h"
51 #include "nsTableOuterFrame.h"
52 #include "nsTableFrame.h"
53 #include "nsTableCellFrame.h"
54 #include "celldata.h"
56 #include "nsMathMLmtableFrame.h"
58 using namespace mozilla;
61 // <mtable> -- table or matrix - implementation
64 // helper function to perform an in-place split of a space-delimited string,
65 // and return an array of pointers for the beginning of each segment, i.e.,
66 // aOffset[0] is the first string, aOffset[1] is the second string, etc.
67 // Used to parse attributes like columnalign='left right', rowalign='top bottom'
68 static void
69 SplitString(nsString& aString, // [IN/OUT]
70 nsTArray<PRUnichar*>& aOffset) // [OUT]
72 static const PRUnichar kNullCh = PRUnichar('\0');
74 aString.Append(kNullCh); // put an extra null at the end
76 PRUnichar* start = aString.BeginWriting();
77 PRUnichar* end = start;
79 while (kNullCh != *start) {
80 while ((kNullCh != *start) && nsCRT::IsAsciiSpace(*start)) { // skip leading space
81 start++;
83 end = start;
85 while ((kNullCh != *end) && (PR_FALSE == nsCRT::IsAsciiSpace(*end))) { // look for space or end
86 end++;
88 *end = kNullCh; // end string here
90 if (start < end) {
91 aOffset.AppendElement(start); // record the beginning of this segment
94 start = ++end;
98 struct nsValueList
100 nsString mData;
101 nsTArray<PRUnichar*> mArray;
103 nsValueList(nsString& aData) {
104 mData.Assign(aData);
105 SplitString(mData, mArray);
109 // Each rowalign='top bottom' or columnalign='left right center' (from
110 // <mtable> or <mtr>) is split once (lazily) into a nsValueList which is
111 // stored in the property table. Row/Cell frames query the property table
112 // to see what values apply to them.
114 // XXX See bug 69409 - MathML attributes are not mapped to style.
116 static void
117 DestroyValueList(void* aPropertyValue)
119 delete static_cast<nsValueList*>(aPropertyValue);
122 NS_DECLARE_FRAME_PROPERTY(RowAlignProperty, DestroyValueList)
123 NS_DECLARE_FRAME_PROPERTY(RowLinesProperty, DestroyValueList)
124 NS_DECLARE_FRAME_PROPERTY(ColumnAlignProperty, DestroyValueList)
125 NS_DECLARE_FRAME_PROPERTY(ColumnLinesProperty, DestroyValueList)
127 static const FramePropertyDescriptor*
128 AttributeToProperty(nsIAtom* aAttribute)
130 if (aAttribute == nsGkAtoms::rowalign_)
131 return RowAlignProperty();
132 if (aAttribute == nsGkAtoms::rowlines_)
133 return RowLinesProperty();
134 if (aAttribute == nsGkAtoms::columnalign_)
135 return ColumnAlignProperty();
136 NS_ASSERTION(aAttribute == nsGkAtoms::columnlines_, "Invalid attribute");
137 return ColumnLinesProperty();
140 static PRUnichar*
141 GetValueAt(nsIFrame* aTableOrRowFrame,
142 const FramePropertyDescriptor* aProperty,
143 nsIAtom* aAttribute,
144 PRInt32 aRowOrColIndex)
146 FrameProperties props = aTableOrRowFrame->Properties();
147 nsValueList* valueList = static_cast<nsValueList*>(props.Get(aProperty));
148 if (!valueList) {
149 // The property isn't there yet, so set it
150 nsAutoString values;
151 aTableOrRowFrame->GetContent()->GetAttr(kNameSpaceID_None, aAttribute, values);
152 if (!values.IsEmpty())
153 valueList = new nsValueList(values);
154 if (!valueList || !valueList->mArray.Length()) {
155 delete valueList; // ok either way, delete is null safe
156 return nsnull;
158 props.Set(aProperty, valueList);
160 PRInt32 count = valueList->mArray.Length();
161 return (aRowOrColIndex < count)
162 ? valueList->mArray[aRowOrColIndex]
163 : valueList->mArray[count-1];
166 #ifdef NS_DEBUG
167 static PRBool
168 IsTable(PRUint8 aDisplay)
170 if ((aDisplay == NS_STYLE_DISPLAY_TABLE) ||
171 (aDisplay == NS_STYLE_DISPLAY_INLINE_TABLE))
172 return PR_TRUE;
173 return PR_FALSE;
176 #define DEBUG_VERIFY_THAT_FRAME_IS(_frame, _expected) \
177 NS_ASSERTION(NS_STYLE_DISPLAY_##_expected == _frame->GetStyleDisplay()->mDisplay, "internal error");
178 #define DEBUG_VERIFY_THAT_FRAME_IS_TABLE(_frame) \
179 NS_ASSERTION(IsTable(_frame->GetStyleDisplay()->mDisplay), "internal error");
180 #else
181 #define DEBUG_VERIFY_THAT_FRAME_IS(_frame, _expected)
182 #define DEBUG_VERIFY_THAT_FRAME_IS_TABLE(_frame)
183 #endif
185 // map attributes that depend on the index of the row:
186 // rowalign, rowlines, XXX need rowspacing too
187 static void
188 MapRowAttributesIntoCSS(nsIFrame* aTableFrame,
189 nsIFrame* aRowFrame)
191 DEBUG_VERIFY_THAT_FRAME_IS_TABLE(aTableFrame);
192 DEBUG_VERIFY_THAT_FRAME_IS(aRowFrame, TABLE_ROW);
193 PRInt32 rowIndex = ((nsTableRowFrame*)aRowFrame)->GetRowIndex();
194 nsIContent* rowContent = aRowFrame->GetContent();
195 PRUnichar* attr;
197 // see if the rowalign attribute is not already set
198 if (!rowContent->HasAttr(kNameSpaceID_None, nsGkAtoms::rowalign_) &&
199 !rowContent->HasAttr(kNameSpaceID_None, nsGkAtoms::_moz_math_rowalign_)) {
200 // see if the rowalign attribute was specified on the table
201 attr = GetValueAt(aTableFrame, RowAlignProperty(),
202 nsGkAtoms::rowalign_, rowIndex);
203 if (attr) {
204 // set our special _moz attribute on the row without notifying a reflow
205 rowContent->SetAttr(kNameSpaceID_None, nsGkAtoms::_moz_math_rowalign_,
206 nsDependentString(attr), PR_FALSE);
210 // if we are not on the first row, see if |rowlines| was specified on the table.
211 // Note that we pass 'rowIndex-1' because the CSS rule in mathml.css is associated
212 // to 'border-top', and it is as if we draw the line on behalf of the previous cell.
213 // This way of doing so allows us to handle selective lines, [row]\hline[row][row]',
214 // and cases of spanning cells without further complications.
215 if (rowIndex > 0 &&
216 !rowContent->HasAttr(kNameSpaceID_None, nsGkAtoms::_moz_math_rowline_)) {
217 attr = GetValueAt(aTableFrame, RowLinesProperty(),
218 nsGkAtoms::rowlines_, rowIndex-1);
219 if (attr) {
220 // set our special _moz attribute on the row without notifying a reflow
221 rowContent->SetAttr(kNameSpaceID_None, nsGkAtoms::_moz_math_rowline_,
222 nsDependentString(attr), PR_FALSE);
227 // map attributes that depend on the index of the column:
228 // columnalign, columnlines, XXX need columnwidth and columnspacing too
229 static void
230 MapColAttributesIntoCSS(nsIFrame* aTableFrame,
231 nsIFrame* aRowFrame,
232 nsIFrame* aCellFrame)
234 DEBUG_VERIFY_THAT_FRAME_IS_TABLE(aTableFrame);
235 DEBUG_VERIFY_THAT_FRAME_IS(aRowFrame, TABLE_ROW);
236 DEBUG_VERIFY_THAT_FRAME_IS(aCellFrame, TABLE_CELL);
237 PRInt32 rowIndex, colIndex;
238 ((nsTableCellFrame*)aCellFrame)->GetCellIndexes(rowIndex, colIndex);
239 nsIContent* cellContent = aCellFrame->GetContent();
240 PRUnichar* attr;
242 // see if the columnalign attribute is not already set
243 if (!cellContent->HasAttr(kNameSpaceID_None, nsGkAtoms::columnalign_) &&
244 !cellContent->HasAttr(kNameSpaceID_None,
245 nsGkAtoms::_moz_math_columnalign_)) {
246 // see if the columnalign attribute was specified on the row
247 attr = GetValueAt(aRowFrame, ColumnAlignProperty(),
248 nsGkAtoms::columnalign_, colIndex);
249 if (!attr) {
250 // see if the columnalign attribute was specified on the table
251 attr = GetValueAt(aTableFrame, ColumnAlignProperty(),
252 nsGkAtoms::columnalign_, colIndex);
254 if (attr) {
255 // set our special _moz attribute without notifying a reflow
256 cellContent->SetAttr(kNameSpaceID_None, nsGkAtoms::_moz_math_columnalign_,
257 nsDependentString(attr), PR_FALSE);
261 // if we are not on the first column, see if |columnlines| was specified on
262 // the table. Note that we pass 'colIndex-1' because the CSS rule in mathml.css
263 // is associated to 'border-left', and it is as if we draw the line on behalf
264 // of the previous cell. This way of doing so allows us to handle selective lines,
265 // e.g., 'r|cl', and cases of spanning cells without further complications.
266 if (colIndex > 0 &&
267 !cellContent->HasAttr(kNameSpaceID_None,
268 nsGkAtoms::_moz_math_columnline_)) {
269 attr = GetValueAt(aTableFrame, ColumnLinesProperty(),
270 nsGkAtoms::columnlines_, colIndex-1);
271 if (attr) {
272 // set our special _moz attribute without notifying a reflow
273 cellContent->SetAttr(kNameSpaceID_None, nsGkAtoms::_moz_math_columnline_,
274 nsDependentString(attr), PR_FALSE);
279 // map all attribues within a table -- requires the indices of rows and cells.
280 // so it can only happen after they are made ready by the table base class.
281 static void
282 MapAllAttributesIntoCSS(nsIFrame* aTableFrame)
284 // mtable is simple and only has one (pseudo) row-group
285 nsIFrame* rgFrame = aTableFrame->GetFirstChild(nsnull);
286 if (!rgFrame || rgFrame->GetType() != nsGkAtoms::tableRowGroupFrame)
287 return;
289 nsIFrame* rowFrame = rgFrame->GetFirstChild(nsnull);
290 for ( ; rowFrame; rowFrame = rowFrame->GetNextSibling()) {
291 DEBUG_VERIFY_THAT_FRAME_IS(rowFrame, TABLE_ROW);
292 if (rowFrame->GetType() == nsGkAtoms::tableRowFrame) {
293 MapRowAttributesIntoCSS(aTableFrame, rowFrame);
294 nsIFrame* cellFrame = rowFrame->GetFirstChild(nsnull);
295 for ( ; cellFrame; cellFrame = cellFrame->GetNextSibling()) {
296 DEBUG_VERIFY_THAT_FRAME_IS(cellFrame, TABLE_CELL);
297 if (IS_TABLE_CELL(cellFrame->GetType())) {
298 MapColAttributesIntoCSS(aTableFrame, rowFrame, cellFrame);
305 // the align attribute of mtable can have a row number which indicates
306 // from where to anchor the table, e.g., top5 means anchor the table at
307 // the top of the 5th row, axis-1 means anchor the table on the axis of
308 // the last row (could have been nicer if the REC used the '#' separator,
309 // e.g., top#5, or axis#-1)
311 enum eAlign {
312 eAlign_top,
313 eAlign_bottom,
314 eAlign_center,
315 eAlign_baseline,
316 eAlign_axis
319 static void
320 ParseAlignAttribute(nsString& aValue, eAlign& aAlign, PRInt32& aRowIndex)
322 // by default, the table is centered about the axis
323 aRowIndex = 0;
324 aAlign = eAlign_axis;
325 PRInt32 len = 0;
326 if (0 == aValue.Find("top")) {
327 len = 3; // 3 is the length of 'top'
328 aAlign = eAlign_top;
330 else if (0 == aValue.Find("bottom")) {
331 len = 6; // 6 is the length of 'bottom'
332 aAlign = eAlign_bottom;
334 else if (0 == aValue.Find("center")) {
335 len = 6; // 6 is the length of 'center'
336 aAlign = eAlign_center;
338 else if (0 == aValue.Find("baseline")) {
339 len = 8; // 8 is the length of 'baseline'
340 aAlign = eAlign_baseline;
342 else if (0 == aValue.Find("axis")) {
343 len = 4; // 4 is the length of 'axis'
344 aAlign = eAlign_axis;
346 if (len) {
347 PRInt32 error;
348 aValue.Cut(0, len); // aValue is not a const here
349 aRowIndex = aValue.ToInteger(&error);
350 if (error)
351 aRowIndex = 0;
355 #ifdef DEBUG_rbs_off
356 // call ListMathMLTree(mParent) to get the big picture
357 static void
358 ListMathMLTree(nsIFrame* atLeast)
360 // climb up to <math> or <body> if <math> isn't there
361 nsIFrame* f = atLeast;
362 for ( ; f; f = f->GetParent()) {
363 nsIContent* c = f->GetContent();
364 if (!c || c->Tag() == nsGkAtoms::math || c->Tag() == nsGkAtoms::body)
365 break;
367 if (!f) f = atLeast;
368 f->List(stdout, 0);
370 #endif
372 // --------
373 // implementation of nsMathMLmtableOuterFrame
375 NS_QUERYFRAME_HEAD(nsMathMLmtableOuterFrame)
376 NS_QUERYFRAME_ENTRY(nsIMathMLFrame)
377 NS_QUERYFRAME_TAIL_INHERITING(nsTableOuterFrame)
379 nsIFrame*
380 NS_NewMathMLmtableOuterFrame (nsIPresShell* aPresShell, nsStyleContext* aContext)
382 return new (aPresShell) nsMathMLmtableOuterFrame(aContext);
385 NS_IMPL_FRAMEARENA_HELPERS(nsMathMLmtableOuterFrame)
387 nsMathMLmtableOuterFrame::~nsMathMLmtableOuterFrame()
391 NS_IMETHODIMP
392 nsMathMLmtableOuterFrame::InheritAutomaticData(nsIFrame* aParent)
394 // XXX the REC says that by default, displaystyle=false in <mtable>
396 // let the base class inherit the displaystyle from our parent
397 nsMathMLFrame::InheritAutomaticData(aParent);
399 // see if the displaystyle attribute is there and let it override what we inherited
400 if (mContent->Tag() == nsGkAtoms::mtable_)
401 nsMathMLFrame::FindAttrDisplaystyle(mContent, mPresentationData);
403 return NS_OK;
406 // displaystyle is special in mtable...
407 // Since UpdatePresentation() and UpdatePresentationDataFromChildAt() can be called
408 // by a parent, ensure that the displaystyle attribute of mtable takes precedence
409 NS_IMETHODIMP
410 nsMathMLmtableOuterFrame::UpdatePresentationData(PRUint32 aFlagsValues,
411 PRUint32 aWhichFlags)
413 if (NS_MATHML_HAS_EXPLICIT_DISPLAYSTYLE(mPresentationData.flags)) {
414 // our current state takes precedence, disallow updating the displastyle
415 aWhichFlags &= ~NS_MATHML_DISPLAYSTYLE;
416 aFlagsValues &= ~NS_MATHML_DISPLAYSTYLE;
419 return nsMathMLFrame::UpdatePresentationData(aFlagsValues, aWhichFlags);
422 NS_IMETHODIMP
423 nsMathMLmtableOuterFrame::UpdatePresentationDataFromChildAt(PRInt32 aFirstIndex,
424 PRInt32 aLastIndex,
425 PRUint32 aFlagsValues,
426 PRUint32 aWhichFlags)
428 if (NS_MATHML_HAS_EXPLICIT_DISPLAYSTYLE(mPresentationData.flags)) {
429 // our current state takes precedence, disallow updating the displastyle
430 aWhichFlags &= ~NS_MATHML_DISPLAYSTYLE;
431 aFlagsValues &= ~NS_MATHML_DISPLAYSTYLE;
434 nsMathMLContainerFrame::PropagatePresentationDataFromChildAt(this,
435 aFirstIndex, aLastIndex, aFlagsValues, aWhichFlags);
437 return NS_OK;
440 NS_IMETHODIMP
441 nsMathMLmtableOuterFrame::AttributeChanged(PRInt32 aNameSpaceID,
442 nsIAtom* aAttribute,
443 PRInt32 aModType)
445 // Attributes specific to <mtable>:
446 // frame : in mathml.css
447 // framespacing : not yet supported
448 // groupalign : not yet supported
449 // equalrows : not yet supported
450 // equalcolumns : not yet supported
451 // displaystyle : here
452 // align : in reflow
453 // rowalign : here
454 // rowlines : here
455 // rowspacing : not yet supported
456 // columnalign : here
457 // columnlines : here
458 // columnspacing : not yet supported
460 // mtable is simple and only has one (pseudo) row-group inside our inner-table
461 nsIFrame* tableFrame = mFrames.FirstChild();
462 if (!tableFrame || tableFrame->GetType() != nsGkAtoms::tableFrame)
463 return NS_OK;
464 nsIFrame* rgFrame = tableFrame->GetFirstChild(nsnull);
465 if (!rgFrame || rgFrame->GetType() != nsGkAtoms::tableRowGroupFrame)
466 return NS_OK;
468 // align - just need to issue a dirty (resize) reflow command
469 if (aAttribute == nsGkAtoms::align) {
470 PresContext()->PresShell()->
471 FrameNeedsReflow(this, nsIPresShell::eResize, NS_FRAME_IS_DIRTY);
472 return NS_OK;
475 // displaystyle - may seem innocuous, but it is actually very harsh --
476 // like changing an unit. Blow away and recompute all our automatic
477 // presentational data, and issue a style-changed reflow request
478 if (aAttribute == nsGkAtoms::displaystyle_) {
479 nsMathMLContainerFrame::RebuildAutomaticDataForChildren(mParent);
480 // Need to reflow the parent, not us, because this can actually
481 // affect siblings.
482 PresContext()->PresShell()->
483 FrameNeedsReflow(mParent, nsIPresShell::eStyleChange, NS_FRAME_IS_DIRTY);
484 return NS_OK;
487 // ...and the other attributes affect rows or columns in one way or another
488 nsIAtom* MOZrowAtom = nsnull;
489 nsIAtom* MOZcolAtom = nsnull;
490 if (aAttribute == nsGkAtoms::rowalign_)
491 MOZrowAtom = nsGkAtoms::_moz_math_rowalign_;
492 else if (aAttribute == nsGkAtoms::rowlines_)
493 MOZrowAtom = nsGkAtoms::_moz_math_rowline_;
494 else if (aAttribute == nsGkAtoms::columnalign_)
495 MOZcolAtom = nsGkAtoms::_moz_math_columnalign_;
496 else if (aAttribute == nsGkAtoms::columnlines_)
497 MOZcolAtom = nsGkAtoms::_moz_math_columnline_;
499 if (!MOZrowAtom && !MOZcolAtom)
500 return NS_OK;
502 nsPresContext* presContext = tableFrame->PresContext();
503 // clear any cached nsValueList for this table
504 presContext->PropertyTable()->
505 Delete(tableFrame, AttributeToProperty(aAttribute));
507 // unset any _moz attribute that we may have set earlier, and re-sync
508 nsIFrame* rowFrame = rgFrame->GetFirstChild(nsnull);
509 for ( ; rowFrame; rowFrame = rowFrame->GetNextSibling()) {
510 if (rowFrame->GetType() == nsGkAtoms::tableRowFrame) {
511 if (MOZrowAtom) { // let rows do the work
512 rowFrame->GetContent()->UnsetAttr(kNameSpaceID_None, MOZrowAtom, PR_FALSE);
513 MapRowAttributesIntoCSS(tableFrame, rowFrame);
514 } else { // let cells do the work
515 nsIFrame* cellFrame = rowFrame->GetFirstChild(nsnull);
516 for ( ; cellFrame; cellFrame = cellFrame->GetNextSibling()) {
517 if (IS_TABLE_CELL(cellFrame->GetType())) {
518 cellFrame->GetContent()->UnsetAttr(kNameSpaceID_None, MOZcolAtom, PR_FALSE);
519 MapColAttributesIntoCSS(tableFrame, rowFrame, cellFrame);
526 // Explicitly request a re-resolve and reflow in our subtree to pick up any changes
527 presContext->PresShell()->FrameConstructor()->
528 PostRestyleEvent(mContent->AsElement(), eRestyle_Subtree,
529 nsChangeHint_ReflowFrame);
531 return NS_OK;
534 nsIFrame*
535 nsMathMLmtableOuterFrame::GetRowFrameAt(nsPresContext* aPresContext,
536 PRInt32 aRowIndex)
538 PRInt32 rowCount, colCount;
539 GetTableSize(rowCount, colCount);
541 // Negative indices mean to find upwards from the end.
542 if (aRowIndex < 0) {
543 aRowIndex = rowCount + aRowIndex;
545 // aRowIndex is 1-based, so convert it to a 0-based index
546 --aRowIndex;
548 // if our inner table says that the index is valid, find the row now
549 if (0 <= aRowIndex && aRowIndex <= rowCount) {
550 nsIFrame* tableFrame = mFrames.FirstChild();
551 if (!tableFrame || tableFrame->GetType() != nsGkAtoms::tableFrame)
552 return nsnull;
553 nsIFrame* rgFrame = tableFrame->GetFirstChild(nsnull);
554 if (!rgFrame || rgFrame->GetType() != nsGkAtoms::tableRowGroupFrame)
555 return nsnull;
556 nsTableIterator rowIter(*rgFrame);
557 nsIFrame* rowFrame = rowIter.First();
558 for ( ; rowFrame; rowFrame = rowIter.Next()) {
559 if (aRowIndex == 0) {
560 DEBUG_VERIFY_THAT_FRAME_IS(rowFrame, TABLE_ROW);
561 if (rowFrame->GetType() != nsGkAtoms::tableRowFrame)
562 return nsnull;
564 return rowFrame;
566 --aRowIndex;
569 return nsnull;
572 NS_IMETHODIMP
573 nsMathMLmtableOuterFrame::Reflow(nsPresContext* aPresContext,
574 nsHTMLReflowMetrics& aDesiredSize,
575 const nsHTMLReflowState& aReflowState,
576 nsReflowStatus& aStatus)
578 nsresult rv;
579 nsAutoString value;
580 // we want to return a table that is anchored according to the align attribute
582 rv = nsTableOuterFrame::Reflow(aPresContext, aDesiredSize, aReflowState,
583 aStatus);
584 NS_ASSERTION(aDesiredSize.height >= 0, "illegal height for mtable");
585 NS_ASSERTION(aDesiredSize.width >= 0, "illegal width for mtable");
587 // see if the user has set the align attribute on the <mtable>
588 // XXX should we also check <mstyle> ?
589 PRInt32 rowIndex = 0;
590 eAlign tableAlign = eAlign_axis;
591 GetAttribute(mContent, nsnull, nsGkAtoms::align, value);
592 if (!value.IsEmpty()) {
593 ParseAlignAttribute(value, tableAlign, rowIndex);
596 // adjustments if there is a specified row from where to anchor the table
597 // (conceptually: when there is no row of reference, picture the table as if
598 // it is wrapped in a single big fictional row at dy = 0, this way of
599 // doing so allows us to have a single code path for all cases).
600 nscoord dy = 0;
601 nscoord height = aDesiredSize.height;
602 nsIFrame* rowFrame = nsnull;
603 if (rowIndex) {
604 rowFrame = GetRowFrameAt(aPresContext, rowIndex);
605 if (rowFrame) {
606 // translate the coordinates to be relative to us
607 nsIFrame* frame = rowFrame;
608 height = frame->GetSize().height;
609 do {
610 dy += frame->GetPosition().y;
611 frame = frame->GetParent();
612 } while (frame != this);
615 switch (tableAlign) {
616 case eAlign_top:
617 aDesiredSize.ascent = dy;
618 break;
619 case eAlign_bottom:
620 aDesiredSize.ascent = dy + height;
621 break;
622 case eAlign_center:
623 aDesiredSize.ascent = dy + height/2;
624 break;
625 case eAlign_baseline:
626 if (rowFrame) {
627 // anchor the table on the baseline of the row of reference
628 nscoord rowAscent = ((nsTableRowFrame*)rowFrame)->GetMaxCellAscent();
629 if (rowAscent) { // the row has at least one cell with 'vertical-align: baseline'
630 aDesiredSize.ascent = dy + rowAscent;
631 break;
634 // in other situations, fallback to center
635 aDesiredSize.ascent = dy + height/2;
636 break;
637 case eAlign_axis:
638 default: {
639 // XXX should instead use style data from the row of reference here ?
640 aReflowState.rendContext->SetFont(GetStyleFont()->mFont,
641 aPresContext->GetUserFontSet());
642 nsCOMPtr<nsIFontMetrics> fm;
643 aReflowState.rendContext->GetFontMetrics(*getter_AddRefs(fm));
644 nscoord axisHeight;
645 GetAxisHeight(*aReflowState.rendContext, fm, axisHeight);
646 if (rowFrame) {
647 // anchor the table on the axis of the row of reference
648 // XXX fallback to baseline because it is a hard problem
649 // XXX need to fetch the axis of the row; would need rowalign=axis to work better
650 nscoord rowAscent = ((nsTableRowFrame*)rowFrame)->GetMaxCellAscent();
651 if (rowAscent) { // the row has at least one cell with 'vertical-align: baseline'
652 aDesiredSize.ascent = dy + rowAscent;
653 break;
656 // in other situations, fallback to using half of the height
657 aDesiredSize.ascent = dy + height/2 + axisHeight;
661 mReference.x = 0;
662 mReference.y = aDesiredSize.ascent;
664 // just make-up a bounding metrics
665 mBoundingMetrics.Clear();
666 mBoundingMetrics.ascent = aDesiredSize.ascent;
667 mBoundingMetrics.descent = aDesiredSize.height - aDesiredSize.ascent;
668 mBoundingMetrics.width = aDesiredSize.width;
669 mBoundingMetrics.leftBearing = 0;
670 mBoundingMetrics.rightBearing = aDesiredSize.width;
672 aDesiredSize.mBoundingMetrics = mBoundingMetrics;
673 NS_FRAME_SET_TRUNCATION(aStatus, aReflowState, aDesiredSize);
675 return rv;
678 nsIFrame*
679 NS_NewMathMLmtableFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
681 return new (aPresShell) nsMathMLmtableFrame(aContext);
684 NS_IMPL_FRAMEARENA_HELPERS(nsMathMLmtableFrame)
686 nsMathMLmtableFrame::~nsMathMLmtableFrame()
690 NS_IMETHODIMP
691 nsMathMLmtableFrame::SetInitialChildList(nsIAtom* aListName,
692 nsFrameList& aChildList)
694 nsresult rv = nsTableFrame::SetInitialChildList(aListName, aChildList);
695 if (NS_FAILED(rv)) return rv;
696 MapAllAttributesIntoCSS(this);
697 return rv;
700 void
701 nsMathMLmtableFrame::RestyleTable()
703 // re-sync MathML specific style data that may have changed
704 MapAllAttributesIntoCSS(this);
706 // Explicitly request a re-resolve and reflow in our subtree to pick up any changes
707 PresContext()->PresShell()->FrameConstructor()->
708 PostRestyleEvent(mContent->AsElement(), eRestyle_Subtree,
709 nsChangeHint_ReflowFrame);
712 // --------
713 // implementation of nsMathMLmtrFrame
715 nsIFrame*
716 NS_NewMathMLmtrFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
718 return new (aPresShell) nsMathMLmtrFrame(aContext);
721 NS_IMPL_FRAMEARENA_HELPERS(nsMathMLmtrFrame)
723 nsMathMLmtrFrame::~nsMathMLmtrFrame()
727 NS_IMETHODIMP
728 nsMathMLmtrFrame::AttributeChanged(PRInt32 aNameSpaceID,
729 nsIAtom* aAttribute,
730 PRInt32 aModType)
732 // Attributes specific to <mtr>:
733 // groupalign : Not yet supported.
734 // rowalign : Fully specified in mathml.css, and so HasAttributeDependentStyle() will
735 // pick it up and nsCSSFrameConstructor will issue a PostRestyleEvent().
736 // columnalign : Need an explicit re-style call.
738 if (aAttribute == nsGkAtoms::rowalign_) {
739 // unset any _moz attribute that we may have set earlier, and re-sync
740 mContent->UnsetAttr(kNameSpaceID_None, nsGkAtoms::_moz_math_rowalign_,
741 PR_FALSE);
742 MapRowAttributesIntoCSS(nsTableFrame::GetTableFrame(this), this);
743 // That's all - see comment above.
744 return NS_OK;
747 if (aAttribute != nsGkAtoms::columnalign_)
748 return NS_OK;
750 nsPresContext* presContext = PresContext();
751 // Clear any cached columnalign's nsValueList for this row
752 presContext->PropertyTable()->Delete(this, AttributeToProperty(aAttribute));
754 // Clear any internal _moz attribute that we may have set earlier
755 // in our cells and re-sync their columnalign attribute
756 nsTableFrame* tableFrame = nsTableFrame::GetTableFrame(this);
757 nsIFrame* cellFrame = GetFirstChild(nsnull);
758 for ( ; cellFrame; cellFrame = cellFrame->GetNextSibling()) {
759 if (IS_TABLE_CELL(cellFrame->GetType())) {
760 cellFrame->GetContent()->
761 UnsetAttr(kNameSpaceID_None, nsGkAtoms::_moz_math_columnalign_,
762 PR_FALSE);
763 MapColAttributesIntoCSS(tableFrame, this, cellFrame);
767 // Explicitly request a re-resolve and reflow in our subtree to pick up any changes
768 presContext->PresShell()->FrameConstructor()->
769 PostRestyleEvent(mContent->AsElement(), eRestyle_Subtree,
770 nsChangeHint_ReflowFrame);
772 return NS_OK;
775 // --------
776 // implementation of nsMathMLmtdFrame
778 nsIFrame*
779 NS_NewMathMLmtdFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
781 return new (aPresShell) nsMathMLmtdFrame(aContext);
784 NS_IMPL_FRAMEARENA_HELPERS(nsMathMLmtdFrame)
786 nsMathMLmtdFrame::~nsMathMLmtdFrame()
790 PRInt32
791 nsMathMLmtdFrame::GetRowSpan()
793 PRInt32 rowspan = 1;
795 // Don't look at the content's rowspan if we're not an mtd or a pseudo cell.
796 if ((mContent->Tag() == nsGkAtoms::mtd_) && !GetStyleContext()->GetPseudo()) {
797 nsAutoString value;
798 mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::rowspan, value);
799 if (!value.IsEmpty()) {
800 PRInt32 error;
801 rowspan = value.ToInteger(&error);
802 if (error || rowspan < 0)
803 rowspan = 1;
804 rowspan = NS_MIN(rowspan, MAX_ROWSPAN);
807 return rowspan;
810 PRInt32
811 nsMathMLmtdFrame::GetColSpan()
813 PRInt32 colspan = 1;
815 // Don't look at the content's colspan if we're not an mtd or a pseudo cell.
816 if ((mContent->Tag() == nsGkAtoms::mtd_) && !GetStyleContext()->GetPseudo()) {
817 nsAutoString value;
818 mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::columnspan_, value);
819 if (!value.IsEmpty()) {
820 PRInt32 error;
821 colspan = value.ToInteger(&error);
822 if (error || colspan < 0 || colspan > MAX_COLSPAN)
823 colspan = 1;
826 return colspan;
829 NS_IMETHODIMP
830 nsMathMLmtdFrame::AttributeChanged(PRInt32 aNameSpaceID,
831 nsIAtom* aAttribute,
832 PRInt32 aModType)
834 // Attributes specific to <mtd>:
835 // groupalign : Not yet supported
836 // rowalign : in mathml.css
837 // columnalign : here
838 // rowspan : here
839 // columnspan : here
841 if (aAttribute == nsGkAtoms::columnalign_) {
842 // unset any _moz attribute that we may have set earlier, and re-sync
843 mContent->UnsetAttr(kNameSpaceID_None, nsGkAtoms::_moz_math_columnalign_,
844 PR_FALSE);
845 MapColAttributesIntoCSS(nsTableFrame::GetTableFrame(this), mParent, this);
846 return NS_OK;
849 if (aAttribute == nsGkAtoms::rowspan ||
850 aAttribute == nsGkAtoms::columnspan_) {
851 // use the naming expected by the base class
852 if (aAttribute == nsGkAtoms::columnspan_)
853 aAttribute = nsGkAtoms::colspan;
854 return nsTableCellFrame::AttributeChanged(aNameSpaceID, aAttribute, aModType);
857 return NS_OK;
860 // --------
861 // implementation of nsMathMLmtdInnerFrame
863 NS_QUERYFRAME_HEAD(nsMathMLmtdInnerFrame)
864 NS_QUERYFRAME_ENTRY(nsIMathMLFrame)
865 NS_QUERYFRAME_TAIL_INHERITING(nsBlockFrame)
867 nsIFrame*
868 NS_NewMathMLmtdInnerFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
870 return new (aPresShell) nsMathMLmtdInnerFrame(aContext);
873 NS_IMPL_FRAMEARENA_HELPERS(nsMathMLmtdInnerFrame)
875 nsMathMLmtdInnerFrame::~nsMathMLmtdInnerFrame()
879 NS_IMETHODIMP
880 nsMathMLmtdInnerFrame::Reflow(nsPresContext* aPresContext,
881 nsHTMLReflowMetrics& aDesiredSize,
882 const nsHTMLReflowState& aReflowState,
883 nsReflowStatus& aStatus)
885 // Let the base class do the reflow
886 nsresult rv = nsBlockFrame::Reflow(aPresContext, aDesiredSize, aReflowState, aStatus);
888 // more about <maligngroup/> and <malignmark/> later
889 // ...
890 return rv;