Coverage Report

Created: 2018-09-25 14:53

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