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