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
4 : *
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/
9 : *
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.
14 : *
15 : * The Original Code is Mozilla MathML Project.
16 : *
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.
21 : *
22 : * Contributor(s):
23 : * Roger B. Sidje <rbs@maths.uq.edu.au>
24 : *
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.
36 : *
37 : * ***** END LICENSE BLOCK ***** */
38 :
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 "nsRenderingContext.h"
47 :
48 : #include "nsTArray.h"
49 : #include "nsCSSFrameConstructor.h"
50 : #include "nsTableOuterFrame.h"
51 : #include "nsTableFrame.h"
52 : #include "nsTableCellFrame.h"
53 : #include "celldata.h"
54 :
55 : #include "nsMathMLmtableFrame.h"
56 :
57 : using namespace mozilla;
58 :
59 : //
60 : // <mtable> -- table or matrix - implementation
61 : //
62 :
63 : // helper function to perform an in-place split of a space-delimited string,
64 : // and return an array of pointers for the beginning of each segment, i.e.,
65 : // aOffset[0] is the first string, aOffset[1] is the second string, etc.
66 : // Used to parse attributes like columnalign='left right', rowalign='top bottom'
67 : static void
68 0 : SplitString(nsString& aString, // [IN/OUT]
69 : nsTArray<PRUnichar*>& aOffset) // [OUT]
70 : {
71 : static const PRUnichar kNullCh = PRUnichar('\0');
72 :
73 0 : aString.Append(kNullCh); // put an extra null at the end
74 :
75 0 : PRUnichar* start = aString.BeginWriting();
76 0 : PRUnichar* end = start;
77 :
78 0 : while (kNullCh != *start) {
79 0 : while ((kNullCh != *start) && nsCRT::IsAsciiSpace(*start)) { // skip leading space
80 0 : start++;
81 : }
82 0 : end = start;
83 :
84 0 : while ((kNullCh != *end) && (false == nsCRT::IsAsciiSpace(*end))) { // look for space or end
85 0 : end++;
86 : }
87 0 : *end = kNullCh; // end string here
88 :
89 0 : if (start < end) {
90 0 : aOffset.AppendElement(start); // record the beginning of this segment
91 : }
92 :
93 0 : start = ++end;
94 : }
95 0 : }
96 :
97 : struct nsValueList
98 0 : {
99 : nsString mData;
100 : nsTArray<PRUnichar*> mArray;
101 :
102 0 : nsValueList(nsString& aData) {
103 0 : mData.Assign(aData);
104 0 : SplitString(mData, mArray);
105 0 : }
106 : };
107 :
108 : // Each rowalign='top bottom' or columnalign='left right center' (from
109 : // <mtable> or <mtr>) is split once (lazily) into a nsValueList which is
110 : // stored in the property table. Row/Cell frames query the property table
111 : // to see what values apply to them.
112 :
113 : // XXX See bug 69409 - MathML attributes are not mapped to style.
114 :
115 : static void
116 0 : DestroyValueList(void* aPropertyValue)
117 : {
118 0 : delete static_cast<nsValueList*>(aPropertyValue);
119 0 : }
120 :
121 0 : NS_DECLARE_FRAME_PROPERTY(RowAlignProperty, DestroyValueList)
122 0 : NS_DECLARE_FRAME_PROPERTY(RowLinesProperty, DestroyValueList)
123 0 : NS_DECLARE_FRAME_PROPERTY(ColumnAlignProperty, DestroyValueList)
124 0 : NS_DECLARE_FRAME_PROPERTY(ColumnLinesProperty, DestroyValueList)
125 :
126 : static const FramePropertyDescriptor*
127 0 : AttributeToProperty(nsIAtom* aAttribute)
128 : {
129 0 : if (aAttribute == nsGkAtoms::rowalign_)
130 0 : return RowAlignProperty();
131 0 : if (aAttribute == nsGkAtoms::rowlines_)
132 0 : return RowLinesProperty();
133 0 : if (aAttribute == nsGkAtoms::columnalign_)
134 0 : return ColumnAlignProperty();
135 0 : NS_ASSERTION(aAttribute == nsGkAtoms::columnlines_, "Invalid attribute");
136 0 : return ColumnLinesProperty();
137 : }
138 :
139 : static PRUnichar*
140 0 : GetValueAt(nsIFrame* aTableOrRowFrame,
141 : const FramePropertyDescriptor* aProperty,
142 : nsIAtom* aAttribute,
143 : PRInt32 aRowOrColIndex)
144 : {
145 0 : FrameProperties props = aTableOrRowFrame->Properties();
146 0 : nsValueList* valueList = static_cast<nsValueList*>(props.Get(aProperty));
147 0 : if (!valueList) {
148 : // The property isn't there yet, so set it
149 0 : nsAutoString values;
150 0 : aTableOrRowFrame->GetContent()->GetAttr(kNameSpaceID_None, aAttribute, values);
151 0 : if (!values.IsEmpty())
152 0 : valueList = new nsValueList(values);
153 0 : if (!valueList || !valueList->mArray.Length()) {
154 0 : delete valueList; // ok either way, delete is null safe
155 0 : return nsnull;
156 : }
157 0 : props.Set(aProperty, valueList);
158 : }
159 0 : PRInt32 count = valueList->mArray.Length();
160 : return (aRowOrColIndex < count)
161 0 : ? valueList->mArray[aRowOrColIndex]
162 0 : : valueList->mArray[count-1];
163 : }
164 :
165 : #ifdef NS_DEBUG
166 : static bool
167 0 : IsTable(PRUint8 aDisplay)
168 : {
169 0 : if ((aDisplay == NS_STYLE_DISPLAY_TABLE) ||
170 : (aDisplay == NS_STYLE_DISPLAY_INLINE_TABLE))
171 0 : return true;
172 0 : return false;
173 : }
174 :
175 : #define DEBUG_VERIFY_THAT_FRAME_IS(_frame, _expected) \
176 : NS_ASSERTION(NS_STYLE_DISPLAY_##_expected == _frame->GetStyleDisplay()->mDisplay, "internal error");
177 : #define DEBUG_VERIFY_THAT_FRAME_IS_TABLE(_frame) \
178 : NS_ASSERTION(IsTable(_frame->GetStyleDisplay()->mDisplay), "internal error");
179 : #else
180 : #define DEBUG_VERIFY_THAT_FRAME_IS(_frame, _expected)
181 : #define DEBUG_VERIFY_THAT_FRAME_IS_TABLE(_frame)
182 : #endif
183 :
184 : // map attributes that depend on the index of the row:
185 : // rowalign, rowlines, XXX need rowspacing too
186 : static void
187 0 : MapRowAttributesIntoCSS(nsIFrame* aTableFrame,
188 : nsIFrame* aRowFrame)
189 : {
190 0 : DEBUG_VERIFY_THAT_FRAME_IS_TABLE(aTableFrame);
191 0 : DEBUG_VERIFY_THAT_FRAME_IS(aRowFrame, TABLE_ROW);
192 0 : PRInt32 rowIndex = ((nsTableRowFrame*)aRowFrame)->GetRowIndex();
193 0 : nsIContent* rowContent = aRowFrame->GetContent();
194 : PRUnichar* attr;
195 :
196 : // see if the rowalign attribute is not already set
197 0 : if (!rowContent->HasAttr(kNameSpaceID_None, nsGkAtoms::rowalign_) &&
198 0 : !rowContent->HasAttr(kNameSpaceID_None, nsGkAtoms::_moz_math_rowalign_)) {
199 : // see if the rowalign attribute was specified on the table
200 : attr = GetValueAt(aTableFrame, RowAlignProperty(),
201 0 : nsGkAtoms::rowalign_, rowIndex);
202 0 : if (attr) {
203 : // set our special _moz attribute on the row without notifying a reflow
204 : rowContent->SetAttr(kNameSpaceID_None, nsGkAtoms::_moz_math_rowalign_,
205 0 : nsDependentString(attr), false);
206 : }
207 : }
208 :
209 : // if we are not on the first row, see if |rowlines| was specified on the table.
210 : // Note that we pass 'rowIndex-1' because the CSS rule in mathml.css is associated
211 : // to 'border-top', and it is as if we draw the line on behalf of the previous cell.
212 : // This way of doing so allows us to handle selective lines, [row]\hline[row][row]',
213 : // and cases of spanning cells without further complications.
214 0 : if (rowIndex > 0 &&
215 0 : !rowContent->HasAttr(kNameSpaceID_None, nsGkAtoms::_moz_math_rowline_)) {
216 : attr = GetValueAt(aTableFrame, RowLinesProperty(),
217 0 : nsGkAtoms::rowlines_, rowIndex-1);
218 0 : if (attr) {
219 : // set our special _moz attribute on the row without notifying a reflow
220 : rowContent->SetAttr(kNameSpaceID_None, nsGkAtoms::_moz_math_rowline_,
221 0 : nsDependentString(attr), false);
222 : }
223 : }
224 0 : }
225 :
226 : // map attributes that depend on the index of the column:
227 : // columnalign, columnlines, XXX need columnwidth and columnspacing too
228 : static void
229 0 : MapColAttributesIntoCSS(nsIFrame* aTableFrame,
230 : nsIFrame* aRowFrame,
231 : nsIFrame* aCellFrame)
232 : {
233 0 : DEBUG_VERIFY_THAT_FRAME_IS_TABLE(aTableFrame);
234 0 : DEBUG_VERIFY_THAT_FRAME_IS(aRowFrame, TABLE_ROW);
235 0 : DEBUG_VERIFY_THAT_FRAME_IS(aCellFrame, TABLE_CELL);
236 : PRInt32 rowIndex, colIndex;
237 0 : ((nsTableCellFrame*)aCellFrame)->GetCellIndexes(rowIndex, colIndex);
238 0 : nsIContent* cellContent = aCellFrame->GetContent();
239 : PRUnichar* attr;
240 :
241 : // see if the columnalign attribute is not already set
242 0 : if (!cellContent->HasAttr(kNameSpaceID_None, nsGkAtoms::columnalign_) &&
243 : !cellContent->HasAttr(kNameSpaceID_None,
244 0 : nsGkAtoms::_moz_math_columnalign_)) {
245 : // see if the columnalign attribute was specified on the row
246 : attr = GetValueAt(aRowFrame, ColumnAlignProperty(),
247 0 : nsGkAtoms::columnalign_, colIndex);
248 0 : if (!attr) {
249 : // see if the columnalign attribute was specified on the table
250 : attr = GetValueAt(aTableFrame, ColumnAlignProperty(),
251 0 : nsGkAtoms::columnalign_, colIndex);
252 : }
253 0 : if (attr) {
254 : // set our special _moz attribute without notifying a reflow
255 : cellContent->SetAttr(kNameSpaceID_None, nsGkAtoms::_moz_math_columnalign_,
256 0 : nsDependentString(attr), false);
257 : }
258 : }
259 :
260 : // if we are not on the first column, see if |columnlines| was specified on
261 : // the table. Note that we pass 'colIndex-1' because the CSS rule in mathml.css
262 : // is associated to 'border-left', and it is as if we draw the line on behalf
263 : // of the previous cell. This way of doing so allows us to handle selective lines,
264 : // e.g., 'r|cl', and cases of spanning cells without further complications.
265 0 : if (colIndex > 0 &&
266 : !cellContent->HasAttr(kNameSpaceID_None,
267 0 : nsGkAtoms::_moz_math_columnline_)) {
268 : attr = GetValueAt(aTableFrame, ColumnLinesProperty(),
269 0 : nsGkAtoms::columnlines_, colIndex-1);
270 0 : if (attr) {
271 : // set our special _moz attribute without notifying a reflow
272 : cellContent->SetAttr(kNameSpaceID_None, nsGkAtoms::_moz_math_columnline_,
273 0 : nsDependentString(attr), false);
274 : }
275 : }
276 0 : }
277 :
278 : // map all attribues within a table -- requires the indices of rows and cells.
279 : // so it can only happen after they are made ready by the table base class.
280 : static void
281 0 : MapAllAttributesIntoCSS(nsIFrame* aTableFrame)
282 : {
283 : // mtable is simple and only has one (pseudo) row-group
284 0 : nsIFrame* rgFrame = aTableFrame->GetFirstPrincipalChild();
285 0 : if (!rgFrame || rgFrame->GetType() != nsGkAtoms::tableRowGroupFrame)
286 0 : return;
287 :
288 0 : nsIFrame* rowFrame = rgFrame->GetFirstPrincipalChild();
289 0 : for ( ; rowFrame; rowFrame = rowFrame->GetNextSibling()) {
290 0 : DEBUG_VERIFY_THAT_FRAME_IS(rowFrame, TABLE_ROW);
291 0 : if (rowFrame->GetType() == nsGkAtoms::tableRowFrame) {
292 0 : MapRowAttributesIntoCSS(aTableFrame, rowFrame);
293 0 : nsIFrame* cellFrame = rowFrame->GetFirstPrincipalChild();
294 0 : for ( ; cellFrame; cellFrame = cellFrame->GetNextSibling()) {
295 0 : DEBUG_VERIFY_THAT_FRAME_IS(cellFrame, TABLE_CELL);
296 0 : if (IS_TABLE_CELL(cellFrame->GetType())) {
297 0 : MapColAttributesIntoCSS(aTableFrame, rowFrame, cellFrame);
298 : }
299 : }
300 : }
301 : }
302 : }
303 :
304 : // the align attribute of mtable can have a row number which indicates
305 : // from where to anchor the table, e.g., top5 means anchor the table at
306 : // the top of the 5th row, axis-1 means anchor the table on the axis of
307 : // the last row (could have been nicer if the REC used the '#' separator,
308 : // e.g., top#5, or axis#-1)
309 :
310 : enum eAlign {
311 : eAlign_top,
312 : eAlign_bottom,
313 : eAlign_center,
314 : eAlign_baseline,
315 : eAlign_axis
316 : };
317 :
318 : static void
319 0 : ParseAlignAttribute(nsString& aValue, eAlign& aAlign, PRInt32& aRowIndex)
320 : {
321 : // by default, the table is centered about the axis
322 0 : aRowIndex = 0;
323 0 : aAlign = eAlign_axis;
324 0 : PRInt32 len = 0;
325 0 : if (0 == aValue.Find("top")) {
326 0 : len = 3; // 3 is the length of 'top'
327 0 : aAlign = eAlign_top;
328 : }
329 0 : else if (0 == aValue.Find("bottom")) {
330 0 : len = 6; // 6 is the length of 'bottom'
331 0 : aAlign = eAlign_bottom;
332 : }
333 0 : else if (0 == aValue.Find("center")) {
334 0 : len = 6; // 6 is the length of 'center'
335 0 : aAlign = eAlign_center;
336 : }
337 0 : else if (0 == aValue.Find("baseline")) {
338 0 : len = 8; // 8 is the length of 'baseline'
339 0 : aAlign = eAlign_baseline;
340 : }
341 0 : else if (0 == aValue.Find("axis")) {
342 0 : len = 4; // 4 is the length of 'axis'
343 0 : aAlign = eAlign_axis;
344 : }
345 0 : if (len) {
346 : PRInt32 error;
347 0 : aValue.Cut(0, len); // aValue is not a const here
348 0 : aRowIndex = aValue.ToInteger(&error);
349 0 : if (error)
350 0 : aRowIndex = 0;
351 : }
352 0 : }
353 :
354 : #ifdef DEBUG_rbs_off
355 : // call ListMathMLTree(mParent) to get the big picture
356 : static void
357 : ListMathMLTree(nsIFrame* atLeast)
358 : {
359 : // climb up to <math> or <body> if <math> isn't there
360 : nsIFrame* f = atLeast;
361 : for ( ; f; f = f->GetParent()) {
362 : nsIContent* c = f->GetContent();
363 : if (!c || c->Tag() == nsGkAtoms::math || c->Tag() == nsGkAtoms::body)
364 : break;
365 : }
366 : if (!f) f = atLeast;
367 : f->List(stdout, 0);
368 : }
369 : #endif
370 :
371 : // --------
372 : // implementation of nsMathMLmtableOuterFrame
373 :
374 0 : NS_QUERYFRAME_HEAD(nsMathMLmtableOuterFrame)
375 0 : NS_QUERYFRAME_ENTRY(nsIMathMLFrame)
376 0 : NS_QUERYFRAME_TAIL_INHERITING(nsTableOuterFrame)
377 :
378 : nsIFrame*
379 0 : NS_NewMathMLmtableOuterFrame (nsIPresShell* aPresShell, nsStyleContext* aContext)
380 : {
381 0 : return new (aPresShell) nsMathMLmtableOuterFrame(aContext);
382 : }
383 :
384 0 : NS_IMPL_FRAMEARENA_HELPERS(nsMathMLmtableOuterFrame)
385 :
386 0 : nsMathMLmtableOuterFrame::~nsMathMLmtableOuterFrame()
387 : {
388 0 : }
389 :
390 : NS_IMETHODIMP
391 0 : nsMathMLmtableOuterFrame::InheritAutomaticData(nsIFrame* aParent)
392 : {
393 : // XXX the REC says that by default, displaystyle=false in <mtable>
394 :
395 : // let the base class inherit the displaystyle from our parent
396 0 : nsMathMLFrame::InheritAutomaticData(aParent);
397 :
398 : // see if the displaystyle attribute is there and let it override what we inherited
399 0 : if (mContent->Tag() == nsGkAtoms::mtable_)
400 0 : nsMathMLFrame::FindAttrDisplaystyle(mContent, mPresentationData);
401 :
402 0 : return NS_OK;
403 : }
404 :
405 : // displaystyle is special in mtable...
406 : // Since UpdatePresentation() and UpdatePresentationDataFromChildAt() can be called
407 : // by a parent, ensure that the displaystyle attribute of mtable takes precedence
408 : NS_IMETHODIMP
409 0 : nsMathMLmtableOuterFrame::UpdatePresentationData(PRUint32 aFlagsValues,
410 : PRUint32 aWhichFlags)
411 : {
412 0 : if (NS_MATHML_HAS_EXPLICIT_DISPLAYSTYLE(mPresentationData.flags)) {
413 : // our current state takes precedence, disallow updating the displastyle
414 0 : aWhichFlags &= ~NS_MATHML_DISPLAYSTYLE;
415 0 : aFlagsValues &= ~NS_MATHML_DISPLAYSTYLE;
416 : }
417 :
418 0 : return nsMathMLFrame::UpdatePresentationData(aFlagsValues, aWhichFlags);
419 : }
420 :
421 : NS_IMETHODIMP
422 0 : nsMathMLmtableOuterFrame::UpdatePresentationDataFromChildAt(PRInt32 aFirstIndex,
423 : PRInt32 aLastIndex,
424 : PRUint32 aFlagsValues,
425 : PRUint32 aWhichFlags)
426 : {
427 0 : if (NS_MATHML_HAS_EXPLICIT_DISPLAYSTYLE(mPresentationData.flags)) {
428 : // our current state takes precedence, disallow updating the displastyle
429 0 : aWhichFlags &= ~NS_MATHML_DISPLAYSTYLE;
430 0 : aFlagsValues &= ~NS_MATHML_DISPLAYSTYLE;
431 : }
432 :
433 : nsMathMLContainerFrame::PropagatePresentationDataFromChildAt(this,
434 0 : aFirstIndex, aLastIndex, aFlagsValues, aWhichFlags);
435 :
436 0 : return NS_OK;
437 : }
438 :
439 : NS_IMETHODIMP
440 0 : nsMathMLmtableOuterFrame::AttributeChanged(PRInt32 aNameSpaceID,
441 : nsIAtom* aAttribute,
442 : PRInt32 aModType)
443 : {
444 : // Attributes specific to <mtable>:
445 : // frame : in mathml.css
446 : // framespacing : not yet supported
447 : // groupalign : not yet supported
448 : // equalrows : not yet supported
449 : // equalcolumns : not yet supported
450 : // displaystyle : here
451 : // align : in reflow
452 : // rowalign : here
453 : // rowlines : here
454 : // rowspacing : not yet supported
455 : // columnalign : here
456 : // columnlines : here
457 : // columnspacing : not yet supported
458 :
459 : // mtable is simple and only has one (pseudo) row-group inside our inner-table
460 0 : nsIFrame* tableFrame = mFrames.FirstChild();
461 0 : NS_ASSERTION(tableFrame && tableFrame->GetType() == nsGkAtoms::tableFrame,
462 : "should always have an inner table frame");
463 0 : nsIFrame* rgFrame = tableFrame->GetFirstPrincipalChild();
464 0 : if (!rgFrame || rgFrame->GetType() != nsGkAtoms::tableRowGroupFrame)
465 0 : return NS_OK;
466 :
467 : // align - just need to issue a dirty (resize) reflow command
468 0 : if (aAttribute == nsGkAtoms::align) {
469 0 : PresContext()->PresShell()->
470 0 : FrameNeedsReflow(this, nsIPresShell::eResize, NS_FRAME_IS_DIRTY);
471 0 : return NS_OK;
472 : }
473 :
474 : // displaystyle - may seem innocuous, but it is actually very harsh --
475 : // like changing an unit. Blow away and recompute all our automatic
476 : // presentational data, and issue a style-changed reflow request
477 0 : if (aAttribute == nsGkAtoms::displaystyle_) {
478 0 : nsMathMLContainerFrame::RebuildAutomaticDataForChildren(mParent);
479 : // Need to reflow the parent, not us, because this can actually
480 : // affect siblings.
481 0 : PresContext()->PresShell()->
482 0 : FrameNeedsReflow(mParent, nsIPresShell::eStyleChange, NS_FRAME_IS_DIRTY);
483 0 : return NS_OK;
484 : }
485 :
486 : // ...and the other attributes affect rows or columns in one way or another
487 0 : nsIAtom* MOZrowAtom = nsnull;
488 0 : nsIAtom* MOZcolAtom = nsnull;
489 0 : if (aAttribute == nsGkAtoms::rowalign_)
490 0 : MOZrowAtom = nsGkAtoms::_moz_math_rowalign_;
491 0 : else if (aAttribute == nsGkAtoms::rowlines_)
492 0 : MOZrowAtom = nsGkAtoms::_moz_math_rowline_;
493 0 : else if (aAttribute == nsGkAtoms::columnalign_)
494 0 : MOZcolAtom = nsGkAtoms::_moz_math_columnalign_;
495 0 : else if (aAttribute == nsGkAtoms::columnlines_)
496 0 : MOZcolAtom = nsGkAtoms::_moz_math_columnline_;
497 :
498 0 : if (!MOZrowAtom && !MOZcolAtom)
499 0 : return NS_OK;
500 :
501 0 : nsPresContext* presContext = tableFrame->PresContext();
502 : // clear any cached nsValueList for this table
503 : presContext->PropertyTable()->
504 0 : Delete(tableFrame, AttributeToProperty(aAttribute));
505 :
506 : // unset any _moz attribute that we may have set earlier, and re-sync
507 0 : nsIFrame* rowFrame = rgFrame->GetFirstPrincipalChild();
508 0 : for ( ; rowFrame; rowFrame = rowFrame->GetNextSibling()) {
509 0 : if (rowFrame->GetType() == nsGkAtoms::tableRowFrame) {
510 0 : if (MOZrowAtom) { // let rows do the work
511 0 : rowFrame->GetContent()->UnsetAttr(kNameSpaceID_None, MOZrowAtom, false);
512 0 : MapRowAttributesIntoCSS(tableFrame, rowFrame);
513 : } else { // let cells do the work
514 0 : nsIFrame* cellFrame = rowFrame->GetFirstPrincipalChild();
515 0 : for ( ; cellFrame; cellFrame = cellFrame->GetNextSibling()) {
516 0 : if (IS_TABLE_CELL(cellFrame->GetType())) {
517 0 : cellFrame->GetContent()->UnsetAttr(kNameSpaceID_None, MOZcolAtom, false);
518 0 : MapColAttributesIntoCSS(tableFrame, rowFrame, cellFrame);
519 : }
520 : }
521 : }
522 : }
523 : }
524 :
525 : // Explicitly request a re-resolve and reflow in our subtree to pick up any changes
526 : presContext->PresShell()->FrameConstructor()->
527 : PostRestyleEvent(mContent->AsElement(), eRestyle_Subtree,
528 0 : nsChangeHint_ReflowFrame);
529 :
530 0 : return NS_OK;
531 : }
532 :
533 : nsIFrame*
534 0 : nsMathMLmtableOuterFrame::GetRowFrameAt(nsPresContext* aPresContext,
535 : PRInt32 aRowIndex)
536 : {
537 : PRInt32 rowCount, colCount;
538 0 : GetTableSize(rowCount, colCount);
539 :
540 : // Negative indices mean to find upwards from the end.
541 0 : if (aRowIndex < 0) {
542 0 : aRowIndex = rowCount + aRowIndex;
543 : } else {
544 : // aRowIndex is 1-based, so convert it to a 0-based index
545 0 : --aRowIndex;
546 : }
547 :
548 : // if our inner table says that the index is valid, find the row now
549 0 : if (0 <= aRowIndex && aRowIndex <= rowCount) {
550 0 : nsIFrame* tableFrame = mFrames.FirstChild();
551 0 : NS_ASSERTION(tableFrame && tableFrame->GetType() == nsGkAtoms::tableFrame,
552 : "should always have an inner table frame");
553 0 : nsIFrame* rgFrame = tableFrame->GetFirstPrincipalChild();
554 0 : if (!rgFrame || rgFrame->GetType() != nsGkAtoms::tableRowGroupFrame)
555 0 : return nsnull;
556 0 : nsTableIterator rowIter(*rgFrame);
557 0 : nsIFrame* rowFrame = rowIter.First();
558 0 : for ( ; rowFrame; rowFrame = rowIter.Next()) {
559 0 : if (aRowIndex == 0) {
560 0 : DEBUG_VERIFY_THAT_FRAME_IS(rowFrame, TABLE_ROW);
561 0 : if (rowFrame->GetType() != nsGkAtoms::tableRowFrame)
562 0 : return nsnull;
563 :
564 0 : return rowFrame;
565 : }
566 0 : --aRowIndex;
567 : }
568 : }
569 0 : return nsnull;
570 : }
571 :
572 : NS_IMETHODIMP
573 0 : nsMathMLmtableOuterFrame::Reflow(nsPresContext* aPresContext,
574 : nsHTMLReflowMetrics& aDesiredSize,
575 : const nsHTMLReflowState& aReflowState,
576 : nsReflowStatus& aStatus)
577 : {
578 : nsresult rv;
579 0 : nsAutoString value;
580 : // we want to return a table that is anchored according to the align attribute
581 :
582 : rv = nsTableOuterFrame::Reflow(aPresContext, aDesiredSize, aReflowState,
583 0 : aStatus);
584 0 : NS_ASSERTION(aDesiredSize.height >= 0, "illegal height for mtable");
585 0 : NS_ASSERTION(aDesiredSize.width >= 0, "illegal width for mtable");
586 :
587 : // see if the user has set the align attribute on the <mtable>
588 : // XXX should we also check <mstyle> ?
589 0 : PRInt32 rowIndex = 0;
590 0 : eAlign tableAlign = eAlign_axis;
591 0 : GetAttribute(mContent, nsnull, nsGkAtoms::align, value);
592 0 : if (!value.IsEmpty()) {
593 0 : ParseAlignAttribute(value, tableAlign, rowIndex);
594 : }
595 :
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 0 : nscoord dy = 0;
601 0 : nscoord height = aDesiredSize.height;
602 0 : nsIFrame* rowFrame = nsnull;
603 0 : if (rowIndex) {
604 0 : rowFrame = GetRowFrameAt(aPresContext, rowIndex);
605 0 : if (rowFrame) {
606 : // translate the coordinates to be relative to us
607 0 : nsIFrame* frame = rowFrame;
608 0 : height = frame->GetSize().height;
609 0 : do {
610 0 : dy += frame->GetPosition().y;
611 0 : frame = frame->GetParent();
612 : } while (frame != this);
613 : }
614 : }
615 0 : switch (tableAlign) {
616 : case eAlign_top:
617 0 : aDesiredSize.ascent = dy;
618 0 : break;
619 : case eAlign_bottom:
620 0 : aDesiredSize.ascent = dy + height;
621 0 : break;
622 : case eAlign_center:
623 0 : aDesiredSize.ascent = dy + height/2;
624 0 : break;
625 : case eAlign_baseline:
626 0 : if (rowFrame) {
627 : // anchor the table on the baseline of the row of reference
628 0 : nscoord rowAscent = ((nsTableRowFrame*)rowFrame)->GetMaxCellAscent();
629 0 : if (rowAscent) { // the row has at least one cell with 'vertical-align: baseline'
630 0 : aDesiredSize.ascent = dy + rowAscent;
631 0 : break;
632 : }
633 : }
634 : // in other situations, fallback to center
635 0 : aDesiredSize.ascent = dy + height/2;
636 0 : break;
637 : case eAlign_axis:
638 : default: {
639 : // XXX should instead use style data from the row of reference here ?
640 0 : nsRefPtr<nsFontMetrics> fm;
641 0 : nsLayoutUtils::GetFontMetricsForFrame(this, getter_AddRefs(fm));
642 0 : aReflowState.rendContext->SetFont(fm);
643 : nscoord axisHeight;
644 : GetAxisHeight(*aReflowState.rendContext,
645 : aReflowState.rendContext->FontMetrics(),
646 0 : axisHeight);
647 0 : if (rowFrame) {
648 : // anchor the table on the axis of the row of reference
649 : // XXX fallback to baseline because it is a hard problem
650 : // XXX need to fetch the axis of the row; would need rowalign=axis to work better
651 0 : nscoord rowAscent = ((nsTableRowFrame*)rowFrame)->GetMaxCellAscent();
652 0 : if (rowAscent) { // the row has at least one cell with 'vertical-align: baseline'
653 0 : aDesiredSize.ascent = dy + rowAscent;
654 : break;
655 : }
656 : }
657 : // in other situations, fallback to using half of the height
658 0 : aDesiredSize.ascent = dy + height/2 + axisHeight;
659 : }
660 : }
661 :
662 0 : mReference.x = 0;
663 0 : mReference.y = aDesiredSize.ascent;
664 :
665 : // just make-up a bounding metrics
666 0 : mBoundingMetrics = nsBoundingMetrics();
667 0 : mBoundingMetrics.ascent = aDesiredSize.ascent;
668 0 : mBoundingMetrics.descent = aDesiredSize.height - aDesiredSize.ascent;
669 0 : mBoundingMetrics.width = aDesiredSize.width;
670 0 : mBoundingMetrics.leftBearing = 0;
671 0 : mBoundingMetrics.rightBearing = aDesiredSize.width;
672 :
673 0 : aDesiredSize.mBoundingMetrics = mBoundingMetrics;
674 0 : NS_FRAME_SET_TRUNCATION(aStatus, aReflowState, aDesiredSize);
675 :
676 0 : return rv;
677 : }
678 :
679 : nsIFrame*
680 0 : NS_NewMathMLmtableFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
681 : {
682 0 : return new (aPresShell) nsMathMLmtableFrame(aContext);
683 : }
684 :
685 0 : NS_IMPL_FRAMEARENA_HELPERS(nsMathMLmtableFrame)
686 :
687 0 : nsMathMLmtableFrame::~nsMathMLmtableFrame()
688 : {
689 0 : }
690 :
691 : NS_IMETHODIMP
692 0 : nsMathMLmtableFrame::SetInitialChildList(ChildListID aListID,
693 : nsFrameList& aChildList)
694 : {
695 0 : nsresult rv = nsTableFrame::SetInitialChildList(aListID, aChildList);
696 0 : if (NS_FAILED(rv)) return rv;
697 0 : MapAllAttributesIntoCSS(this);
698 0 : return rv;
699 : }
700 :
701 : void
702 0 : nsMathMLmtableFrame::RestyleTable()
703 : {
704 : // re-sync MathML specific style data that may have changed
705 0 : MapAllAttributesIntoCSS(this);
706 :
707 : // Explicitly request a re-resolve and reflow in our subtree to pick up any changes
708 : PresContext()->PresShell()->FrameConstructor()->
709 : PostRestyleEvent(mContent->AsElement(), eRestyle_Subtree,
710 0 : nsChangeHint_ReflowFrame);
711 0 : }
712 :
713 : // --------
714 : // implementation of nsMathMLmtrFrame
715 :
716 : nsIFrame*
717 0 : NS_NewMathMLmtrFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
718 : {
719 0 : return new (aPresShell) nsMathMLmtrFrame(aContext);
720 : }
721 :
722 0 : NS_IMPL_FRAMEARENA_HELPERS(nsMathMLmtrFrame)
723 :
724 0 : nsMathMLmtrFrame::~nsMathMLmtrFrame()
725 : {
726 0 : }
727 :
728 : NS_IMETHODIMP
729 0 : nsMathMLmtrFrame::AttributeChanged(PRInt32 aNameSpaceID,
730 : nsIAtom* aAttribute,
731 : PRInt32 aModType)
732 : {
733 : // Attributes specific to <mtr>:
734 : // groupalign : Not yet supported.
735 : // rowalign : Fully specified in mathml.css, and so HasAttributeDependentStyle() will
736 : // pick it up and nsCSSFrameConstructor will issue a PostRestyleEvent().
737 : // columnalign : Need an explicit re-style call.
738 :
739 0 : if (aAttribute == nsGkAtoms::rowalign_) {
740 : // unset any _moz attribute that we may have set earlier, and re-sync
741 : mContent->UnsetAttr(kNameSpaceID_None, nsGkAtoms::_moz_math_rowalign_,
742 0 : false);
743 0 : MapRowAttributesIntoCSS(nsTableFrame::GetTableFrame(this), this);
744 : // That's all - see comment above.
745 0 : return NS_OK;
746 : }
747 :
748 0 : if (aAttribute != nsGkAtoms::columnalign_)
749 0 : return NS_OK;
750 :
751 0 : nsPresContext* presContext = PresContext();
752 : // Clear any cached columnalign's nsValueList for this row
753 0 : presContext->PropertyTable()->Delete(this, AttributeToProperty(aAttribute));
754 :
755 : // Clear any internal _moz attribute that we may have set earlier
756 : // in our cells and re-sync their columnalign attribute
757 0 : nsTableFrame* tableFrame = nsTableFrame::GetTableFrame(this);
758 0 : nsIFrame* cellFrame = GetFirstPrincipalChild();
759 0 : for ( ; cellFrame; cellFrame = cellFrame->GetNextSibling()) {
760 0 : if (IS_TABLE_CELL(cellFrame->GetType())) {
761 0 : cellFrame->GetContent()->
762 : UnsetAttr(kNameSpaceID_None, nsGkAtoms::_moz_math_columnalign_,
763 0 : false);
764 0 : MapColAttributesIntoCSS(tableFrame, this, cellFrame);
765 : }
766 : }
767 :
768 : // Explicitly request a re-resolve and reflow in our subtree to pick up any changes
769 : presContext->PresShell()->FrameConstructor()->
770 : PostRestyleEvent(mContent->AsElement(), eRestyle_Subtree,
771 0 : nsChangeHint_ReflowFrame);
772 :
773 0 : return NS_OK;
774 : }
775 :
776 : // --------
777 : // implementation of nsMathMLmtdFrame
778 :
779 : nsIFrame*
780 0 : NS_NewMathMLmtdFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
781 : {
782 0 : return new (aPresShell) nsMathMLmtdFrame(aContext);
783 : }
784 :
785 0 : NS_IMPL_FRAMEARENA_HELPERS(nsMathMLmtdFrame)
786 :
787 0 : nsMathMLmtdFrame::~nsMathMLmtdFrame()
788 : {
789 0 : }
790 :
791 : PRInt32
792 0 : nsMathMLmtdFrame::GetRowSpan()
793 : {
794 0 : PRInt32 rowspan = 1;
795 :
796 : // Don't look at the content's rowspan if we're not an mtd or a pseudo cell.
797 0 : if ((mContent->Tag() == nsGkAtoms::mtd_) && !GetStyleContext()->GetPseudo()) {
798 0 : nsAutoString value;
799 0 : mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::rowspan, value);
800 0 : if (!value.IsEmpty()) {
801 : PRInt32 error;
802 0 : rowspan = value.ToInteger(&error);
803 0 : if (error || rowspan < 0)
804 0 : rowspan = 1;
805 0 : rowspan = NS_MIN(rowspan, MAX_ROWSPAN);
806 : }
807 : }
808 0 : return rowspan;
809 : }
810 :
811 : PRInt32
812 0 : nsMathMLmtdFrame::GetColSpan()
813 : {
814 0 : PRInt32 colspan = 1;
815 :
816 : // Don't look at the content's colspan if we're not an mtd or a pseudo cell.
817 0 : if ((mContent->Tag() == nsGkAtoms::mtd_) && !GetStyleContext()->GetPseudo()) {
818 0 : nsAutoString value;
819 0 : mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::columnspan_, value);
820 0 : if (!value.IsEmpty()) {
821 : PRInt32 error;
822 0 : colspan = value.ToInteger(&error);
823 0 : if (error || colspan < 0 || colspan > MAX_COLSPAN)
824 0 : colspan = 1;
825 : }
826 : }
827 0 : return colspan;
828 : }
829 :
830 : NS_IMETHODIMP
831 0 : nsMathMLmtdFrame::AttributeChanged(PRInt32 aNameSpaceID,
832 : nsIAtom* aAttribute,
833 : PRInt32 aModType)
834 : {
835 : // Attributes specific to <mtd>:
836 : // groupalign : Not yet supported
837 : // rowalign : in mathml.css
838 : // columnalign : here
839 : // rowspan : here
840 : // columnspan : here
841 :
842 0 : if (aAttribute == nsGkAtoms::columnalign_) {
843 : // unset any _moz attribute that we may have set earlier, and re-sync
844 : mContent->UnsetAttr(kNameSpaceID_None, nsGkAtoms::_moz_math_columnalign_,
845 0 : false);
846 0 : MapColAttributesIntoCSS(nsTableFrame::GetTableFrame(this), mParent, this);
847 0 : return NS_OK;
848 : }
849 :
850 0 : if (aAttribute == nsGkAtoms::rowspan ||
851 : aAttribute == nsGkAtoms::columnspan_) {
852 : // use the naming expected by the base class
853 0 : if (aAttribute == nsGkAtoms::columnspan_)
854 0 : aAttribute = nsGkAtoms::colspan;
855 0 : return nsTableCellFrame::AttributeChanged(aNameSpaceID, aAttribute, aModType);
856 : }
857 :
858 0 : return NS_OK;
859 : }
860 :
861 : // --------
862 : // implementation of nsMathMLmtdInnerFrame
863 :
864 0 : NS_QUERYFRAME_HEAD(nsMathMLmtdInnerFrame)
865 0 : NS_QUERYFRAME_ENTRY(nsIMathMLFrame)
866 0 : NS_QUERYFRAME_TAIL_INHERITING(nsBlockFrame)
867 :
868 : nsIFrame*
869 0 : NS_NewMathMLmtdInnerFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
870 : {
871 0 : return new (aPresShell) nsMathMLmtdInnerFrame(aContext);
872 : }
873 :
874 0 : NS_IMPL_FRAMEARENA_HELPERS(nsMathMLmtdInnerFrame)
875 :
876 0 : nsMathMLmtdInnerFrame::~nsMathMLmtdInnerFrame()
877 : {
878 0 : }
879 :
880 : NS_IMETHODIMP
881 0 : nsMathMLmtdInnerFrame::Reflow(nsPresContext* aPresContext,
882 : nsHTMLReflowMetrics& aDesiredSize,
883 : const nsHTMLReflowState& aReflowState,
884 : nsReflowStatus& aStatus)
885 : {
886 : // Let the base class do the reflow
887 0 : nsresult rv = nsBlockFrame::Reflow(aPresContext, aDesiredSize, aReflowState, aStatus);
888 :
889 : // more about <maligngroup/> and <malignmark/> later
890 : // ...
891 0 : return rv;
892 : }
|