/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 | } |