WebCore/wml/WMLTableElement.cpp
changeset 0 4f2f89ce4247
equal deleted inserted replaced
-1:000000000000 0:4f2f89ce4247
       
     1 /**
       
     2  * Copyright (C) 2008 Torch Mobile Inc. All rights reserved. (http://www.torchmobile.com/)
       
     3  *
       
     4  * This library is free software; you can redistribute it and/or
       
     5  * modify it under the terms of the GNU Library General Public
       
     6  * License as published by the Free Software Foundation; either
       
     7  * version 2 of the License, or (at your option) any later version.
       
     8  *
       
     9  * This library is distributed in the hope that it will be useful,
       
    10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
       
    11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
       
    12  * Library General Public License for more details.
       
    13  *
       
    14  * You should have received a copy of the GNU Library General Public License
       
    15  * along with this library; see the file COPYING.LIB.  If not, write to
       
    16  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
       
    17  * Boston, MA 02110-1301, USA.
       
    18  *
       
    19  */
       
    20 
       
    21 #include "config.h"
       
    22 
       
    23 #if ENABLE(WML)
       
    24 #include "WMLTableElement.h"
       
    25 
       
    26 #include "Attribute.h"
       
    27 #include "CSSPropertyNames.h"
       
    28 #include "CSSValueKeywords.h"
       
    29 #include "CharacterNames.h"
       
    30 #include "Document.h"
       
    31 #include "HTMLNames.h"
       
    32 #include "NodeList.h"
       
    33 #include "RenderObject.h"
       
    34 #include "Text.h"
       
    35 #include "WMLErrorHandling.h"
       
    36 #include "WMLNames.h"
       
    37 
       
    38 namespace WebCore {
       
    39 
       
    40 using namespace WMLNames;
       
    41 
       
    42 WMLTableElement::WMLTableElement(const QualifiedName& tagName, Document* doc)
       
    43     : WMLElement(tagName, doc)
       
    44     , m_columns(0)
       
    45 {
       
    46 }
       
    47 
       
    48 WMLTableElement::~WMLTableElement()
       
    49 {
       
    50 }
       
    51 
       
    52 bool WMLTableElement::mapToEntry(const QualifiedName& attrName, MappedAttributeEntry& result) const
       
    53 {
       
    54     if (attrName == HTMLNames::alignAttr) {
       
    55         result = eTable;
       
    56         return false;
       
    57     }
       
    58 
       
    59     return WMLElement::mapToEntry(attrName, result);
       
    60 }
       
    61 
       
    62 void WMLTableElement::parseMappedAttribute(Attribute* attr)
       
    63 {
       
    64     if (attr->name() == columnsAttr) {
       
    65         bool isNumber = false;
       
    66         m_columns = attr->value().string().toUIntStrict(&isNumber);
       
    67 
       
    68         // Spec: This required attribute specifies the number of columns for the table.
       
    69         // The user agent must create a table with exactly the number of columns specified
       
    70         // by the attribute value. It is an error to specify a value of zero ("0")
       
    71         if (!m_columns || !isNumber)
       
    72             reportWMLError(document(), WMLErrorInvalidColumnsNumberInTable);
       
    73     } else if (attr->name() == HTMLNames::alignAttr)
       
    74         m_alignment = parseValueForbiddingVariableReferences(attr->value());
       
    75     else
       
    76         WMLElement::parseMappedAttribute(attr);
       
    77 }
       
    78 
       
    79 void WMLTableElement::finishParsingChildren()
       
    80 {
       
    81     WMLElement::finishParsingChildren();
       
    82 
       
    83     if (!m_columns) {
       
    84         reportWMLError(document(), WMLErrorInvalidColumnsNumberInTable);
       
    85         return;
       
    86     }
       
    87 
       
    88     Vector<WMLElement*> rowElements = scanTableChildElements(this, trTag);
       
    89     if (rowElements.isEmpty())
       
    90         return;
       
    91 
       
    92     Vector<WMLElement*>::iterator it = rowElements.begin();
       
    93     Vector<WMLElement*>::iterator end = rowElements.end();
       
    94 
       
    95     for (; it != end; ++it) {
       
    96         WMLElement* rowElement = (*it);
       
    97 
       
    98         // Squeeze the table to fit in the desired number of columns
       
    99         Vector<WMLElement*> columnElements = scanTableChildElements(rowElement, tdTag);
       
   100         unsigned actualNumberOfColumns = columnElements.size();
       
   101 
       
   102         if (actualNumberOfColumns > m_columns) {
       
   103             joinSuperflousColumns(columnElements, rowElement);
       
   104             columnElements = scanTableChildElements(rowElement, tdTag);
       
   105         } else if (actualNumberOfColumns < m_columns) {
       
   106             padWithEmptyColumns(columnElements, rowElement);
       
   107             columnElements = scanTableChildElements(rowElement, tdTag);
       
   108         }
       
   109 
       
   110         // Layout cells according to the 'align' attribute
       
   111         alignCells(columnElements, rowElement);
       
   112     }
       
   113 }
       
   114 
       
   115 Vector<WMLElement*> WMLTableElement::scanTableChildElements(WMLElement* startElement, const QualifiedName& tagName) const
       
   116 {
       
   117     Vector<WMLElement*> childElements;
       
   118 
       
   119     RefPtr<NodeList> children = startElement->childNodes();
       
   120     if (!children)
       
   121         return childElements;
       
   122 
       
   123     unsigned length = children->length();
       
   124     for (unsigned i = 0; i < length; ++i) {
       
   125         Node* child = children->item(i);
       
   126         if (child->hasTagName(tagName))
       
   127             childElements.append(static_cast<WMLElement*>(child));
       
   128     }
       
   129 
       
   130     return childElements;
       
   131 }
       
   132 
       
   133 void WMLTableElement::transferAllChildrenOfElementToTargetElement(WMLElement* sourceElement, WMLElement* targetElement, unsigned startOffset) const
       
   134 {
       
   135     RefPtr<NodeList> children = sourceElement->childNodes();
       
   136     if (!children)
       
   137         return;
       
   138 
       
   139     ExceptionCode ec = 0;
       
   140 
       
   141     unsigned length = children->length();
       
   142     for (unsigned i = startOffset; i < length; ++i) {
       
   143         RefPtr<Node> clonedNode = children->item(i)->cloneNode(true);
       
   144         targetElement->appendChild(clonedNode.release(), ec);
       
   145         ASSERT(ec == 0);
       
   146     }
       
   147 }
       
   148 
       
   149 bool WMLTableElement::tryMergeAdjacentTextCells(Node* item, Node* nextItem) const
       
   150 {
       
   151     if (!item || !nextItem)
       
   152         return false;
       
   153 
       
   154     if (!item->isTextNode() || !nextItem->isTextNode())
       
   155         return false;
       
   156 
       
   157     Text* itemText = static_cast<Text*>(item);
       
   158     Text* nextItemText = static_cast<Text*>(nextItem);
       
   159 
       
   160     String newContent = " ";
       
   161     newContent += nextItemText->data();
       
   162 
       
   163     ExceptionCode ec = 0;
       
   164     itemText->appendData(newContent, ec);
       
   165     ASSERT(ec == 0);
       
   166 
       
   167     return true;
       
   168 }
       
   169 
       
   170 void WMLTableElement::joinSuperflousColumns(Vector<WMLElement*>& columnElements, WMLElement* rowElement) const
       
   171 {
       
   172     // Spec: If the actual number of columns in a row is greater than the value specified
       
   173     // by this attribute, the extra columns of the row must be aggregated into the last
       
   174     // column such that the row contains exactly the number of columns specified.
       
   175     WMLElement* lastColumn = columnElements.at(m_columns - 1);
       
   176     ASSERT(lastColumn);
       
   177 
       
   178     // Merge superflous columns into a single one
       
   179     RefPtr<WMLElement> newCell = WMLElement::create(tdTag, document());
       
   180     transferAllChildrenOfElementToTargetElement(lastColumn, newCell.get(), 0);
       
   181 
       
   182     ExceptionCode ec = 0;
       
   183     unsigned actualNumberOfColumns = columnElements.size();
       
   184 
       
   185     for (unsigned i = m_columns; i < actualNumberOfColumns; ++i) {
       
   186         WMLElement* columnElement = columnElements.at(i);
       
   187         unsigned startOffset = 0;
       
   188 
       
   189         // Spec: A single inter-word space must be inserted between two cells that are being aggregated.
       
   190         if (tryMergeAdjacentTextCells(newCell->lastChild(), columnElement->firstChild()))
       
   191             ++startOffset;
       
   192 
       
   193         transferAllChildrenOfElementToTargetElement(columnElement, newCell.get(), startOffset);
       
   194     }
       
   195 
       
   196     // Remove the columns, that have just been merged
       
   197     unsigned i = actualNumberOfColumns;
       
   198     for (; i > m_columns; --i) {
       
   199         rowElement->removeChild(columnElements.at(i - 1), ec);
       
   200         ASSERT(ec == 0);
       
   201     }
       
   202 
       
   203     // Replace the last column in the row with the new merged column
       
   204     rowElement->replaceChild(newCell.release(), lastColumn, ec);
       
   205     ASSERT(ec == 0);
       
   206 }
       
   207 
       
   208 void WMLTableElement::padWithEmptyColumns(Vector<WMLElement*>& columnElements, WMLElement* rowElement) const
       
   209 {
       
   210     // Spec: If the actual number of columns in a row is less than the value specified by the columns 
       
   211     // attribute, the row must be padded with empty columns effectively as if the user agent 
       
   212     // appended empty td elements to the row. 
       
   213     ExceptionCode ec = 0;
       
   214 
       
   215     for (unsigned i = columnElements.size(); i < m_columns; ++i) {
       
   216         RefPtr<WMLElement> newCell = WMLElement::create(tdTag, document());
       
   217         rowElement->appendChild(newCell.release(), ec);
       
   218         ASSERT(ec == 0);
       
   219     }
       
   220 }
       
   221 
       
   222 void WMLTableElement::alignCells(Vector<WMLElement*>& columnElements, WMLElement* rowElement) const
       
   223 {
       
   224     // Spec: User agents should consider the current language when determining
       
   225     // the default alignment and the direction of the table.
       
   226     bool rtl = false;
       
   227     if (RenderObject* renderer = rowElement->renderer()) {
       
   228         if (RenderStyle* style = renderer->style())
       
   229             rtl = style->direction() == RTL;
       
   230     }
       
   231 
       
   232     rowElement->setAttribute(HTMLNames::alignAttr, rtl ? "right" : "left");
       
   233 
       
   234     if (m_alignment.isEmpty())
       
   235         return;
       
   236 
       
   237     unsigned alignLength = m_alignment.length();
       
   238 
       
   239     Vector<WMLElement*>::iterator it = columnElements.begin();
       
   240     Vector<WMLElement*>::iterator end = columnElements.end();
       
   241 
       
   242     for (unsigned i = 0; it != end; ++it, ++i) {
       
   243         if (i == alignLength)
       
   244             break;
       
   245 
       
   246         String alignmentValue;
       
   247         switch (m_alignment[i]) {
       
   248         case 'C':
       
   249             alignmentValue = "center";
       
   250             break;
       
   251         case 'L':
       
   252             alignmentValue = "left";
       
   253             break;
       
   254         case 'R':
       
   255             alignmentValue = "right";
       
   256             break;
       
   257         default:
       
   258             break;
       
   259         }
       
   260 
       
   261         if (alignmentValue.isEmpty())
       
   262             continue;
       
   263 
       
   264         (*it)->setAttribute(HTMLNames::alignAttr, alignmentValue);
       
   265     }
       
   266 }
       
   267 
       
   268 }
       
   269 
       
   270 #endif