Line data Source 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/. */
5 :
6 : #include "gfxContext.h"
7 : #include "nsMathMLmtableFrame.h"
8 : #include "nsPresContext.h"
9 : #include "nsStyleContext.h"
10 : #include "nsStyleConsts.h"
11 : #include "nsNameSpaceManager.h"
12 : #include "nsCSSRendering.h"
13 : #include "nsMathMLElement.h"
14 :
15 : #include "nsTArray.h"
16 : #include "nsTableFrame.h"
17 : #include "celldata.h"
18 :
19 : #include "mozilla/RestyleManager.h"
20 : #include "mozilla/RestyleManagerInlines.h"
21 : #include <algorithm>
22 :
23 : #include "nsIScriptError.h"
24 : #include "nsContentUtils.h"
25 :
26 : using namespace mozilla;
27 : using namespace mozilla::image;
28 :
29 : //
30 : // <mtable> -- table or matrix - implementation
31 : //
32 :
33 : static int8_t
34 0 : ParseStyleValue(nsIAtom* aAttribute, const nsAString& aAttributeValue)
35 : {
36 0 : if (aAttribute == nsGkAtoms::rowalign_) {
37 0 : if (aAttributeValue.EqualsLiteral("top"))
38 0 : return NS_STYLE_VERTICAL_ALIGN_TOP;
39 0 : else if (aAttributeValue.EqualsLiteral("bottom"))
40 0 : return NS_STYLE_VERTICAL_ALIGN_BOTTOM;
41 0 : else if (aAttributeValue.EqualsLiteral("center"))
42 0 : return NS_STYLE_VERTICAL_ALIGN_MIDDLE;
43 : else
44 0 : return NS_STYLE_VERTICAL_ALIGN_BASELINE;
45 0 : } else if (aAttribute == nsGkAtoms::columnalign_) {
46 0 : if (aAttributeValue.EqualsLiteral("left"))
47 0 : return NS_STYLE_TEXT_ALIGN_LEFT;
48 0 : else if (aAttributeValue.EqualsLiteral("right"))
49 0 : return NS_STYLE_TEXT_ALIGN_RIGHT;
50 : else
51 0 : return NS_STYLE_TEXT_ALIGN_CENTER;
52 0 : } else if (aAttribute == nsGkAtoms::rowlines_ ||
53 0 : aAttribute == nsGkAtoms::columnlines_) {
54 0 : if (aAttributeValue.EqualsLiteral("solid"))
55 0 : return NS_STYLE_BORDER_STYLE_SOLID;
56 0 : else if (aAttributeValue.EqualsLiteral("dashed"))
57 0 : return NS_STYLE_BORDER_STYLE_DASHED;
58 : else
59 0 : return NS_STYLE_BORDER_STYLE_NONE;
60 : } else {
61 0 : MOZ_CRASH("Unrecognized attribute.");
62 : }
63 :
64 : return -1;
65 : }
66 :
67 : static nsTArray<int8_t>*
68 0 : ExtractStyleValues(const nsAString& aString,
69 : nsIAtom* aAttribute,
70 : bool aAllowMultiValues)
71 : {
72 0 : nsTArray<int8_t>* styleArray = nullptr;
73 :
74 0 : const char16_t* start = aString.BeginReading();
75 0 : const char16_t* end = aString.EndReading();
76 :
77 0 : int32_t startIndex = 0;
78 0 : int32_t count = 0;
79 :
80 0 : while (start < end) {
81 : // Skip leading spaces.
82 0 : while ((start < end) && nsCRT::IsAsciiSpace(*start)) {
83 0 : start++;
84 0 : startIndex++;
85 : }
86 :
87 : // Look for the end of the string, or another space.
88 0 : while ((start < end) && !nsCRT::IsAsciiSpace(*start)) {
89 0 : start++;
90 0 : count++;
91 : }
92 :
93 : // Grab the value found and process it.
94 0 : if (count > 0) {
95 0 : if (!styleArray)
96 0 : styleArray = new nsTArray<int8_t>();
97 :
98 : // We want to return a null array if an attribute gives multiple values,
99 : // but multiple values aren't allowed.
100 0 : if (styleArray->Length() > 1 && !aAllowMultiValues) {
101 0 : delete styleArray;
102 0 : return nullptr;
103 : }
104 :
105 0 : nsDependentSubstring valueString(aString, startIndex, count);
106 0 : int8_t styleValue = ParseStyleValue(aAttribute, valueString);
107 0 : styleArray->AppendElement(styleValue);
108 :
109 0 : startIndex += count;
110 0 : count = 0;
111 : }
112 : }
113 0 : return styleArray;
114 : }
115 :
116 : static nsresult
117 0 : ReportParseError(nsIFrame* aFrame,
118 : const char16_t* aAttribute,
119 : const char16_t* aValue)
120 : {
121 0 : nsIContent* content = aFrame->GetContent();
122 :
123 : const char16_t* params[] =
124 0 : { aValue, aAttribute, content->NodeInfo()->NameAtom()->GetUTF16String() };
125 :
126 0 : return nsContentUtils::ReportToConsole(nsIScriptError::errorFlag,
127 0 : NS_LITERAL_CSTRING("Layout: MathML"),
128 0 : content->OwnerDoc(),
129 : nsContentUtils::eMATHML_PROPERTIES,
130 0 : "AttributeParsingError", params, 3);
131 : }
132 :
133 : // Each rowalign='top bottom' or columnalign='left right center' (from
134 : // <mtable> or <mtr>) is split once into an nsTArray<int8_t> which is
135 : // stored in the property table. Row/Cell frames query the property table
136 : // to see what values apply to them.
137 :
138 0 : NS_DECLARE_FRAME_PROPERTY_DELETABLE(RowAlignProperty, nsTArray<int8_t>)
139 0 : NS_DECLARE_FRAME_PROPERTY_DELETABLE(RowLinesProperty, nsTArray<int8_t>)
140 0 : NS_DECLARE_FRAME_PROPERTY_DELETABLE(ColumnAlignProperty, nsTArray<int8_t>)
141 0 : NS_DECLARE_FRAME_PROPERTY_DELETABLE(ColumnLinesProperty, nsTArray<int8_t>)
142 :
143 : static const FramePropertyDescriptor<nsTArray<int8_t>>*
144 0 : AttributeToProperty(nsIAtom* aAttribute)
145 : {
146 0 : if (aAttribute == nsGkAtoms::rowalign_)
147 0 : return RowAlignProperty();
148 0 : if (aAttribute == nsGkAtoms::rowlines_)
149 0 : return RowLinesProperty();
150 0 : if (aAttribute == nsGkAtoms::columnalign_)
151 0 : return ColumnAlignProperty();
152 0 : NS_ASSERTION(aAttribute == nsGkAtoms::columnlines_, "Invalid attribute");
153 0 : return ColumnLinesProperty();
154 : }
155 :
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 hierarchy 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 0 : FindCellProperty(const nsIFrame* aCellFrame,
164 : const FramePropertyDescriptor<nsTArray<int8_t>>* aFrameProperty)
165 : {
166 0 : const nsIFrame* currentFrame = aCellFrame;
167 0 : nsTArray<int8_t>* propertyData = nullptr;
168 :
169 0 : while (currentFrame) {
170 0 : propertyData = currentFrame->GetProperty(aFrameProperty);
171 0 : bool frameIsTable = (currentFrame->IsTableFrame());
172 :
173 0 : if (propertyData || frameIsTable)
174 0 : currentFrame = nullptr; // A null frame pointer exits the loop
175 : else
176 0 : currentFrame = currentFrame->GetParent(); // Go to the parent frame
177 : }
178 :
179 0 : return propertyData;
180 : }
181 :
182 : static void
183 0 : ApplyBorderToStyle(const nsMathMLmtdFrame* aFrame,
184 : nsStyleBorder& aStyleBorder)
185 : {
186 : int32_t rowIndex;
187 : int32_t columnIndex;
188 0 : aFrame->GetRowIndex(rowIndex);
189 0 : aFrame->GetColIndex(columnIndex);
190 :
191 : nscoord borderWidth =
192 0 : nsPresContext::GetBorderWidthForKeyword(NS_STYLE_BORDER_WIDTH_THIN);
193 :
194 : nsTArray<int8_t>* rowLinesList =
195 0 : FindCellProperty(aFrame, RowLinesProperty());
196 :
197 : nsTArray<int8_t>* columnLinesList =
198 0 : FindCellProperty(aFrame, ColumnLinesProperty());
199 :
200 : // We don't place a row line on top of the first row
201 0 : if (rowIndex > 0 && rowLinesList) {
202 : // If the row number is greater than the number of provided rowline
203 : // values, we simply repeat the last value.
204 0 : int32_t listLength = rowLinesList->Length();
205 0 : if (rowIndex < listLength) {
206 0 : aStyleBorder.SetBorderStyle(eSideTop,
207 0 : rowLinesList->ElementAt(rowIndex - 1));
208 : } else {
209 0 : aStyleBorder.SetBorderStyle(eSideTop,
210 0 : rowLinesList->ElementAt(listLength - 1));
211 : }
212 0 : aStyleBorder.SetBorderWidth(eSideTop, borderWidth);
213 : }
214 :
215 : // We don't place a column line on the left of the first column.
216 0 : if (columnIndex > 0 && columnLinesList) {
217 : // If the column number is greater than the number of provided columline
218 : // values, we simply repeat the last value.
219 0 : int32_t listLength = columnLinesList->Length();
220 0 : if (columnIndex < listLength) {
221 0 : aStyleBorder.SetBorderStyle(eSideLeft,
222 0 : columnLinesList->ElementAt(columnIndex - 1));
223 : } else {
224 0 : aStyleBorder.SetBorderStyle(eSideLeft,
225 0 : columnLinesList->ElementAt(listLength - 1));
226 : }
227 0 : aStyleBorder.SetBorderWidth(eSideLeft, borderWidth);
228 : }
229 0 : }
230 :
231 : static nsMargin
232 0 : ComputeBorderOverflow(nsMathMLmtdFrame* aFrame,
233 : const nsStyleBorder& aStyleBorder)
234 : {
235 0 : nsMargin overflow;
236 : int32_t rowIndex;
237 : int32_t columnIndex;
238 0 : nsTableFrame* table = aFrame->GetTableFrame();
239 0 : aFrame->GetCellIndexes(rowIndex, columnIndex);
240 0 : if (!columnIndex) {
241 0 : overflow.left = table->GetColSpacing(-1);
242 0 : overflow.right = table->GetColSpacing(0) / 2;
243 0 : } else if (columnIndex == table->GetColCount() - 1) {
244 0 : overflow.left = table->GetColSpacing(columnIndex - 1) / 2;
245 0 : overflow.right = table->GetColSpacing(columnIndex + 1);
246 : } else {
247 0 : overflow.left = table->GetColSpacing(columnIndex - 1) / 2;
248 0 : overflow.right = table->GetColSpacing(columnIndex) / 2;
249 : }
250 0 : if (!rowIndex) {
251 0 : overflow.top = table->GetRowSpacing(-1);
252 0 : overflow.bottom = table->GetRowSpacing(0) / 2;
253 0 : } else if (rowIndex == table->GetRowCount() - 1) {
254 0 : overflow.top = table->GetRowSpacing(rowIndex - 1) / 2;
255 0 : overflow.bottom = table->GetRowSpacing(rowIndex + 1);
256 : } else {
257 0 : overflow.top = table->GetRowSpacing(rowIndex - 1) / 2;
258 0 : overflow.bottom = table->GetRowSpacing(rowIndex) / 2;
259 : }
260 0 : return overflow;
261 : }
262 :
263 : /*
264 : * A variant of the nsDisplayBorder contains special code to render a border
265 : * around a nsMathMLmtdFrame based on the rowline and columnline properties
266 : * set on the cell frame.
267 : */
268 0 : class nsDisplaymtdBorder : public nsDisplayBorder
269 : {
270 : public:
271 0 : nsDisplaymtdBorder(nsDisplayListBuilder* aBuilder, nsMathMLmtdFrame* aFrame)
272 0 : : nsDisplayBorder(aBuilder, aFrame)
273 : {
274 0 : }
275 :
276 0 : nsDisplayItemGeometry* AllocateGeometry(nsDisplayListBuilder* aBuilder) override
277 : {
278 0 : return new nsDisplayItemGenericImageGeometry(this, aBuilder);
279 : }
280 :
281 0 : void ComputeInvalidationRegion(nsDisplayListBuilder* aBuilder,
282 : const nsDisplayItemGeometry* aGeometry,
283 : nsRegion* aInvalidRegion) override
284 : {
285 : auto geometry =
286 0 : static_cast<const nsDisplayItemGenericImageGeometry*>(aGeometry);
287 :
288 0 : if (aBuilder->ShouldSyncDecodeImages() &&
289 0 : geometry->ShouldInvalidateToSyncDecodeImages()) {
290 : bool snap;
291 0 : aInvalidRegion->Or(*aInvalidRegion, GetBounds(aBuilder, &snap));
292 : }
293 :
294 0 : nsDisplayItem::ComputeInvalidationRegion(aBuilder, aGeometry, aInvalidRegion);
295 0 : }
296 :
297 0 : virtual nsRect GetBounds(nsDisplayListBuilder* aBuilder, bool* aSnap) override
298 : {
299 0 : *aSnap = true;
300 0 : nsStyleBorder styleBorder = *mFrame->StyleBorder();
301 0 : nsMathMLmtdFrame* frame = static_cast<nsMathMLmtdFrame*>(mFrame);
302 0 : ApplyBorderToStyle(frame, styleBorder);
303 0 : nsRect bounds = CalculateBounds(styleBorder).GetBounds();
304 0 : nsMargin overflow = ComputeBorderOverflow(frame, styleBorder);
305 0 : bounds.Inflate(overflow);
306 0 : return bounds;
307 : }
308 :
309 0 : virtual void Paint(nsDisplayListBuilder* aBuilder, gfxContext* aCtx) override
310 : {
311 0 : nsStyleBorder styleBorder = *mFrame->StyleBorder();
312 0 : nsMathMLmtdFrame* frame = static_cast<nsMathMLmtdFrame*>(mFrame);
313 0 : ApplyBorderToStyle(frame, styleBorder);
314 :
315 0 : nsRect bounds = nsRect(ToReferenceFrame(), mFrame->GetSize());
316 0 : nsMargin overflow = ComputeBorderOverflow(frame, styleBorder);
317 0 : bounds.Inflate(overflow);
318 :
319 0 : PaintBorderFlags flags = aBuilder->ShouldSyncDecodeImages()
320 0 : ? PaintBorderFlags::SYNC_DECODE_IMAGES
321 0 : : PaintBorderFlags();
322 :
323 : DrawResult result =
324 0 : nsCSSRendering::PaintBorderWithStyleBorder(mFrame->PresContext(), *aCtx,
325 : mFrame, mVisibleRect,
326 : bounds,
327 : styleBorder,
328 0 : mFrame->StyleContext(),
329 : flags,
330 0 : mFrame->GetSkipSides());
331 :
332 0 : nsDisplayItemGenericImageGeometry::UpdateDrawResult(this, result);
333 0 : }
334 : };
335 :
336 : #ifdef DEBUG
337 : #define DEBUG_VERIFY_THAT_FRAME_IS(_frame, _expected) \
338 : MOZ_ASSERT(mozilla::StyleDisplay::_expected == _frame->StyleDisplay()->mDisplay, \
339 : "internal error");
340 : #else
341 : #define DEBUG_VERIFY_THAT_FRAME_IS(_frame, _expected)
342 : #endif
343 :
344 : static void
345 0 : ParseFrameAttribute(nsIFrame* aFrame,
346 : nsIAtom* aAttribute,
347 : bool aAllowMultiValues)
348 : {
349 0 : nsAutoString attrValue;
350 :
351 0 : nsIContent* frameContent = aFrame->GetContent();
352 0 : frameContent->GetAttr(kNameSpaceID_None, aAttribute, attrValue);
353 :
354 0 : if (!attrValue.IsEmpty()) {
355 : nsTArray<int8_t>* valueList =
356 0 : ExtractStyleValues(attrValue, aAttribute, aAllowMultiValues);
357 :
358 : // If valueList is null, that indicates a problem with the attribute value.
359 : // Only set properties on a valid attribute value.
360 0 : if (valueList) {
361 : // The code reading the property assumes that this list is nonempty.
362 0 : NS_ASSERTION(valueList->Length() >= 1, "valueList should not be empty!");
363 0 : aFrame->SetProperty(AttributeToProperty(aAttribute), valueList);
364 : } else {
365 0 : ReportParseError(aFrame, aAttribute->GetUTF16String(), attrValue.get());
366 : }
367 : }
368 0 : }
369 :
370 : // rowspacing
371 : //
372 : // Specifies the distance between successive rows in an mtable. Multiple
373 : // lengths can be specified, each corresponding to its respective position
374 : // between rows. For example:
375 : //
376 : // [ROW_0]
377 : // rowspace_0
378 : // [ROW_1]
379 : // rowspace_1
380 : // [ROW_2]
381 : //
382 : // If the number of row gaps exceeds the number of lengths specified, the final
383 : // specified length is repeated. Additional lengths are ignored.
384 : //
385 : // values: (length)+
386 : // default: 1.0ex
387 : //
388 : // Unitless values are permitted and provide a multiple of the default value
389 : // Negative values are forbidden.
390 : //
391 :
392 : // columnspacing
393 : //
394 : // Specifies the distance between successive columns in an mtable. Multiple
395 : // lengths can be specified, each corresponding to its respective position
396 : // between columns. For example:
397 : //
398 : // [COLUMN_0] columnspace_0 [COLUMN_1] columnspace_1 [COLUMN_2]
399 : //
400 : // If the number of column gaps exceeds the number of lengths specified, the
401 : // final specified length is repeated. Additional lengths are ignored.
402 : //
403 : // values: (length)+
404 : // default: 0.8em
405 : //
406 : // Unitless values are permitted and provide a multiple of the default value
407 : // Negative values are forbidden.
408 : //
409 :
410 : // framespacing
411 : //
412 : // Specifies the distance between the mtable and its frame (if any). The
413 : // first value specified provides the spacing between the left and right edge
414 : // of the table and the frame, the second value determines the spacing between
415 : // the top and bottom edges and the frame.
416 : //
417 : // An error is reported if only one length is passed. Any additional lengths
418 : // are ignored
419 : //
420 : // values: length length
421 : // default: 0em 0ex If frame attribute is "none" or not specified,
422 : // 0.4em 0.5ex otherwise
423 : //
424 : // Unitless values are permitted and provide a multiple of the default value
425 : // Negative values are forbidden.
426 : //
427 :
428 : static const float kDefaultRowspacingEx = 1.0f;
429 : static const float kDefaultColumnspacingEm = 0.8f;
430 : static const float kDefaultFramespacingArg0Em = 0.4f;
431 : static const float kDefaultFramespacingArg1Ex = 0.5f;
432 :
433 : static void
434 0 : ExtractSpacingValues(const nsAString& aString,
435 : nsIAtom* aAttribute,
436 : nsTArray<nscoord>& aSpacingArray,
437 : nsIFrame* aFrame,
438 : nscoord aDefaultValue0,
439 : nscoord aDefaultValue1,
440 : float aFontSizeInflation)
441 : {
442 0 : nsPresContext* presContext = aFrame->PresContext();
443 0 : nsStyleContext* styleContext = aFrame->StyleContext();
444 :
445 0 : const char16_t* start = aString.BeginReading();
446 0 : const char16_t* end = aString.EndReading();
447 :
448 0 : int32_t startIndex = 0;
449 0 : int32_t count = 0;
450 0 : int32_t elementNum = 0;
451 :
452 0 : while (start < end) {
453 : // Skip leading spaces.
454 0 : while ((start < end) && nsCRT::IsAsciiSpace(*start)) {
455 0 : start++;
456 0 : startIndex++;
457 : }
458 :
459 : // Look for the end of the string, or another space.
460 0 : while ((start < end) && !nsCRT::IsAsciiSpace(*start)) {
461 0 : start++;
462 0 : count++;
463 : }
464 :
465 : // Grab the value found and process it.
466 0 : if (count > 0) {
467 0 : const nsAString& str = Substring(aString, startIndex, count);
468 0 : nsAutoString valueString;
469 0 : valueString.Assign(str);
470 : nscoord newValue;
471 0 : if (aAttribute == nsGkAtoms::framespacing_ && elementNum) {
472 0 : newValue = aDefaultValue1;
473 : } else {
474 0 : newValue = aDefaultValue0;
475 : }
476 : nsMathMLFrame::ParseNumericValue(valueString, &newValue,
477 : nsMathMLElement::PARSE_ALLOW_UNITLESS,
478 : presContext, styleContext,
479 0 : aFontSizeInflation);
480 0 : aSpacingArray.AppendElement(newValue);
481 :
482 0 : startIndex += count;
483 0 : count = 0;
484 0 : elementNum++;
485 : }
486 : }
487 0 : }
488 :
489 : static void
490 0 : ParseSpacingAttribute(nsMathMLmtableFrame* aFrame, nsIAtom* aAttribute)
491 : {
492 0 : NS_ASSERTION(aAttribute == nsGkAtoms::rowspacing_ ||
493 : aAttribute == nsGkAtoms::columnspacing_ ||
494 : aAttribute == nsGkAtoms::framespacing_,
495 : "Non spacing attribute passed");
496 :
497 0 : nsAutoString attrValue;
498 0 : nsIContent* frameContent = aFrame->GetContent();
499 0 : frameContent->GetAttr(kNameSpaceID_None, aAttribute, attrValue);
500 :
501 0 : if (nsGkAtoms::framespacing_ == aAttribute) {
502 0 : nsAutoString frame;
503 0 : frameContent->GetAttr(kNameSpaceID_None, nsGkAtoms::frame, frame);
504 0 : if (frame.IsEmpty() || frame.EqualsLiteral("none")) {
505 0 : aFrame->SetFrameSpacing(0, 0);
506 0 : return;
507 : }
508 : }
509 :
510 : nscoord value;
511 : nscoord value2;
512 : // Set defaults
513 0 : float fontSizeInflation = nsLayoutUtils::FontSizeInflationFor(aFrame);
514 : RefPtr<nsFontMetrics> fm =
515 0 : nsLayoutUtils::GetFontMetricsForFrame(aFrame, fontSizeInflation);
516 0 : if (nsGkAtoms::rowspacing_ == aAttribute) {
517 0 : value = kDefaultRowspacingEx * fm->XHeight();
518 0 : value2 = 0;
519 0 : } else if (nsGkAtoms::columnspacing_ == aAttribute) {
520 0 : value = kDefaultColumnspacingEm * fm->EmHeight();
521 0 : value2 = 0;
522 : } else {
523 0 : value = kDefaultFramespacingArg0Em * fm->EmHeight();
524 0 : value2 = kDefaultFramespacingArg1Ex * fm->XHeight();
525 : }
526 :
527 0 : nsTArray<nscoord> valueList;
528 : ExtractSpacingValues(attrValue, aAttribute, valueList, aFrame, value, value2,
529 0 : fontSizeInflation);
530 0 : if (valueList.Length() == 0) {
531 0 : if (frameContent->HasAttr(kNameSpaceID_None, aAttribute)) {
532 0 : ReportParseError(aFrame, aAttribute->GetUTF16String(),
533 0 : attrValue.get());
534 : }
535 0 : valueList.AppendElement(value);
536 : }
537 0 : if (aAttribute == nsGkAtoms::framespacing_) {
538 0 : if (valueList.Length() == 1) {
539 0 : if(frameContent->HasAttr(kNameSpaceID_None, aAttribute)) {
540 0 : ReportParseError(aFrame, aAttribute->GetUTF16String(),
541 0 : attrValue.get());
542 : }
543 0 : valueList.AppendElement(value2);
544 0 : } else if (valueList.Length() != 2) {
545 0 : ReportParseError(aFrame, aAttribute->GetUTF16String(),
546 0 : attrValue.get());
547 : }
548 : }
549 :
550 0 : if (aAttribute == nsGkAtoms::rowspacing_) {
551 0 : aFrame->SetRowSpacingArray(valueList);
552 0 : } else if (aAttribute == nsGkAtoms::columnspacing_) {
553 0 : aFrame->SetColSpacingArray(valueList);
554 : } else {
555 0 : aFrame->SetFrameSpacing(valueList.ElementAt(0),
556 0 : valueList.ElementAt(1));
557 : }
558 : }
559 :
560 0 : static void ParseSpacingAttributes(nsMathMLmtableFrame* aTableFrame)
561 : {
562 0 : ParseSpacingAttribute(aTableFrame, nsGkAtoms::rowspacing_);
563 0 : ParseSpacingAttribute(aTableFrame, nsGkAtoms::columnspacing_);
564 0 : ParseSpacingAttribute(aTableFrame, nsGkAtoms::framespacing_);
565 0 : aTableFrame->SetUseCSSSpacing();
566 0 : }
567 :
568 : // map all attributes within a table -- requires the indices of rows and cells.
569 : // so it can only happen after they are made ready by the table base class.
570 : static void
571 0 : MapAllAttributesIntoCSS(nsMathMLmtableFrame* aTableFrame)
572 : {
573 : // Map mtable rowalign & rowlines.
574 0 : ParseFrameAttribute(aTableFrame, nsGkAtoms::rowalign_, true);
575 0 : ParseFrameAttribute(aTableFrame, nsGkAtoms::rowlines_, true);
576 :
577 : // Map mtable columnalign & columnlines.
578 0 : ParseFrameAttribute(aTableFrame, nsGkAtoms::columnalign_, true);
579 0 : ParseFrameAttribute(aTableFrame, nsGkAtoms::columnlines_, true);
580 :
581 : // Map mtable rowspacing, columnspacing & framespacing
582 0 : ParseSpacingAttributes(aTableFrame);
583 :
584 : // mtable is simple and only has one (pseudo) row-group
585 0 : nsIFrame* rgFrame = aTableFrame->PrincipalChildList().FirstChild();
586 0 : if (!rgFrame || !rgFrame->IsTableRowGroupFrame())
587 0 : return;
588 :
589 0 : for (nsIFrame* rowFrame : rgFrame->PrincipalChildList()) {
590 0 : DEBUG_VERIFY_THAT_FRAME_IS(rowFrame, TableRow);
591 0 : if (rowFrame->IsTableRowFrame()) {
592 : // Map row rowalign.
593 0 : ParseFrameAttribute(rowFrame, nsGkAtoms::rowalign_, false);
594 : // Map row columnalign.
595 0 : ParseFrameAttribute(rowFrame, nsGkAtoms::columnalign_, true);
596 :
597 0 : for (nsIFrame* cellFrame : rowFrame->PrincipalChildList()) {
598 0 : DEBUG_VERIFY_THAT_FRAME_IS(cellFrame, TableCell);
599 0 : if (IS_TABLE_CELL(cellFrame->Type())) {
600 : // Map cell rowalign.
601 0 : ParseFrameAttribute(cellFrame, nsGkAtoms::rowalign_, false);
602 : // Map row columnalign.
603 0 : ParseFrameAttribute(cellFrame, nsGkAtoms::columnalign_, false);
604 : }
605 : }
606 : }
607 : }
608 : }
609 :
610 : // the align attribute of mtable can have a row number which indicates
611 : // from where to anchor the table, e.g., top 5 means anchor the table at
612 : // the top of the 5th row, axis -1 means anchor the table on the axis of
613 : // the last row
614 :
615 : // The REC says that the syntax is
616 : // '\s*(top|bottom|center|baseline|axis)(\s+-?[0-9]+)?\s*'
617 : // the parsing could have been simpler with that syntax
618 : // but for backward compatibility we make optional
619 : // the whitespaces between the alignment name and the row number
620 :
621 : enum eAlign {
622 : eAlign_top,
623 : eAlign_bottom,
624 : eAlign_center,
625 : eAlign_baseline,
626 : eAlign_axis
627 : };
628 :
629 : static void
630 0 : ParseAlignAttribute(nsString& aValue, eAlign& aAlign, int32_t& aRowIndex)
631 : {
632 : // by default, the table is centered about the axis
633 0 : aRowIndex = 0;
634 0 : aAlign = eAlign_axis;
635 0 : int32_t len = 0;
636 :
637 : // we only have to remove the leading spaces because
638 : // ToInteger ignores the whitespaces around the number
639 0 : aValue.CompressWhitespace(true, false);
640 :
641 0 : if (0 == aValue.Find("top")) {
642 0 : len = 3; // 3 is the length of 'top'
643 0 : aAlign = eAlign_top;
644 : }
645 0 : else if (0 == aValue.Find("bottom")) {
646 0 : len = 6; // 6 is the length of 'bottom'
647 0 : aAlign = eAlign_bottom;
648 : }
649 0 : else if (0 == aValue.Find("center")) {
650 0 : len = 6; // 6 is the length of 'center'
651 0 : aAlign = eAlign_center;
652 : }
653 0 : else if (0 == aValue.Find("baseline")) {
654 0 : len = 8; // 8 is the length of 'baseline'
655 0 : aAlign = eAlign_baseline;
656 : }
657 0 : else if (0 == aValue.Find("axis")) {
658 0 : len = 4; // 4 is the length of 'axis'
659 0 : aAlign = eAlign_axis;
660 : }
661 0 : if (len) {
662 : nsresult error;
663 0 : aValue.Cut(0, len); // aValue is not a const here
664 0 : aRowIndex = aValue.ToInteger(&error);
665 0 : if (NS_FAILED(error))
666 0 : aRowIndex = 0;
667 : }
668 0 : }
669 :
670 : #ifdef DEBUG_rbs_off
671 : // call ListMathMLTree(mParent) to get the big picture
672 : static void
673 : ListMathMLTree(nsIFrame* atLeast)
674 : {
675 : // climb up to <math> or <body> if <math> isn't there
676 : nsIFrame* f = atLeast;
677 : for ( ; f; f = f->GetParent()) {
678 : nsIContent* c = f->GetContent();
679 : if (!c || c->IsMathMLElement(nsGkAtoms::math) ||
680 : c->NodeInfo()->NameAtom(nsGkAtoms::body)) // XXXbaku which kind of body tag?
681 : break;
682 : }
683 : if (!f) f = atLeast;
684 : f->List(stdout, 0);
685 : }
686 : #endif
687 :
688 : // --------
689 : // implementation of nsMathMLmtableWrapperFrame
690 :
691 0 : NS_QUERYFRAME_HEAD(nsMathMLmtableWrapperFrame)
692 0 : NS_QUERYFRAME_ENTRY(nsIMathMLFrame)
693 0 : NS_QUERYFRAME_TAIL_INHERITING(nsTableWrapperFrame)
694 :
695 : nsContainerFrame*
696 0 : NS_NewMathMLmtableOuterFrame (nsIPresShell* aPresShell, nsStyleContext* aContext)
697 : {
698 0 : return new (aPresShell) nsMathMLmtableWrapperFrame(aContext);
699 : }
700 :
701 0 : NS_IMPL_FRAMEARENA_HELPERS(nsMathMLmtableWrapperFrame)
702 :
703 0 : nsMathMLmtableWrapperFrame::~nsMathMLmtableWrapperFrame()
704 : {
705 0 : }
706 :
707 : nsresult
708 0 : nsMathMLmtableWrapperFrame::AttributeChanged(int32_t aNameSpaceID,
709 : nsIAtom* aAttribute,
710 : int32_t aModType)
711 : {
712 : // Attributes specific to <mtable>:
713 : // frame : in mathml.css
714 : // framespacing : here
715 : // groupalign : not yet supported
716 : // equalrows : not yet supported
717 : // equalcolumns : not yet supported
718 : // displaystyle : here and in mathml.css
719 : // align : in reflow
720 : // rowalign : here
721 : // rowlines : here
722 : // rowspacing : here
723 : // columnalign : here
724 : // columnlines : here
725 : // columnspacing : here
726 :
727 : // mtable is simple and only has one (pseudo) row-group inside our inner-table
728 0 : nsIFrame* tableFrame = mFrames.FirstChild();
729 0 : NS_ASSERTION(tableFrame && tableFrame->IsTableFrame(),
730 : "should always have an inner table frame");
731 0 : nsIFrame* rgFrame = tableFrame->PrincipalChildList().FirstChild();
732 0 : if (!rgFrame || !rgFrame->IsTableRowGroupFrame())
733 0 : return NS_OK;
734 :
735 : // align - just need to issue a dirty (resize) reflow command
736 0 : if (aAttribute == nsGkAtoms::align) {
737 0 : PresContext()->PresShell()->
738 0 : FrameNeedsReflow(this, nsIPresShell::eResize, NS_FRAME_IS_DIRTY);
739 0 : return NS_OK;
740 : }
741 :
742 : // displaystyle - may seem innocuous, but it is actually very harsh --
743 : // like changing an unit. Blow away and recompute all our automatic
744 : // presentational data, and issue a style-changed reflow request
745 0 : if (aAttribute == nsGkAtoms::displaystyle_) {
746 0 : nsMathMLContainerFrame::RebuildAutomaticDataForChildren(GetParent());
747 : // Need to reflow the parent, not us, because this can actually
748 : // affect siblings.
749 0 : PresContext()->PresShell()->
750 0 : FrameNeedsReflow(GetParent(), nsIPresShell::eStyleChange, NS_FRAME_IS_DIRTY);
751 0 : return NS_OK;
752 : }
753 :
754 : // ...and the other attributes affect rows or columns in one way or another
755 :
756 0 : nsPresContext* presContext = tableFrame->PresContext();
757 0 : if (aAttribute == nsGkAtoms::rowspacing_ ||
758 0 : aAttribute == nsGkAtoms::columnspacing_ ||
759 0 : aAttribute == nsGkAtoms::framespacing_ ) {
760 0 : nsMathMLmtableFrame* mathMLmtableFrame = do_QueryFrame(tableFrame);
761 0 : if (mathMLmtableFrame) {
762 0 : ParseSpacingAttribute(mathMLmtableFrame, aAttribute);
763 0 : mathMLmtableFrame->SetUseCSSSpacing();
764 0 : }
765 0 : } else if (aAttribute == nsGkAtoms::rowalign_ ||
766 0 : aAttribute == nsGkAtoms::rowlines_ ||
767 0 : aAttribute == nsGkAtoms::columnalign_ ||
768 0 : aAttribute == nsGkAtoms::columnlines_) {
769 : // clear any cached property list for this table
770 0 : tableFrame->DeleteProperty(AttributeToProperty(aAttribute));
771 : // Reparse the new attribute on the table.
772 0 : ParseFrameAttribute(tableFrame, aAttribute, true);
773 : } else {
774 : // Ignore attributes that do not affect layout.
775 0 : return NS_OK;
776 : }
777 :
778 : // Explicitly request a reflow in our subtree to pick up any changes
779 0 : presContext->PresShell()->
780 0 : FrameNeedsReflow(this, nsIPresShell::eStyleChange, NS_FRAME_IS_DIRTY);
781 :
782 0 : return NS_OK;
783 : }
784 :
785 : nsIFrame*
786 0 : nsMathMLmtableWrapperFrame::GetRowFrameAt(int32_t aRowIndex)
787 : {
788 0 : int32_t rowCount = GetRowCount();
789 :
790 : // Negative indices mean to find upwards from the end.
791 0 : if (aRowIndex < 0) {
792 0 : aRowIndex = rowCount + aRowIndex;
793 : } else {
794 : // aRowIndex is 1-based, so convert it to a 0-based index
795 0 : --aRowIndex;
796 : }
797 :
798 : // if our inner table says that the index is valid, find the row now
799 0 : if (0 <= aRowIndex && aRowIndex <= rowCount) {
800 0 : nsIFrame* tableFrame = mFrames.FirstChild();
801 0 : NS_ASSERTION(tableFrame && tableFrame->IsTableFrame(),
802 : "should always have an inner table frame");
803 0 : nsIFrame* rgFrame = tableFrame->PrincipalChildList().FirstChild();
804 0 : if (!rgFrame || !rgFrame->IsTableRowGroupFrame())
805 0 : return nullptr;
806 0 : for (nsIFrame* rowFrame : rgFrame->PrincipalChildList()) {
807 0 : if (aRowIndex == 0) {
808 0 : DEBUG_VERIFY_THAT_FRAME_IS(rowFrame, TableRow);
809 0 : if (!rowFrame->IsTableRowFrame())
810 0 : return nullptr;
811 :
812 0 : return rowFrame;
813 : }
814 0 : --aRowIndex;
815 : }
816 : }
817 0 : return nullptr;
818 : }
819 :
820 : void
821 0 : nsMathMLmtableWrapperFrame::Reflow(nsPresContext* aPresContext,
822 : ReflowOutput& aDesiredSize,
823 : const ReflowInput& aReflowInput,
824 : nsReflowStatus& aStatus)
825 : {
826 0 : nsAutoString value;
827 : // we want to return a table that is anchored according to the align attribute
828 :
829 0 : nsTableWrapperFrame::Reflow(aPresContext, aDesiredSize, aReflowInput, aStatus);
830 0 : NS_ASSERTION(aDesiredSize.Height() >= 0, "illegal height for mtable");
831 0 : NS_ASSERTION(aDesiredSize.Width() >= 0, "illegal width for mtable");
832 :
833 : // see if the user has set the align attribute on the <mtable>
834 0 : int32_t rowIndex = 0;
835 0 : eAlign tableAlign = eAlign_axis;
836 0 : mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::align, value);
837 0 : if (!value.IsEmpty()) {
838 0 : ParseAlignAttribute(value, tableAlign, rowIndex);
839 : }
840 :
841 : // adjustments if there is a specified row from where to anchor the table
842 : // (conceptually: when there is no row of reference, picture the table as if
843 : // it is wrapped in a single big fictional row at dy = 0, this way of
844 : // doing so allows us to have a single code path for all cases).
845 0 : nscoord dy = 0;
846 0 : WritingMode wm = aDesiredSize.GetWritingMode();
847 0 : nscoord blockSize = aDesiredSize.BSize(wm);
848 0 : nsIFrame* rowFrame = nullptr;
849 0 : if (rowIndex) {
850 0 : rowFrame = GetRowFrameAt(rowIndex);
851 0 : if (rowFrame) {
852 : // translate the coordinates to be relative to us and in our writing mode
853 0 : nsIFrame* frame = rowFrame;
854 0 : LogicalRect rect(wm, frame->GetRect(),
855 0 : aReflowInput.ComputedSizeAsContainerIfConstrained());
856 0 : blockSize = rect.BSize(wm);
857 0 : do {
858 0 : nsIFrame* parent = frame->GetParent();
859 0 : dy += frame->BStart(wm, parent->GetSize());
860 0 : frame = parent;
861 0 : } while (frame != this);
862 : }
863 : }
864 0 : switch (tableAlign) {
865 : case eAlign_top:
866 0 : aDesiredSize.SetBlockStartAscent(dy);
867 0 : break;
868 : case eAlign_bottom:
869 0 : aDesiredSize.SetBlockStartAscent(dy + blockSize);
870 0 : break;
871 : case eAlign_center:
872 0 : aDesiredSize.SetBlockStartAscent(dy + blockSize / 2);
873 0 : break;
874 : case eAlign_baseline:
875 0 : if (rowFrame) {
876 : // anchor the table on the baseline of the row of reference
877 0 : nscoord rowAscent = ((nsTableRowFrame*)rowFrame)->GetMaxCellAscent();
878 0 : if (rowAscent) { // the row has at least one cell with 'vertical-align: baseline'
879 0 : aDesiredSize.SetBlockStartAscent(dy + rowAscent);
880 0 : break;
881 : }
882 : }
883 : // in other situations, fallback to center
884 0 : aDesiredSize.SetBlockStartAscent(dy + blockSize / 2);
885 0 : break;
886 : case eAlign_axis:
887 : default: {
888 : // XXX should instead use style data from the row of reference here ?
889 : RefPtr<nsFontMetrics> fm =
890 0 : nsLayoutUtils::GetInflatedFontMetricsForFrame(this);
891 : nscoord axisHeight;
892 0 : GetAxisHeight(aReflowInput.mRenderingContext->GetDrawTarget(), fm, axisHeight);
893 0 : if (rowFrame) {
894 : // anchor the table on the axis of the row of reference
895 : // XXX fallback to baseline because it is a hard problem
896 : // XXX need to fetch the axis of the row; would need rowalign=axis to work better
897 0 : nscoord rowAscent = ((nsTableRowFrame*)rowFrame)->GetMaxCellAscent();
898 0 : if (rowAscent) { // the row has at least one cell with 'vertical-align: baseline'
899 0 : aDesiredSize.SetBlockStartAscent(dy + rowAscent);
900 0 : break;
901 : }
902 : }
903 : // in other situations, fallback to using half of the height
904 0 : aDesiredSize.SetBlockStartAscent(dy + blockSize / 2 + axisHeight);
905 : }
906 : }
907 :
908 0 : mReference.x = 0;
909 0 : mReference.y = aDesiredSize.BlockStartAscent();
910 :
911 : // just make-up a bounding metrics
912 0 : mBoundingMetrics = nsBoundingMetrics();
913 0 : mBoundingMetrics.ascent = aDesiredSize.BlockStartAscent();
914 0 : mBoundingMetrics.descent = aDesiredSize.Height() -
915 0 : aDesiredSize.BlockStartAscent();
916 0 : mBoundingMetrics.width = aDesiredSize.Width();
917 0 : mBoundingMetrics.leftBearing = 0;
918 0 : mBoundingMetrics.rightBearing = aDesiredSize.Width();
919 :
920 0 : aDesiredSize.mBoundingMetrics = mBoundingMetrics;
921 0 : NS_FRAME_SET_TRUNCATION(aStatus, aReflowInput, aDesiredSize);
922 0 : }
923 :
924 : nsContainerFrame*
925 0 : NS_NewMathMLmtableFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
926 : {
927 0 : return new (aPresShell) nsMathMLmtableFrame(aContext);
928 : }
929 :
930 0 : NS_IMPL_FRAMEARENA_HELPERS(nsMathMLmtableFrame)
931 :
932 0 : nsMathMLmtableFrame::~nsMathMLmtableFrame()
933 : {
934 0 : }
935 :
936 : void
937 0 : nsMathMLmtableFrame::SetInitialChildList(ChildListID aListID,
938 : nsFrameList& aChildList)
939 : {
940 0 : nsTableFrame::SetInitialChildList(aListID, aChildList);
941 0 : MapAllAttributesIntoCSS(this);
942 0 : }
943 :
944 : void
945 0 : nsMathMLmtableFrame::RestyleTable()
946 : {
947 : // re-sync MathML specific style data that may have changed
948 0 : MapAllAttributesIntoCSS(this);
949 :
950 : // Explicitly request a re-resolve and reflow in our subtree to pick up any changes
951 : PresContext()->RestyleManager()->
952 0 : PostRestyleEvent(mContent->AsElement(), eRestyle_Subtree,
953 0 : nsChangeHint_AllReflowHints);
954 0 : }
955 :
956 : nscoord
957 0 : nsMathMLmtableFrame::GetColSpacing(int32_t aColIndex)
958 : {
959 0 : if (mUseCSSSpacing) {
960 0 : return nsTableFrame::GetColSpacing(aColIndex);
961 : }
962 0 : if (!mColSpacing.Length()) {
963 0 : NS_ERROR("mColSpacing should not be empty");
964 0 : return 0;
965 : }
966 0 : if (aColIndex < 0 || aColIndex >= GetColCount()) {
967 0 : NS_ASSERTION(aColIndex == -1 || aColIndex == GetColCount(),
968 : "Desired column beyond bounds of table and border");
969 0 : return mFrameSpacingX;
970 : }
971 0 : if ((uint32_t) aColIndex >= mColSpacing.Length()) {
972 0 : return mColSpacing.LastElement();
973 : }
974 0 : return mColSpacing.ElementAt(aColIndex);
975 : }
976 :
977 : nscoord
978 0 : nsMathMLmtableFrame::GetColSpacing(int32_t aStartColIndex,
979 : int32_t aEndColIndex)
980 : {
981 0 : if (mUseCSSSpacing) {
982 0 : return nsTableFrame::GetColSpacing(aStartColIndex, aEndColIndex);
983 : }
984 0 : if (aStartColIndex == aEndColIndex) {
985 0 : return 0;
986 : }
987 0 : if (!mColSpacing.Length()) {
988 0 : NS_ERROR("mColSpacing should not be empty");
989 0 : return 0;
990 : }
991 0 : nscoord space = 0;
992 0 : if (aStartColIndex < 0) {
993 0 : NS_ASSERTION(aStartColIndex == -1,
994 : "Desired column beyond bounds of table and border");
995 0 : space += mFrameSpacingX;
996 0 : aStartColIndex = 0;
997 : }
998 0 : if (aEndColIndex >= GetColCount()) {
999 0 : NS_ASSERTION(aEndColIndex == GetColCount(),
1000 : "Desired column beyond bounds of table and border");
1001 0 : space += mFrameSpacingX;
1002 0 : aEndColIndex = GetColCount();
1003 : }
1004 : // Only iterate over column spacing when there is the potential to vary
1005 0 : int32_t min = std::min(aEndColIndex, (int32_t) mColSpacing.Length());
1006 0 : for (int32_t i = aStartColIndex; i < min; i++) {
1007 0 : space += mColSpacing.ElementAt(i);
1008 : }
1009 : // The remaining values are constant. Note that if there are more
1010 : // column spacings specified than there are columns, LastElement() will be
1011 : // multiplied by 0, so it is still safe to use.
1012 0 : space += (aEndColIndex - min) * mColSpacing.LastElement();
1013 0 : return space;
1014 : }
1015 :
1016 : nscoord
1017 0 : nsMathMLmtableFrame::GetRowSpacing(int32_t aRowIndex)
1018 : {
1019 0 : if (mUseCSSSpacing) {
1020 0 : return nsTableFrame::GetRowSpacing(aRowIndex);
1021 : }
1022 0 : if (!mRowSpacing.Length()) {
1023 0 : NS_ERROR("mRowSpacing should not be empty");
1024 0 : return 0;
1025 : }
1026 0 : if (aRowIndex < 0 || aRowIndex >= GetRowCount()) {
1027 0 : NS_ASSERTION(aRowIndex == -1 || aRowIndex == GetRowCount(),
1028 : "Desired row beyond bounds of table and border");
1029 0 : return mFrameSpacingY;
1030 : }
1031 0 : if ((uint32_t) aRowIndex >= mRowSpacing.Length()) {
1032 0 : return mRowSpacing.LastElement();
1033 : }
1034 0 : return mRowSpacing.ElementAt(aRowIndex);
1035 : }
1036 :
1037 : nscoord
1038 0 : nsMathMLmtableFrame::GetRowSpacing(int32_t aStartRowIndex,
1039 : int32_t aEndRowIndex)
1040 : {
1041 0 : if (mUseCSSSpacing) {
1042 0 : return nsTableFrame::GetRowSpacing(aStartRowIndex, aEndRowIndex);
1043 : }
1044 0 : if (aStartRowIndex == aEndRowIndex) {
1045 0 : return 0;
1046 : }
1047 0 : if (!mRowSpacing.Length()) {
1048 0 : NS_ERROR("mRowSpacing should not be empty");
1049 0 : return 0;
1050 : }
1051 0 : nscoord space = 0;
1052 0 : if (aStartRowIndex < 0) {
1053 0 : NS_ASSERTION(aStartRowIndex == -1,
1054 : "Desired row beyond bounds of table and border");
1055 0 : space += mFrameSpacingY;
1056 0 : aStartRowIndex = 0;
1057 : }
1058 0 : if (aEndRowIndex >= GetRowCount()) {
1059 0 : NS_ASSERTION(aEndRowIndex == GetRowCount(),
1060 : "Desired row beyond bounds of table and border");
1061 0 : space += mFrameSpacingY;
1062 0 : aEndRowIndex = GetRowCount();
1063 : }
1064 : // Only iterate over row spacing when there is the potential to vary
1065 0 : int32_t min = std::min(aEndRowIndex, (int32_t) mRowSpacing.Length());
1066 0 : for (int32_t i = aStartRowIndex; i < min; i++) {
1067 0 : space += mRowSpacing.ElementAt(i);
1068 : }
1069 : // The remaining values are constant. Note that if there are more
1070 : // row spacings specified than there are row, LastElement() will be
1071 : // multiplied by 0, so it is still safe to use.
1072 0 : space += (aEndRowIndex - min) * mRowSpacing.LastElement();
1073 0 : return space;
1074 : }
1075 :
1076 : void
1077 0 : nsMathMLmtableFrame::SetUseCSSSpacing()
1078 : {
1079 0 : mUseCSSSpacing =
1080 0 : !(mContent->HasAttr(kNameSpaceID_None, nsGkAtoms::rowspacing_) ||
1081 0 : mContent->HasAttr(kNameSpaceID_None, nsGkAtoms::columnspacing_) ||
1082 0 : mContent->HasAttr(kNameSpaceID_None, nsGkAtoms::framespacing_));
1083 0 : }
1084 :
1085 0 : NS_QUERYFRAME_HEAD(nsMathMLmtableFrame)
1086 0 : NS_QUERYFRAME_ENTRY(nsMathMLmtableFrame)
1087 0 : NS_QUERYFRAME_TAIL_INHERITING(nsTableFrame)
1088 :
1089 : // --------
1090 : // implementation of nsMathMLmtrFrame
1091 :
1092 : nsContainerFrame*
1093 0 : NS_NewMathMLmtrFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
1094 : {
1095 0 : return new (aPresShell) nsMathMLmtrFrame(aContext);
1096 : }
1097 :
1098 0 : NS_IMPL_FRAMEARENA_HELPERS(nsMathMLmtrFrame)
1099 :
1100 0 : nsMathMLmtrFrame::~nsMathMLmtrFrame()
1101 : {
1102 0 : }
1103 :
1104 : nsresult
1105 0 : nsMathMLmtrFrame::AttributeChanged(int32_t aNameSpaceID,
1106 : nsIAtom* aAttribute,
1107 : int32_t aModType)
1108 : {
1109 : // Attributes specific to <mtr>:
1110 : // groupalign : Not yet supported.
1111 : // rowalign : Here
1112 : // columnalign : Here
1113 :
1114 0 : nsPresContext* presContext = PresContext();
1115 :
1116 0 : if (aAttribute != nsGkAtoms::rowalign_ &&
1117 0 : aAttribute != nsGkAtoms::columnalign_) {
1118 0 : return NS_OK;
1119 : }
1120 :
1121 0 : DeleteProperty(AttributeToProperty(aAttribute));
1122 :
1123 0 : bool allowMultiValues = (aAttribute == nsGkAtoms::columnalign_);
1124 :
1125 : // Reparse the new attribute.
1126 0 : ParseFrameAttribute(this, aAttribute, allowMultiValues);
1127 :
1128 : // Explicitly request a reflow in our subtree to pick up any changes
1129 0 : presContext->PresShell()->
1130 0 : FrameNeedsReflow(this, nsIPresShell::eStyleChange, NS_FRAME_IS_DIRTY);
1131 :
1132 0 : return NS_OK;
1133 : }
1134 :
1135 : // --------
1136 : // implementation of nsMathMLmtdFrame
1137 :
1138 : nsContainerFrame*
1139 0 : NS_NewMathMLmtdFrame(nsIPresShell* aPresShell,
1140 : nsStyleContext* aContext,
1141 : nsTableFrame* aTableFrame)
1142 : {
1143 0 : return new (aPresShell) nsMathMLmtdFrame(aContext, aTableFrame);
1144 : }
1145 :
1146 0 : NS_IMPL_FRAMEARENA_HELPERS(nsMathMLmtdFrame)
1147 :
1148 0 : nsMathMLmtdFrame::~nsMathMLmtdFrame()
1149 : {
1150 0 : }
1151 :
1152 : void
1153 0 : nsMathMLmtdFrame::Init(nsIContent* aContent,
1154 : nsContainerFrame* aParent,
1155 : nsIFrame* aPrevInFlow)
1156 : {
1157 0 : nsTableCellFrame::Init(aContent, aParent, aPrevInFlow);
1158 :
1159 : // We want to use the ancestor <math> element's font inflation to avoid
1160 : // individual cells having their own varying font inflation.
1161 0 : RemoveStateBits(NS_FRAME_FONT_INFLATION_FLOW_ROOT);
1162 0 : }
1163 :
1164 : nsresult
1165 0 : nsMathMLmtdFrame::AttributeChanged(int32_t aNameSpaceID,
1166 : nsIAtom* aAttribute,
1167 : int32_t aModType)
1168 : {
1169 : // Attributes specific to <mtd>:
1170 : // groupalign : Not yet supported
1171 : // rowalign : here
1172 : // columnalign : here
1173 : // rowspan : here
1174 : // columnspan : here
1175 :
1176 0 : if (aAttribute == nsGkAtoms::rowalign_ ||
1177 0 : aAttribute == nsGkAtoms::columnalign_) {
1178 :
1179 0 : DeleteProperty(AttributeToProperty(aAttribute));
1180 :
1181 : // Reparse the attribute.
1182 0 : ParseFrameAttribute(this, aAttribute, false);
1183 0 : return NS_OK;
1184 : }
1185 :
1186 0 : if (aAttribute == nsGkAtoms::rowspan ||
1187 0 : aAttribute == nsGkAtoms::columnspan_) {
1188 : // use the naming expected by the base class
1189 0 : if (aAttribute == nsGkAtoms::columnspan_)
1190 0 : aAttribute = nsGkAtoms::colspan;
1191 0 : return nsTableCellFrame::AttributeChanged(aNameSpaceID, aAttribute, aModType);
1192 : }
1193 :
1194 0 : return NS_OK;
1195 : }
1196 :
1197 : uint8_t
1198 0 : nsMathMLmtdFrame::GetVerticalAlign() const
1199 : {
1200 : // Set the default alignment in case no alignment was specified
1201 0 : uint8_t alignment = nsTableCellFrame::GetVerticalAlign();
1202 :
1203 0 : nsTArray<int8_t>* alignmentList = FindCellProperty(this, RowAlignProperty());
1204 :
1205 0 : if (alignmentList) {
1206 : int32_t rowIndex;
1207 0 : GetRowIndex(rowIndex);
1208 :
1209 : // If the row number is greater than the number of provided rowalign values,
1210 : // we simply repeat the last value.
1211 0 : if (rowIndex < (int32_t)alignmentList->Length())
1212 0 : alignment = alignmentList->ElementAt(rowIndex);
1213 : else
1214 0 : alignment = alignmentList->ElementAt(alignmentList->Length() - 1);
1215 : }
1216 :
1217 0 : return alignment;
1218 : }
1219 :
1220 : nsresult
1221 0 : nsMathMLmtdFrame::ProcessBorders(nsTableFrame* aFrame,
1222 : nsDisplayListBuilder* aBuilder,
1223 : const nsDisplayListSet& aLists)
1224 : {
1225 0 : aLists.BorderBackground()->AppendNewToTop(new (aBuilder)
1226 0 : nsDisplaymtdBorder(aBuilder, this));
1227 0 : return NS_OK;
1228 : }
1229 :
1230 : LogicalMargin
1231 0 : nsMathMLmtdFrame::GetBorderWidth(WritingMode aWM) const
1232 : {
1233 0 : nsStyleBorder styleBorder = *StyleBorder();
1234 0 : ApplyBorderToStyle(this, styleBorder);
1235 0 : return LogicalMargin(aWM, styleBorder.GetComputedBorder());
1236 : }
1237 :
1238 : nsMargin
1239 0 : nsMathMLmtdFrame::GetBorderOverflow()
1240 : {
1241 0 : nsStyleBorder styleBorder = *StyleBorder();
1242 0 : ApplyBorderToStyle(this, styleBorder);
1243 0 : nsMargin overflow = ComputeBorderOverflow(this, styleBorder);
1244 0 : return overflow;
1245 : }
1246 :
1247 : // --------
1248 : // implementation of nsMathMLmtdInnerFrame
1249 :
1250 0 : NS_QUERYFRAME_HEAD(nsMathMLmtdInnerFrame)
1251 0 : NS_QUERYFRAME_ENTRY(nsIMathMLFrame)
1252 0 : NS_QUERYFRAME_TAIL_INHERITING(nsBlockFrame)
1253 :
1254 : nsContainerFrame*
1255 0 : NS_NewMathMLmtdInnerFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
1256 : {
1257 0 : return new (aPresShell) nsMathMLmtdInnerFrame(aContext);
1258 : }
1259 :
1260 0 : NS_IMPL_FRAMEARENA_HELPERS(nsMathMLmtdInnerFrame)
1261 :
1262 0 : nsMathMLmtdInnerFrame::nsMathMLmtdInnerFrame(nsStyleContext* aContext)
1263 0 : : nsBlockFrame(aContext, kClassID)
1264 : {
1265 : // Make a copy of the parent nsStyleText for later modification.
1266 0 : mUniqueStyleText = new (PresContext()) nsStyleText(*StyleText());
1267 0 : }
1268 :
1269 0 : nsMathMLmtdInnerFrame::~nsMathMLmtdInnerFrame()
1270 : {
1271 0 : mUniqueStyleText->Destroy(PresContext());
1272 0 : }
1273 :
1274 : void
1275 0 : nsMathMLmtdInnerFrame::Reflow(nsPresContext* aPresContext,
1276 : ReflowOutput& aDesiredSize,
1277 : const ReflowInput& aReflowInput,
1278 : nsReflowStatus& aStatus)
1279 : {
1280 : // Let the base class do the reflow
1281 0 : nsBlockFrame::Reflow(aPresContext, aDesiredSize, aReflowInput, aStatus);
1282 :
1283 : // more about <maligngroup/> and <malignmark/> later
1284 : // ...
1285 0 : }
1286 :
1287 : const
1288 0 : nsStyleText* nsMathMLmtdInnerFrame::StyleTextForLineLayout()
1289 : {
1290 : // Set the default alignment in case nothing was specified
1291 0 : uint8_t alignment = StyleText()->mTextAlign;
1292 :
1293 : nsTArray<int8_t>* alignmentList =
1294 0 : FindCellProperty(this, ColumnAlignProperty());
1295 :
1296 0 : if (alignmentList) {
1297 0 : nsMathMLmtdFrame* cellFrame = (nsMathMLmtdFrame*)GetParent();
1298 : int32_t columnIndex;
1299 0 : cellFrame->GetColIndex(columnIndex);
1300 :
1301 : // If the column number is greater than the number of provided columalign
1302 : // values, we simply repeat the last value.
1303 0 : if (columnIndex < (int32_t)alignmentList->Length())
1304 0 : alignment = alignmentList->ElementAt(columnIndex);
1305 : else
1306 0 : alignment = alignmentList->ElementAt(alignmentList->Length() - 1);
1307 : }
1308 :
1309 0 : mUniqueStyleText->mTextAlign = alignment;
1310 0 : return mUniqueStyleText;
1311 : }
1312 :
1313 : /* virtual */ void
1314 0 : nsMathMLmtdInnerFrame::DidSetStyleContext(nsStyleContext* aOldStyleContext)
1315 : {
1316 0 : nsBlockFrame::DidSetStyleContext(aOldStyleContext);
1317 0 : mUniqueStyleText->Destroy(PresContext());
1318 0 : mUniqueStyleText = new (PresContext()) nsStyleText(*StyleText());
1319 0 : }
|