layout/mathml/nsMathMLmtableFrame.cpp

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/layout/mathml/nsMathMLmtableFrame.cpp	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,937 @@
     1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
     1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public
     1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this
     1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     1.8 +
     1.9 +#include "nsMathMLmtableFrame.h"
    1.10 +#include "nsPresContext.h"
    1.11 +#include "nsStyleContext.h"
    1.12 +#include "nsStyleConsts.h"
    1.13 +#include "nsNameSpaceManager.h"
    1.14 +#include "nsRenderingContext.h"
    1.15 +#include "nsCSSRendering.h"
    1.16 +
    1.17 +#include "nsTArray.h"
    1.18 +#include "nsTableFrame.h"
    1.19 +#include "celldata.h"
    1.20 +
    1.21 +#include "RestyleManager.h"
    1.22 +#include <algorithm>
    1.23 +
    1.24 +#include "nsIScriptError.h"
    1.25 +#include "nsContentUtils.h"
    1.26 +
    1.27 +using namespace mozilla;
    1.28 +
    1.29 +//
    1.30 +// <mtable> -- table or matrix - implementation
    1.31 +//
    1.32 +
    1.33 +static int8_t
    1.34 +ParseStyleValue(nsIAtom* aAttribute, const nsAString& aAttributeValue)
    1.35 +{
    1.36 +  if (aAttribute == nsGkAtoms::rowalign_) {
    1.37 +    if (aAttributeValue.EqualsLiteral("top"))
    1.38 +      return NS_STYLE_VERTICAL_ALIGN_TOP;
    1.39 +    else if (aAttributeValue.EqualsLiteral("bottom"))
    1.40 +      return NS_STYLE_VERTICAL_ALIGN_BOTTOM;
    1.41 +    else if (aAttributeValue.EqualsLiteral("center"))
    1.42 +      return NS_STYLE_VERTICAL_ALIGN_MIDDLE;
    1.43 +    else
    1.44 +      return NS_STYLE_VERTICAL_ALIGN_BASELINE;
    1.45 +  } else if (aAttribute == nsGkAtoms::columnalign_) {
    1.46 +    if (aAttributeValue.EqualsLiteral("left"))
    1.47 +      return NS_STYLE_TEXT_ALIGN_LEFT;
    1.48 +    else if (aAttributeValue.EqualsLiteral("right"))
    1.49 +      return NS_STYLE_TEXT_ALIGN_RIGHT;
    1.50 +    else
    1.51 +      return NS_STYLE_TEXT_ALIGN_CENTER;
    1.52 +  } else if (aAttribute == nsGkAtoms::rowlines_ ||
    1.53 +             aAttribute == nsGkAtoms::columnlines_) {
    1.54 +    if (aAttributeValue.EqualsLiteral("solid"))
    1.55 +      return NS_STYLE_BORDER_STYLE_SOLID;
    1.56 +    else if (aAttributeValue.EqualsLiteral("dashed"))
    1.57 +      return NS_STYLE_BORDER_STYLE_DASHED;
    1.58 +    else
    1.59 +      return NS_STYLE_BORDER_STYLE_NONE;
    1.60 +  } else {
    1.61 +    MOZ_CRASH("Unrecognized attribute.");
    1.62 +  }
    1.63 +
    1.64 +  return -1;
    1.65 +}
    1.66 +
    1.67 +static nsTArray<int8_t>*
    1.68 +ExtractStyleValues(const nsAString& aString, nsIAtom* aAttribute,
    1.69 +                   bool aAllowMultiValues)
    1.70 +{
    1.71 +  nsTArray<int8_t>* styleArray = nullptr;
    1.72 +
    1.73 +  const char16_t* start = aString.BeginReading();
    1.74 +  const char16_t* end = aString.EndReading();
    1.75 +
    1.76 +  int32_t startIndex = 0;
    1.77 +  int32_t count = 0;
    1.78 +
    1.79 +  while (start < end) {
    1.80 +    // Skip leading spaces.
    1.81 +    while ((start < end) && nsCRT::IsAsciiSpace(*start)) {
    1.82 +      start++;
    1.83 +      startIndex++;
    1.84 +    }
    1.85 +
    1.86 +    // Look for the end of the string, or another space.
    1.87 +    while ((start < end) && !nsCRT::IsAsciiSpace(*start)) {
    1.88 +      start++;
    1.89 +      count++;
    1.90 +    }
    1.91 +
    1.92 +    // Grab the value found and process it.
    1.93 +    if (count > 0) {
    1.94 +      if (!styleArray)
    1.95 +        styleArray = new nsTArray<int8_t>();
    1.96 +
    1.97 +      // We want to return a null array if an attribute gives multiple values,
    1.98 +      // but multiple values aren't allowed.
    1.99 +      if (styleArray->Length() > 1 && !aAllowMultiValues) {
   1.100 +        delete styleArray;
   1.101 +        return nullptr;
   1.102 +      }
   1.103 +
   1.104 +      nsDependentSubstring valueString(aString, startIndex, count);
   1.105 +      int8_t styleValue = ParseStyleValue(aAttribute, valueString);
   1.106 +      styleArray->AppendElement(styleValue);
   1.107 +
   1.108 +      startIndex += count;
   1.109 +      count = 0;
   1.110 +    }
   1.111 +  }
   1.112 +  return styleArray;
   1.113 +}
   1.114 +
   1.115 +static nsresult ReportParseError(nsIFrame* aFrame, const char16_t* aAttribute,
   1.116 +                                 const char16_t* aValue)
   1.117 +{
   1.118 +  nsIContent* content = aFrame->GetContent();
   1.119 +
   1.120 +  const char16_t* params[] =
   1.121 +    { aValue, aAttribute, content->Tag()->GetUTF16String() };
   1.122 +
   1.123 +  return nsContentUtils::ReportToConsole(nsIScriptError::errorFlag,
   1.124 +                                         NS_LITERAL_CSTRING("MathML"),
   1.125 +                                         content->OwnerDoc(),
   1.126 +                                         nsContentUtils::eMATHML_PROPERTIES,
   1.127 +                                         "AttributeParsingError", params, 3);
   1.128 +}
   1.129 +
   1.130 +// Each rowalign='top bottom' or columnalign='left right center' (from
   1.131 +// <mtable> or <mtr>) is split once into an nsTArray<int8_t> which is
   1.132 +// stored in the property table. Row/Cell frames query the property table
   1.133 +// to see what values apply to them.
   1.134 +
   1.135 +static void
   1.136 +DestroyStylePropertyList(void* aPropertyValue)
   1.137 +{
   1.138 +  delete static_cast<nsTArray<int8_t>*>(aPropertyValue);
   1.139 +}
   1.140 +
   1.141 +NS_DECLARE_FRAME_PROPERTY(RowAlignProperty, DestroyStylePropertyList)
   1.142 +NS_DECLARE_FRAME_PROPERTY(RowLinesProperty, DestroyStylePropertyList)
   1.143 +NS_DECLARE_FRAME_PROPERTY(ColumnAlignProperty, DestroyStylePropertyList)
   1.144 +NS_DECLARE_FRAME_PROPERTY(ColumnLinesProperty, DestroyStylePropertyList)
   1.145 +
   1.146 +static const FramePropertyDescriptor*
   1.147 +AttributeToProperty(nsIAtom* aAttribute)
   1.148 +{
   1.149 +  if (aAttribute == nsGkAtoms::rowalign_)
   1.150 +    return RowAlignProperty();
   1.151 +  if (aAttribute == nsGkAtoms::rowlines_)
   1.152 +    return RowLinesProperty();
   1.153 +  if (aAttribute == nsGkAtoms::columnalign_)
   1.154 +    return ColumnAlignProperty();
   1.155 +  NS_ASSERTION(aAttribute == nsGkAtoms::columnlines_, "Invalid attribute");
   1.156 +  return ColumnLinesProperty();
   1.157 +}
   1.158 +
   1.159 +/* This method looks for a property that applies to a cell, but it looks
   1.160 + * recursively because some cell properties can come from the cell, a row,
   1.161 + * a table, etc. This function searches through the heirarchy for a property
   1.162 + * and returns its value. The function stops searching after checking a <mtable>
   1.163 + * frame.
   1.164 + */
   1.165 +static nsTArray<int8_t>*
   1.166 +FindCellProperty(const nsIFrame* aCellFrame,
   1.167 +                 const FramePropertyDescriptor* aFrameProperty)
   1.168 +{
   1.169 +  const nsIFrame* currentFrame = aCellFrame;
   1.170 +  nsTArray<int8_t>* propertyData = nullptr;
   1.171 +
   1.172 +  while (currentFrame) {
   1.173 +    FrameProperties props = currentFrame->Properties();
   1.174 +    propertyData = static_cast<nsTArray<int8_t>*>(props.Get(aFrameProperty));
   1.175 +    bool frameIsTable = (currentFrame->GetType() == nsGkAtoms::tableFrame);
   1.176 +
   1.177 +    if (propertyData || frameIsTable)
   1.178 +      currentFrame = nullptr; // A null frame pointer exits the loop
   1.179 +    else
   1.180 +      currentFrame = currentFrame->GetParent(); // Go to the parent frame
   1.181 +  }
   1.182 +
   1.183 +  return propertyData;
   1.184 +}
   1.185 +
   1.186 +static void
   1.187 +ApplyBorderToStyle(const nsMathMLmtdFrame* aFrame,
   1.188 +                   nsStyleBorder& aStyleBorder)
   1.189 +{
   1.190 +  int32_t rowIndex;
   1.191 +  int32_t columnIndex;
   1.192 +  aFrame->GetRowIndex(rowIndex);
   1.193 +  aFrame->GetColIndex(columnIndex);
   1.194 +
   1.195 +  nscoord borderWidth =
   1.196 +    aFrame->PresContext()->GetBorderWidthTable()[NS_STYLE_BORDER_WIDTH_THIN];
   1.197 +
   1.198 +  nsTArray<int8_t>* rowLinesList =
   1.199 +    FindCellProperty(aFrame, RowLinesProperty());
   1.200 +
   1.201 +  nsTArray<int8_t>* columnLinesList =
   1.202 +    FindCellProperty(aFrame, ColumnLinesProperty());
   1.203 +
   1.204 +  // We don't place a row line on top of the first row
   1.205 +  if (rowIndex > 0 && rowLinesList) {
   1.206 +    // If the row number is greater than the number of provided rowline
   1.207 +    // values, we simply repeat the last value.
   1.208 +    int32_t listLength = rowLinesList->Length();
   1.209 +    if (rowIndex < listLength) {
   1.210 +      aStyleBorder.SetBorderStyle(NS_SIDE_TOP,
   1.211 +                    rowLinesList->ElementAt(rowIndex - 1));
   1.212 +    } else {
   1.213 +      aStyleBorder.SetBorderStyle(NS_SIDE_TOP,
   1.214 +                    rowLinesList->ElementAt(listLength - 1));
   1.215 +    }
   1.216 +    aStyleBorder.SetBorderWidth(NS_SIDE_TOP, borderWidth);
   1.217 +  }
   1.218 +
   1.219 +  // We don't place a column line on the left of the first column.
   1.220 +  if (columnIndex > 0 && columnLinesList) {
   1.221 +    // If the column number is greater than the number of provided columline
   1.222 +    // values, we simply repeat the last value.
   1.223 +    int32_t listLength = columnLinesList->Length();
   1.224 +    if (columnIndex < listLength) {
   1.225 +      aStyleBorder.SetBorderStyle(NS_SIDE_LEFT,
   1.226 +                    columnLinesList->ElementAt(columnIndex - 1));
   1.227 +    } else {
   1.228 +      aStyleBorder.SetBorderStyle(NS_SIDE_LEFT,
   1.229 +                    columnLinesList->ElementAt(listLength - 1));
   1.230 +    }
   1.231 +    aStyleBorder.SetBorderWidth(NS_SIDE_LEFT, borderWidth);
   1.232 +  }
   1.233 +}
   1.234 +
   1.235 +/*
   1.236 + * A variant of the nsDisplayBorder contains special code to render a border
   1.237 + * around a nsMathMLmtdFrame based on the rowline and columnline properties
   1.238 + * set on the cell frame.
   1.239 + */
   1.240 +class nsDisplaymtdBorder : public nsDisplayBorder {
   1.241 +public:
   1.242 +  nsDisplaymtdBorder(nsDisplayListBuilder* aBuilder, nsMathMLmtdFrame* aFrame)
   1.243 +    : nsDisplayBorder(aBuilder, aFrame)
   1.244 +  {
   1.245 +  }
   1.246 +
   1.247 +  virtual nsRect GetBounds(nsDisplayListBuilder* aBuilder, bool* aSnap) MOZ_OVERRIDE
   1.248 +  {
   1.249 +    nsStyleBorder styleBorder = *mFrame->StyleBorder();
   1.250 +    ApplyBorderToStyle(static_cast<nsMathMLmtdFrame*>(mFrame), styleBorder);
   1.251 +    return CalculateBounds(styleBorder);
   1.252 +  }
   1.253 +
   1.254 +  virtual void Paint(nsDisplayListBuilder* aBuilder, nsRenderingContext* aCtx) MOZ_OVERRIDE
   1.255 +  {
   1.256 +    nsStyleBorder styleBorder = *mFrame->StyleBorder();
   1.257 +    ApplyBorderToStyle(static_cast<nsMathMLmtdFrame*>(mFrame), styleBorder);
   1.258 +
   1.259 +    nsPoint offset = ToReferenceFrame();
   1.260 +    nsCSSRendering::PaintBorderWithStyleBorder(mFrame->PresContext(), *aCtx,
   1.261 +                                               mFrame, mVisibleRect,
   1.262 +                                               nsRect(offset,
   1.263 +                                                      mFrame->GetSize()),
   1.264 +                                               styleBorder,
   1.265 +                                               mFrame->StyleContext(),
   1.266 +                                               mFrame->GetSkipSides());
   1.267 +  }
   1.268 +};
   1.269 +
   1.270 +#ifdef DEBUG
   1.271 +#define DEBUG_VERIFY_THAT_FRAME_IS(_frame, _expected) \
   1.272 +  NS_ASSERTION(NS_STYLE_DISPLAY_##_expected == _frame->StyleDisplay()->mDisplay, "internal error");
   1.273 +#else
   1.274 +#define DEBUG_VERIFY_THAT_FRAME_IS(_frame, _expected)
   1.275 +#endif
   1.276 +
   1.277 +static void
   1.278 +ParseFrameAttribute(nsIFrame* aFrame, nsIAtom* aAttribute,
   1.279 +                    bool aAllowMultiValues)
   1.280 +{
   1.281 +  nsAutoString attrValue;
   1.282 +
   1.283 +  nsIContent* frameContent = aFrame->GetContent();
   1.284 +  frameContent->GetAttr(kNameSpaceID_None, aAttribute, attrValue);
   1.285 +
   1.286 +  if (!attrValue.IsEmpty()) {
   1.287 +    nsTArray<int8_t>* valueList =
   1.288 +      ExtractStyleValues(attrValue, aAttribute, aAllowMultiValues);
   1.289 +
   1.290 +    // If valueList is null, that indicates a problem with the attribute value.
   1.291 +    // Only set properties on a valid attribute value.
   1.292 +    if (valueList) {
   1.293 +      // The code reading the property assumes that this list is nonempty.
   1.294 +      NS_ASSERTION(valueList->Length() >= 1, "valueList should not be empty!");
   1.295 +      FrameProperties props = aFrame->Properties();
   1.296 +      props.Set(AttributeToProperty(aAttribute), valueList);
   1.297 +    } else {
   1.298 +      ReportParseError(aFrame, aAttribute->GetUTF16String(), attrValue.get());
   1.299 +    }
   1.300 +  }
   1.301 +}
   1.302 +
   1.303 +// map all attribues within a table -- requires the indices of rows and cells.
   1.304 +// so it can only happen after they are made ready by the table base class.
   1.305 +static void
   1.306 +MapAllAttributesIntoCSS(nsIFrame* aTableFrame)
   1.307 +{
   1.308 +  // Map mtable rowalign & rowlines.
   1.309 +  ParseFrameAttribute(aTableFrame, nsGkAtoms::rowalign_, true);
   1.310 +  ParseFrameAttribute(aTableFrame, nsGkAtoms::rowlines_, true);
   1.311 +
   1.312 +  // Map mtable columnalign & columnlines.
   1.313 +  ParseFrameAttribute(aTableFrame, nsGkAtoms::columnalign_, true);
   1.314 +  ParseFrameAttribute(aTableFrame, nsGkAtoms::columnlines_, true);
   1.315 +
   1.316 +  // mtable is simple and only has one (pseudo) row-group
   1.317 +  nsIFrame* rgFrame = aTableFrame->GetFirstPrincipalChild();
   1.318 +  if (!rgFrame || rgFrame->GetType() != nsGkAtoms::tableRowGroupFrame)
   1.319 +    return;
   1.320 +
   1.321 +  nsIFrame* rowFrame = rgFrame->GetFirstPrincipalChild();
   1.322 +  for ( ; rowFrame; rowFrame = rowFrame->GetNextSibling()) {
   1.323 +    DEBUG_VERIFY_THAT_FRAME_IS(rowFrame, TABLE_ROW);
   1.324 +    if (rowFrame->GetType() == nsGkAtoms::tableRowFrame) {
   1.325 +      // Map row rowalign.
   1.326 +      ParseFrameAttribute(rowFrame, nsGkAtoms::rowalign_, false);
   1.327 +      // Map row columnalign.
   1.328 +      ParseFrameAttribute(rowFrame, nsGkAtoms::columnalign_, true);
   1.329 +
   1.330 +      nsIFrame* cellFrame = rowFrame->GetFirstPrincipalChild();
   1.331 +      for ( ; cellFrame; cellFrame = cellFrame->GetNextSibling()) {
   1.332 +        DEBUG_VERIFY_THAT_FRAME_IS(cellFrame, TABLE_CELL);
   1.333 +        if (IS_TABLE_CELL(cellFrame->GetType())) {
   1.334 +          // Map cell rowalign.
   1.335 +          ParseFrameAttribute(cellFrame, nsGkAtoms::rowalign_, false);
   1.336 +          // Map row columnalign.
   1.337 +          ParseFrameAttribute(cellFrame, nsGkAtoms::columnalign_, false);
   1.338 +        }
   1.339 +      }
   1.340 +    }
   1.341 +  }
   1.342 +}
   1.343 +
   1.344 +// the align attribute of mtable can have a row number which indicates
   1.345 +// from where to anchor the table, e.g., top 5 means anchor the table at
   1.346 +// the top of the 5th row, axis -1 means anchor the table on the axis of
   1.347 +// the last row
   1.348 +
   1.349 +// The REC says that the syntax is 
   1.350 +// '\s*(top|bottom|center|baseline|axis)(\s+-?[0-9]+)?\s*' 
   1.351 +// the parsing could have been simpler with that syntax
   1.352 +// but for backward compatibility we make optional 
   1.353 +// the whitespaces between the alignment name and the row number
   1.354 +
   1.355 +enum eAlign {
   1.356 +  eAlign_top,
   1.357 +  eAlign_bottom,
   1.358 +  eAlign_center,
   1.359 +  eAlign_baseline,
   1.360 +  eAlign_axis
   1.361 +};
   1.362 +
   1.363 +static void
   1.364 +ParseAlignAttribute(nsString& aValue, eAlign& aAlign, int32_t& aRowIndex)
   1.365 +{
   1.366 +  // by default, the table is centered about the axis
   1.367 +  aRowIndex = 0;
   1.368 +  aAlign = eAlign_axis;
   1.369 +  int32_t len = 0;
   1.370 +
   1.371 +  // we only have to remove the leading spaces because 
   1.372 +  // ToInteger ignores the whitespaces around the number
   1.373 +  aValue.CompressWhitespace(true, false);
   1.374 +
   1.375 +  if (0 == aValue.Find("top")) {
   1.376 +    len = 3; // 3 is the length of 'top'
   1.377 +    aAlign = eAlign_top;
   1.378 +  }
   1.379 +  else if (0 == aValue.Find("bottom")) {
   1.380 +    len = 6; // 6 is the length of 'bottom'
   1.381 +    aAlign = eAlign_bottom;
   1.382 +  }
   1.383 +  else if (0 == aValue.Find("center")) {
   1.384 +    len = 6; // 6 is the length of 'center'
   1.385 +    aAlign = eAlign_center;
   1.386 +  }
   1.387 +  else if (0 == aValue.Find("baseline")) {
   1.388 +    len = 8; // 8 is the length of 'baseline'
   1.389 +    aAlign = eAlign_baseline;
   1.390 +  }
   1.391 +  else if (0 == aValue.Find("axis")) {
   1.392 +    len = 4; // 4 is the length of 'axis'
   1.393 +    aAlign = eAlign_axis;
   1.394 +  }
   1.395 +  if (len) {
   1.396 +    nsresult error;
   1.397 +    aValue.Cut(0, len); // aValue is not a const here
   1.398 +    aRowIndex = aValue.ToInteger(&error);
   1.399 +    if (NS_FAILED(error))
   1.400 +      aRowIndex = 0;
   1.401 +  }
   1.402 +}
   1.403 +
   1.404 +#ifdef DEBUG_rbs_off
   1.405 +// call ListMathMLTree(mParent) to get the big picture
   1.406 +static void
   1.407 +ListMathMLTree(nsIFrame* atLeast)
   1.408 +{
   1.409 +  // climb up to <math> or <body> if <math> isn't there
   1.410 +  nsIFrame* f = atLeast;
   1.411 +  for ( ; f; f = f->GetParent()) {
   1.412 +    nsIContent* c = f->GetContent();
   1.413 +    if (!c || c->Tag() == nsGkAtoms::math || c->Tag() == nsGkAtoms::body)
   1.414 +      break;
   1.415 +  }
   1.416 +  if (!f) f = atLeast;
   1.417 +  f->List(stdout, 0);
   1.418 +}
   1.419 +#endif
   1.420 +
   1.421 +// --------
   1.422 +// implementation of nsMathMLmtableOuterFrame
   1.423 +
   1.424 +NS_QUERYFRAME_HEAD(nsMathMLmtableOuterFrame)
   1.425 +  NS_QUERYFRAME_ENTRY(nsIMathMLFrame)
   1.426 +NS_QUERYFRAME_TAIL_INHERITING(nsTableOuterFrame)
   1.427 +
   1.428 +nsIFrame*
   1.429 +NS_NewMathMLmtableOuterFrame (nsIPresShell* aPresShell, nsStyleContext* aContext)
   1.430 +{
   1.431 +  return new (aPresShell) nsMathMLmtableOuterFrame(aContext);
   1.432 +}
   1.433 +
   1.434 +NS_IMPL_FRAMEARENA_HELPERS(nsMathMLmtableOuterFrame)
   1.435 + 
   1.436 +nsMathMLmtableOuterFrame::~nsMathMLmtableOuterFrame()
   1.437 +{
   1.438 +}
   1.439 +
   1.440 +nsresult
   1.441 +nsMathMLmtableOuterFrame::AttributeChanged(int32_t  aNameSpaceID,
   1.442 +                                           nsIAtom* aAttribute,
   1.443 +                                           int32_t  aModType)
   1.444 +{
   1.445 +  // Attributes specific to <mtable>:
   1.446 +  // frame         : in mathml.css
   1.447 +  // framespacing  : not yet supported 
   1.448 +  // groupalign    : not yet supported
   1.449 +  // equalrows     : not yet supported 
   1.450 +  // equalcolumns  : not yet supported 
   1.451 +  // displaystyle  : here and in mathml.css
   1.452 +  // align         : in reflow 
   1.453 +  // rowalign      : here
   1.454 +  // rowlines      : here 
   1.455 +  // rowspacing    : not yet supported 
   1.456 +  // columnalign   : here 
   1.457 +  // columnlines   : here 
   1.458 +  // columnspacing : not yet supported 
   1.459 +
   1.460 +  // mtable is simple and only has one (pseudo) row-group inside our inner-table
   1.461 +  nsIFrame* tableFrame = mFrames.FirstChild();
   1.462 +  NS_ASSERTION(tableFrame && tableFrame->GetType() == nsGkAtoms::tableFrame,
   1.463 +               "should always have an inner table frame");
   1.464 +  nsIFrame* rgFrame = tableFrame->GetFirstPrincipalChild();
   1.465 +  if (!rgFrame || rgFrame->GetType() != nsGkAtoms::tableRowGroupFrame)
   1.466 +    return NS_OK;
   1.467 +
   1.468 +  // align - just need to issue a dirty (resize) reflow command
   1.469 +  if (aAttribute == nsGkAtoms::align) {
   1.470 +    PresContext()->PresShell()->
   1.471 +      FrameNeedsReflow(this, nsIPresShell::eResize, NS_FRAME_IS_DIRTY);
   1.472 +    return NS_OK;
   1.473 +  }
   1.474 +
   1.475 +  // displaystyle - may seem innocuous, but it is actually very harsh --
   1.476 +  // like changing an unit. Blow away and recompute all our automatic
   1.477 +  // presentational data, and issue a style-changed reflow request
   1.478 +  if (aAttribute == nsGkAtoms::displaystyle_) {
   1.479 +    nsMathMLContainerFrame::RebuildAutomaticDataForChildren(mParent);
   1.480 +    // Need to reflow the parent, not us, because this can actually
   1.481 +    // affect siblings.
   1.482 +    PresContext()->PresShell()->
   1.483 +      FrameNeedsReflow(mParent, nsIPresShell::eStyleChange, NS_FRAME_IS_DIRTY);
   1.484 +    return NS_OK;
   1.485 +  }
   1.486 +
   1.487 +  // ...and the other attributes affect rows or columns in one way or another
   1.488 +
   1.489 +  // Ignore attributes that do not affect layout.
   1.490 +  if (aAttribute != nsGkAtoms::rowalign_ &&
   1.491 +      aAttribute != nsGkAtoms::rowlines_ &&
   1.492 +      aAttribute != nsGkAtoms::columnalign_ &&
   1.493 +      aAttribute != nsGkAtoms::columnlines_) {
   1.494 +    return NS_OK;
   1.495 +  }
   1.496 +
   1.497 +  nsPresContext* presContext = tableFrame->PresContext();
   1.498 +
   1.499 +  // clear any cached property list for this table
   1.500 +  presContext->PropertyTable()->
   1.501 +    Delete(tableFrame, AttributeToProperty(aAttribute));
   1.502 +
   1.503 +  // Reparse the new attribute on the table.
   1.504 +  ParseFrameAttribute(tableFrame, aAttribute, true);
   1.505 +
   1.506 +  // Explicitly request a reflow in our subtree to pick up any changes
   1.507 +  presContext->PresShell()->
   1.508 +      FrameNeedsReflow(this, nsIPresShell::eStyleChange, NS_FRAME_IS_DIRTY);
   1.509 +
   1.510 +  return NS_OK;
   1.511 +}
   1.512 +
   1.513 +nsIFrame*
   1.514 +nsMathMLmtableOuterFrame::GetRowFrameAt(nsPresContext* aPresContext,
   1.515 +                                        int32_t         aRowIndex)
   1.516 +{
   1.517 +  int32_t rowCount = GetRowCount();
   1.518 +
   1.519 +  // Negative indices mean to find upwards from the end.
   1.520 +  if (aRowIndex < 0) {
   1.521 +    aRowIndex = rowCount + aRowIndex;
   1.522 +  } else {
   1.523 +    // aRowIndex is 1-based, so convert it to a 0-based index
   1.524 +    --aRowIndex;
   1.525 +  }
   1.526 +
   1.527 +  // if our inner table says that the index is valid, find the row now
   1.528 +  if (0 <= aRowIndex && aRowIndex <= rowCount) {
   1.529 +    nsIFrame* tableFrame = mFrames.FirstChild();
   1.530 +    NS_ASSERTION(tableFrame && tableFrame->GetType() == nsGkAtoms::tableFrame,
   1.531 +                 "should always have an inner table frame");
   1.532 +    nsIFrame* rgFrame = tableFrame->GetFirstPrincipalChild();
   1.533 +    if (!rgFrame || rgFrame->GetType() != nsGkAtoms::tableRowGroupFrame)
   1.534 +      return nullptr;
   1.535 +    nsTableIterator rowIter(*rgFrame);
   1.536 +    nsIFrame* rowFrame = rowIter.First();
   1.537 +    for ( ; rowFrame; rowFrame = rowIter.Next()) {
   1.538 +      if (aRowIndex == 0) {
   1.539 +        DEBUG_VERIFY_THAT_FRAME_IS(rowFrame, TABLE_ROW);
   1.540 +        if (rowFrame->GetType() != nsGkAtoms::tableRowFrame)
   1.541 +          return nullptr;
   1.542 +
   1.543 +        return rowFrame;
   1.544 +      }
   1.545 +      --aRowIndex;
   1.546 +    }
   1.547 +  }
   1.548 +  return nullptr;
   1.549 +}
   1.550 +
   1.551 +nsresult
   1.552 +nsMathMLmtableOuterFrame::Reflow(nsPresContext*          aPresContext,
   1.553 +                                 nsHTMLReflowMetrics&     aDesiredSize,
   1.554 +                                 const nsHTMLReflowState& aReflowState,
   1.555 +                                 nsReflowStatus&          aStatus)
   1.556 +{
   1.557 +  nsresult rv;
   1.558 +  nsAutoString value;
   1.559 +  // we want to return a table that is anchored according to the align attribute
   1.560 +
   1.561 +  rv = nsTableOuterFrame::Reflow(aPresContext, aDesiredSize, aReflowState,
   1.562 +                                 aStatus);
   1.563 +  NS_ASSERTION(aDesiredSize.Height() >= 0, "illegal height for mtable");
   1.564 +  NS_ASSERTION(aDesiredSize.Width() >= 0, "illegal width for mtable");
   1.565 +
   1.566 +  // see if the user has set the align attribute on the <mtable>
   1.567 +  int32_t rowIndex = 0;
   1.568 +  eAlign tableAlign = eAlign_axis;
   1.569 +  mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::align, value);
   1.570 +  if (!value.IsEmpty()) {
   1.571 +    ParseAlignAttribute(value, tableAlign, rowIndex);
   1.572 +  }
   1.573 +
   1.574 +  // adjustments if there is a specified row from where to anchor the table
   1.575 +  // (conceptually: when there is no row of reference, picture the table as if
   1.576 +  // it is wrapped in a single big fictional row at dy = 0, this way of
   1.577 +  // doing so allows us to have a single code path for all cases).
   1.578 +  nscoord dy = 0;
   1.579 +  nscoord height = aDesiredSize.Height();
   1.580 +  nsIFrame* rowFrame = nullptr;
   1.581 +  if (rowIndex) {
   1.582 +    rowFrame = GetRowFrameAt(aPresContext, rowIndex);
   1.583 +    if (rowFrame) {
   1.584 +      // translate the coordinates to be relative to us
   1.585 +      nsIFrame* frame = rowFrame;
   1.586 +      height = frame->GetSize().height;
   1.587 +      do {
   1.588 +        dy += frame->GetPosition().y;
   1.589 +        frame = frame->GetParent();
   1.590 +      } while (frame != this);
   1.591 +    }
   1.592 +  }
   1.593 +  switch (tableAlign) {
   1.594 +    case eAlign_top:
   1.595 +      aDesiredSize.SetTopAscent(dy);
   1.596 +      break;
   1.597 +    case eAlign_bottom:
   1.598 +      aDesiredSize.SetTopAscent(dy + height);
   1.599 +      break;
   1.600 +    case eAlign_center:
   1.601 +      aDesiredSize.SetTopAscent(dy + height / 2);
   1.602 +      break;
   1.603 +    case eAlign_baseline:
   1.604 +      if (rowFrame) {
   1.605 +        // anchor the table on the baseline of the row of reference
   1.606 +        nscoord rowAscent = ((nsTableRowFrame*)rowFrame)->GetMaxCellAscent();
   1.607 +        if (rowAscent) { // the row has at least one cell with 'vertical-align: baseline'
   1.608 +          aDesiredSize.SetTopAscent(dy + rowAscent);
   1.609 +          break;
   1.610 +        }
   1.611 +      }
   1.612 +      // in other situations, fallback to center
   1.613 +      aDesiredSize.SetTopAscent(dy + height / 2);
   1.614 +      break;
   1.615 +    case eAlign_axis:
   1.616 +    default: {
   1.617 +      // XXX should instead use style data from the row of reference here ?
   1.618 +      nsRefPtr<nsFontMetrics> fm;
   1.619 +      nsLayoutUtils::GetFontMetricsForFrame(this, getter_AddRefs(fm));
   1.620 +      aReflowState.rendContext->SetFont(fm);
   1.621 +      nscoord axisHeight;
   1.622 +      GetAxisHeight(*aReflowState.rendContext,
   1.623 +                    aReflowState.rendContext->FontMetrics(),
   1.624 +                    axisHeight);
   1.625 +      if (rowFrame) {
   1.626 +        // anchor the table on the axis of the row of reference
   1.627 +        // XXX fallback to baseline because it is a hard problem
   1.628 +        // XXX need to fetch the axis of the row; would need rowalign=axis to work better
   1.629 +        nscoord rowAscent = ((nsTableRowFrame*)rowFrame)->GetMaxCellAscent();
   1.630 +        if (rowAscent) { // the row has at least one cell with 'vertical-align: baseline'
   1.631 +          aDesiredSize.SetTopAscent(dy + rowAscent);
   1.632 +          break;
   1.633 +        }
   1.634 +      }
   1.635 +      // in other situations, fallback to using half of the height
   1.636 +      aDesiredSize.SetTopAscent(dy + height / 2 + axisHeight);
   1.637 +    }
   1.638 +  }
   1.639 +
   1.640 +  mReference.x = 0;
   1.641 +  mReference.y = aDesiredSize.TopAscent();
   1.642 +
   1.643 +  // just make-up a bounding metrics
   1.644 +  mBoundingMetrics = nsBoundingMetrics();
   1.645 +  mBoundingMetrics.ascent = aDesiredSize.TopAscent();
   1.646 +  mBoundingMetrics.descent = aDesiredSize.Height() - aDesiredSize.TopAscent();
   1.647 +  mBoundingMetrics.width = aDesiredSize.Width();
   1.648 +  mBoundingMetrics.leftBearing = 0;
   1.649 +  mBoundingMetrics.rightBearing = aDesiredSize.Width();
   1.650 +
   1.651 +  aDesiredSize.mBoundingMetrics = mBoundingMetrics;
   1.652 +  NS_FRAME_SET_TRUNCATION(aStatus, aReflowState, aDesiredSize);
   1.653 +
   1.654 +  return rv;
   1.655 +}
   1.656 +
   1.657 +nsIFrame*
   1.658 +NS_NewMathMLmtableFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
   1.659 +{
   1.660 +  return new (aPresShell) nsMathMLmtableFrame(aContext);
   1.661 +}
   1.662 +
   1.663 +NS_IMPL_FRAMEARENA_HELPERS(nsMathMLmtableFrame)
   1.664 +
   1.665 +nsMathMLmtableFrame::~nsMathMLmtableFrame()
   1.666 +{
   1.667 +}
   1.668 +
   1.669 +nsresult
   1.670 +nsMathMLmtableFrame::SetInitialChildList(ChildListID  aListID,
   1.671 +                                         nsFrameList& aChildList)
   1.672 +{
   1.673 +  nsresult rv = nsTableFrame::SetInitialChildList(aListID, aChildList);
   1.674 +  if (NS_FAILED(rv)) return rv;
   1.675 +  MapAllAttributesIntoCSS(this);
   1.676 +  return rv;
   1.677 +}
   1.678 +
   1.679 +void
   1.680 +nsMathMLmtableFrame::RestyleTable()
   1.681 +{
   1.682 +  // re-sync MathML specific style data that may have changed
   1.683 +  MapAllAttributesIntoCSS(this);
   1.684 +
   1.685 +  // Explicitly request a re-resolve and reflow in our subtree to pick up any changes
   1.686 +  PresContext()->RestyleManager()->
   1.687 +    PostRestyleEvent(mContent->AsElement(), eRestyle_Subtree,
   1.688 +                     nsChangeHint_AllReflowHints);
   1.689 +}
   1.690 +
   1.691 +// --------
   1.692 +// implementation of nsMathMLmtrFrame
   1.693 +
   1.694 +nsIFrame*
   1.695 +NS_NewMathMLmtrFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
   1.696 +{
   1.697 +  return new (aPresShell) nsMathMLmtrFrame(aContext);
   1.698 +}
   1.699 +
   1.700 +NS_IMPL_FRAMEARENA_HELPERS(nsMathMLmtrFrame)
   1.701 +
   1.702 +nsMathMLmtrFrame::~nsMathMLmtrFrame()
   1.703 +{
   1.704 +}
   1.705 +
   1.706 +nsresult
   1.707 +nsMathMLmtrFrame::AttributeChanged(int32_t  aNameSpaceID,
   1.708 +                                   nsIAtom* aAttribute,
   1.709 +                                   int32_t  aModType)
   1.710 +{
   1.711 +  // Attributes specific to <mtr>:
   1.712 +  // groupalign  : Not yet supported.
   1.713 +  // rowalign    : Here
   1.714 +  // columnalign : Here
   1.715 +
   1.716 +  nsPresContext* presContext = PresContext();
   1.717 +
   1.718 +  if (aAttribute != nsGkAtoms::rowalign_ &&
   1.719 +      aAttribute != nsGkAtoms::columnalign_) {
   1.720 +    return NS_OK;
   1.721 +  }
   1.722 +
   1.723 +  presContext->PropertyTable()->Delete(this, AttributeToProperty(aAttribute));
   1.724 +
   1.725 +  bool allowMultiValues = (aAttribute == nsGkAtoms::columnalign_);
   1.726 +
   1.727 +  // Reparse the new attribute.
   1.728 +  ParseFrameAttribute(this, aAttribute, allowMultiValues);
   1.729 +
   1.730 +  // Explicitly request a reflow in our subtree to pick up any changes
   1.731 +  presContext->PresShell()->
   1.732 +      FrameNeedsReflow(this, nsIPresShell::eStyleChange, NS_FRAME_IS_DIRTY);
   1.733 +
   1.734 +  return NS_OK;
   1.735 +}
   1.736 +
   1.737 +// --------
   1.738 +// implementation of nsMathMLmtdFrame
   1.739 +
   1.740 +nsIFrame*
   1.741 +NS_NewMathMLmtdFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
   1.742 +{
   1.743 +  return new (aPresShell) nsMathMLmtdFrame(aContext);
   1.744 +}
   1.745 +
   1.746 +NS_IMPL_FRAMEARENA_HELPERS(nsMathMLmtdFrame)
   1.747 +
   1.748 +nsMathMLmtdFrame::~nsMathMLmtdFrame()
   1.749 +{
   1.750 +}
   1.751 +
   1.752 +int32_t
   1.753 +nsMathMLmtdFrame::GetRowSpan()
   1.754 +{
   1.755 +  int32_t rowspan = 1;
   1.756 +
   1.757 +  // Don't look at the content's rowspan if we're not an mtd or a pseudo cell.
   1.758 +  if ((mContent->Tag() == nsGkAtoms::mtd_) && !StyleContext()->GetPseudo()) {
   1.759 +    nsAutoString value;
   1.760 +    mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::rowspan, value);
   1.761 +    if (!value.IsEmpty()) {
   1.762 +      nsresult error;
   1.763 +      rowspan = value.ToInteger(&error);
   1.764 +      if (NS_FAILED(error) || rowspan < 0)
   1.765 +        rowspan = 1;
   1.766 +      rowspan = std::min(rowspan, MAX_ROWSPAN);
   1.767 +    }
   1.768 +  }
   1.769 +  return rowspan;
   1.770 +}
   1.771 +
   1.772 +int32_t
   1.773 +nsMathMLmtdFrame::GetColSpan()
   1.774 +{
   1.775 +  int32_t colspan = 1;
   1.776 +
   1.777 +  // Don't look at the content's colspan if we're not an mtd or a pseudo cell.
   1.778 +  if ((mContent->Tag() == nsGkAtoms::mtd_) && !StyleContext()->GetPseudo()) {
   1.779 +    nsAutoString value;
   1.780 +    mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::columnspan_, value);
   1.781 +    if (!value.IsEmpty()) {
   1.782 +      nsresult error;
   1.783 +      colspan = value.ToInteger(&error);
   1.784 +      if (NS_FAILED(error) || colspan < 0 || colspan > MAX_COLSPAN)
   1.785 +        colspan = 1;
   1.786 +    }
   1.787 +  }
   1.788 +  return colspan;
   1.789 +}
   1.790 +
   1.791 +nsresult
   1.792 +nsMathMLmtdFrame::AttributeChanged(int32_t  aNameSpaceID,
   1.793 +                                   nsIAtom* aAttribute,
   1.794 +                                   int32_t  aModType)
   1.795 +{
   1.796 +  // Attributes specific to <mtd>:
   1.797 +  // groupalign  : Not yet supported
   1.798 +  // rowalign    : here
   1.799 +  // columnalign : here
   1.800 +  // rowspan     : here
   1.801 +  // columnspan  : here
   1.802 +
   1.803 +  if (aAttribute == nsGkAtoms::rowalign_ ||
   1.804 +      aAttribute == nsGkAtoms::columnalign_) {
   1.805 +
   1.806 +    nsPresContext* presContext = PresContext();
   1.807 +    presContext->PropertyTable()->Delete(this, AttributeToProperty(aAttribute));
   1.808 +
   1.809 +    // Reparse the attribute.
   1.810 +    ParseFrameAttribute(this, aAttribute, false);
   1.811 +    return NS_OK;
   1.812 +  }
   1.813 +
   1.814 +  if (aAttribute == nsGkAtoms::rowspan ||
   1.815 +      aAttribute == nsGkAtoms::columnspan_) {
   1.816 +    // use the naming expected by the base class 
   1.817 +    if (aAttribute == nsGkAtoms::columnspan_)
   1.818 +      aAttribute = nsGkAtoms::colspan;
   1.819 +    return nsTableCellFrame::AttributeChanged(aNameSpaceID, aAttribute, aModType);
   1.820 +  }
   1.821 +
   1.822 +  return NS_OK;
   1.823 +}
   1.824 +
   1.825 +uint8_t
   1.826 +nsMathMLmtdFrame::GetVerticalAlign() const
   1.827 +{
   1.828 +  // Set the default alignment in case no alignment was specified
   1.829 +  uint8_t alignment = nsTableCellFrame::GetVerticalAlign();
   1.830 +
   1.831 +  nsTArray<int8_t>* alignmentList = FindCellProperty(this, RowAlignProperty());
   1.832 +
   1.833 +  if (alignmentList) {
   1.834 +    int32_t rowIndex;
   1.835 +    GetRowIndex(rowIndex);
   1.836 +
   1.837 +    // If the row number is greater than the number of provided rowalign values,
   1.838 +    // we simply repeat the last value.
   1.839 +    if (rowIndex < (int32_t)alignmentList->Length())
   1.840 +      alignment = alignmentList->ElementAt(rowIndex);
   1.841 +    else
   1.842 +      alignment = alignmentList->ElementAt(alignmentList->Length() - 1);
   1.843 +  }
   1.844 +
   1.845 +  return alignment;
   1.846 +}
   1.847 +
   1.848 +nsresult
   1.849 +nsMathMLmtdFrame::ProcessBorders(nsTableFrame* aFrame,
   1.850 +                                 nsDisplayListBuilder* aBuilder,
   1.851 +                                 const nsDisplayListSet& aLists)
   1.852 +{
   1.853 +  aLists.BorderBackground()->AppendNewToTop(new (aBuilder)
   1.854 +                                            nsDisplaymtdBorder(aBuilder, this));
   1.855 +  return NS_OK;
   1.856 +}
   1.857 +
   1.858 +nsMargin*
   1.859 +nsMathMLmtdFrame::GetBorderWidth(nsMargin& aBorder) const
   1.860 +{
   1.861 +  nsStyleBorder styleBorder = *StyleBorder();
   1.862 +  ApplyBorderToStyle(this, styleBorder);
   1.863 +  aBorder = styleBorder.GetComputedBorder();
   1.864 +  return &aBorder;
   1.865 +}
   1.866 +
   1.867 +// --------
   1.868 +// implementation of nsMathMLmtdInnerFrame
   1.869 +
   1.870 +NS_QUERYFRAME_HEAD(nsMathMLmtdInnerFrame)
   1.871 +  NS_QUERYFRAME_ENTRY(nsIMathMLFrame)
   1.872 +NS_QUERYFRAME_TAIL_INHERITING(nsBlockFrame)
   1.873 +
   1.874 +nsIFrame*
   1.875 +NS_NewMathMLmtdInnerFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
   1.876 +{
   1.877 +  return new (aPresShell) nsMathMLmtdInnerFrame(aContext);
   1.878 +}
   1.879 +
   1.880 +NS_IMPL_FRAMEARENA_HELPERS(nsMathMLmtdInnerFrame)
   1.881 +
   1.882 +nsMathMLmtdInnerFrame::nsMathMLmtdInnerFrame(nsStyleContext* aContext)
   1.883 +  : nsBlockFrame(aContext)
   1.884 +{
   1.885 +  // Make a copy of the parent nsStyleText for later modificaiton.
   1.886 +  mUniqueStyleText = new (PresContext()) nsStyleText(*StyleText());
   1.887 +}
   1.888 +
   1.889 +nsMathMLmtdInnerFrame::~nsMathMLmtdInnerFrame()
   1.890 +{
   1.891 +  mUniqueStyleText->Destroy(PresContext());
   1.892 +}
   1.893 +
   1.894 +nsresult
   1.895 +nsMathMLmtdInnerFrame::Reflow(nsPresContext*          aPresContext,
   1.896 +                              nsHTMLReflowMetrics&     aDesiredSize,
   1.897 +                              const nsHTMLReflowState& aReflowState,
   1.898 +                              nsReflowStatus&          aStatus)
   1.899 +{
   1.900 +  // Let the base class do the reflow
   1.901 +  nsresult rv = nsBlockFrame::Reflow(aPresContext, aDesiredSize, aReflowState, aStatus);
   1.902 +
   1.903 +  // more about <maligngroup/> and <malignmark/> later
   1.904 +  // ...
   1.905 +  return rv;
   1.906 +}
   1.907 +
   1.908 +const
   1.909 +nsStyleText* nsMathMLmtdInnerFrame::StyleTextForLineLayout()
   1.910 +{
   1.911 +  // Set the default alignment in case nothing was specified
   1.912 +  uint8_t alignment = StyleText()->mTextAlign;
   1.913 +
   1.914 +  nsTArray<int8_t>* alignmentList =
   1.915 +    FindCellProperty(this, ColumnAlignProperty());
   1.916 +
   1.917 +  if (alignmentList) {
   1.918 +    nsMathMLmtdFrame* cellFrame = (nsMathMLmtdFrame*)GetParent();
   1.919 +    int32_t columnIndex;
   1.920 +    cellFrame->GetColIndex(columnIndex);
   1.921 +
   1.922 +    // If the column number is greater than the number of provided columalign
   1.923 +    // values, we simply repeat the last value.
   1.924 +    if (columnIndex < (int32_t)alignmentList->Length())
   1.925 +      alignment = alignmentList->ElementAt(columnIndex);
   1.926 +    else
   1.927 +      alignment = alignmentList->ElementAt(alignmentList->Length() - 1);
   1.928 +  }
   1.929 +
   1.930 +  mUniqueStyleText->mTextAlign = alignment;
   1.931 +  return mUniqueStyleText;
   1.932 +}
   1.933 +
   1.934 +/* virtual */ void
   1.935 +nsMathMLmtdInnerFrame::DidSetStyleContext(nsStyleContext* aOldStyleContext)
   1.936 +{
   1.937 +  nsBlockFrame::DidSetStyleContext(aOldStyleContext);
   1.938 +  mUniqueStyleText->Destroy(PresContext());
   1.939 +  mUniqueStyleText = new (PresContext()) nsStyleText(*StyleText());
   1.940 +}

mercurial