• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "Document.h"
30 #include "HTMLNames.h"
31 #include "NodeList.h"
32 #include "RenderObject.h"
33 #include "Text.h"
34 #include "WMLErrorHandling.h"
35 #include "WMLNames.h"
36 #include <wtf/unicode/CharacterNames.h>
37 
38 namespace WebCore {
39 
40 using namespace WMLNames;
41 
WMLTableElement(const QualifiedName & tagName,Document * doc)42 WMLTableElement::WMLTableElement(const QualifiedName& tagName, Document* doc)
43     : WMLElement(tagName, doc)
44     , m_columns(0)
45 {
46 }
47 
create(const QualifiedName & tagName,Document * document)48 PassRefPtr<WMLTableElement> WMLTableElement::create(const QualifiedName& tagName, Document* document)
49 {
50     return adoptRef(new WMLTableElement(tagName, document));
51 }
52 
~WMLTableElement()53 WMLTableElement::~WMLTableElement()
54 {
55 }
56 
mapToEntry(const QualifiedName & attrName,MappedAttributeEntry & result) const57 bool WMLTableElement::mapToEntry(const QualifiedName& attrName, MappedAttributeEntry& result) const
58 {
59     if (attrName == HTMLNames::alignAttr) {
60         result = eTable;
61         return false;
62     }
63 
64     return WMLElement::mapToEntry(attrName, result);
65 }
66 
parseMappedAttribute(Attribute * attr)67 void WMLTableElement::parseMappedAttribute(Attribute* attr)
68 {
69     if (attr->name() == columnsAttr) {
70         bool isNumber = false;
71         m_columns = attr->value().string().toUIntStrict(&isNumber);
72 
73         // Spec: This required attribute specifies the number of columns for the table.
74         // The user agent must create a table with exactly the number of columns specified
75         // by the attribute value. It is an error to specify a value of zero ("0")
76         if (!m_columns || !isNumber)
77             reportWMLError(document(), WMLErrorInvalidColumnsNumberInTable);
78     } else if (attr->name() == HTMLNames::alignAttr)
79         m_alignment = parseValueForbiddingVariableReferences(attr->value());
80     else
81         WMLElement::parseMappedAttribute(attr);
82 }
83 
finishParsingChildren()84 void WMLTableElement::finishParsingChildren()
85 {
86     WMLElement::finishParsingChildren();
87 
88     if (!m_columns) {
89         reportWMLError(document(), WMLErrorInvalidColumnsNumberInTable);
90         return;
91     }
92 
93     Vector<WMLElement*> rowElements = scanTableChildElements(this, trTag);
94     if (rowElements.isEmpty())
95         return;
96 
97     Vector<WMLElement*>::iterator it = rowElements.begin();
98     Vector<WMLElement*>::iterator end = rowElements.end();
99 
100     for (; it != end; ++it) {
101         WMLElement* rowElement = (*it);
102 
103         // Squeeze the table to fit in the desired number of columns
104         Vector<WMLElement*> columnElements = scanTableChildElements(rowElement, tdTag);
105         unsigned actualNumberOfColumns = columnElements.size();
106 
107         if (actualNumberOfColumns > m_columns) {
108             joinSuperflousColumns(columnElements, rowElement);
109             columnElements = scanTableChildElements(rowElement, tdTag);
110         } else if (actualNumberOfColumns < m_columns) {
111             padWithEmptyColumns(columnElements, rowElement);
112             columnElements = scanTableChildElements(rowElement, tdTag);
113         }
114 
115         // Layout cells according to the 'align' attribute
116         alignCells(columnElements, rowElement);
117     }
118 }
119 
scanTableChildElements(WMLElement * startElement,const QualifiedName & tagName) const120 Vector<WMLElement*> WMLTableElement::scanTableChildElements(WMLElement* startElement, const QualifiedName& tagName) const
121 {
122     Vector<WMLElement*> childElements;
123 
124     RefPtr<NodeList> children = startElement->childNodes();
125     if (!children)
126         return childElements;
127 
128     unsigned length = children->length();
129     for (unsigned i = 0; i < length; ++i) {
130         Node* child = children->item(i);
131         if (child->hasTagName(tagName))
132             childElements.append(static_cast<WMLElement*>(child));
133     }
134 
135     return childElements;
136 }
137 
transferAllChildrenOfElementToTargetElement(WMLElement * sourceElement,WMLElement * targetElement,unsigned startOffset) const138 void WMLTableElement::transferAllChildrenOfElementToTargetElement(WMLElement* sourceElement, WMLElement* targetElement, unsigned startOffset) const
139 {
140     RefPtr<NodeList> children = sourceElement->childNodes();
141     if (!children)
142         return;
143 
144     ExceptionCode ec = 0;
145 
146     unsigned length = children->length();
147     for (unsigned i = startOffset; i < length; ++i) {
148         RefPtr<Node> clonedNode = children->item(i)->cloneNode(true);
149         targetElement->appendChild(clonedNode.release(), ec);
150         ASSERT(ec == 0);
151     }
152 }
153 
tryMergeAdjacentTextCells(Node * item,Node * nextItem) const154 bool WMLTableElement::tryMergeAdjacentTextCells(Node* item, Node* nextItem) const
155 {
156     if (!item || !nextItem)
157         return false;
158 
159     if (!item->isTextNode() || !nextItem->isTextNode())
160         return false;
161 
162     Text* itemText = static_cast<Text*>(item);
163     Text* nextItemText = static_cast<Text*>(nextItem);
164 
165     String newContent = " ";
166     newContent += nextItemText->data();
167 
168     ExceptionCode ec = 0;
169     itemText->appendData(newContent, ec);
170     ASSERT(ec == 0);
171 
172     return true;
173 }
174 
joinSuperflousColumns(Vector<WMLElement * > & columnElements,WMLElement * rowElement) const175 void WMLTableElement::joinSuperflousColumns(Vector<WMLElement*>& columnElements, WMLElement* rowElement) const
176 {
177     // Spec: If the actual number of columns in a row is greater than the value specified
178     // by this attribute, the extra columns of the row must be aggregated into the last
179     // column such that the row contains exactly the number of columns specified.
180     WMLElement* lastColumn = columnElements.at(m_columns - 1);
181     ASSERT(lastColumn);
182 
183     // Merge superflous columns into a single one
184     RefPtr<WMLElement> newCell = WMLElement::create(tdTag, document());
185     transferAllChildrenOfElementToTargetElement(lastColumn, newCell.get(), 0);
186 
187     ExceptionCode ec = 0;
188     unsigned actualNumberOfColumns = columnElements.size();
189 
190     for (unsigned i = m_columns; i < actualNumberOfColumns; ++i) {
191         WMLElement* columnElement = columnElements.at(i);
192         unsigned startOffset = 0;
193 
194         // Spec: A single inter-word space must be inserted between two cells that are being aggregated.
195         if (tryMergeAdjacentTextCells(newCell->lastChild(), columnElement->firstChild()))
196             ++startOffset;
197 
198         transferAllChildrenOfElementToTargetElement(columnElement, newCell.get(), startOffset);
199     }
200 
201     // Remove the columns, that have just been merged
202     unsigned i = actualNumberOfColumns;
203     for (; i > m_columns; --i) {
204         rowElement->removeChild(columnElements.at(i - 1), ec);
205         ASSERT(ec == 0);
206     }
207 
208     // Replace the last column in the row with the new merged column
209     rowElement->replaceChild(newCell.release(), lastColumn, ec);
210     ASSERT(ec == 0);
211 }
212 
padWithEmptyColumns(Vector<WMLElement * > & columnElements,WMLElement * rowElement) const213 void WMLTableElement::padWithEmptyColumns(Vector<WMLElement*>& columnElements, WMLElement* rowElement) const
214 {
215     // Spec: If the actual number of columns in a row is less than the value specified by the columns
216     // attribute, the row must be padded with empty columns effectively as if the user agent
217     // appended empty td elements to the row.
218     ExceptionCode ec = 0;
219 
220     for (unsigned i = columnElements.size(); i < m_columns; ++i) {
221         RefPtr<WMLElement> newCell = WMLElement::create(tdTag, document());
222         rowElement->appendChild(newCell.release(), ec);
223         ASSERT(ec == 0);
224     }
225 }
226 
alignCells(Vector<WMLElement * > & columnElements,WMLElement * rowElement) const227 void WMLTableElement::alignCells(Vector<WMLElement*>& columnElements, WMLElement* rowElement) const
228 {
229     // Spec: User agents should consider the current language when determining
230     // the default alignment and the direction of the table.
231     bool rtl = false;
232     if (RenderObject* renderer = rowElement->renderer()) {
233         if (RenderStyle* style = renderer->style())
234             rtl = !style->isLeftToRightDirection();
235     }
236 
237     rowElement->setAttribute(HTMLNames::alignAttr, rtl ? "right" : "left");
238 
239     if (m_alignment.isEmpty())
240         return;
241 
242     unsigned alignLength = m_alignment.length();
243 
244     Vector<WMLElement*>::iterator it = columnElements.begin();
245     Vector<WMLElement*>::iterator end = columnElements.end();
246 
247     for (unsigned i = 0; it != end; ++it, ++i) {
248         if (i == alignLength)
249             break;
250 
251         String alignmentValue;
252         switch (m_alignment[i]) {
253         case 'C':
254             alignmentValue = "center";
255             break;
256         case 'L':
257             alignmentValue = "left";
258             break;
259         case 'R':
260             alignmentValue = "right";
261             break;
262         default:
263             break;
264         }
265 
266         if (alignmentValue.isEmpty())
267             continue;
268 
269         (*it)->setAttribute(HTMLNames::alignAttr, alignmentValue);
270     }
271 }
272 
273 }
274 
275 #endif
276