layout/mathml/nsMathMLmtableFrame.cpp

Tue, 06 Jan 2015 21:39:09 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Tue, 06 Jan 2015 21:39:09 +0100
branch
TOR_BUG_9701
changeset 8
97036ab72558
permissions
-rw-r--r--

Conditionally force memory storage according to privacy.thirdparty.isolate;
This solves Tor bug #9701, complying with disk avoidance documented in
https://www.torproject.org/projects/torbrowser/design/#disk-avoidance.

michael@0 1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
michael@0 2 /* This Source Code Form is subject to the terms of the Mozilla Public
michael@0 3 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 5
michael@0 6 #include "nsMathMLmtableFrame.h"
michael@0 7 #include "nsPresContext.h"
michael@0 8 #include "nsStyleContext.h"
michael@0 9 #include "nsStyleConsts.h"
michael@0 10 #include "nsNameSpaceManager.h"
michael@0 11 #include "nsRenderingContext.h"
michael@0 12 #include "nsCSSRendering.h"
michael@0 13
michael@0 14 #include "nsTArray.h"
michael@0 15 #include "nsTableFrame.h"
michael@0 16 #include "celldata.h"
michael@0 17
michael@0 18 #include "RestyleManager.h"
michael@0 19 #include <algorithm>
michael@0 20
michael@0 21 #include "nsIScriptError.h"
michael@0 22 #include "nsContentUtils.h"
michael@0 23
michael@0 24 using namespace mozilla;
michael@0 25
michael@0 26 //
michael@0 27 // <mtable> -- table or matrix - implementation
michael@0 28 //
michael@0 29
michael@0 30 static int8_t
michael@0 31 ParseStyleValue(nsIAtom* aAttribute, const nsAString& aAttributeValue)
michael@0 32 {
michael@0 33 if (aAttribute == nsGkAtoms::rowalign_) {
michael@0 34 if (aAttributeValue.EqualsLiteral("top"))
michael@0 35 return NS_STYLE_VERTICAL_ALIGN_TOP;
michael@0 36 else if (aAttributeValue.EqualsLiteral("bottom"))
michael@0 37 return NS_STYLE_VERTICAL_ALIGN_BOTTOM;
michael@0 38 else if (aAttributeValue.EqualsLiteral("center"))
michael@0 39 return NS_STYLE_VERTICAL_ALIGN_MIDDLE;
michael@0 40 else
michael@0 41 return NS_STYLE_VERTICAL_ALIGN_BASELINE;
michael@0 42 } else if (aAttribute == nsGkAtoms::columnalign_) {
michael@0 43 if (aAttributeValue.EqualsLiteral("left"))
michael@0 44 return NS_STYLE_TEXT_ALIGN_LEFT;
michael@0 45 else if (aAttributeValue.EqualsLiteral("right"))
michael@0 46 return NS_STYLE_TEXT_ALIGN_RIGHT;
michael@0 47 else
michael@0 48 return NS_STYLE_TEXT_ALIGN_CENTER;
michael@0 49 } else if (aAttribute == nsGkAtoms::rowlines_ ||
michael@0 50 aAttribute == nsGkAtoms::columnlines_) {
michael@0 51 if (aAttributeValue.EqualsLiteral("solid"))
michael@0 52 return NS_STYLE_BORDER_STYLE_SOLID;
michael@0 53 else if (aAttributeValue.EqualsLiteral("dashed"))
michael@0 54 return NS_STYLE_BORDER_STYLE_DASHED;
michael@0 55 else
michael@0 56 return NS_STYLE_BORDER_STYLE_NONE;
michael@0 57 } else {
michael@0 58 MOZ_CRASH("Unrecognized attribute.");
michael@0 59 }
michael@0 60
michael@0 61 return -1;
michael@0 62 }
michael@0 63
michael@0 64 static nsTArray<int8_t>*
michael@0 65 ExtractStyleValues(const nsAString& aString, nsIAtom* aAttribute,
michael@0 66 bool aAllowMultiValues)
michael@0 67 {
michael@0 68 nsTArray<int8_t>* styleArray = nullptr;
michael@0 69
michael@0 70 const char16_t* start = aString.BeginReading();
michael@0 71 const char16_t* end = aString.EndReading();
michael@0 72
michael@0 73 int32_t startIndex = 0;
michael@0 74 int32_t count = 0;
michael@0 75
michael@0 76 while (start < end) {
michael@0 77 // Skip leading spaces.
michael@0 78 while ((start < end) && nsCRT::IsAsciiSpace(*start)) {
michael@0 79 start++;
michael@0 80 startIndex++;
michael@0 81 }
michael@0 82
michael@0 83 // Look for the end of the string, or another space.
michael@0 84 while ((start < end) && !nsCRT::IsAsciiSpace(*start)) {
michael@0 85 start++;
michael@0 86 count++;
michael@0 87 }
michael@0 88
michael@0 89 // Grab the value found and process it.
michael@0 90 if (count > 0) {
michael@0 91 if (!styleArray)
michael@0 92 styleArray = new nsTArray<int8_t>();
michael@0 93
michael@0 94 // We want to return a null array if an attribute gives multiple values,
michael@0 95 // but multiple values aren't allowed.
michael@0 96 if (styleArray->Length() > 1 && !aAllowMultiValues) {
michael@0 97 delete styleArray;
michael@0 98 return nullptr;
michael@0 99 }
michael@0 100
michael@0 101 nsDependentSubstring valueString(aString, startIndex, count);
michael@0 102 int8_t styleValue = ParseStyleValue(aAttribute, valueString);
michael@0 103 styleArray->AppendElement(styleValue);
michael@0 104
michael@0 105 startIndex += count;
michael@0 106 count = 0;
michael@0 107 }
michael@0 108 }
michael@0 109 return styleArray;
michael@0 110 }
michael@0 111
michael@0 112 static nsresult ReportParseError(nsIFrame* aFrame, const char16_t* aAttribute,
michael@0 113 const char16_t* aValue)
michael@0 114 {
michael@0 115 nsIContent* content = aFrame->GetContent();
michael@0 116
michael@0 117 const char16_t* params[] =
michael@0 118 { aValue, aAttribute, content->Tag()->GetUTF16String() };
michael@0 119
michael@0 120 return nsContentUtils::ReportToConsole(nsIScriptError::errorFlag,
michael@0 121 NS_LITERAL_CSTRING("MathML"),
michael@0 122 content->OwnerDoc(),
michael@0 123 nsContentUtils::eMATHML_PROPERTIES,
michael@0 124 "AttributeParsingError", params, 3);
michael@0 125 }
michael@0 126
michael@0 127 // Each rowalign='top bottom' or columnalign='left right center' (from
michael@0 128 // <mtable> or <mtr>) is split once into an nsTArray<int8_t> which is
michael@0 129 // stored in the property table. Row/Cell frames query the property table
michael@0 130 // to see what values apply to them.
michael@0 131
michael@0 132 static void
michael@0 133 DestroyStylePropertyList(void* aPropertyValue)
michael@0 134 {
michael@0 135 delete static_cast<nsTArray<int8_t>*>(aPropertyValue);
michael@0 136 }
michael@0 137
michael@0 138 NS_DECLARE_FRAME_PROPERTY(RowAlignProperty, DestroyStylePropertyList)
michael@0 139 NS_DECLARE_FRAME_PROPERTY(RowLinesProperty, DestroyStylePropertyList)
michael@0 140 NS_DECLARE_FRAME_PROPERTY(ColumnAlignProperty, DestroyStylePropertyList)
michael@0 141 NS_DECLARE_FRAME_PROPERTY(ColumnLinesProperty, DestroyStylePropertyList)
michael@0 142
michael@0 143 static const FramePropertyDescriptor*
michael@0 144 AttributeToProperty(nsIAtom* aAttribute)
michael@0 145 {
michael@0 146 if (aAttribute == nsGkAtoms::rowalign_)
michael@0 147 return RowAlignProperty();
michael@0 148 if (aAttribute == nsGkAtoms::rowlines_)
michael@0 149 return RowLinesProperty();
michael@0 150 if (aAttribute == nsGkAtoms::columnalign_)
michael@0 151 return ColumnAlignProperty();
michael@0 152 NS_ASSERTION(aAttribute == nsGkAtoms::columnlines_, "Invalid attribute");
michael@0 153 return ColumnLinesProperty();
michael@0 154 }
michael@0 155
michael@0 156 /* This method looks for a property that applies to a cell, but it looks
michael@0 157 * recursively because some cell properties can come from the cell, a row,
michael@0 158 * a table, etc. This function searches through the heirarchy for a property
michael@0 159 * and returns its value. The function stops searching after checking a <mtable>
michael@0 160 * frame.
michael@0 161 */
michael@0 162 static nsTArray<int8_t>*
michael@0 163 FindCellProperty(const nsIFrame* aCellFrame,
michael@0 164 const FramePropertyDescriptor* aFrameProperty)
michael@0 165 {
michael@0 166 const nsIFrame* currentFrame = aCellFrame;
michael@0 167 nsTArray<int8_t>* propertyData = nullptr;
michael@0 168
michael@0 169 while (currentFrame) {
michael@0 170 FrameProperties props = currentFrame->Properties();
michael@0 171 propertyData = static_cast<nsTArray<int8_t>*>(props.Get(aFrameProperty));
michael@0 172 bool frameIsTable = (currentFrame->GetType() == nsGkAtoms::tableFrame);
michael@0 173
michael@0 174 if (propertyData || frameIsTable)
michael@0 175 currentFrame = nullptr; // A null frame pointer exits the loop
michael@0 176 else
michael@0 177 currentFrame = currentFrame->GetParent(); // Go to the parent frame
michael@0 178 }
michael@0 179
michael@0 180 return propertyData;
michael@0 181 }
michael@0 182
michael@0 183 static void
michael@0 184 ApplyBorderToStyle(const nsMathMLmtdFrame* aFrame,
michael@0 185 nsStyleBorder& aStyleBorder)
michael@0 186 {
michael@0 187 int32_t rowIndex;
michael@0 188 int32_t columnIndex;
michael@0 189 aFrame->GetRowIndex(rowIndex);
michael@0 190 aFrame->GetColIndex(columnIndex);
michael@0 191
michael@0 192 nscoord borderWidth =
michael@0 193 aFrame->PresContext()->GetBorderWidthTable()[NS_STYLE_BORDER_WIDTH_THIN];
michael@0 194
michael@0 195 nsTArray<int8_t>* rowLinesList =
michael@0 196 FindCellProperty(aFrame, RowLinesProperty());
michael@0 197
michael@0 198 nsTArray<int8_t>* columnLinesList =
michael@0 199 FindCellProperty(aFrame, ColumnLinesProperty());
michael@0 200
michael@0 201 // We don't place a row line on top of the first row
michael@0 202 if (rowIndex > 0 && rowLinesList) {
michael@0 203 // If the row number is greater than the number of provided rowline
michael@0 204 // values, we simply repeat the last value.
michael@0 205 int32_t listLength = rowLinesList->Length();
michael@0 206 if (rowIndex < listLength) {
michael@0 207 aStyleBorder.SetBorderStyle(NS_SIDE_TOP,
michael@0 208 rowLinesList->ElementAt(rowIndex - 1));
michael@0 209 } else {
michael@0 210 aStyleBorder.SetBorderStyle(NS_SIDE_TOP,
michael@0 211 rowLinesList->ElementAt(listLength - 1));
michael@0 212 }
michael@0 213 aStyleBorder.SetBorderWidth(NS_SIDE_TOP, borderWidth);
michael@0 214 }
michael@0 215
michael@0 216 // We don't place a column line on the left of the first column.
michael@0 217 if (columnIndex > 0 && columnLinesList) {
michael@0 218 // If the column number is greater than the number of provided columline
michael@0 219 // values, we simply repeat the last value.
michael@0 220 int32_t listLength = columnLinesList->Length();
michael@0 221 if (columnIndex < listLength) {
michael@0 222 aStyleBorder.SetBorderStyle(NS_SIDE_LEFT,
michael@0 223 columnLinesList->ElementAt(columnIndex - 1));
michael@0 224 } else {
michael@0 225 aStyleBorder.SetBorderStyle(NS_SIDE_LEFT,
michael@0 226 columnLinesList->ElementAt(listLength - 1));
michael@0 227 }
michael@0 228 aStyleBorder.SetBorderWidth(NS_SIDE_LEFT, borderWidth);
michael@0 229 }
michael@0 230 }
michael@0 231
michael@0 232 /*
michael@0 233 * A variant of the nsDisplayBorder contains special code to render a border
michael@0 234 * around a nsMathMLmtdFrame based on the rowline and columnline properties
michael@0 235 * set on the cell frame.
michael@0 236 */
michael@0 237 class nsDisplaymtdBorder : public nsDisplayBorder {
michael@0 238 public:
michael@0 239 nsDisplaymtdBorder(nsDisplayListBuilder* aBuilder, nsMathMLmtdFrame* aFrame)
michael@0 240 : nsDisplayBorder(aBuilder, aFrame)
michael@0 241 {
michael@0 242 }
michael@0 243
michael@0 244 virtual nsRect GetBounds(nsDisplayListBuilder* aBuilder, bool* aSnap) MOZ_OVERRIDE
michael@0 245 {
michael@0 246 nsStyleBorder styleBorder = *mFrame->StyleBorder();
michael@0 247 ApplyBorderToStyle(static_cast<nsMathMLmtdFrame*>(mFrame), styleBorder);
michael@0 248 return CalculateBounds(styleBorder);
michael@0 249 }
michael@0 250
michael@0 251 virtual void Paint(nsDisplayListBuilder* aBuilder, nsRenderingContext* aCtx) MOZ_OVERRIDE
michael@0 252 {
michael@0 253 nsStyleBorder styleBorder = *mFrame->StyleBorder();
michael@0 254 ApplyBorderToStyle(static_cast<nsMathMLmtdFrame*>(mFrame), styleBorder);
michael@0 255
michael@0 256 nsPoint offset = ToReferenceFrame();
michael@0 257 nsCSSRendering::PaintBorderWithStyleBorder(mFrame->PresContext(), *aCtx,
michael@0 258 mFrame, mVisibleRect,
michael@0 259 nsRect(offset,
michael@0 260 mFrame->GetSize()),
michael@0 261 styleBorder,
michael@0 262 mFrame->StyleContext(),
michael@0 263 mFrame->GetSkipSides());
michael@0 264 }
michael@0 265 };
michael@0 266
michael@0 267 #ifdef DEBUG
michael@0 268 #define DEBUG_VERIFY_THAT_FRAME_IS(_frame, _expected) \
michael@0 269 NS_ASSERTION(NS_STYLE_DISPLAY_##_expected == _frame->StyleDisplay()->mDisplay, "internal error");
michael@0 270 #else
michael@0 271 #define DEBUG_VERIFY_THAT_FRAME_IS(_frame, _expected)
michael@0 272 #endif
michael@0 273
michael@0 274 static void
michael@0 275 ParseFrameAttribute(nsIFrame* aFrame, nsIAtom* aAttribute,
michael@0 276 bool aAllowMultiValues)
michael@0 277 {
michael@0 278 nsAutoString attrValue;
michael@0 279
michael@0 280 nsIContent* frameContent = aFrame->GetContent();
michael@0 281 frameContent->GetAttr(kNameSpaceID_None, aAttribute, attrValue);
michael@0 282
michael@0 283 if (!attrValue.IsEmpty()) {
michael@0 284 nsTArray<int8_t>* valueList =
michael@0 285 ExtractStyleValues(attrValue, aAttribute, aAllowMultiValues);
michael@0 286
michael@0 287 // If valueList is null, that indicates a problem with the attribute value.
michael@0 288 // Only set properties on a valid attribute value.
michael@0 289 if (valueList) {
michael@0 290 // The code reading the property assumes that this list is nonempty.
michael@0 291 NS_ASSERTION(valueList->Length() >= 1, "valueList should not be empty!");
michael@0 292 FrameProperties props = aFrame->Properties();
michael@0 293 props.Set(AttributeToProperty(aAttribute), valueList);
michael@0 294 } else {
michael@0 295 ReportParseError(aFrame, aAttribute->GetUTF16String(), attrValue.get());
michael@0 296 }
michael@0 297 }
michael@0 298 }
michael@0 299
michael@0 300 // map all attribues within a table -- requires the indices of rows and cells.
michael@0 301 // so it can only happen after they are made ready by the table base class.
michael@0 302 static void
michael@0 303 MapAllAttributesIntoCSS(nsIFrame* aTableFrame)
michael@0 304 {
michael@0 305 // Map mtable rowalign & rowlines.
michael@0 306 ParseFrameAttribute(aTableFrame, nsGkAtoms::rowalign_, true);
michael@0 307 ParseFrameAttribute(aTableFrame, nsGkAtoms::rowlines_, true);
michael@0 308
michael@0 309 // Map mtable columnalign & columnlines.
michael@0 310 ParseFrameAttribute(aTableFrame, nsGkAtoms::columnalign_, true);
michael@0 311 ParseFrameAttribute(aTableFrame, nsGkAtoms::columnlines_, true);
michael@0 312
michael@0 313 // mtable is simple and only has one (pseudo) row-group
michael@0 314 nsIFrame* rgFrame = aTableFrame->GetFirstPrincipalChild();
michael@0 315 if (!rgFrame || rgFrame->GetType() != nsGkAtoms::tableRowGroupFrame)
michael@0 316 return;
michael@0 317
michael@0 318 nsIFrame* rowFrame = rgFrame->GetFirstPrincipalChild();
michael@0 319 for ( ; rowFrame; rowFrame = rowFrame->GetNextSibling()) {
michael@0 320 DEBUG_VERIFY_THAT_FRAME_IS(rowFrame, TABLE_ROW);
michael@0 321 if (rowFrame->GetType() == nsGkAtoms::tableRowFrame) {
michael@0 322 // Map row rowalign.
michael@0 323 ParseFrameAttribute(rowFrame, nsGkAtoms::rowalign_, false);
michael@0 324 // Map row columnalign.
michael@0 325 ParseFrameAttribute(rowFrame, nsGkAtoms::columnalign_, true);
michael@0 326
michael@0 327 nsIFrame* cellFrame = rowFrame->GetFirstPrincipalChild();
michael@0 328 for ( ; cellFrame; cellFrame = cellFrame->GetNextSibling()) {
michael@0 329 DEBUG_VERIFY_THAT_FRAME_IS(cellFrame, TABLE_CELL);
michael@0 330 if (IS_TABLE_CELL(cellFrame->GetType())) {
michael@0 331 // Map cell rowalign.
michael@0 332 ParseFrameAttribute(cellFrame, nsGkAtoms::rowalign_, false);
michael@0 333 // Map row columnalign.
michael@0 334 ParseFrameAttribute(cellFrame, nsGkAtoms::columnalign_, false);
michael@0 335 }
michael@0 336 }
michael@0 337 }
michael@0 338 }
michael@0 339 }
michael@0 340
michael@0 341 // the align attribute of mtable can have a row number which indicates
michael@0 342 // from where to anchor the table, e.g., top 5 means anchor the table at
michael@0 343 // the top of the 5th row, axis -1 means anchor the table on the axis of
michael@0 344 // the last row
michael@0 345
michael@0 346 // The REC says that the syntax is
michael@0 347 // '\s*(top|bottom|center|baseline|axis)(\s+-?[0-9]+)?\s*'
michael@0 348 // the parsing could have been simpler with that syntax
michael@0 349 // but for backward compatibility we make optional
michael@0 350 // the whitespaces between the alignment name and the row number
michael@0 351
michael@0 352 enum eAlign {
michael@0 353 eAlign_top,
michael@0 354 eAlign_bottom,
michael@0 355 eAlign_center,
michael@0 356 eAlign_baseline,
michael@0 357 eAlign_axis
michael@0 358 };
michael@0 359
michael@0 360 static void
michael@0 361 ParseAlignAttribute(nsString& aValue, eAlign& aAlign, int32_t& aRowIndex)
michael@0 362 {
michael@0 363 // by default, the table is centered about the axis
michael@0 364 aRowIndex = 0;
michael@0 365 aAlign = eAlign_axis;
michael@0 366 int32_t len = 0;
michael@0 367
michael@0 368 // we only have to remove the leading spaces because
michael@0 369 // ToInteger ignores the whitespaces around the number
michael@0 370 aValue.CompressWhitespace(true, false);
michael@0 371
michael@0 372 if (0 == aValue.Find("top")) {
michael@0 373 len = 3; // 3 is the length of 'top'
michael@0 374 aAlign = eAlign_top;
michael@0 375 }
michael@0 376 else if (0 == aValue.Find("bottom")) {
michael@0 377 len = 6; // 6 is the length of 'bottom'
michael@0 378 aAlign = eAlign_bottom;
michael@0 379 }
michael@0 380 else if (0 == aValue.Find("center")) {
michael@0 381 len = 6; // 6 is the length of 'center'
michael@0 382 aAlign = eAlign_center;
michael@0 383 }
michael@0 384 else if (0 == aValue.Find("baseline")) {
michael@0 385 len = 8; // 8 is the length of 'baseline'
michael@0 386 aAlign = eAlign_baseline;
michael@0 387 }
michael@0 388 else if (0 == aValue.Find("axis")) {
michael@0 389 len = 4; // 4 is the length of 'axis'
michael@0 390 aAlign = eAlign_axis;
michael@0 391 }
michael@0 392 if (len) {
michael@0 393 nsresult error;
michael@0 394 aValue.Cut(0, len); // aValue is not a const here
michael@0 395 aRowIndex = aValue.ToInteger(&error);
michael@0 396 if (NS_FAILED(error))
michael@0 397 aRowIndex = 0;
michael@0 398 }
michael@0 399 }
michael@0 400
michael@0 401 #ifdef DEBUG_rbs_off
michael@0 402 // call ListMathMLTree(mParent) to get the big picture
michael@0 403 static void
michael@0 404 ListMathMLTree(nsIFrame* atLeast)
michael@0 405 {
michael@0 406 // climb up to <math> or <body> if <math> isn't there
michael@0 407 nsIFrame* f = atLeast;
michael@0 408 for ( ; f; f = f->GetParent()) {
michael@0 409 nsIContent* c = f->GetContent();
michael@0 410 if (!c || c->Tag() == nsGkAtoms::math || c->Tag() == nsGkAtoms::body)
michael@0 411 break;
michael@0 412 }
michael@0 413 if (!f) f = atLeast;
michael@0 414 f->List(stdout, 0);
michael@0 415 }
michael@0 416 #endif
michael@0 417
michael@0 418 // --------
michael@0 419 // implementation of nsMathMLmtableOuterFrame
michael@0 420
michael@0 421 NS_QUERYFRAME_HEAD(nsMathMLmtableOuterFrame)
michael@0 422 NS_QUERYFRAME_ENTRY(nsIMathMLFrame)
michael@0 423 NS_QUERYFRAME_TAIL_INHERITING(nsTableOuterFrame)
michael@0 424
michael@0 425 nsIFrame*
michael@0 426 NS_NewMathMLmtableOuterFrame (nsIPresShell* aPresShell, nsStyleContext* aContext)
michael@0 427 {
michael@0 428 return new (aPresShell) nsMathMLmtableOuterFrame(aContext);
michael@0 429 }
michael@0 430
michael@0 431 NS_IMPL_FRAMEARENA_HELPERS(nsMathMLmtableOuterFrame)
michael@0 432
michael@0 433 nsMathMLmtableOuterFrame::~nsMathMLmtableOuterFrame()
michael@0 434 {
michael@0 435 }
michael@0 436
michael@0 437 nsresult
michael@0 438 nsMathMLmtableOuterFrame::AttributeChanged(int32_t aNameSpaceID,
michael@0 439 nsIAtom* aAttribute,
michael@0 440 int32_t aModType)
michael@0 441 {
michael@0 442 // Attributes specific to <mtable>:
michael@0 443 // frame : in mathml.css
michael@0 444 // framespacing : not yet supported
michael@0 445 // groupalign : not yet supported
michael@0 446 // equalrows : not yet supported
michael@0 447 // equalcolumns : not yet supported
michael@0 448 // displaystyle : here and in mathml.css
michael@0 449 // align : in reflow
michael@0 450 // rowalign : here
michael@0 451 // rowlines : here
michael@0 452 // rowspacing : not yet supported
michael@0 453 // columnalign : here
michael@0 454 // columnlines : here
michael@0 455 // columnspacing : not yet supported
michael@0 456
michael@0 457 // mtable is simple and only has one (pseudo) row-group inside our inner-table
michael@0 458 nsIFrame* tableFrame = mFrames.FirstChild();
michael@0 459 NS_ASSERTION(tableFrame && tableFrame->GetType() == nsGkAtoms::tableFrame,
michael@0 460 "should always have an inner table frame");
michael@0 461 nsIFrame* rgFrame = tableFrame->GetFirstPrincipalChild();
michael@0 462 if (!rgFrame || rgFrame->GetType() != nsGkAtoms::tableRowGroupFrame)
michael@0 463 return NS_OK;
michael@0 464
michael@0 465 // align - just need to issue a dirty (resize) reflow command
michael@0 466 if (aAttribute == nsGkAtoms::align) {
michael@0 467 PresContext()->PresShell()->
michael@0 468 FrameNeedsReflow(this, nsIPresShell::eResize, NS_FRAME_IS_DIRTY);
michael@0 469 return NS_OK;
michael@0 470 }
michael@0 471
michael@0 472 // displaystyle - may seem innocuous, but it is actually very harsh --
michael@0 473 // like changing an unit. Blow away and recompute all our automatic
michael@0 474 // presentational data, and issue a style-changed reflow request
michael@0 475 if (aAttribute == nsGkAtoms::displaystyle_) {
michael@0 476 nsMathMLContainerFrame::RebuildAutomaticDataForChildren(mParent);
michael@0 477 // Need to reflow the parent, not us, because this can actually
michael@0 478 // affect siblings.
michael@0 479 PresContext()->PresShell()->
michael@0 480 FrameNeedsReflow(mParent, nsIPresShell::eStyleChange, NS_FRAME_IS_DIRTY);
michael@0 481 return NS_OK;
michael@0 482 }
michael@0 483
michael@0 484 // ...and the other attributes affect rows or columns in one way or another
michael@0 485
michael@0 486 // Ignore attributes that do not affect layout.
michael@0 487 if (aAttribute != nsGkAtoms::rowalign_ &&
michael@0 488 aAttribute != nsGkAtoms::rowlines_ &&
michael@0 489 aAttribute != nsGkAtoms::columnalign_ &&
michael@0 490 aAttribute != nsGkAtoms::columnlines_) {
michael@0 491 return NS_OK;
michael@0 492 }
michael@0 493
michael@0 494 nsPresContext* presContext = tableFrame->PresContext();
michael@0 495
michael@0 496 // clear any cached property list for this table
michael@0 497 presContext->PropertyTable()->
michael@0 498 Delete(tableFrame, AttributeToProperty(aAttribute));
michael@0 499
michael@0 500 // Reparse the new attribute on the table.
michael@0 501 ParseFrameAttribute(tableFrame, aAttribute, true);
michael@0 502
michael@0 503 // Explicitly request a reflow in our subtree to pick up any changes
michael@0 504 presContext->PresShell()->
michael@0 505 FrameNeedsReflow(this, nsIPresShell::eStyleChange, NS_FRAME_IS_DIRTY);
michael@0 506
michael@0 507 return NS_OK;
michael@0 508 }
michael@0 509
michael@0 510 nsIFrame*
michael@0 511 nsMathMLmtableOuterFrame::GetRowFrameAt(nsPresContext* aPresContext,
michael@0 512 int32_t aRowIndex)
michael@0 513 {
michael@0 514 int32_t rowCount = GetRowCount();
michael@0 515
michael@0 516 // Negative indices mean to find upwards from the end.
michael@0 517 if (aRowIndex < 0) {
michael@0 518 aRowIndex = rowCount + aRowIndex;
michael@0 519 } else {
michael@0 520 // aRowIndex is 1-based, so convert it to a 0-based index
michael@0 521 --aRowIndex;
michael@0 522 }
michael@0 523
michael@0 524 // if our inner table says that the index is valid, find the row now
michael@0 525 if (0 <= aRowIndex && aRowIndex <= rowCount) {
michael@0 526 nsIFrame* tableFrame = mFrames.FirstChild();
michael@0 527 NS_ASSERTION(tableFrame && tableFrame->GetType() == nsGkAtoms::tableFrame,
michael@0 528 "should always have an inner table frame");
michael@0 529 nsIFrame* rgFrame = tableFrame->GetFirstPrincipalChild();
michael@0 530 if (!rgFrame || rgFrame->GetType() != nsGkAtoms::tableRowGroupFrame)
michael@0 531 return nullptr;
michael@0 532 nsTableIterator rowIter(*rgFrame);
michael@0 533 nsIFrame* rowFrame = rowIter.First();
michael@0 534 for ( ; rowFrame; rowFrame = rowIter.Next()) {
michael@0 535 if (aRowIndex == 0) {
michael@0 536 DEBUG_VERIFY_THAT_FRAME_IS(rowFrame, TABLE_ROW);
michael@0 537 if (rowFrame->GetType() != nsGkAtoms::tableRowFrame)
michael@0 538 return nullptr;
michael@0 539
michael@0 540 return rowFrame;
michael@0 541 }
michael@0 542 --aRowIndex;
michael@0 543 }
michael@0 544 }
michael@0 545 return nullptr;
michael@0 546 }
michael@0 547
michael@0 548 nsresult
michael@0 549 nsMathMLmtableOuterFrame::Reflow(nsPresContext* aPresContext,
michael@0 550 nsHTMLReflowMetrics& aDesiredSize,
michael@0 551 const nsHTMLReflowState& aReflowState,
michael@0 552 nsReflowStatus& aStatus)
michael@0 553 {
michael@0 554 nsresult rv;
michael@0 555 nsAutoString value;
michael@0 556 // we want to return a table that is anchored according to the align attribute
michael@0 557
michael@0 558 rv = nsTableOuterFrame::Reflow(aPresContext, aDesiredSize, aReflowState,
michael@0 559 aStatus);
michael@0 560 NS_ASSERTION(aDesiredSize.Height() >= 0, "illegal height for mtable");
michael@0 561 NS_ASSERTION(aDesiredSize.Width() >= 0, "illegal width for mtable");
michael@0 562
michael@0 563 // see if the user has set the align attribute on the <mtable>
michael@0 564 int32_t rowIndex = 0;
michael@0 565 eAlign tableAlign = eAlign_axis;
michael@0 566 mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::align, value);
michael@0 567 if (!value.IsEmpty()) {
michael@0 568 ParseAlignAttribute(value, tableAlign, rowIndex);
michael@0 569 }
michael@0 570
michael@0 571 // adjustments if there is a specified row from where to anchor the table
michael@0 572 // (conceptually: when there is no row of reference, picture the table as if
michael@0 573 // it is wrapped in a single big fictional row at dy = 0, this way of
michael@0 574 // doing so allows us to have a single code path for all cases).
michael@0 575 nscoord dy = 0;
michael@0 576 nscoord height = aDesiredSize.Height();
michael@0 577 nsIFrame* rowFrame = nullptr;
michael@0 578 if (rowIndex) {
michael@0 579 rowFrame = GetRowFrameAt(aPresContext, rowIndex);
michael@0 580 if (rowFrame) {
michael@0 581 // translate the coordinates to be relative to us
michael@0 582 nsIFrame* frame = rowFrame;
michael@0 583 height = frame->GetSize().height;
michael@0 584 do {
michael@0 585 dy += frame->GetPosition().y;
michael@0 586 frame = frame->GetParent();
michael@0 587 } while (frame != this);
michael@0 588 }
michael@0 589 }
michael@0 590 switch (tableAlign) {
michael@0 591 case eAlign_top:
michael@0 592 aDesiredSize.SetTopAscent(dy);
michael@0 593 break;
michael@0 594 case eAlign_bottom:
michael@0 595 aDesiredSize.SetTopAscent(dy + height);
michael@0 596 break;
michael@0 597 case eAlign_center:
michael@0 598 aDesiredSize.SetTopAscent(dy + height / 2);
michael@0 599 break;
michael@0 600 case eAlign_baseline:
michael@0 601 if (rowFrame) {
michael@0 602 // anchor the table on the baseline of the row of reference
michael@0 603 nscoord rowAscent = ((nsTableRowFrame*)rowFrame)->GetMaxCellAscent();
michael@0 604 if (rowAscent) { // the row has at least one cell with 'vertical-align: baseline'
michael@0 605 aDesiredSize.SetTopAscent(dy + rowAscent);
michael@0 606 break;
michael@0 607 }
michael@0 608 }
michael@0 609 // in other situations, fallback to center
michael@0 610 aDesiredSize.SetTopAscent(dy + height / 2);
michael@0 611 break;
michael@0 612 case eAlign_axis:
michael@0 613 default: {
michael@0 614 // XXX should instead use style data from the row of reference here ?
michael@0 615 nsRefPtr<nsFontMetrics> fm;
michael@0 616 nsLayoutUtils::GetFontMetricsForFrame(this, getter_AddRefs(fm));
michael@0 617 aReflowState.rendContext->SetFont(fm);
michael@0 618 nscoord axisHeight;
michael@0 619 GetAxisHeight(*aReflowState.rendContext,
michael@0 620 aReflowState.rendContext->FontMetrics(),
michael@0 621 axisHeight);
michael@0 622 if (rowFrame) {
michael@0 623 // anchor the table on the axis of the row of reference
michael@0 624 // XXX fallback to baseline because it is a hard problem
michael@0 625 // XXX need to fetch the axis of the row; would need rowalign=axis to work better
michael@0 626 nscoord rowAscent = ((nsTableRowFrame*)rowFrame)->GetMaxCellAscent();
michael@0 627 if (rowAscent) { // the row has at least one cell with 'vertical-align: baseline'
michael@0 628 aDesiredSize.SetTopAscent(dy + rowAscent);
michael@0 629 break;
michael@0 630 }
michael@0 631 }
michael@0 632 // in other situations, fallback to using half of the height
michael@0 633 aDesiredSize.SetTopAscent(dy + height / 2 + axisHeight);
michael@0 634 }
michael@0 635 }
michael@0 636
michael@0 637 mReference.x = 0;
michael@0 638 mReference.y = aDesiredSize.TopAscent();
michael@0 639
michael@0 640 // just make-up a bounding metrics
michael@0 641 mBoundingMetrics = nsBoundingMetrics();
michael@0 642 mBoundingMetrics.ascent = aDesiredSize.TopAscent();
michael@0 643 mBoundingMetrics.descent = aDesiredSize.Height() - aDesiredSize.TopAscent();
michael@0 644 mBoundingMetrics.width = aDesiredSize.Width();
michael@0 645 mBoundingMetrics.leftBearing = 0;
michael@0 646 mBoundingMetrics.rightBearing = aDesiredSize.Width();
michael@0 647
michael@0 648 aDesiredSize.mBoundingMetrics = mBoundingMetrics;
michael@0 649 NS_FRAME_SET_TRUNCATION(aStatus, aReflowState, aDesiredSize);
michael@0 650
michael@0 651 return rv;
michael@0 652 }
michael@0 653
michael@0 654 nsIFrame*
michael@0 655 NS_NewMathMLmtableFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
michael@0 656 {
michael@0 657 return new (aPresShell) nsMathMLmtableFrame(aContext);
michael@0 658 }
michael@0 659
michael@0 660 NS_IMPL_FRAMEARENA_HELPERS(nsMathMLmtableFrame)
michael@0 661
michael@0 662 nsMathMLmtableFrame::~nsMathMLmtableFrame()
michael@0 663 {
michael@0 664 }
michael@0 665
michael@0 666 nsresult
michael@0 667 nsMathMLmtableFrame::SetInitialChildList(ChildListID aListID,
michael@0 668 nsFrameList& aChildList)
michael@0 669 {
michael@0 670 nsresult rv = nsTableFrame::SetInitialChildList(aListID, aChildList);
michael@0 671 if (NS_FAILED(rv)) return rv;
michael@0 672 MapAllAttributesIntoCSS(this);
michael@0 673 return rv;
michael@0 674 }
michael@0 675
michael@0 676 void
michael@0 677 nsMathMLmtableFrame::RestyleTable()
michael@0 678 {
michael@0 679 // re-sync MathML specific style data that may have changed
michael@0 680 MapAllAttributesIntoCSS(this);
michael@0 681
michael@0 682 // Explicitly request a re-resolve and reflow in our subtree to pick up any changes
michael@0 683 PresContext()->RestyleManager()->
michael@0 684 PostRestyleEvent(mContent->AsElement(), eRestyle_Subtree,
michael@0 685 nsChangeHint_AllReflowHints);
michael@0 686 }
michael@0 687
michael@0 688 // --------
michael@0 689 // implementation of nsMathMLmtrFrame
michael@0 690
michael@0 691 nsIFrame*
michael@0 692 NS_NewMathMLmtrFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
michael@0 693 {
michael@0 694 return new (aPresShell) nsMathMLmtrFrame(aContext);
michael@0 695 }
michael@0 696
michael@0 697 NS_IMPL_FRAMEARENA_HELPERS(nsMathMLmtrFrame)
michael@0 698
michael@0 699 nsMathMLmtrFrame::~nsMathMLmtrFrame()
michael@0 700 {
michael@0 701 }
michael@0 702
michael@0 703 nsresult
michael@0 704 nsMathMLmtrFrame::AttributeChanged(int32_t aNameSpaceID,
michael@0 705 nsIAtom* aAttribute,
michael@0 706 int32_t aModType)
michael@0 707 {
michael@0 708 // Attributes specific to <mtr>:
michael@0 709 // groupalign : Not yet supported.
michael@0 710 // rowalign : Here
michael@0 711 // columnalign : Here
michael@0 712
michael@0 713 nsPresContext* presContext = PresContext();
michael@0 714
michael@0 715 if (aAttribute != nsGkAtoms::rowalign_ &&
michael@0 716 aAttribute != nsGkAtoms::columnalign_) {
michael@0 717 return NS_OK;
michael@0 718 }
michael@0 719
michael@0 720 presContext->PropertyTable()->Delete(this, AttributeToProperty(aAttribute));
michael@0 721
michael@0 722 bool allowMultiValues = (aAttribute == nsGkAtoms::columnalign_);
michael@0 723
michael@0 724 // Reparse the new attribute.
michael@0 725 ParseFrameAttribute(this, aAttribute, allowMultiValues);
michael@0 726
michael@0 727 // Explicitly request a reflow in our subtree to pick up any changes
michael@0 728 presContext->PresShell()->
michael@0 729 FrameNeedsReflow(this, nsIPresShell::eStyleChange, NS_FRAME_IS_DIRTY);
michael@0 730
michael@0 731 return NS_OK;
michael@0 732 }
michael@0 733
michael@0 734 // --------
michael@0 735 // implementation of nsMathMLmtdFrame
michael@0 736
michael@0 737 nsIFrame*
michael@0 738 NS_NewMathMLmtdFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
michael@0 739 {
michael@0 740 return new (aPresShell) nsMathMLmtdFrame(aContext);
michael@0 741 }
michael@0 742
michael@0 743 NS_IMPL_FRAMEARENA_HELPERS(nsMathMLmtdFrame)
michael@0 744
michael@0 745 nsMathMLmtdFrame::~nsMathMLmtdFrame()
michael@0 746 {
michael@0 747 }
michael@0 748
michael@0 749 int32_t
michael@0 750 nsMathMLmtdFrame::GetRowSpan()
michael@0 751 {
michael@0 752 int32_t rowspan = 1;
michael@0 753
michael@0 754 // Don't look at the content's rowspan if we're not an mtd or a pseudo cell.
michael@0 755 if ((mContent->Tag() == nsGkAtoms::mtd_) && !StyleContext()->GetPseudo()) {
michael@0 756 nsAutoString value;
michael@0 757 mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::rowspan, value);
michael@0 758 if (!value.IsEmpty()) {
michael@0 759 nsresult error;
michael@0 760 rowspan = value.ToInteger(&error);
michael@0 761 if (NS_FAILED(error) || rowspan < 0)
michael@0 762 rowspan = 1;
michael@0 763 rowspan = std::min(rowspan, MAX_ROWSPAN);
michael@0 764 }
michael@0 765 }
michael@0 766 return rowspan;
michael@0 767 }
michael@0 768
michael@0 769 int32_t
michael@0 770 nsMathMLmtdFrame::GetColSpan()
michael@0 771 {
michael@0 772 int32_t colspan = 1;
michael@0 773
michael@0 774 // Don't look at the content's colspan if we're not an mtd or a pseudo cell.
michael@0 775 if ((mContent->Tag() == nsGkAtoms::mtd_) && !StyleContext()->GetPseudo()) {
michael@0 776 nsAutoString value;
michael@0 777 mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::columnspan_, value);
michael@0 778 if (!value.IsEmpty()) {
michael@0 779 nsresult error;
michael@0 780 colspan = value.ToInteger(&error);
michael@0 781 if (NS_FAILED(error) || colspan < 0 || colspan > MAX_COLSPAN)
michael@0 782 colspan = 1;
michael@0 783 }
michael@0 784 }
michael@0 785 return colspan;
michael@0 786 }
michael@0 787
michael@0 788 nsresult
michael@0 789 nsMathMLmtdFrame::AttributeChanged(int32_t aNameSpaceID,
michael@0 790 nsIAtom* aAttribute,
michael@0 791 int32_t aModType)
michael@0 792 {
michael@0 793 // Attributes specific to <mtd>:
michael@0 794 // groupalign : Not yet supported
michael@0 795 // rowalign : here
michael@0 796 // columnalign : here
michael@0 797 // rowspan : here
michael@0 798 // columnspan : here
michael@0 799
michael@0 800 if (aAttribute == nsGkAtoms::rowalign_ ||
michael@0 801 aAttribute == nsGkAtoms::columnalign_) {
michael@0 802
michael@0 803 nsPresContext* presContext = PresContext();
michael@0 804 presContext->PropertyTable()->Delete(this, AttributeToProperty(aAttribute));
michael@0 805
michael@0 806 // Reparse the attribute.
michael@0 807 ParseFrameAttribute(this, aAttribute, false);
michael@0 808 return NS_OK;
michael@0 809 }
michael@0 810
michael@0 811 if (aAttribute == nsGkAtoms::rowspan ||
michael@0 812 aAttribute == nsGkAtoms::columnspan_) {
michael@0 813 // use the naming expected by the base class
michael@0 814 if (aAttribute == nsGkAtoms::columnspan_)
michael@0 815 aAttribute = nsGkAtoms::colspan;
michael@0 816 return nsTableCellFrame::AttributeChanged(aNameSpaceID, aAttribute, aModType);
michael@0 817 }
michael@0 818
michael@0 819 return NS_OK;
michael@0 820 }
michael@0 821
michael@0 822 uint8_t
michael@0 823 nsMathMLmtdFrame::GetVerticalAlign() const
michael@0 824 {
michael@0 825 // Set the default alignment in case no alignment was specified
michael@0 826 uint8_t alignment = nsTableCellFrame::GetVerticalAlign();
michael@0 827
michael@0 828 nsTArray<int8_t>* alignmentList = FindCellProperty(this, RowAlignProperty());
michael@0 829
michael@0 830 if (alignmentList) {
michael@0 831 int32_t rowIndex;
michael@0 832 GetRowIndex(rowIndex);
michael@0 833
michael@0 834 // If the row number is greater than the number of provided rowalign values,
michael@0 835 // we simply repeat the last value.
michael@0 836 if (rowIndex < (int32_t)alignmentList->Length())
michael@0 837 alignment = alignmentList->ElementAt(rowIndex);
michael@0 838 else
michael@0 839 alignment = alignmentList->ElementAt(alignmentList->Length() - 1);
michael@0 840 }
michael@0 841
michael@0 842 return alignment;
michael@0 843 }
michael@0 844
michael@0 845 nsresult
michael@0 846 nsMathMLmtdFrame::ProcessBorders(nsTableFrame* aFrame,
michael@0 847 nsDisplayListBuilder* aBuilder,
michael@0 848 const nsDisplayListSet& aLists)
michael@0 849 {
michael@0 850 aLists.BorderBackground()->AppendNewToTop(new (aBuilder)
michael@0 851 nsDisplaymtdBorder(aBuilder, this));
michael@0 852 return NS_OK;
michael@0 853 }
michael@0 854
michael@0 855 nsMargin*
michael@0 856 nsMathMLmtdFrame::GetBorderWidth(nsMargin& aBorder) const
michael@0 857 {
michael@0 858 nsStyleBorder styleBorder = *StyleBorder();
michael@0 859 ApplyBorderToStyle(this, styleBorder);
michael@0 860 aBorder = styleBorder.GetComputedBorder();
michael@0 861 return &aBorder;
michael@0 862 }
michael@0 863
michael@0 864 // --------
michael@0 865 // implementation of nsMathMLmtdInnerFrame
michael@0 866
michael@0 867 NS_QUERYFRAME_HEAD(nsMathMLmtdInnerFrame)
michael@0 868 NS_QUERYFRAME_ENTRY(nsIMathMLFrame)
michael@0 869 NS_QUERYFRAME_TAIL_INHERITING(nsBlockFrame)
michael@0 870
michael@0 871 nsIFrame*
michael@0 872 NS_NewMathMLmtdInnerFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
michael@0 873 {
michael@0 874 return new (aPresShell) nsMathMLmtdInnerFrame(aContext);
michael@0 875 }
michael@0 876
michael@0 877 NS_IMPL_FRAMEARENA_HELPERS(nsMathMLmtdInnerFrame)
michael@0 878
michael@0 879 nsMathMLmtdInnerFrame::nsMathMLmtdInnerFrame(nsStyleContext* aContext)
michael@0 880 : nsBlockFrame(aContext)
michael@0 881 {
michael@0 882 // Make a copy of the parent nsStyleText for later modificaiton.
michael@0 883 mUniqueStyleText = new (PresContext()) nsStyleText(*StyleText());
michael@0 884 }
michael@0 885
michael@0 886 nsMathMLmtdInnerFrame::~nsMathMLmtdInnerFrame()
michael@0 887 {
michael@0 888 mUniqueStyleText->Destroy(PresContext());
michael@0 889 }
michael@0 890
michael@0 891 nsresult
michael@0 892 nsMathMLmtdInnerFrame::Reflow(nsPresContext* aPresContext,
michael@0 893 nsHTMLReflowMetrics& aDesiredSize,
michael@0 894 const nsHTMLReflowState& aReflowState,
michael@0 895 nsReflowStatus& aStatus)
michael@0 896 {
michael@0 897 // Let the base class do the reflow
michael@0 898 nsresult rv = nsBlockFrame::Reflow(aPresContext, aDesiredSize, aReflowState, aStatus);
michael@0 899
michael@0 900 // more about <maligngroup/> and <malignmark/> later
michael@0 901 // ...
michael@0 902 return rv;
michael@0 903 }
michael@0 904
michael@0 905 const
michael@0 906 nsStyleText* nsMathMLmtdInnerFrame::StyleTextForLineLayout()
michael@0 907 {
michael@0 908 // Set the default alignment in case nothing was specified
michael@0 909 uint8_t alignment = StyleText()->mTextAlign;
michael@0 910
michael@0 911 nsTArray<int8_t>* alignmentList =
michael@0 912 FindCellProperty(this, ColumnAlignProperty());
michael@0 913
michael@0 914 if (alignmentList) {
michael@0 915 nsMathMLmtdFrame* cellFrame = (nsMathMLmtdFrame*)GetParent();
michael@0 916 int32_t columnIndex;
michael@0 917 cellFrame->GetColIndex(columnIndex);
michael@0 918
michael@0 919 // If the column number is greater than the number of provided columalign
michael@0 920 // values, we simply repeat the last value.
michael@0 921 if (columnIndex < (int32_t)alignmentList->Length())
michael@0 922 alignment = alignmentList->ElementAt(columnIndex);
michael@0 923 else
michael@0 924 alignment = alignmentList->ElementAt(alignmentList->Length() - 1);
michael@0 925 }
michael@0 926
michael@0 927 mUniqueStyleText->mTextAlign = alignment;
michael@0 928 return mUniqueStyleText;
michael@0 929 }
michael@0 930
michael@0 931 /* virtual */ void
michael@0 932 nsMathMLmtdInnerFrame::DidSetStyleContext(nsStyleContext* aOldStyleContext)
michael@0 933 {
michael@0 934 nsBlockFrame::DidSetStyleContext(aOldStyleContext);
michael@0 935 mUniqueStyleText->Destroy(PresContext());
michael@0 936 mUniqueStyleText = new (PresContext()) nsStyleText(*StyleText());
michael@0 937 }

mercurial