diff -r 000000000000 -r 4f2f89ce4247 WebCore/wml/WMLTableElement.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/WebCore/wml/WMLTableElement.cpp Fri Sep 17 09:02:29 2010 +0300 @@ -0,0 +1,270 @@ +/** + * Copyright (C) 2008 Torch Mobile Inc. All rights reserved. (http://www.torchmobile.com/) + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ + +#include "config.h" + +#if ENABLE(WML) +#include "WMLTableElement.h" + +#include "Attribute.h" +#include "CSSPropertyNames.h" +#include "CSSValueKeywords.h" +#include "CharacterNames.h" +#include "Document.h" +#include "HTMLNames.h" +#include "NodeList.h" +#include "RenderObject.h" +#include "Text.h" +#include "WMLErrorHandling.h" +#include "WMLNames.h" + +namespace WebCore { + +using namespace WMLNames; + +WMLTableElement::WMLTableElement(const QualifiedName& tagName, Document* doc) + : WMLElement(tagName, doc) + , m_columns(0) +{ +} + +WMLTableElement::~WMLTableElement() +{ +} + +bool WMLTableElement::mapToEntry(const QualifiedName& attrName, MappedAttributeEntry& result) const +{ + if (attrName == HTMLNames::alignAttr) { + result = eTable; + return false; + } + + return WMLElement::mapToEntry(attrName, result); +} + +void WMLTableElement::parseMappedAttribute(Attribute* attr) +{ + if (attr->name() == columnsAttr) { + bool isNumber = false; + m_columns = attr->value().string().toUIntStrict(&isNumber); + + // Spec: This required attribute specifies the number of columns for the table. + // The user agent must create a table with exactly the number of columns specified + // by the attribute value. It is an error to specify a value of zero ("0") + if (!m_columns || !isNumber) + reportWMLError(document(), WMLErrorInvalidColumnsNumberInTable); + } else if (attr->name() == HTMLNames::alignAttr) + m_alignment = parseValueForbiddingVariableReferences(attr->value()); + else + WMLElement::parseMappedAttribute(attr); +} + +void WMLTableElement::finishParsingChildren() +{ + WMLElement::finishParsingChildren(); + + if (!m_columns) { + reportWMLError(document(), WMLErrorInvalidColumnsNumberInTable); + return; + } + + Vector rowElements = scanTableChildElements(this, trTag); + if (rowElements.isEmpty()) + return; + + Vector::iterator it = rowElements.begin(); + Vector::iterator end = rowElements.end(); + + for (; it != end; ++it) { + WMLElement* rowElement = (*it); + + // Squeeze the table to fit in the desired number of columns + Vector columnElements = scanTableChildElements(rowElement, tdTag); + unsigned actualNumberOfColumns = columnElements.size(); + + if (actualNumberOfColumns > m_columns) { + joinSuperflousColumns(columnElements, rowElement); + columnElements = scanTableChildElements(rowElement, tdTag); + } else if (actualNumberOfColumns < m_columns) { + padWithEmptyColumns(columnElements, rowElement); + columnElements = scanTableChildElements(rowElement, tdTag); + } + + // Layout cells according to the 'align' attribute + alignCells(columnElements, rowElement); + } +} + +Vector WMLTableElement::scanTableChildElements(WMLElement* startElement, const QualifiedName& tagName) const +{ + Vector childElements; + + RefPtr children = startElement->childNodes(); + if (!children) + return childElements; + + unsigned length = children->length(); + for (unsigned i = 0; i < length; ++i) { + Node* child = children->item(i); + if (child->hasTagName(tagName)) + childElements.append(static_cast(child)); + } + + return childElements; +} + +void WMLTableElement::transferAllChildrenOfElementToTargetElement(WMLElement* sourceElement, WMLElement* targetElement, unsigned startOffset) const +{ + RefPtr children = sourceElement->childNodes(); + if (!children) + return; + + ExceptionCode ec = 0; + + unsigned length = children->length(); + for (unsigned i = startOffset; i < length; ++i) { + RefPtr clonedNode = children->item(i)->cloneNode(true); + targetElement->appendChild(clonedNode.release(), ec); + ASSERT(ec == 0); + } +} + +bool WMLTableElement::tryMergeAdjacentTextCells(Node* item, Node* nextItem) const +{ + if (!item || !nextItem) + return false; + + if (!item->isTextNode() || !nextItem->isTextNode()) + return false; + + Text* itemText = static_cast(item); + Text* nextItemText = static_cast(nextItem); + + String newContent = " "; + newContent += nextItemText->data(); + + ExceptionCode ec = 0; + itemText->appendData(newContent, ec); + ASSERT(ec == 0); + + return true; +} + +void WMLTableElement::joinSuperflousColumns(Vector& columnElements, WMLElement* rowElement) const +{ + // Spec: If the actual number of columns in a row is greater than the value specified + // by this attribute, the extra columns of the row must be aggregated into the last + // column such that the row contains exactly the number of columns specified. + WMLElement* lastColumn = columnElements.at(m_columns - 1); + ASSERT(lastColumn); + + // Merge superflous columns into a single one + RefPtr newCell = WMLElement::create(tdTag, document()); + transferAllChildrenOfElementToTargetElement(lastColumn, newCell.get(), 0); + + ExceptionCode ec = 0; + unsigned actualNumberOfColumns = columnElements.size(); + + for (unsigned i = m_columns; i < actualNumberOfColumns; ++i) { + WMLElement* columnElement = columnElements.at(i); + unsigned startOffset = 0; + + // Spec: A single inter-word space must be inserted between two cells that are being aggregated. + if (tryMergeAdjacentTextCells(newCell->lastChild(), columnElement->firstChild())) + ++startOffset; + + transferAllChildrenOfElementToTargetElement(columnElement, newCell.get(), startOffset); + } + + // Remove the columns, that have just been merged + unsigned i = actualNumberOfColumns; + for (; i > m_columns; --i) { + rowElement->removeChild(columnElements.at(i - 1), ec); + ASSERT(ec == 0); + } + + // Replace the last column in the row with the new merged column + rowElement->replaceChild(newCell.release(), lastColumn, ec); + ASSERT(ec == 0); +} + +void WMLTableElement::padWithEmptyColumns(Vector& columnElements, WMLElement* rowElement) const +{ + // Spec: If the actual number of columns in a row is less than the value specified by the columns + // attribute, the row must be padded with empty columns effectively as if the user agent + // appended empty td elements to the row. + ExceptionCode ec = 0; + + for (unsigned i = columnElements.size(); i < m_columns; ++i) { + RefPtr newCell = WMLElement::create(tdTag, document()); + rowElement->appendChild(newCell.release(), ec); + ASSERT(ec == 0); + } +} + +void WMLTableElement::alignCells(Vector& columnElements, WMLElement* rowElement) const +{ + // Spec: User agents should consider the current language when determining + // the default alignment and the direction of the table. + bool rtl = false; + if (RenderObject* renderer = rowElement->renderer()) { + if (RenderStyle* style = renderer->style()) + rtl = style->direction() == RTL; + } + + rowElement->setAttribute(HTMLNames::alignAttr, rtl ? "right" : "left"); + + if (m_alignment.isEmpty()) + return; + + unsigned alignLength = m_alignment.length(); + + Vector::iterator it = columnElements.begin(); + Vector::iterator end = columnElements.end(); + + for (unsigned i = 0; it != end; ++it, ++i) { + if (i == alignLength) + break; + + String alignmentValue; + switch (m_alignment[i]) { + case 'C': + alignmentValue = "center"; + break; + case 'L': + alignmentValue = "left"; + break; + case 'R': + alignmentValue = "right"; + break; + default: + break; + } + + if (alignmentValue.isEmpty()) + continue; + + (*it)->setAttribute(HTMLNames::alignAttr, alignmentValue); + } +} + +} + +#endif