summaryrefslogtreecommitdiff
path: root/Source/WebCore/accessibility/AccessibilityTable.cpp
diff options
context:
space:
mode:
authorLorry Tar Creator <lorry-tar-importer@lorry>2017-06-27 06:07:23 +0000
committerLorry Tar Creator <lorry-tar-importer@lorry>2017-06-27 06:07:23 +0000
commit1bf1084f2b10c3b47fd1a588d85d21ed0eb41d0c (patch)
tree46dcd36c86e7fbc6e5df36deb463b33e9967a6f7 /Source/WebCore/accessibility/AccessibilityTable.cpp
parent32761a6cee1d0dee366b885b7b9c777e67885688 (diff)
downloadWebKitGtk-tarball-master.tar.gz
Diffstat (limited to 'Source/WebCore/accessibility/AccessibilityTable.cpp')
-rw-r--r--Source/WebCore/accessibility/AccessibilityTable.cpp351
1 files changed, 229 insertions, 122 deletions
diff --git a/Source/WebCore/accessibility/AccessibilityTable.cpp b/Source/WebCore/accessibility/AccessibilityTable.cpp
index d67783672..e5bb16978 100644
--- a/Source/WebCore/accessibility/AccessibilityTable.cpp
+++ b/Source/WebCore/accessibility/AccessibilityTable.cpp
@@ -10,7 +10,7 @@
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
- * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
+ * 3. Neither the name of Apple Inc. ("Apple") nor the names of
* its contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
@@ -44,14 +44,16 @@
#include "RenderTableCell.h"
#include "RenderTableSection.h"
+#include <wtf/Deque.h>
+
namespace WebCore {
using namespace HTMLNames;
AccessibilityTable::AccessibilityTable(RenderObject* renderer)
: AccessibilityRenderObject(renderer)
- , m_headerContainer(0)
- , m_isAccessibilityTable(true)
+ , m_headerContainer(nullptr)
+ , m_isExposableThroughAccessibility(true)
{
}
@@ -62,12 +64,12 @@ AccessibilityTable::~AccessibilityTable()
void AccessibilityTable::init()
{
AccessibilityRenderObject::init();
- m_isAccessibilityTable = isTableExposableThroughAccessibility();
+ m_isExposableThroughAccessibility = computeIsTableExposableThroughAccessibility();
}
-PassRefPtr<AccessibilityTable> AccessibilityTable::create(RenderObject* renderer)
+Ref<AccessibilityTable> AccessibilityTable::create(RenderObject* renderer)
{
- return adoptRef(new AccessibilityTable(renderer));
+ return adoptRef(*new AccessibilityTable(renderer));
}
bool AccessibilityTable::hasARIARole() const
@@ -82,14 +84,34 @@ bool AccessibilityTable::hasARIARole() const
return false;
}
-bool AccessibilityTable::isAccessibilityTable() const
+bool AccessibilityTable::isExposableThroughAccessibility() const
{
if (!m_renderer)
return false;
- return m_isAccessibilityTable;
+ return m_isExposableThroughAccessibility;
}
+HTMLTableElement* AccessibilityTable::tableElement() const
+{
+ if (!is<RenderTable>(*m_renderer))
+ return nullptr;
+
+ RenderTable& table = downcast<RenderTable>(*m_renderer);
+ if (is<HTMLTableElement>(table.element()))
+ return downcast<HTMLTableElement>(table.element());
+
+ table.forceSectionsRecalc();
+
+ // If the table has a display:table-row-group, then the RenderTable does not have a pointer to it's HTMLTableElement.
+ // We can instead find it by asking the firstSection for its parent.
+ RenderTableSection* firstBody = table.firstBody();
+ if (!firstBody || !firstBody->element())
+ return nullptr;
+
+ return ancestorsOfType<HTMLTableElement>(*(firstBody->element())).first();
+}
+
bool AccessibilityTable::isDataTable() const
{
if (!m_renderer)
@@ -105,34 +127,38 @@ bool AccessibilityTable::isDataTable() const
if (node() && node()->hasEditableStyle())
return true;
+ if (!is<RenderTable>(*m_renderer))
+ return false;
+
// This employs a heuristic to determine if this table should appear.
// Only "data" tables should be exposed as tables.
// Unfortunately, there is no good way to determine the difference
// between a "layout" table and a "data" table.
-
- RenderTable* table = toRenderTable(m_renderer);
- if (!table->element() || !isHTMLTableElement(table->element()))
- return false;
-
- // if there is a caption element, summary, THEAD, or TFOOT section, it's most certainly a data table
- HTMLTableElement* tableElement = toHTMLTableElement(table->element());
- if (!tableElement->summary().isEmpty() || tableElement->tHead() || tableElement->tFoot() || tableElement->caption())
- return true;
-
- // if someone used "rules" attribute than the table should appear
- if (!tableElement->rules().isEmpty())
- return true;
-
- // if there's a colgroup or col element, it's probably a data table.
- for (const auto& child : childrenOfType<Element>(*tableElement)) {
- if (child.hasTagName(colTag) || child.hasTagName(colgroupTag))
+ if (HTMLTableElement* tableElement = this->tableElement()) {
+ // If there is a caption element, summary, THEAD, or TFOOT section, it's most certainly a data table.
+ if (!tableElement->summary().isEmpty() || tableElement->tHead() || tableElement->tFoot() || tableElement->caption())
return true;
+
+ // If someone used "rules" attribute than the table should appear.
+ if (!tableElement->rules().isEmpty())
+ return true;
+
+ // If there's a colgroup or col element, it's probably a data table.
+ for (const auto& child : childrenOfType<HTMLElement>(*tableElement)) {
+ if (child.hasTagName(colTag) || child.hasTagName(colgroupTag))
+ return true;
+ }
}
+ // The following checks should only apply if this is a real <table> element.
+ if (!hasTagName(tableTag))
+ return false;
+
+ RenderTable& table = downcast<RenderTable>(*m_renderer);
// go through the cell's and check for tell-tale signs of "data" table status
// cells have borders, or use attributes like headers, abbr, scope or axis
- table->recalcSectionsIfNeeded();
- RenderTableSection* firstBody = table->firstBody();
+ table.recalcSectionsIfNeeded();
+ RenderTableSection* firstBody = table.firstBody();
if (!firstBody)
return false;
@@ -148,7 +174,7 @@ bool AccessibilityTable::isDataTable() const
return true;
// Store the background color of the table to check against cell's background colors.
- const RenderStyle& tableStyle = table->style();
+ const RenderStyle& tableStyle = table.style();
Color tableBGColor = tableStyle.visitedDependentColor(CSSPropertyBackgroundColor);
// check enough of the cells to find if the table matches our criteria
@@ -183,22 +209,22 @@ bool AccessibilityTable::isDataTable() const
if (cell->width() < 1 || cell->height() < 1)
continue;
- validCellCount++;
+ ++validCellCount;
bool isTHCell = cellElement->hasTagName(thTag);
// If the first row is comprised of all <th> tags, assume it is a data table.
if (!row && isTHCell)
- headersInFirstRowCount++;
+ ++headersInFirstRowCount;
// If the first column is comprised of all <th> tags, assume it is a data table.
if (!col && isTHCell)
- headersInFirstColumnCount++;
+ ++headersInFirstColumnCount;
// In this case, the developer explicitly assigned a "data" table attribute.
- if (cellElement->hasTagName(tdTag) || cellElement->hasTagName(thTag)) {
- HTMLTableCellElement* tableCellElement = toHTMLTableCellElement(cellElement);
- if (!tableCellElement->headers().isEmpty() || !tableCellElement->abbr().isEmpty()
- || !tableCellElement->axis().isEmpty() || !tableCellElement->scope().isEmpty())
+ if (is<HTMLTableCellElement>(*cellElement)) {
+ HTMLTableCellElement& tableCellElement = downcast<HTMLTableCellElement>(*cellElement);
+ if (!tableCellElement.headers().isEmpty() || !tableCellElement.abbr().isEmpty()
+ || !tableCellElement.axis().isEmpty() || !tableCellElement.scope().isEmpty())
return true;
}
const RenderStyle& renderStyle = cell->style();
@@ -210,25 +236,25 @@ bool AccessibilityTable::isDataTable() const
// If a cell has matching bordered sides, call it a (fully) bordered cell.
if ((cell->borderTop() > 0 && cell->borderBottom() > 0)
|| (cell->borderLeft() > 0 && cell->borderRight() > 0))
- borderedCellCount++;
+ ++borderedCellCount;
// Also keep track of each individual border, so we can catch tables where most
// cells have a bottom border, for example.
if (cell->borderTop() > 0)
- cellsWithTopBorder++;
+ ++cellsWithTopBorder;
if (cell->borderBottom() > 0)
- cellsWithBottomBorder++;
+ ++cellsWithBottomBorder;
if (cell->borderLeft() > 0)
- cellsWithLeftBorder++;
+ ++cellsWithLeftBorder;
if (cell->borderRight() > 0)
- cellsWithRightBorder++;
+ ++cellsWithRightBorder;
// If the cell has a different color from the table and there is cell spacing,
// then it is probably a data table cell (spacing and colors take the place of borders).
Color cellColor = renderStyle.visitedDependentColor(CSSPropertyBackgroundColor);
- if (table->hBorderSpacing() > 0 && table->vBorderSpacing() > 0
+ if (table.hBorderSpacing() > 0 && table.vBorderSpacing() > 0
&& tableBGColor != cellColor && cellColor.alpha() != 1)
- backgroundDifferenceCellCount++;
+ ++backgroundDifferenceCellCount;
// If we've found 10 "good" cells, we don't need to keep searching.
if (borderedCellCount >= 10 || backgroundDifferenceCellCount >= 10)
@@ -236,13 +262,13 @@ bool AccessibilityTable::isDataTable() const
// For the first 5 rows, cache the background color so we can check if this table has zebra-striped rows.
if (row < 5 && row == alternatingRowColorCount) {
- RenderObject* renderRow = cell->parent();
- if (!renderRow || !renderRow->isBoxModelObject() || !toRenderBoxModelObject(renderRow)->isTableRow())
+ RenderElement* renderRow = cell->parent();
+ if (!is<RenderTableRow>(renderRow))
continue;
const RenderStyle& rowRenderStyle = renderRow->style();
Color rowColor = rowRenderStyle.visitedDependentColor(CSSPropertyBackgroundColor);
alternatingRowColors[alternatingRowColorCount] = rowColor;
- alternatingRowColorCount++;
+ ++alternatingRowColorCount;
}
}
@@ -287,7 +313,7 @@ bool AccessibilityTable::isDataTable() const
return false;
}
-bool AccessibilityTable::isTableExposableThroughAccessibility() const
+bool AccessibilityTable::computeIsTableExposableThroughAccessibility() const
{
// The following is a heuristic used to determine if a
// <table> should be exposed as an AXTable. The goal
@@ -302,12 +328,6 @@ bool AccessibilityTable::isTableExposableThroughAccessibility() const
if (hasARIARole())
return false;
- // Gtk+ ATs expect all tables to be exposed as tables.
-#if PLATFORM(GTK) || PLATFORM(EFL)
- Element* tableNode = toRenderTable(m_renderer)->element();
- return tableNode && isHTMLTableElement(tableNode);
-#endif
-
return isDataTable();
}
@@ -319,13 +339,13 @@ void AccessibilityTable::clearChildren()
if (m_headerContainer) {
m_headerContainer->detachFromParent();
- m_headerContainer = 0;
+ m_headerContainer = nullptr;
}
}
void AccessibilityTable::addChildren()
{
- if (!isAccessibilityTable()) {
+ if (!isExposableThroughAccessibility()) {
AccessibilityRenderObject::addChildren();
return;
}
@@ -333,68 +353,120 @@ void AccessibilityTable::addChildren()
ASSERT(!m_haveChildren);
m_haveChildren = true;
- if (!m_renderer || !m_renderer->isTable())
+ if (!is<RenderTable>(m_renderer))
return;
- RenderTable* table = toRenderTable(m_renderer);
- AXObjectCache* axCache = m_renderer->document().axObjectCache();
-
+ RenderTable& table = downcast<RenderTable>(*m_renderer);
// Go through all the available sections to pull out the rows and add them as children.
- table->recalcSectionsIfNeeded();
- RenderTableSection* tableSection = table->topSection();
- if (!tableSection)
- return;
+ table.recalcSectionsIfNeeded();
- unsigned maxColumnCount = 0;
- while (tableSection) {
-
- HashSet<AccessibilityObject*> appendedRows;
- unsigned numRows = tableSection->numRows();
- for (unsigned rowIndex = 0; rowIndex < numRows; ++rowIndex) {
-
- RenderTableRow* renderRow = tableSection->rowRendererAt(rowIndex);
- if (!renderRow)
- continue;
-
- AccessibilityObject* rowObject = axCache->getOrCreate(renderRow);
- if (!rowObject->isTableRow())
- continue;
-
- AccessibilityTableRow* row = toAccessibilityTableRow(rowObject);
- // We need to check every cell for a new row, because cell spans
- // can cause us to miss rows if we just check the first column.
- if (appendedRows.contains(row))
- continue;
-
- row->setRowIndex(static_cast<int>(m_rows.size()));
- m_rows.append(row);
- if (!row->accessibilityIsIgnored())
- m_children.append(row);
-#if PLATFORM(GTK) || PLATFORM(EFL)
- else
- m_children.appendVector(row->children());
-#endif
- appendedRows.add(row);
+ if (HTMLTableElement* tableElement = this->tableElement()) {
+ if (HTMLTableCaptionElement* caption = tableElement->caption()) {
+ AccessibilityObject* axCaption = axObjectCache()->getOrCreate(caption);
+ if (axCaption && !axCaption->accessibilityIsIgnored())
+ m_children.append(axCaption);
}
+ }
+
+ unsigned maxColumnCount = 0;
+ RenderTableSection* footer = table.footer();
- maxColumnCount = std::max(tableSection->numColumns(), maxColumnCount);
- tableSection = table->sectionBelow(tableSection, SkipEmptySections);
+ for (RenderTableSection* tableSection = table.topSection(); tableSection; tableSection = table.sectionBelow(tableSection, SkipEmptySections)) {
+ if (tableSection == footer)
+ continue;
+ addChildrenFromSection(tableSection, maxColumnCount);
}
+ // Process the footer last, in case it was ordered earlier in the DOM.
+ if (footer)
+ addChildrenFromSection(footer, maxColumnCount);
+
+ AXObjectCache* axCache = m_renderer->document().axObjectCache();
// make the columns based on the number of columns in the first body
unsigned length = maxColumnCount;
for (unsigned i = 0; i < length; ++i) {
- AccessibilityTableColumn* column = toAccessibilityTableColumn(axCache->getOrCreate(ColumnRole));
- column->setColumnIndex((int)i);
- column->setParent(this);
- m_columns.append(column);
- if (!column->accessibilityIsIgnored())
- m_children.append(column);
+ auto& column = downcast<AccessibilityTableColumn>(*axCache->getOrCreate(ColumnRole));
+ column.setColumnIndex((int)i);
+ column.setParent(this);
+ m_columns.append(&column);
+ if (!column.accessibilityIsIgnored())
+ m_children.append(&column);
}
AccessibilityObject* headerContainerObject = headerContainer();
if (headerContainerObject && !headerContainerObject->accessibilityIsIgnored())
m_children.append(headerContainerObject);
+
+ // Sometimes the cell gets the wrong role initially because it is created before the parent
+ // determines whether it is an accessibility table. Iterate all the cells and allow them to
+ // update their roles now that the table knows its status.
+ // see bug: https://bugs.webkit.org/show_bug.cgi?id=147001
+ for (const auto& row : m_rows) {
+ for (const auto& cell : row->children())
+ cell->updateAccessibilityRole();
+ }
+
+}
+
+void AccessibilityTable::addTableCellChild(AccessibilityObject* rowObject, HashSet<AccessibilityObject*>& appendedRows, unsigned& columnCount)
+{
+ if (!rowObject || !is<AccessibilityTableRow>(*rowObject))
+ return;
+
+ auto& row = downcast<AccessibilityTableRow>(*rowObject);
+ // We need to check every cell for a new row, because cell spans
+ // can cause us to miss rows if we just check the first column.
+ if (appendedRows.contains(&row))
+ return;
+
+ row.setRowIndex(static_cast<int>(m_rows.size()));
+ m_rows.append(&row);
+ if (!row.accessibilityIsIgnored())
+ m_children.append(&row);
+ appendedRows.add(&row);
+
+ // store the maximum number of columns
+ unsigned rowCellCount = row.children().size();
+ if (rowCellCount > columnCount)
+ columnCount = rowCellCount;
+}
+
+void AccessibilityTable::addChildrenFromSection(RenderTableSection* tableSection, unsigned& maxColumnCount)
+{
+ ASSERT(tableSection);
+ if (!tableSection)
+ return;
+
+ AXObjectCache* axCache = m_renderer->document().axObjectCache();
+ HashSet<AccessibilityObject*> appendedRows;
+ unsigned numRows = tableSection->numRows();
+ for (unsigned rowIndex = 0; rowIndex < numRows; ++rowIndex) {
+
+ RenderTableRow* renderRow = tableSection->rowRendererAt(rowIndex);
+ if (!renderRow)
+ continue;
+
+ AccessibilityObject& rowObject = *axCache->getOrCreate(renderRow);
+
+ // If the row is anonymous, we should dive deeper into the descendants to try to find a valid row.
+ if (renderRow->isAnonymous()) {
+ Deque<AccessibilityObject*> queue;
+ queue.append(&rowObject);
+
+ while (!queue.isEmpty()) {
+ AccessibilityObject* obj = queue.takeFirst();
+ if (obj->node() && is<AccessibilityTableRow>(*obj)) {
+ addTableCellChild(obj, appendedRows, maxColumnCount);
+ continue;
+ }
+ for (auto* child = obj->firstChild(); child; child = child->nextSibling())
+ queue.append(child);
+ }
+ } else
+ addTableCellChild(&rowObject, appendedRows, maxColumnCount);
+ }
+
+ maxColumnCount = std::max(tableSection->numColumns(), maxColumnCount);
}
AccessibilityObject* AccessibilityTable::headerContainer()
@@ -402,10 +474,10 @@ AccessibilityObject* AccessibilityTable::headerContainer()
if (m_headerContainer)
return m_headerContainer.get();
- AccessibilityMockObject* tableHeader = toAccessibilityMockObject(axObjectCache()->getOrCreate(TableHeaderContainerRole));
- tableHeader->setParent(this);
+ auto& tableHeader = downcast<AccessibilityMockObject>(*axObjectCache()->getOrCreate(TableHeaderContainerRole));
+ tableHeader.setParent(this);
- m_headerContainer = tableHeader;
+ m_headerContainer = &tableHeader;
return m_headerContainer.get();
}
@@ -430,8 +502,11 @@ void AccessibilityTable::columnHeaders(AccessibilityChildrenVector& headers)
updateChildrenIfNecessary();
- for (const auto& column : m_columns) {
- if (AccessibilityObject* header = toAccessibilityTableColumn(column.get())->headerObject())
+ // Sometimes m_columns can be reset during the iteration, we cache it here to be safe.
+ AccessibilityChildrenVector columnsCopy = m_columns;
+
+ for (const auto& column : columnsCopy) {
+ if (AccessibilityObject* header = downcast<AccessibilityTableColumn>(*column).headerObject())
headers.append(header);
}
}
@@ -443,8 +518,11 @@ void AccessibilityTable::rowHeaders(AccessibilityChildrenVector& headers)
updateChildrenIfNecessary();
- for (const auto& row : m_rows) {
- if (AccessibilityObject* header = toAccessibilityTableRow(row.get())->headerObject())
+ // Sometimes m_rows can be reset during the iteration, we cache it here to be safe.
+ AccessibilityChildrenVector rowsCopy = m_rows;
+
+ for (const auto& row : rowsCopy) {
+ if (AccessibilityObject* header = downcast<AccessibilityTableRow>(*row).headerObject())
headers.append(header);
}
}
@@ -491,7 +569,7 @@ int AccessibilityTable::tableLevel() const
{
int level = 0;
for (AccessibilityObject* obj = static_cast<AccessibilityObject*>(const_cast<AccessibilityTable*>(this)); obj; obj = obj->parentObject()) {
- if (obj->isAccessibilityTable())
+ if (is<AccessibilityTable>(*obj) && downcast<AccessibilityTable>(*obj).isExposableThroughAccessibility())
++level;
}
@@ -502,7 +580,7 @@ AccessibilityTableCell* AccessibilityTable::cellForColumnAndRow(unsigned column,
{
updateChildrenIfNecessary();
if (column >= columnCount() || row >= rowCount())
- return 0;
+ return nullptr;
// Iterate backwards through the rows in case the desired cell has a rowspan and exists in a previous row.
for (unsigned rowIndexCounter = row + 1; rowIndexCounter > 0; --rowIndexCounter) {
@@ -513,29 +591,33 @@ AccessibilityTableCell* AccessibilityTable::cellForColumnAndRow(unsigned column,
for (unsigned colIndexCounter = std::min(static_cast<unsigned>(children.size()), column + 1); colIndexCounter > 0; --colIndexCounter) {
unsigned colIndex = colIndexCounter - 1;
AccessibilityObject* child = children[colIndex].get();
- ASSERT(child->isTableCell());
- if (!child->isTableCell())
+ ASSERT(is<AccessibilityTableCell>(*child));
+ if (!is<AccessibilityTableCell>(*child))
continue;
std::pair<unsigned, unsigned> columnRange;
std::pair<unsigned, unsigned> rowRange;
- AccessibilityTableCell* tableCellChild = toAccessibilityTableCell(child);
- tableCellChild->columnIndexRange(columnRange);
- tableCellChild->rowIndexRange(rowRange);
+ auto& tableCellChild = downcast<AccessibilityTableCell>(*child);
+ tableCellChild.columnIndexRange(columnRange);
+ tableCellChild.rowIndexRange(rowRange);
if ((column >= columnRange.first && column < (columnRange.first + columnRange.second))
&& (row >= rowRange.first && row < (rowRange.first + rowRange.second)))
- return tableCellChild;
+ return &tableCellChild;
}
}
- return 0;
+ return nullptr;
}
AccessibilityRole AccessibilityTable::roleValue() const
{
- if (!isAccessibilityTable())
+ if (!isExposableThroughAccessibility())
return AccessibilityRenderObject::roleValue();
+
+ AccessibilityRole ariaRole = ariaRoleAttribute();
+ if (ariaRole == GridRole || ariaRole == TreeGridRole)
+ return GridRole;
return TableRole;
}
@@ -548,7 +630,7 @@ bool AccessibilityTable::computeAccessibilityIsIgnored() const
if (decision == IgnoreObject)
return true;
- if (!isAccessibilityTable())
+ if (!isExposableThroughAccessibility())
return AccessibilityRenderObject::computeAccessibilityIsIgnored();
return false;
@@ -563,7 +645,7 @@ void AccessibilityTable::titleElementText(Vector<AccessibilityText>& textOrder)
String AccessibilityTable::title() const
{
- if (!isAccessibilityTable())
+ if (!isExposableThroughAccessibility())
return AccessibilityRenderObject::title();
String title;
@@ -572,9 +654,8 @@ String AccessibilityTable::title() const
// see if there is a caption
Node* tableElement = m_renderer->node();
- if (tableElement && isHTMLTableElement(tableElement)) {
- HTMLTableCaptionElement* caption = toHTMLTableElement(tableElement)->caption();
- if (caption)
+ if (is<HTMLTableElement>(tableElement)) {
+ if (HTMLTableCaptionElement* caption = downcast<HTMLTableElement>(*tableElement).caption())
title = caption->innerText();
}
@@ -585,4 +666,30 @@ String AccessibilityTable::title() const
return title;
}
+int AccessibilityTable::ariaColumnCount() const
+{
+ const AtomicString& colCountValue = getAttribute(aria_colcountAttr);
+
+ int colCountInt = colCountValue.toInt();
+ // If only a portion of the columns is present in the DOM at a given moment, this attribute is needed to
+ // provide an explicit indication of the number of columns in the full table.
+ if (colCountInt > (int)m_columns.size())
+ return colCountInt;
+
+ return -1;
+}
+
+int AccessibilityTable::ariaRowCount() const
+{
+ const AtomicString& rowCountValue = getAttribute(aria_rowcountAttr);
+
+ int rowCountInt = rowCountValue.toInt();
+ // If only a portion of the rows is present in the DOM at a given moment, this attribute is needed to
+ // provide an explicit indication of the number of rows in the full table.
+ if (rowCountInt > (int)m_rows.size())
+ return rowCountInt;
+
+ return -1;
+}
+
} // namespace WebCore